From a4af4246cd7a8e9b282dc93363cfa09b8afca469 Mon Sep 17 00:00:00 2001 From: Swarvanu Sengupta Date: Wed, 15 Aug 2018 18:28:53 +0900 Subject: [PATCH 1/8] Add initial tracing Signed-off-by: Swarvanu Sengupta --- ci/script/buildlib.sh | 2 +- example/stack.yml | 4 + sdk/chain.go | 2 +- sdk/function.go | 10 +- sdk/phase.go | 2 +- sdk/request.go | 2 +- template/faaschain/handle_chain.go | 339 +++ template/faaschain/main.go | 317 +-- template/faaschain/tracer.go | 119 ++ .../codahale/hdrhistogram/.travis.yml | 5 + .../github.com/codahale/hdrhistogram/LICENSE | 21 + .../codahale/hdrhistogram/README.md | 15 + .../github.com/codahale/hdrhistogram/hdr.go | 564 +++++ .../codahale/hdrhistogram/hdr_test.go | 388 ++++ .../codahale/hdrhistogram/window.go | 45 + .../codahale/hdrhistogram/window_test.go | 64 + .../opentracing-go/.github/ISSUE_TEMPLATE.md | 23 + .../opentracing/opentracing-go/.gitignore | 13 + .../opentracing/opentracing-go/.travis.yml | 15 + .../opentracing/opentracing-go/CHANGELOG.md | 14 + .../opentracing/opentracing-go/LICENSE | 201 ++ .../opentracing/opentracing-go/Makefile | 32 + .../opentracing/opentracing-go/README.md | 171 ++ .../opentracing/opentracing-go/ext/tags.go | 210 ++ .../opentracing-go/ext/tags_test.go | 148 ++ .../opentracing-go/globaltracer.go | 32 + .../opentracing/opentracing-go/gocontext.go | 54 + .../opentracing-go/gocontext_test.go | 81 + .../opentracing-go/harness/api_checkers.go | 472 +++++ .../opentracing-go/harness/noop_api_test.go | 17 + .../opentracing/opentracing-go/log/field.go | 269 +++ .../opentracing-go/log/field_test.go | 51 + .../opentracing/opentracing-go/log/util.go | 54 + .../mocktracer/mocklogrecord.go | 105 + .../opentracing-go/mocktracer/mockspan.go | 284 +++ .../opentracing-go/mocktracer/mocktracer.go | 105 + .../mocktracer/mocktracer_test.go | 284 +++ .../opentracing-go/mocktracer/propagation.go | 120 ++ .../opentracing/opentracing-go/noop.go | 64 + .../opentracing-go/options_test.go | 31 + .../opentracing/opentracing-go/propagation.go | 176 ++ .../opentracing-go/propagation_test.go | 93 + .../opentracing/opentracing-go/span.go | 189 ++ .../opentracing-go/testtracer_test.go | 138 ++ .../opentracing/opentracing-go/tracer.go | 305 +++ .../vendor/github.com/pkg/errors/.gitignore | 24 + .../vendor/github.com/pkg/errors/.travis.yml | 14 + .../vendor/github.com/pkg/errors/LICENSE | 23 + .../vendor/github.com/pkg/errors/README.md | 52 + .../vendor/github.com/pkg/errors/appveyor.yml | 32 + .../github.com/pkg/errors/bench_test.go | 63 + .../vendor/github.com/pkg/errors/errors.go | 269 +++ .../github.com/pkg/errors/errors_test.go | 225 ++ .../github.com/pkg/errors/example_test.go | 205 ++ .../github.com/pkg/errors/format_test.go | 535 +++++ .../vendor/github.com/pkg/errors/stack.go | 147 ++ .../github.com/pkg/errors/stack_test.go | 274 +++ .../github.com/s8sg/faaschain/sdk/chain.go | 2 +- .../github.com/s8sg/faaschain/sdk/function.go | 10 +- .../github.com/s8sg/faaschain/sdk/phase.go | 2 +- .../github.com/s8sg/faaschain/sdk/request.go | 2 +- .../.github/ISSUE_TEMPLATE.md | 28 + .../.github/PULL_REQUEST_TEMPLATE.md | 18 + .../uber/jaeger-client-go/.gitignore | 15 + .../uber/jaeger-client-go/.gitmodules | 3 + .../uber/jaeger-client-go/.travis.yml | 48 + .../uber/jaeger-client-go/CHANGELOG.md | 186 ++ .../uber/jaeger-client-go/CONTRIBUTING.md | 170 ++ .../github.com/uber/jaeger-client-go/DCO | 37 + .../uber/jaeger-client-go/Gopkg.lock | 164 ++ .../uber/jaeger-client-go/Gopkg.toml | 27 + .../github.com/uber/jaeger-client-go/LICENSE | 201 ++ .../github.com/uber/jaeger-client-go/Makefile | 117 ++ .../uber/jaeger-client-go/README.md | 269 +++ .../uber/jaeger-client-go/RELEASE.md | 11 + .../uber/jaeger-client-go/baggage_setter.go | 77 + .../jaeger-client-go/baggage_setter_test.go | 126 ++ .../uber/jaeger-client-go/config/config.go | 394 ++++ .../jaeger-client-go/config/config_env.go | 232 +++ .../jaeger-client-go/config/config_test.go | 607 ++++++ .../jaeger-client-go/config/example_test.go | 128 ++ .../uber/jaeger-client-go/config/options.go | 148 ++ .../jaeger-client-go/config/options_test.go | 107 + .../uber/jaeger-client-go/constants.go | 88 + .../uber/jaeger-client-go/constants_test.go | 29 + .../uber/jaeger-client-go/context.go | 258 +++ .../uber/jaeger-client-go/context_test.go | 110 + .../uber/jaeger-client-go/contrib_observer.go | 56 + .../jaeger-client-go/crossdock/.gitignore | 1 + .../jaeger-client-go/crossdock/Dockerfile | 10 + .../uber/jaeger-client-go/crossdock/README.md | 78 + .../crossdock/client/client.go | 107 + .../crossdock/client/client_test.go | 106 + .../crossdock/client/constants.go | 42 + .../crossdock/client/trace.go | 163 ++ .../crossdock/common/constants.go | 26 + .../jaeger-client-go/crossdock/common/json.go | 73 + .../crossdock/docker-compose.yml | 59 + .../crossdock/endtoend/handler.go | 156 ++ .../crossdock/endtoend/handler_test.go | 154 ++ .../jaeger-client-go/crossdock/log/logger.go | 29 + .../uber/jaeger-client-go/crossdock/main.go | 64 + .../uber/jaeger-client-go/crossdock/rules.mk | 32 + .../crossdock/server/constants.go | 25 + .../crossdock/server/server.go | 150 ++ .../crossdock/server/server_test.go | 87 + .../crossdock/server/trace.go | 99 + .../crossdock/thrift/.nocover | 0 .../crossdock/thrift/tracetest/constants.go | 18 + .../thrift/tracetest/tracedservice.go | 747 +++++++ .../crossdock/thrift/tracetest/ttypes.go | 1103 ++++++++++ .../github.com/uber/jaeger-client-go/doc.go | 24 + .../uber/jaeger-client-go/glide.lock | 89 + .../uber/jaeger-client-go/glide.yaml | 22 + .../uber/jaeger-client-go/header.go | 64 + .../uber/jaeger-client-go/header_test.go | 50 + .../uber/jaeger-client-go/idl/.gitignore | 5 + .../uber/jaeger-client-go/idl/.travis.yml | 10 + .../uber/jaeger-client-go/idl/LICENSE | 21 + .../uber/jaeger-client-go/idl/Makefile | 32 + .../uber/jaeger-client-go/idl/README.md | 7 + .../jaeger-client-go/idl/thrift/agent.thrift | 31 + .../idl/thrift/aggregation_validator.thrift | 31 + .../idl/thrift/baggage.thrift | 36 + .../idl/thrift/crossdock/tracetest.thrift | 47 + .../idl/thrift/dependency.thrift | 40 + .../jaeger-client-go/idl/thrift/jaeger.thrift | 87 + .../idl/thrift/sampling.thrift | 63 + .../idl/thrift/zipkincore.thrift | 300 +++ .../internal/baggage/remote/options.go | 101 + .../baggage/remote/restriction_manager.go | 157 ++ .../remote/restriction_manager_test.go | 220 ++ .../internal/baggage/restriction_manager.go | 71 + .../baggage/restriction_manager_test.go | 29 + .../jaeger-client-go/internal/spanlog/json.go | 81 + .../internal/throttler/remote/options.go | 99 + .../internal/throttler/remote/options_test.go | 50 + .../internal/throttler/remote/throttler.go | 216 ++ .../throttler/remote/throttler_test.go | 268 +++ .../internal/throttler/throttler.go | 32 + .../internal/throttler/throttler_test.go | 28 + .../uber/jaeger-client-go/interop.go | 55 + .../uber/jaeger-client-go/jaeger_tag.go | 84 + .../jaeger-client-go/jaeger_thrift_span.go | 179 ++ .../jaeger_thrift_span_test.go | 423 ++++ .../uber/jaeger-client-go/log/logger.go | 90 + .../uber/jaeger-client-go/log/logger_test.go | 32 + .../uber/jaeger-client-go/log/zap/field.go | 60 + .../jaeger-client-go/log/zap/field_test.go | 64 + .../uber/jaeger-client-go/log/zap/logger.go | 39 + .../jaeger-client-go/log/zap/logger_test.go | 35 + .../uber/jaeger-client-go/logger.go | 53 + .../uber/jaeger-client-go/logger_test.go | 40 + .../uber/jaeger-client-go/metrics.go | 107 + .../metrics/prometheus/metrics_test.go | 36 + .../uber/jaeger-client-go/metrics_test.go | 48 + .../uber/jaeger-client-go/observer.go | 88 + .../uber/jaeger-client-go/observer_test.go | 109 + .../uber/jaeger-client-go/process.go | 29 + .../uber/jaeger-client-go/propagation.go | 300 +++ .../uber/jaeger-client-go/propagation_test.go | 295 +++ .../uber/jaeger-client-go/reference.go | 23 + .../uber/jaeger-client-go/reporter.go | 289 +++ .../uber/jaeger-client-go/reporter_options.go | 69 + .../uber/jaeger-client-go/reporter_test.go | 378 ++++ .../jaeger-client-go/rpcmetrics/README.md | 5 + .../uber/jaeger-client-go/rpcmetrics/doc.go | 16 + .../jaeger-client-go/rpcmetrics/endpoints.go | 63 + .../rpcmetrics/endpoints_test.go | 43 + .../jaeger-client-go/rpcmetrics/metrics.go | 124 ++ .../rpcmetrics/metrics_test.go | 61 + .../jaeger-client-go/rpcmetrics/normalizer.go | 101 + .../rpcmetrics/normalizer_test.go | 34 + .../jaeger-client-go/rpcmetrics/observer.go | 171 ++ .../rpcmetrics/observer_test.go | 177 ++ .../uber/jaeger-client-go/sampler.go | 556 +++++ .../uber/jaeger-client-go/sampler_options.go | 81 + .../uber/jaeger-client-go/sampler_test.go | 690 +++++++ .../uber/jaeger-client-go/scripts/cover.sh | 60 + .../jaeger-client-go/scripts/updateLicense.py | 93 + .../scripts/updateLicenses.sh | 5 + .../github.com/uber/jaeger-client-go/span.go | 249 +++ .../uber/jaeger-client-go/span_test.go | 148 ++ .../jaeger-client-go/testutils/mock_agent.go | 189 ++ .../testutils/mock_agent_test.go | 93 + .../testutils/sampling_manager.go | 53 + .../testutils/udp_transport.go | 106 + .../testutils/udp_transport_test.go | 67 + .../thrift-gen/agent/agent.go | 411 ++++ .../thrift-gen/agent/constants.go | 23 + .../thrift-gen/agent/ttypes.go | 21 + .../baggage/baggagerestrictionmanager.go | 435 ++++ .../thrift-gen/baggage/constants.go | 18 + .../thrift-gen/baggage/ttypes.go | 154 ++ .../thrift-gen/jaeger/agent.go | 242 +++ .../thrift-gen/jaeger/constants.go | 18 + .../thrift-gen/jaeger/ttypes.go | 1838 +++++++++++++++++ .../thrift-gen/sampling/constants.go | 18 + .../thrift-gen/sampling/samplingmanager.go | 410 ++++ .../thrift-gen/sampling/ttypes.go | 873 ++++++++ .../thrift-gen/zipkincore/constants.go | 32 + .../thrift-gen/zipkincore/ttypes.go | 1247 +++++++++++ .../thrift-gen/zipkincore/zipkincollector.go | 446 ++++ .../uber/jaeger-client-go/thrift/.nocover | 0 .../uber/jaeger-client-go/thrift/README.md | 7 + .../thrift/application_exception.go | 142 ++ .../thrift/binary_protocol.go | 514 +++++ .../thrift/compact_protocol.go | 815 ++++++++ .../uber/jaeger-client-go/thrift/exception.go | 44 + .../jaeger-client-go/thrift/memory_buffer.go | 79 + .../jaeger-client-go/thrift/messagetype.go | 31 + .../uber/jaeger-client-go/thrift/numeric.go | 164 ++ .../uber/jaeger-client-go/thrift/processor.go | 30 + .../uber/jaeger-client-go/thrift/protocol.go | 175 ++ .../thrift/protocol_exception.go | 78 + .../thrift/protocol_factory.go | 25 + .../jaeger-client-go/thrift/rich_transport.go | 69 + .../jaeger-client-go/thrift/serializer.go | 75 + .../thrift/simple_json_protocol.go | 1337 ++++++++++++ .../uber/jaeger-client-go/thrift/transport.go | 68 + .../thrift/transport_exception.go | 90 + .../thrift/transport_factory.go | 39 + .../uber/jaeger-client-go/thrift/type.go | 69 + .../uber/jaeger-client-go/tracer.go | 431 ++++ .../uber/jaeger-client-go/tracer_options.go | 159 ++ .../uber/jaeger-client-go/tracer_test.go | 427 ++++ .../uber/jaeger-client-go/transport.go | 38 + .../uber/jaeger-client-go/transport/doc.go | 23 + .../uber/jaeger-client-go/transport/http.go | 155 ++ .../jaeger-client-go/transport/http_test.go | 156 ++ .../jaeger-client-go/transport/zipkin/doc.go | 17 + .../transport/zipkin/example_test.go | 46 + .../jaeger-client-go/transport/zipkin/http.go | 166 ++ .../transport/zipkin/http_test.go | 178 ++ .../uber/jaeger-client-go/transport_udp.go | 131 ++ .../jaeger-client-go/transport_udp_test.go | 220 ++ .../travis/build-crossdock.sh | 23 + .../travis/install-crossdock-deps.sh | 12 + .../uber/jaeger-client-go/utils/http_json.go | 54 + .../jaeger-client-go/utils/http_json_test.go | 58 + .../uber/jaeger-client-go/utils/localip.go | 84 + .../uber/jaeger-client-go/utils/rand.go | 46 + .../jaeger-client-go/utils/rate_limiter.go | 77 + .../utils/rate_limiter_test.go | 75 + .../uber/jaeger-client-go/utils/udp_client.go | 98 + .../uber/jaeger-client-go/utils/utils.go | 87 + .../uber/jaeger-client-go/utils/utils_test.go | 91 + .../uber/jaeger-client-go/zipkin.go | 76 + .../uber/jaeger-client-go/zipkin/README.md | 91 + .../uber/jaeger-client-go/zipkin/doc.go | 16 + .../jaeger-client-go/zipkin/propagation.go | 95 + .../zipkin/propagation_test.go | 113 + .../uber/jaeger-client-go/zipkin_test.go | 68 + .../jaeger-client-go/zipkin_thrift_span.go | 322 +++ .../zipkin_thrift_span_test.go | 359 ++++ .../uber/jaeger-lib/.github/ISSUE_TEMPLATE.md | 28 + .../.github/PULL_REQUEST_TEMPLATE.md | 18 + .../github.com/uber/jaeger-lib/.gitignore | 11 + .../github.com/uber/jaeger-lib/.travis.yml | 36 + .../github.com/uber/jaeger-lib/CHANGELOG.md | 56 + .../uber/jaeger-lib/CONTRIBUTING.md | 169 ++ .../vendor/github.com/uber/jaeger-lib/DCO | 37 + .../github.com/uber/jaeger-lib/Gopkg.lock | 114 + .../github.com/uber/jaeger-lib/Gopkg.toml | 23 + .../vendor/github.com/uber/jaeger-lib/LICENSE | 201 ++ .../github.com/uber/jaeger-lib/Makefile | 98 + .../github.com/uber/jaeger-lib/README.md | 27 + .../jaeger-lib/client/log/go-kit/logger.go | 64 + .../client/log/go-kit/logger_test.go | 36 + .../github.com/uber/jaeger-lib/glide.lock | 78 + .../github.com/uber/jaeger-lib/glide.yaml | 13 + .../uber/jaeger-lib/metrics/adapters/cache.go | 69 + .../jaeger-lib/metrics/adapters/cache_test.go | 46 + .../jaeger-lib/metrics/adapters/factory.go | 123 ++ .../metrics/adapters/factory_test.go | 119 ++ .../jaeger-lib/metrics/adapters/tagless.go | 61 + .../uber/jaeger-lib/metrics/counter.go | 28 + .../uber/jaeger-lib/metrics/expvar/factory.go | 49 + .../jaeger-lib/metrics/expvar/factory_test.go | 104 + .../uber/jaeger-lib/metrics/factory.go | 35 + .../uber/jaeger-lib/metrics/gauge.go | 28 + .../metrics/go-kit/expvar/factory.go | 58 + .../metrics/go-kit/expvar/factory_test.go | 43 + .../uber/jaeger-lib/metrics/go-kit/factory.go | 161 ++ .../jaeger-lib/metrics/go-kit/factory_test.go | 202 ++ .../metrics/go-kit/influx/factory.go | 49 + .../metrics/go-kit/influx/factory_test.go | 87 + .../uber/jaeger-lib/metrics/go-kit/metrics.go | 66 + .../jaeger-lib/metrics/go-kit/metrics_test.go | 32 + .../metrics/go-kit/prometheus/factory.go | 93 + .../metrics/go-kit/prometheus/factory_test.go | 64 + .../uber/jaeger-lib/metrics/local.go | 337 +++ .../uber/jaeger-lib/metrics/local_test.go | 118 ++ .../uber/jaeger-lib/metrics/metrics.go | 85 + .../uber/jaeger-lib/metrics/metrics_test.go | 89 + .../uber/jaeger-lib/metrics/multi/multi.go | 107 + .../jaeger-lib/metrics/multi/multi_test.go | 32 + .../jaeger-lib/metrics/prometheus/cache.go | 86 + .../jaeger-lib/metrics/prometheus/factory.go | 246 +++ .../metrics/prometheus/factory_test.go | 185 ++ .../uber/jaeger-lib/metrics/stopwatch.go | 43 + .../uber/jaeger-lib/metrics/tally/factory.go | 63 + .../jaeger-lib/metrics/tally/factory_test.go | 47 + .../uber/jaeger-lib/metrics/tally/metrics.go | 66 + .../jaeger-lib/metrics/testutils/testutils.go | 55 + .../metrics/testutils/testutils_test.go | 17 + .../uber/jaeger-lib/metrics/timer.go | 33 + .../uber/jaeger-lib/sample/sample.go | 24 + .../uber/jaeger-lib/sample/sample_test.go | 12 + .../uber/jaeger-lib/scripts/cover.sh | 64 + .../uber/jaeger-lib/scripts/updateLicense.py | 93 + .../uber/jaeger-lib/scripts/updateLicenses.sh | 6 + .../uber/jaeger-lib/utils/rate_limiter.go | 77 + .../jaeger-lib/utils/rate_limiter_test.go | 75 + 314 files changed, 41115 insertions(+), 333 deletions(-) create mode 100644 template/faaschain/handle_chain.go create mode 100644 template/faaschain/tracer.go create mode 100644 template/faaschain/vendor/github.com/codahale/hdrhistogram/.travis.yml create mode 100644 template/faaschain/vendor/github.com/codahale/hdrhistogram/LICENSE create mode 100644 template/faaschain/vendor/github.com/codahale/hdrhistogram/README.md create mode 100644 template/faaschain/vendor/github.com/codahale/hdrhistogram/hdr.go create mode 100644 template/faaschain/vendor/github.com/codahale/hdrhistogram/hdr_test.go create mode 100644 template/faaschain/vendor/github.com/codahale/hdrhistogram/window.go create mode 100644 template/faaschain/vendor/github.com/codahale/hdrhistogram/window_test.go create mode 100644 template/faaschain/vendor/github.com/opentracing/opentracing-go/.github/ISSUE_TEMPLATE.md create mode 100644 template/faaschain/vendor/github.com/opentracing/opentracing-go/.gitignore create mode 100644 template/faaschain/vendor/github.com/opentracing/opentracing-go/.travis.yml create mode 100644 template/faaschain/vendor/github.com/opentracing/opentracing-go/CHANGELOG.md create mode 100644 template/faaschain/vendor/github.com/opentracing/opentracing-go/LICENSE create mode 100644 template/faaschain/vendor/github.com/opentracing/opentracing-go/Makefile create mode 100644 template/faaschain/vendor/github.com/opentracing/opentracing-go/README.md create mode 100644 template/faaschain/vendor/github.com/opentracing/opentracing-go/ext/tags.go create mode 100644 template/faaschain/vendor/github.com/opentracing/opentracing-go/ext/tags_test.go create mode 100644 template/faaschain/vendor/github.com/opentracing/opentracing-go/globaltracer.go create mode 100644 template/faaschain/vendor/github.com/opentracing/opentracing-go/gocontext.go create mode 100644 template/faaschain/vendor/github.com/opentracing/opentracing-go/gocontext_test.go create mode 100644 template/faaschain/vendor/github.com/opentracing/opentracing-go/harness/api_checkers.go create mode 100644 template/faaschain/vendor/github.com/opentracing/opentracing-go/harness/noop_api_test.go create mode 100644 template/faaschain/vendor/github.com/opentracing/opentracing-go/log/field.go create mode 100644 template/faaschain/vendor/github.com/opentracing/opentracing-go/log/field_test.go create mode 100644 template/faaschain/vendor/github.com/opentracing/opentracing-go/log/util.go create mode 100644 template/faaschain/vendor/github.com/opentracing/opentracing-go/mocktracer/mocklogrecord.go create mode 100644 template/faaschain/vendor/github.com/opentracing/opentracing-go/mocktracer/mockspan.go create mode 100644 template/faaschain/vendor/github.com/opentracing/opentracing-go/mocktracer/mocktracer.go create mode 100644 template/faaschain/vendor/github.com/opentracing/opentracing-go/mocktracer/mocktracer_test.go create mode 100644 template/faaschain/vendor/github.com/opentracing/opentracing-go/mocktracer/propagation.go create mode 100644 template/faaschain/vendor/github.com/opentracing/opentracing-go/noop.go create mode 100644 template/faaschain/vendor/github.com/opentracing/opentracing-go/options_test.go create mode 100644 template/faaschain/vendor/github.com/opentracing/opentracing-go/propagation.go create mode 100644 template/faaschain/vendor/github.com/opentracing/opentracing-go/propagation_test.go create mode 100644 template/faaschain/vendor/github.com/opentracing/opentracing-go/span.go create mode 100644 template/faaschain/vendor/github.com/opentracing/opentracing-go/testtracer_test.go create mode 100644 template/faaschain/vendor/github.com/opentracing/opentracing-go/tracer.go create mode 100644 template/faaschain/vendor/github.com/pkg/errors/.gitignore create mode 100644 template/faaschain/vendor/github.com/pkg/errors/.travis.yml create mode 100644 template/faaschain/vendor/github.com/pkg/errors/LICENSE create mode 100644 template/faaschain/vendor/github.com/pkg/errors/README.md create mode 100644 template/faaschain/vendor/github.com/pkg/errors/appveyor.yml create mode 100644 template/faaschain/vendor/github.com/pkg/errors/bench_test.go create mode 100644 template/faaschain/vendor/github.com/pkg/errors/errors.go create mode 100644 template/faaschain/vendor/github.com/pkg/errors/errors_test.go create mode 100644 template/faaschain/vendor/github.com/pkg/errors/example_test.go create mode 100644 template/faaschain/vendor/github.com/pkg/errors/format_test.go create mode 100644 template/faaschain/vendor/github.com/pkg/errors/stack.go create mode 100644 template/faaschain/vendor/github.com/pkg/errors/stack_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/.github/ISSUE_TEMPLATE.md create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/.github/PULL_REQUEST_TEMPLATE.md create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/.gitignore create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/.gitmodules create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/.travis.yml create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/CHANGELOG.md create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/CONTRIBUTING.md create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/DCO create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/Gopkg.lock create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/Gopkg.toml create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/LICENSE create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/Makefile create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/README.md create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/RELEASE.md create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/baggage_setter.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/baggage_setter_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/config/config.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/config/config_env.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/config/config_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/config/example_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/config/options.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/config/options_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/constants.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/constants_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/context.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/context_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/contrib_observer.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/.gitignore create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/Dockerfile create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/README.md create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/client/client.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/client/client_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/client/constants.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/client/trace.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/common/constants.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/common/json.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/docker-compose.yml create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/endtoend/handler.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/endtoend/handler_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/log/logger.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/main.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/rules.mk create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/server/constants.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/server/server.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/server/server_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/server/trace.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/thrift/.nocover create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/thrift/tracetest/constants.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/thrift/tracetest/tracedservice.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/thrift/tracetest/ttypes.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/doc.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/glide.lock create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/glide.yaml create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/header.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/header_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/.gitignore create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/.travis.yml create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/LICENSE create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/Makefile create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/README.md create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/agent.thrift create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/aggregation_validator.thrift create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/baggage.thrift create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/crossdock/tracetest.thrift create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/dependency.thrift create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/jaeger.thrift create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/sampling.thrift create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/zipkincore.thrift create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/baggage/remote/options.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/baggage/remote/restriction_manager.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/baggage/remote/restriction_manager_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/baggage/restriction_manager.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/baggage/restriction_manager_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/spanlog/json.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/throttler/remote/options.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/throttler/remote/options_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/throttler/remote/throttler.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/throttler/remote/throttler_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/throttler/throttler.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/throttler/throttler_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/interop.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/jaeger_tag.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/jaeger_thrift_span.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/jaeger_thrift_span_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/log/logger.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/log/logger_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/log/zap/field.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/log/zap/field_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/log/zap/logger.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/log/zap/logger_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/logger.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/logger_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/metrics.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/metrics/prometheus/metrics_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/metrics_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/observer.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/observer_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/process.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/propagation.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/propagation_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/reference.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/reporter.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/reporter_options.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/reporter_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/README.md create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/doc.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/endpoints.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/endpoints_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/metrics.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/metrics_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/normalizer.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/normalizer_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/observer.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/observer_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/sampler.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/sampler_options.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/sampler_test.go create mode 100755 template/faaschain/vendor/github.com/uber/jaeger-client-go/scripts/cover.sh create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/scripts/updateLicense.py create mode 100755 template/faaschain/vendor/github.com/uber/jaeger-client-go/scripts/updateLicenses.sh create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/span.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/span_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/testutils/mock_agent.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/testutils/mock_agent_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/testutils/sampling_manager.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/testutils/udp_transport.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/testutils/udp_transport_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/agent.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/constants.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/ttypes.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/baggage/baggagerestrictionmanager.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/baggage/constants.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/baggage/ttypes.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/agent.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/constants.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/ttypes.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/sampling/constants.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/sampling/samplingmanager.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/sampling/ttypes.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/constants.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/ttypes.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/zipkincollector.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/.nocover create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/README.md create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/application_exception.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/binary_protocol.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/compact_protocol.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/exception.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/memory_buffer.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/messagetype.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/numeric.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/processor.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/protocol.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/protocol_exception.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/protocol_factory.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/rich_transport.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/serializer.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/simple_json_protocol.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/transport.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/transport_exception.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/transport_factory.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/type.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/tracer.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/tracer_options.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/tracer_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/transport.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/transport/doc.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/transport/http.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/transport/http_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/transport/zipkin/doc.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/transport/zipkin/example_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/transport/zipkin/http.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/transport/zipkin/http_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/transport_udp.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/transport_udp_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/travis/build-crossdock.sh create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/travis/install-crossdock-deps.sh create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/http_json.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/http_json_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/localip.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/rand.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/rate_limiter.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/rate_limiter_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/udp_client.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/utils.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/utils_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin/README.md create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin/doc.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin/propagation.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin/propagation_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin_thrift_span.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin_thrift_span_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/.github/ISSUE_TEMPLATE.md create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/.github/PULL_REQUEST_TEMPLATE.md create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/.gitignore create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/.travis.yml create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/CHANGELOG.md create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/CONTRIBUTING.md create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/DCO create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/Gopkg.lock create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/Gopkg.toml create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/LICENSE create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/Makefile create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/README.md create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/client/log/go-kit/logger.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/client/log/go-kit/logger_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/glide.lock create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/glide.yaml create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/adapters/cache.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/adapters/cache_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/adapters/factory.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/adapters/factory_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/adapters/tagless.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/counter.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/expvar/factory.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/expvar/factory_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/factory.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/gauge.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/expvar/factory.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/expvar/factory_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/factory.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/factory_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/influx/factory.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/influx/factory_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/metrics.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/metrics_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/prometheus/factory.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/prometheus/factory_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/local.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/local_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/metrics.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/metrics_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/multi/multi.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/multi/multi_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/prometheus/cache.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/prometheus/factory.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/prometheus/factory_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/stopwatch.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/tally/factory.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/tally/factory_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/tally/metrics.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/testutils/testutils.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/testutils/testutils_test.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/timer.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/sample/sample.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/sample/sample_test.go create mode 100755 template/faaschain/vendor/github.com/uber/jaeger-lib/scripts/cover.sh create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/scripts/updateLicense.py create mode 100755 template/faaschain/vendor/github.com/uber/jaeger-lib/scripts/updateLicenses.sh create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/utils/rate_limiter.go create mode 100644 template/faaschain/vendor/github.com/uber/jaeger-lib/utils/rate_limiter_test.go diff --git a/ci/script/buildlib.sh b/ci/script/buildlib.sh index 35557aa8..549790d0 100755 --- a/ci/script/buildlib.sh +++ b/ci/script/buildlib.sh @@ -1,6 +1,6 @@ #!/bin/bash # Run a gofmt and exclude all vendored code. -test -z "$(gofmt -l $(find . -type f -name '*.go' -not -path "./vendor/*" -not -path "./example/*" -not -path "./faaschain/*" -not -path "./" ))" || { echo "Run \"gofmt -s -w\" on your Golang code"; exit 1; } +test -z "$(gofmt -l $(find . -type f -name '*.go' -not -path "./vendor/*" -not -path "./example/*" -not -path "./template/*" -not -path "./doc/*" -not -path "./ci/" -not -path "./" ))" || { echo "Run \"gofmt -s -w\" on your Golang code"; exit 1; } go test ./chain.go ./chain_test.go -cover diff --git a/example/stack.yml b/example/stack.yml index b5773a2c..dc044464 100644 --- a/example/stack.yml +++ b/example/stack.yml @@ -15,6 +15,8 @@ functions: write_timeout: 120 write_debug: true combine_output: false + enable_tracing: true + trace_server: "jaegertracing:5775" upload-chain-async: @@ -28,6 +30,8 @@ functions: write_timeout: 120 write_debug: true combine_output: false + enable_tracing: true + trace_server: "jaegertracing:5775" colorization: lang: Dockerfile diff --git a/sdk/chain.go b/sdk/chain.go index 8e64c813..4fa512b6 100644 --- a/sdk/chain.go +++ b/sdk/chain.go @@ -5,7 +5,7 @@ import ( ) type Chain struct { - Phases []*Phase `json:"-"` // Phases that will be executed in async + Phases []*Phase `json:"phases"` // Phases that will be executed in async ExecutionPosition int `json:"position"` // Position of Executor } diff --git a/sdk/function.go b/sdk/function.go index d6b55f80..37726738 100644 --- a/sdk/function.go +++ b/sdk/function.go @@ -7,11 +7,11 @@ import ( type Modifier func([]byte) ([]byte, error) type Function struct { - Function string `json:"-"` // The name of the function - CallbackUrl string `json:"-"` // Callback Url - Mod Modifier `json:"-"` // Modifier - Header map[string]string `json:"-"` // The HTTP call header - Param map[string][]string `json:"-"` // The Parameter in Query string + Function string `json:"function"` // The name of the function + CallbackUrl string `json:"callback"` // Callback Url + Mod Modifier `json:"-"` // Modifier + Header map[string]string `json:"header"` // The HTTP call header + Param map[string][]string `json:"param"` // The Parameter in Query string } // Create a function with execution name diff --git a/sdk/phase.go b/sdk/phase.go index 278f0c6c..ef72f461 100644 --- a/sdk/phase.go +++ b/sdk/phase.go @@ -2,7 +2,7 @@ package sdk type Phase struct { // The list of function in the Phase - Functions []*Function `json:"-"` + Functions []*Function `json:"functions"` } func CreateExecutionPhase() *Phase { diff --git a/sdk/request.go b/sdk/request.go index 119a682b..763a558d 100644 --- a/sdk/request.go +++ b/sdk/request.go @@ -8,8 +8,8 @@ import ( type Request struct { Sign string `json: "sign"` ID string `json: "id"` - Chaindef string `json: "-"` Data []byte `json: "data"` + Chaindef string `json: "-"` } const ( diff --git a/template/faaschain/handle_chain.go b/template/faaschain/handle_chain.go new file mode 100644 index 00000000..40bc44f9 --- /dev/null +++ b/template/faaschain/handle_chain.go @@ -0,0 +1,339 @@ +package main + +import ( + "bytes" + "fmt" + "github.com/rs/xid" + "github.com/s8sg/faaschain" + "github.com/s8sg/faaschain/sdk" + "handler/function" + "io/ioutil" + "log" + "net/http" + "os" +) + +var ( + chainName = "" +) + +func getGateway() string { + gateway := os.Getenv("gateway") + if gateway == "" { + gateway = "gateway:8080" + } + return gateway +} + +func getChainName() string { + return os.Getenv("chain_name") +} + +func makeQueryStringFromParam(params map[string][]string) string { + if params == nil { + return "" + } + result := "" + for key, array := range params { + for _, value := range array { + keyVal := fmt.Sprintf("%s-%s", key, value) + if result == "" { + result = "?" + keyVal + } else { + result = result + "&" + keyVal + } + } + } + return result +} + +// build upstream request for function +func buildUpstreamRequest(function string, data []byte, params map[string][]string, headers map[string]string) *http.Request { + url := "http://" + function + ":8080" + queryString := makeQueryStringFromParam(params) + if queryString != "" { + url = url + queryString + } + + var method string + + if method, ok := headers["method"]; !ok { + method = os.Getenv("default-method") + if method == "" { + method = "POST" + } + } + + httpreq, _ := http.NewRequest(method, url, bytes.NewBuffer(data)) + + for key, value := range headers { + httpreq.Header.Set(key, value) + } + + return httpreq +} + +// build upstream request for callback +func buildUpstreamRequestCallback(callbackUrl string, data []byte, params map[string][]string, headers map[string]string) *http.Request { + url := callbackUrl + queryString := makeQueryStringFromParam(params) + if queryString != "" { + url = url + queryString + } + + var method string + + if method, ok := headers["method"]; !ok { + method = os.Getenv("default-method") + if method == "" { + method = "POST" + } + } + + httpreq, _ := http.NewRequest(method, url, bytes.NewBuffer(data)) + + for key, value := range headers { + httpreq.Header.Set(key, value) + } + + return httpreq +} + +// builds a chain from request +func buildChain(data []byte) (fchain *faaschain.Fchain, requestData []byte) { + + requestId := "" + + // decode the request to find if chain definition exists + request, err := sdk.DecodeRequest(data) + if err != nil { + fchain = faaschain.NewFaaschain("http://"+getGateway(), chainName) + // Generate request Id + requestId = xid.New().String() + log.Printf("[request `%s`] Created", requestId) + // Set execution Pos + fchain.GetChain().ExecutionPosition = 0 + requestData = data + } else { + // Get the request ID + requestId = request.GetID() + log.Printf("[Request `%s`] Received", requestId) + chain, err := request.GetChain() + if err != nil { + log.Fatalf("[request `%s`] Failed, error : Failed to parse chain def, %v", requestId, err) + } + // Get execution position + executionPos := chain.ExecutionPosition + + // Override the old chain with new + fchain = faaschain.NewFaaschain("http://"+getGateway(), chainName) + // Set execution Pos + fchain.GetChain().ExecutionPosition = executionPos + requestData = request.GetData() + } + // set request ID + fchain.SetId(requestId) + + // trace req - mark as start of req + startReqSpan(requestId) + + return +} + +// Execute functions for current phase +func execute(fchain *faaschain.Fchain, request []byte) ([]byte, error) { + var result []byte + var err error + var httpreq *http.Request + + chain := fchain.GetChain() + + phase := chain.GetCurrentPhase() + + // trace phase - mark as start of phase + startPhaseSpan(chain.ExecutionPosition, fchain.GetId()) + + // Execute all function + for _, function := range phase.GetFunctions() { + + switch { + // If function + case function.GetName() != "": + name := function.GetName() + params := function.GetParams() + headers := function.GetHeaders() + + // Check if intermidiate data + if result == nil { + httpreq = buildUpstreamRequest(name, request, params, headers) + } else { + httpreq = buildUpstreamRequest(name, result, params, headers) + } + client := &http.Client{} + resp, err := client.Do(httpreq) + if err != nil { + err = fmt.Errorf("Phase(%d), Function(%s), error: function execution failed, %v", chain.ExecutionPosition, name, err) + return nil, err + } + if resp.StatusCode < 200 || resp.StatusCode > 299 { + return nil, fmt.Errorf("Phase(%d), Function(%s), error: function execution failed, %s", chain.ExecutionPosition, name, resp.Status) + } + // read Response + result, err = ioutil.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("Phase(%d), Function(%s), error: function execution failed, %v", chain.ExecutionPosition, name, err) + } + // If callback + case function.CallbackUrl != "": + cburl := function.CallbackUrl + params := function.GetParams() + headers := function.GetHeaders() + + // Check if intermidiate data + if result == nil { + httpreq = buildUpstreamRequestCallback(cburl, request, params, headers) + } else { + httpreq = buildUpstreamRequestCallback(cburl, result, params, headers) + } + client := &http.Client{} + resp, err := client.Do(httpreq) + cbresult, _ := ioutil.ReadAll(resp.Body) + if err != nil { + err = fmt.Errorf("Phase(%d), Callback(%s), error: callback failed, %v %s", chain.ExecutionPosition, cburl, err, string(cbresult)) + return nil, err + } + if resp.StatusCode < 200 || resp.StatusCode > 299 { + log.Printf("Phase(%d), Callback(%s), error: callback failed, %s, %s", chain.ExecutionPosition, cburl, resp.Status, string(cbresult)) + } + // If modifier + default: + // Check if intermidiate data + if result == nil { + result, err = function.Mod(request) + } else { + result, err = function.Mod(result) + } + if err != nil { + return nil, fmt.Errorf("Phase(%d), error: Failed at modifier, %v", chain.ExecutionPosition, err) + } + if result == nil { + result = []byte("") + } + } + } + + // trace phase - mark as end of phase + stopPhaseSpan(chain.ExecutionPosition) + + // Update execution position + chain.UpdateExecutionPosition() + + return result, nil +} + +func forwardAsync(fchain *faaschain.Fchain, result []byte) ([]byte, error) { + + // get chain + chain := fchain.GetChain() + + // get request id + id := fchain.GetId() + + // Build current chain definition + chaindef, _ := chain.Encode() + // Build request + uprequest := sdk.BuildRequest(id, string(chaindef), result) + // Make request data + data, _ := uprequest.Encode() + + // build url for calling the chain in async + httpreq, _ := http.NewRequest(http.MethodPost, fchain.GetAsyncUrl(), bytes.NewReader(data)) + httpreq.Header.Add("Accept", "application/json") + httpreq.Header.Add("Content-Type", "application/json") + + client := &http.Client{} + res, resErr := client.Do(httpreq) + resdata, _ := ioutil.ReadAll(res.Body) + if resErr != nil { + return resdata, resErr + } + + if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted { + return resdata, fmt.Errorf(res.Status) + } + return resdata, nil +} + +// Handle request Response +func handleResponse(fchain *faaschain.Fchain, result []byte) ([]byte, error) { + + // get chain + chain := fchain.GetChain() + + switch { + + // If chain has only one phase or if the chain has completed excution return + case chain.CountPhases() == 1 || chain.GetCurrentPhase() == nil: + log.Printf("[request `%s`] Finished successfully ", fchain.GetId()) + return result, nil + + // In default case we forward the partial chain to same chain-function + default: + // forward the chain request + resp, forwardErr := forwardAsync(fchain, result) + if forwardErr != nil { + return nil, fmt.Errorf("Phase(%d): error: %v, %s, url %s", chain.ExecutionPosition, forwardErr, string(resp), fchain.GetAsyncUrl()) + } + log.Printf("[Request `%s`] Phase %d request submitted", chain.ExecutionPosition) + } + + return []byte(""), nil +} + +func handleChain(data []byte) string { + + var resp []byte + + // Get chain name + chainName = getChainName() + if chainName == "" { + fmt.Errorf("Error: chain name must be provided when chain deployed as function") + os.Exit(1) + } + + // initialize traceserve if tracing enabled + err := initGlobalTracer(chainName) + if err != nil { + log.Printf(err.Error()) + } + + // Chain Execution Steps + { + // BUILD: build the chain based on execution request + fchain, data := buildChain(data) + + // DEFINE: Get Chain definition from user implemented Define() + err := function.Define(fchain) + if err != nil { + log.Fatalf("[request `%s`] Failed, %v", fchain.GetId(), err) + } + + // EXECUTE: execute the chain based on current phase + result, err := execute(fchain, data) + if err != nil { + log.Fatalf("[request `%s`] Failed, %v", fchain.GetId(), err) + } + + // HANDLE: Handle the execution state of last phase + resp, err = handleResponse(fchain, result) + if err != nil { + log.Fatalf("[request `%s`] Failed, %v", fchain.GetId(), err) + } + } + + stopReqSpan() + // flash any pending trace item if tracing enabled + flushTracer() + + return string(resp) +} diff --git a/template/faaschain/main.go b/template/faaschain/main.go index 8cca831b..ba288664 100644 --- a/template/faaschain/main.go +++ b/template/faaschain/main.go @@ -1,332 +1,17 @@ package main import ( - "bytes" "fmt" - "github.com/rs/xid" - "github.com/s8sg/faaschain" - "github.com/s8sg/faaschain/sdk" - "handler/function" "io/ioutil" "log" - "net/http" "os" ) -func getGateway() string { - gateway := os.Getenv("gateway") - if gateway == "" { - gateway = "gateway:8080" - } - return gateway -} - -func getChainName() string { - return os.Getenv("chain_name") -} - -func makeQueryStringFromParam(params map[string][]string) string { - if params == nil { - return "" - } - result := "" - for key, array := range params { - for _, value := range array { - keyVal := fmt.Sprintf("%s-%s", key, value) - if result == "" { - result = "?" + keyVal - } else { - result = result + "&" + keyVal - } - } - } - return result -} - -// build upstream request for function -func buildUpstreamRequest(function string, data []byte, params map[string][]string, headers map[string]string) *http.Request { - url := "http://" + function + ":8080" - queryString := makeQueryStringFromParam(params) - if queryString != "" { - url = url + queryString - } - - var method string - - if method, ok := headers["method"]; !ok { - method = os.Getenv("default-method") - if method == "" { - method = "POST" - } - } - - httpreq, _ := http.NewRequest(method, url, bytes.NewBuffer(data)) - - for key, value := range headers { - httpreq.Header.Set(key, value) - } - - return httpreq -} - -// build upstream request for callback -func buildUpstreamRequestCallback(callbackUrl string, data []byte, params map[string][]string, headers map[string]string) *http.Request { - url := callbackUrl - queryString := makeQueryStringFromParam(params) - if queryString != "" { - url = url + queryString - } - - var method string - - if method, ok := headers["method"]; !ok { - method = os.Getenv("default-method") - if method == "" { - method = "POST" - } - } - - httpreq, _ := http.NewRequest(method, url, bytes.NewBuffer(data)) - - for key, value := range headers { - httpreq.Header.Set(key, value) - } - - return httpreq -} - -// builds a chain from request -func buildChain(data []byte) (fchain *faaschain.Fchain, requestData []byte) { - - requestId := "" - // decode the request to find if chain definition exists - request, err := sdk.DecodeRequest(data) - if err != nil { - chainname := getChainName() - if chainname == "" { - log.Printf("Chain name must be provided when chain deployed as function") - } - - fchain = faaschain.NewFaaschain("http://"+getGateway(), chainname) - // Generate request Id - requestId = xid.New().String() - log.Printf("[request `%s`] Created", requestId) - // Set execution Pos - fchain.GetChain().ExecutionPosition = 0 - requestData = data - } else { - // Get the request ID - requestId = request.GetID() - log.Printf("[Request `%s`] Received", requestId) - chain, err := request.GetChain() - if err != nil { - log.Fatalf("[request `%s`] Failed, error : Failed to parse chain def, %v", requestId, err) - } - // Get execution position - executionPos := chain.ExecutionPosition - - chainname := getChainName() - if chainname == "" { - log.Printf("Chain name must be provided when chain deployed as function") - } - - // Override the old chain with new - fchain = faaschain.NewFaaschain("http://"+getGateway(), chainname) - // Set execution Pos - fchain.GetChain().ExecutionPosition = executionPos - requestData = request.GetData() - } - // set request ID - fchain.SetId(requestId) - - return -} - -// Execute functions for current phase -func execute(fchain *faaschain.Fchain, request []byte) ([]byte, error) { - var result []byte - var err error - var httpreq *http.Request - - chain := fchain.GetChain() - - phase := chain.GetCurrentPhase() - - // Execute all function - for _, function := range phase.GetFunctions() { - - switch { - // If function - case function.GetName() != "": - name := function.GetName() - params := function.GetParams() - headers := function.GetHeaders() - - // Check if intermidiate data - if result == nil { - httpreq = buildUpstreamRequest(name, request, params, headers) - } else { - httpreq = buildUpstreamRequest(name, result, params, headers) - } - client := &http.Client{} - resp, err := client.Do(httpreq) - if err != nil { - err = fmt.Errorf("Phase(%d), Function(%s), error : Failed to execute function %v", chain.ExecutionPosition, name, err) - return nil, err - } - if resp.StatusCode < 200 || resp.StatusCode > 299 { - return nil, fmt.Errorf("Phase(%d), Function(%s), error : Failed to execute function %s", chain.ExecutionPosition, name, resp.Status) - } - // read Response - result, err = ioutil.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("Phase(%d), Function(%s), error : Failed to read result %v", chain.ExecutionPosition, name, err) - } - // If callback - case function.CallbackUrl != "": - cburl := function.CallbackUrl - params := function.GetParams() - headers := function.GetHeaders() - - // Check if intermidiate data - if result == nil { - httpreq = buildUpstreamRequestCallback(cburl, request, params, headers) - } else { - httpreq = buildUpstreamRequestCallback(cburl, result, params, headers) - } - client := &http.Client{} - resp, err := client.Do(httpreq) - cbresult, _ := ioutil.ReadAll(resp.Body) - if err != nil { - err = fmt.Errorf("Phase(%d), Callback(%s), error : Failed to execute callback %v, %s", chain.ExecutionPosition, cburl, err, string(cbresult)) - return nil, err - } - if resp.StatusCode < 200 || resp.StatusCode > 299 { - log.Printf("Phase(%d), Callback(%s), error : Failed to execute callback %s, %s", chain.ExecutionPosition, cburl, resp.Status, string(cbresult)) - } - // If modifier - default: - // Check if intermidiate data - if result == nil { - result, err = function.Mod(request) - } else { - result, err = function.Mod(result) - } - if err != nil { - return nil, fmt.Errorf("Phase(%d), error : Failed to execute modifier %v", chain.ExecutionPosition, err) - } - if result == nil { - result = []byte("") - } - } - } - - // Update execution position - chain.UpdateExecutionPosition() - - return result, nil -} - -// Handle request Response -func handleResponse(fchain *faaschain.Fchain, result []byte) ([]byte, error) { - - // get request Id - id := fchain.GetId() - - // get chain - chain := fchain.GetChain() - - // for a single phase just return the result to the caller - // Single phase means no async call has been made by the chain - // In that case if chain is executed in async mode the x-callback-url - // will be handled by the caller - if chain.CountPhases() == 1 { - return result, nil - } - - // If chain has already finished execution - // Then return the result which will be handled by the "X-Callback-Url" If provided - // TODO: Allow Callback Call in Chain definition - if chain.GetCurrentPhase() == nil { - return result, nil - } - - // If the chain has phases left to handle - // Create new request with chain info and - // Build current chain definition - chaindef, _ := chain.Encode() - // Build request - uprequest := sdk.BuildRequest(id, string(chaindef), result) - // Make request data - data, _ := uprequest.Encode() - - // build url for async function - httpreq, _ := http.NewRequest(http.MethodPost, fchain.GetAsyncUrl(), bytes.NewReader(data)) - httpreq.Header.Add("Accept", "application/json") - httpreq.Header.Add("Content-Type", "application/json") - - // If the chain has only one more phase to execute and callback URL was provided - // Execute the last phase with the callback URL so that the returned result gets - // handled via openfaas callback - /* - if chain.IsLastPhase() { - if chain.CallbackUrl != "" { - httpreq.Header.Add("X-Callback-Url", chain.CallbackUrl) - } - }*/ - - client := &http.Client{} - res, resErr := client.Do(httpreq) - resdata, _ := ioutil.ReadAll(res.Body) - if resErr != nil { - return nil, fmt.Errorf("Phase(%d): error %v, %s, url %s", chain.ExecutionPosition, resErr, string(resdata), fchain.GetAsyncUrl()) - } - - if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted { - return nil, fmt.Errorf("Phase(%d): error %s, %s, url %s", chain.ExecutionPosition, res.Status, string(resdata), fchain.GetAsyncUrl()) - } - - log.Printf("Execution request for phase %d has been successfully submitted", chain.ExecutionPosition) - - return []byte(""), nil -} - -func handle(data []byte) string { - - // BUILD: build the chain based on execution request - log.Printf("building chain request") - fchain, data := buildChain(data) - - // DEFINE: Get Chain definition from user implemented Define() - log.Printf("generating function defintion") - err := function.Define(fchain) - if err != nil { - log.Fatalf("[request `%s`] Failed, %v", fchain.GetId(), err) - } - - // Execute: execute the chain based on current phase - log.Printf("executing phase %d", fchain.GetChain().ExecutionPosition) - result, err := execute(fchain, data) - if err != nil { - log.Fatalf("[request `%s`] Failed, %v", fchain.GetId(), err) - } - - // Handle: handle a chain response - log.Printf("handling response for %d", fchain.GetChain().ExecutionPosition) - // Handle a response of FaaSChain Function - resp, err := handleResponse(fchain, result) - if err != nil { - log.Fatalf("[request `%s`] Failed, %v", fchain.GetId(), err) - } - - return string(resp) -} - func main() { input, err := ioutil.ReadAll(os.Stdin) if err != nil { log.Fatalf("Unable to read standard input: %s", err.Error()) } - fmt.Println(handle(input)) + fmt.Println(handleChain(input)) } diff --git a/template/faaschain/tracer.go b/template/faaschain/tracer.go new file mode 100644 index 00000000..85910bbb --- /dev/null +++ b/template/faaschain/tracer.go @@ -0,0 +1,119 @@ +package main + +import ( + "fmt" + "github.com/opentracing/opentracing-go" + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/config" + "io" + "os" + "strings" + "time" +) + +var ( + closer io.Closer + reqSpan opentracing.Span + phaseSpans map[int]opentracing.Span +) + +func isTracingEnabled() bool { + tracing := os.Getenv("enable_tracing") + if strings.ToUpper(tracing) == "TRUE" { + return true + } + return false +} + +func getTraceServer() string { + traceServer := os.Getenv("trace_server") + if traceServer == "" { + traceServer = "jaegertracing:5775" + } + return traceServer +} + +func initGlobalTracer(chainName string) error { + + if !isTracingEnabled() { + fmt.Fprintf(os.Stderr, "tracing is disabled\n") + return nil + } + + agentPort := getTraceServer() + + fmt.Fprintf(os.Stderr, "tracing is enabled, agent %s\n", agentPort) + + cfg := config.Configuration{ + ServiceName: chainName, + Sampler: &config.SamplerConfig{ + Type: "const", + Param: 1, + }, + Reporter: &config.ReporterConfig{ + LogSpans: true, + BufferFlushInterval: 1 * time.Second, + LocalAgentHostPort: agentPort, + }, + } + + tracer, traceCloser, err := cfg.NewTracer( + config.Logger(jaeger.StdLogger), + ) + if err != nil { + return fmt.Errorf("Failed to init tracer, error %v", err.Error()) + } + + closer = traceCloser + + opentracing.SetGlobalTracer(tracer) + + phaseSpans = make(map[int]opentracing.Span) + + return nil +} + +// TODO: make use of context.Background() +func startReqSpan(reqId string) { + if !isTracingEnabled() { + return + } + + reqSpan = opentracing.GlobalTracer().StartSpan(reqId) +} + +func stopReqSpan() { + if !isTracingEnabled() { + return + } + + reqSpan.Finish() +} + +// TODO: make use of context.Background() +func startPhaseSpan(phase int, reqId string) { + if !isTracingEnabled() { + return + } + + phasename := fmt.Sprintf("%s-phase-%d", reqId, phase) + phaseSpan := opentracing.GlobalTracer().StartSpan( + phasename, opentracing.ChildOf(reqSpan.Context())) + phaseSpans[phase] = phaseSpan +} + +func stopPhaseSpan(phase int) { + if !isTracingEnabled() { + return + } + + phaseSpans[phase].Finish() +} + +func flushTracer() { + if !isTracingEnabled() { + return + } + + closer.Close() +} diff --git a/template/faaschain/vendor/github.com/codahale/hdrhistogram/.travis.yml b/template/faaschain/vendor/github.com/codahale/hdrhistogram/.travis.yml new file mode 100644 index 00000000..7960fc95 --- /dev/null +++ b/template/faaschain/vendor/github.com/codahale/hdrhistogram/.travis.yml @@ -0,0 +1,5 @@ +language: go +go: + - 1.5 + - 1.6 + - tip diff --git a/template/faaschain/vendor/github.com/codahale/hdrhistogram/LICENSE b/template/faaschain/vendor/github.com/codahale/hdrhistogram/LICENSE new file mode 100644 index 00000000..f9835c24 --- /dev/null +++ b/template/faaschain/vendor/github.com/codahale/hdrhistogram/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Coda Hale + +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/template/faaschain/vendor/github.com/codahale/hdrhistogram/README.md b/template/faaschain/vendor/github.com/codahale/hdrhistogram/README.md new file mode 100644 index 00000000..614b197c --- /dev/null +++ b/template/faaschain/vendor/github.com/codahale/hdrhistogram/README.md @@ -0,0 +1,15 @@ +hdrhistogram +============ + +[![Build Status](https://travis-ci.org/codahale/hdrhistogram.png?branch=master)](https://travis-ci.org/codahale/hdrhistogram) + +A pure Go implementation of the [HDR Histogram](https://github.com/HdrHistogram/HdrHistogram). + +> A Histogram that supports recording and analyzing sampled data value counts +> across a configurable integer value range with configurable value precision +> within the range. Value precision is expressed as the number of significant +> digits in the value recording, and provides control over value quantization +> behavior across the value range and the subsequent value resolution at any +> given level. + +For documentation, check [godoc](http://godoc.org/github.com/codahale/hdrhistogram). diff --git a/template/faaschain/vendor/github.com/codahale/hdrhistogram/hdr.go b/template/faaschain/vendor/github.com/codahale/hdrhistogram/hdr.go new file mode 100644 index 00000000..c9784292 --- /dev/null +++ b/template/faaschain/vendor/github.com/codahale/hdrhistogram/hdr.go @@ -0,0 +1,564 @@ +// Package hdrhistogram provides an implementation of Gil Tene's HDR Histogram +// data structure. The HDR Histogram allows for fast and accurate analysis of +// the extreme ranges of data with non-normal distributions, like latency. +package hdrhistogram + +import ( + "fmt" + "math" +) + +// A Bracket is a part of a cumulative distribution. +type Bracket struct { + Quantile float64 + Count, ValueAt int64 +} + +// A Snapshot is an exported view of a Histogram, useful for serializing them. +// A Histogram can be constructed from it by passing it to Import. +type Snapshot struct { + LowestTrackableValue int64 + HighestTrackableValue int64 + SignificantFigures int64 + Counts []int64 +} + +// A Histogram is a lossy data structure used to record the distribution of +// non-normally distributed data (like latency) with a high degree of accuracy +// and a bounded degree of precision. +type Histogram struct { + lowestTrackableValue int64 + highestTrackableValue int64 + unitMagnitude int64 + significantFigures int64 + subBucketHalfCountMagnitude int32 + subBucketHalfCount int32 + subBucketMask int64 + subBucketCount int32 + bucketCount int32 + countsLen int32 + totalCount int64 + counts []int64 +} + +// New returns a new Histogram instance capable of tracking values in the given +// range and with the given amount of precision. +func New(minValue, maxValue int64, sigfigs int) *Histogram { + if sigfigs < 1 || 5 < sigfigs { + panic(fmt.Errorf("sigfigs must be [1,5] (was %d)", sigfigs)) + } + + largestValueWithSingleUnitResolution := 2 * math.Pow10(sigfigs) + subBucketCountMagnitude := int32(math.Ceil(math.Log2(float64(largestValueWithSingleUnitResolution)))) + + subBucketHalfCountMagnitude := subBucketCountMagnitude + if subBucketHalfCountMagnitude < 1 { + subBucketHalfCountMagnitude = 1 + } + subBucketHalfCountMagnitude-- + + unitMagnitude := int32(math.Floor(math.Log2(float64(minValue)))) + if unitMagnitude < 0 { + unitMagnitude = 0 + } + + subBucketCount := int32(math.Pow(2, float64(subBucketHalfCountMagnitude)+1)) + + subBucketHalfCount := subBucketCount / 2 + subBucketMask := int64(subBucketCount-1) << uint(unitMagnitude) + + // determine exponent range needed to support the trackable value with no + // overflow: + smallestUntrackableValue := int64(subBucketCount) << uint(unitMagnitude) + bucketsNeeded := int32(1) + for smallestUntrackableValue < maxValue { + smallestUntrackableValue <<= 1 + bucketsNeeded++ + } + + bucketCount := bucketsNeeded + countsLen := (bucketCount + 1) * (subBucketCount / 2) + + return &Histogram{ + lowestTrackableValue: minValue, + highestTrackableValue: maxValue, + unitMagnitude: int64(unitMagnitude), + significantFigures: int64(sigfigs), + subBucketHalfCountMagnitude: subBucketHalfCountMagnitude, + subBucketHalfCount: subBucketHalfCount, + subBucketMask: subBucketMask, + subBucketCount: subBucketCount, + bucketCount: bucketCount, + countsLen: countsLen, + totalCount: 0, + counts: make([]int64, countsLen), + } +} + +// ByteSize returns an estimate of the amount of memory allocated to the +// histogram in bytes. +// +// N.B.: This does not take into account the overhead for slices, which are +// small, constant, and specific to the compiler version. +func (h *Histogram) ByteSize() int { + return 6*8 + 5*4 + len(h.counts)*8 +} + +// Merge merges the data stored in the given histogram with the receiver, +// returning the number of recorded values which had to be dropped. +func (h *Histogram) Merge(from *Histogram) (dropped int64) { + i := from.rIterator() + for i.next() { + v := i.valueFromIdx + c := i.countAtIdx + + if h.RecordValues(v, c) != nil { + dropped += c + } + } + + return +} + +// TotalCount returns total number of values recorded. +func (h *Histogram) TotalCount() int64 { + return h.totalCount +} + +// Max returns the approximate maximum recorded value. +func (h *Histogram) Max() int64 { + var max int64 + i := h.iterator() + for i.next() { + if i.countAtIdx != 0 { + max = i.highestEquivalentValue + } + } + return h.highestEquivalentValue(max) +} + +// Min returns the approximate minimum recorded value. +func (h *Histogram) Min() int64 { + var min int64 + i := h.iterator() + for i.next() { + if i.countAtIdx != 0 && min == 0 { + min = i.highestEquivalentValue + break + } + } + return h.lowestEquivalentValue(min) +} + +// Mean returns the approximate arithmetic mean of the recorded values. +func (h *Histogram) Mean() float64 { + if h.totalCount == 0 { + return 0 + } + var total int64 + i := h.iterator() + for i.next() { + if i.countAtIdx != 0 { + total += i.countAtIdx * h.medianEquivalentValue(i.valueFromIdx) + } + } + return float64(total) / float64(h.totalCount) +} + +// StdDev returns the approximate standard deviation of the recorded values. +func (h *Histogram) StdDev() float64 { + if h.totalCount == 0 { + return 0 + } + + mean := h.Mean() + geometricDevTotal := 0.0 + + i := h.iterator() + for i.next() { + if i.countAtIdx != 0 { + dev := float64(h.medianEquivalentValue(i.valueFromIdx)) - mean + geometricDevTotal += (dev * dev) * float64(i.countAtIdx) + } + } + + return math.Sqrt(geometricDevTotal / float64(h.totalCount)) +} + +// Reset deletes all recorded values and restores the histogram to its original +// state. +func (h *Histogram) Reset() { + h.totalCount = 0 + for i := range h.counts { + h.counts[i] = 0 + } +} + +// RecordValue records the given value, returning an error if the value is out +// of range. +func (h *Histogram) RecordValue(v int64) error { + return h.RecordValues(v, 1) +} + +// RecordCorrectedValue records the given value, correcting for stalls in the +// recording process. This only works for processes which are recording values +// at an expected interval (e.g., doing jitter analysis). Processes which are +// recording ad-hoc values (e.g., latency for incoming requests) can't take +// advantage of this. +func (h *Histogram) RecordCorrectedValue(v, expectedInterval int64) error { + if err := h.RecordValue(v); err != nil { + return err + } + + if expectedInterval <= 0 || v <= expectedInterval { + return nil + } + + missingValue := v - expectedInterval + for missingValue >= expectedInterval { + if err := h.RecordValue(missingValue); err != nil { + return err + } + missingValue -= expectedInterval + } + + return nil +} + +// RecordValues records n occurrences of the given value, returning an error if +// the value is out of range. +func (h *Histogram) RecordValues(v, n int64) error { + idx := h.countsIndexFor(v) + if idx < 0 || int(h.countsLen) <= idx { + return fmt.Errorf("value %d is too large to be recorded", v) + } + h.counts[idx] += n + h.totalCount += n + + return nil +} + +// ValueAtQuantile returns the recorded value at the given quantile (0..100). +func (h *Histogram) ValueAtQuantile(q float64) int64 { + if q > 100 { + q = 100 + } + + total := int64(0) + countAtPercentile := int64(((q / 100) * float64(h.totalCount)) + 0.5) + + i := h.iterator() + for i.next() { + total += i.countAtIdx + if total >= countAtPercentile { + return h.highestEquivalentValue(i.valueFromIdx) + } + } + + return 0 +} + +// CumulativeDistribution returns an ordered list of brackets of the +// distribution of recorded values. +func (h *Histogram) CumulativeDistribution() []Bracket { + var result []Bracket + + i := h.pIterator(1) + for i.next() { + result = append(result, Bracket{ + Quantile: i.percentile, + Count: i.countToIdx, + ValueAt: i.highestEquivalentValue, + }) + } + + return result +} + +// SignificantFigures returns the significant figures used to create the +// histogram +func (h *Histogram) SignificantFigures() int64 { + return h.significantFigures +} + +// LowestTrackableValue returns the lower bound on values that will be added +// to the histogram +func (h *Histogram) LowestTrackableValue() int64 { + return h.lowestTrackableValue +} + +// HighestTrackableValue returns the upper bound on values that will be added +// to the histogram +func (h *Histogram) HighestTrackableValue() int64 { + return h.highestTrackableValue +} + +// Histogram bar for plotting +type Bar struct { + From, To, Count int64 +} + +// Pretty print as csv for easy plotting +func (b Bar) String() string { + return fmt.Sprintf("%v, %v, %v\n", b.From, b.To, b.Count) +} + +// Distribution returns an ordered list of bars of the +// distribution of recorded values, counts can be normalized to a probability +func (h *Histogram) Distribution() (result []Bar) { + i := h.iterator() + for i.next() { + result = append(result, Bar{ + Count: i.countAtIdx, + From: h.lowestEquivalentValue(i.valueFromIdx), + To: i.highestEquivalentValue, + }) + } + + return result +} + +// Equals returns true if the two Histograms are equivalent, false if not. +func (h *Histogram) Equals(other *Histogram) bool { + switch { + case + h.lowestTrackableValue != other.lowestTrackableValue, + h.highestTrackableValue != other.highestTrackableValue, + h.unitMagnitude != other.unitMagnitude, + h.significantFigures != other.significantFigures, + h.subBucketHalfCountMagnitude != other.subBucketHalfCountMagnitude, + h.subBucketHalfCount != other.subBucketHalfCount, + h.subBucketMask != other.subBucketMask, + h.subBucketCount != other.subBucketCount, + h.bucketCount != other.bucketCount, + h.countsLen != other.countsLen, + h.totalCount != other.totalCount: + return false + default: + for i, c := range h.counts { + if c != other.counts[i] { + return false + } + } + } + return true +} + +// Export returns a snapshot view of the Histogram. This can be later passed to +// Import to construct a new Histogram with the same state. +func (h *Histogram) Export() *Snapshot { + return &Snapshot{ + LowestTrackableValue: h.lowestTrackableValue, + HighestTrackableValue: h.highestTrackableValue, + SignificantFigures: h.significantFigures, + Counts: append([]int64(nil), h.counts...), // copy + } +} + +// Import returns a new Histogram populated from the Snapshot data (which the +// caller must stop accessing). +func Import(s *Snapshot) *Histogram { + h := New(s.LowestTrackableValue, s.HighestTrackableValue, int(s.SignificantFigures)) + h.counts = s.Counts + totalCount := int64(0) + for i := int32(0); i < h.countsLen; i++ { + countAtIndex := h.counts[i] + if countAtIndex > 0 { + totalCount += countAtIndex + } + } + h.totalCount = totalCount + return h +} + +func (h *Histogram) iterator() *iterator { + return &iterator{ + h: h, + subBucketIdx: -1, + } +} + +func (h *Histogram) rIterator() *rIterator { + return &rIterator{ + iterator: iterator{ + h: h, + subBucketIdx: -1, + }, + } +} + +func (h *Histogram) pIterator(ticksPerHalfDistance int32) *pIterator { + return &pIterator{ + iterator: iterator{ + h: h, + subBucketIdx: -1, + }, + ticksPerHalfDistance: ticksPerHalfDistance, + } +} + +func (h *Histogram) sizeOfEquivalentValueRange(v int64) int64 { + bucketIdx := h.getBucketIndex(v) + subBucketIdx := h.getSubBucketIdx(v, bucketIdx) + adjustedBucket := bucketIdx + if subBucketIdx >= h.subBucketCount { + adjustedBucket++ + } + return int64(1) << uint(h.unitMagnitude+int64(adjustedBucket)) +} + +func (h *Histogram) valueFromIndex(bucketIdx, subBucketIdx int32) int64 { + return int64(subBucketIdx) << uint(int64(bucketIdx)+h.unitMagnitude) +} + +func (h *Histogram) lowestEquivalentValue(v int64) int64 { + bucketIdx := h.getBucketIndex(v) + subBucketIdx := h.getSubBucketIdx(v, bucketIdx) + return h.valueFromIndex(bucketIdx, subBucketIdx) +} + +func (h *Histogram) nextNonEquivalentValue(v int64) int64 { + return h.lowestEquivalentValue(v) + h.sizeOfEquivalentValueRange(v) +} + +func (h *Histogram) highestEquivalentValue(v int64) int64 { + return h.nextNonEquivalentValue(v) - 1 +} + +func (h *Histogram) medianEquivalentValue(v int64) int64 { + return h.lowestEquivalentValue(v) + (h.sizeOfEquivalentValueRange(v) >> 1) +} + +func (h *Histogram) getCountAtIndex(bucketIdx, subBucketIdx int32) int64 { + return h.counts[h.countsIndex(bucketIdx, subBucketIdx)] +} + +func (h *Histogram) countsIndex(bucketIdx, subBucketIdx int32) int32 { + bucketBaseIdx := (bucketIdx + 1) << uint(h.subBucketHalfCountMagnitude) + offsetInBucket := subBucketIdx - h.subBucketHalfCount + return bucketBaseIdx + offsetInBucket +} + +func (h *Histogram) getBucketIndex(v int64) int32 { + pow2Ceiling := bitLen(v | h.subBucketMask) + return int32(pow2Ceiling - int64(h.unitMagnitude) - + int64(h.subBucketHalfCountMagnitude+1)) +} + +func (h *Histogram) getSubBucketIdx(v int64, idx int32) int32 { + return int32(v >> uint(int64(idx)+int64(h.unitMagnitude))) +} + +func (h *Histogram) countsIndexFor(v int64) int { + bucketIdx := h.getBucketIndex(v) + subBucketIdx := h.getSubBucketIdx(v, bucketIdx) + return int(h.countsIndex(bucketIdx, subBucketIdx)) +} + +type iterator struct { + h *Histogram + bucketIdx, subBucketIdx int32 + countAtIdx, countToIdx, valueFromIdx int64 + highestEquivalentValue int64 +} + +func (i *iterator) next() bool { + if i.countToIdx >= i.h.totalCount { + return false + } + + // increment bucket + i.subBucketIdx++ + if i.subBucketIdx >= i.h.subBucketCount { + i.subBucketIdx = i.h.subBucketHalfCount + i.bucketIdx++ + } + + if i.bucketIdx >= i.h.bucketCount { + return false + } + + i.countAtIdx = i.h.getCountAtIndex(i.bucketIdx, i.subBucketIdx) + i.countToIdx += i.countAtIdx + i.valueFromIdx = i.h.valueFromIndex(i.bucketIdx, i.subBucketIdx) + i.highestEquivalentValue = i.h.highestEquivalentValue(i.valueFromIdx) + + return true +} + +type rIterator struct { + iterator + countAddedThisStep int64 +} + +func (r *rIterator) next() bool { + for r.iterator.next() { + if r.countAtIdx != 0 { + r.countAddedThisStep = r.countAtIdx + return true + } + } + return false +} + +type pIterator struct { + iterator + seenLastValue bool + ticksPerHalfDistance int32 + percentileToIteratorTo float64 + percentile float64 +} + +func (p *pIterator) next() bool { + if !(p.countToIdx < p.h.totalCount) { + if p.seenLastValue { + return false + } + + p.seenLastValue = true + p.percentile = 100 + + return true + } + + if p.subBucketIdx == -1 && !p.iterator.next() { + return false + } + + var done = false + for !done { + currentPercentile := (100.0 * float64(p.countToIdx)) / float64(p.h.totalCount) + if p.countAtIdx != 0 && p.percentileToIteratorTo <= currentPercentile { + p.percentile = p.percentileToIteratorTo + halfDistance := math.Trunc(math.Pow(2, math.Trunc(math.Log2(100.0/(100.0-p.percentileToIteratorTo)))+1)) + percentileReportingTicks := float64(p.ticksPerHalfDistance) * halfDistance + p.percentileToIteratorTo += 100.0 / percentileReportingTicks + return true + } + done = !p.iterator.next() + } + + return true +} + +func bitLen(x int64) (n int64) { + for ; x >= 0x8000; x >>= 16 { + n += 16 + } + if x >= 0x80 { + x >>= 8 + n += 8 + } + if x >= 0x8 { + x >>= 4 + n += 4 + } + if x >= 0x2 { + x >>= 2 + n += 2 + } + if x >= 0x1 { + n++ + } + return +} diff --git a/template/faaschain/vendor/github.com/codahale/hdrhistogram/hdr_test.go b/template/faaschain/vendor/github.com/codahale/hdrhistogram/hdr_test.go new file mode 100644 index 00000000..309f0ea8 --- /dev/null +++ b/template/faaschain/vendor/github.com/codahale/hdrhistogram/hdr_test.go @@ -0,0 +1,388 @@ +package hdrhistogram_test + +import ( + "math" + "reflect" + "testing" + + "github.com/codahale/hdrhistogram" +) + +func TestHighSigFig(t *testing.T) { + input := []int64{ + 459876, 669187, 711612, 816326, 931423, 1033197, 1131895, 2477317, + 3964974, 12718782, + } + + hist := hdrhistogram.New(459876, 12718782, 5) + for _, sample := range input { + hist.RecordValue(sample) + } + + if v, want := hist.ValueAtQuantile(50), int64(1048575); v != want { + t.Errorf("Median was %v, but expected %v", v, want) + } +} + +func TestValueAtQuantile(t *testing.T) { + h := hdrhistogram.New(1, 10000000, 3) + + for i := 0; i < 1000000; i++ { + if err := h.RecordValue(int64(i)); err != nil { + t.Fatal(err) + } + } + + data := []struct { + q float64 + v int64 + }{ + {q: 50, v: 500223}, + {q: 75, v: 750079}, + {q: 90, v: 900095}, + {q: 95, v: 950271}, + {q: 99, v: 990207}, + {q: 99.9, v: 999423}, + {q: 99.99, v: 999935}, + } + + for _, d := range data { + if v := h.ValueAtQuantile(d.q); v != d.v { + t.Errorf("P%v was %v, but expected %v", d.q, v, d.v) + } + } +} + +func TestMean(t *testing.T) { + h := hdrhistogram.New(1, 10000000, 3) + + for i := 0; i < 1000000; i++ { + if err := h.RecordValue(int64(i)); err != nil { + t.Fatal(err) + } + } + + if v, want := h.Mean(), 500000.013312; v != want { + t.Errorf("Mean was %v, but expected %v", v, want) + } +} + +func TestStdDev(t *testing.T) { + h := hdrhistogram.New(1, 10000000, 3) + + for i := 0; i < 1000000; i++ { + if err := h.RecordValue(int64(i)); err != nil { + t.Fatal(err) + } + } + + if v, want := h.StdDev(), 288675.1403682715; v != want { + t.Errorf("StdDev was %v, but expected %v", v, want) + } +} + +func TestTotalCount(t *testing.T) { + h := hdrhistogram.New(1, 10000000, 3) + + for i := 0; i < 1000000; i++ { + if err := h.RecordValue(int64(i)); err != nil { + t.Fatal(err) + } + if v, want := h.TotalCount(), int64(i+1); v != want { + t.Errorf("TotalCount was %v, but expected %v", v, want) + } + } +} + +func TestMax(t *testing.T) { + h := hdrhistogram.New(1, 10000000, 3) + + for i := 0; i < 1000000; i++ { + if err := h.RecordValue(int64(i)); err != nil { + t.Fatal(err) + } + } + + if v, want := h.Max(), int64(1000447); v != want { + t.Errorf("Max was %v, but expected %v", v, want) + } +} + +func TestReset(t *testing.T) { + h := hdrhistogram.New(1, 10000000, 3) + + for i := 0; i < 1000000; i++ { + if err := h.RecordValue(int64(i)); err != nil { + t.Fatal(err) + } + } + + h.Reset() + + if v, want := h.Max(), int64(0); v != want { + t.Errorf("Max was %v, but expected %v", v, want) + } +} + +func TestMerge(t *testing.T) { + h1 := hdrhistogram.New(1, 1000, 3) + h2 := hdrhistogram.New(1, 1000, 3) + + for i := 0; i < 100; i++ { + if err := h1.RecordValue(int64(i)); err != nil { + t.Fatal(err) + } + } + + for i := 100; i < 200; i++ { + if err := h2.RecordValue(int64(i)); err != nil { + t.Fatal(err) + } + } + + h1.Merge(h2) + + if v, want := h1.ValueAtQuantile(50), int64(99); v != want { + t.Errorf("Median was %v, but expected %v", v, want) + } +} + +func TestMin(t *testing.T) { + h := hdrhistogram.New(1, 10000000, 3) + + for i := 0; i < 1000000; i++ { + if err := h.RecordValue(int64(i)); err != nil { + t.Fatal(err) + } + } + + if v, want := h.Min(), int64(0); v != want { + t.Errorf("Min was %v, but expected %v", v, want) + } +} + +func TestByteSize(t *testing.T) { + h := hdrhistogram.New(1, 100000, 3) + + if v, want := h.ByteSize(), 65604; v != want { + t.Errorf("ByteSize was %v, but expected %d", v, want) + } +} + +func TestRecordCorrectedValue(t *testing.T) { + h := hdrhistogram.New(1, 100000, 3) + + if err := h.RecordCorrectedValue(10, 100); err != nil { + t.Fatal(err) + } + + if v, want := h.ValueAtQuantile(75), int64(10); v != want { + t.Errorf("Corrected value was %v, but expected %v", v, want) + } +} + +func TestRecordCorrectedValueStall(t *testing.T) { + h := hdrhistogram.New(1, 100000, 3) + + if err := h.RecordCorrectedValue(1000, 100); err != nil { + t.Fatal(err) + } + + if v, want := h.ValueAtQuantile(75), int64(800); v != want { + t.Errorf("Corrected value was %v, but expected %v", v, want) + } +} + +func TestCumulativeDistribution(t *testing.T) { + h := hdrhistogram.New(1, 100000000, 3) + + for i := 0; i < 1000000; i++ { + if err := h.RecordValue(int64(i)); err != nil { + t.Fatal(err) + } + } + + actual := h.CumulativeDistribution() + expected := []hdrhistogram.Bracket{ + hdrhistogram.Bracket{Quantile: 0, Count: 1, ValueAt: 0}, + hdrhistogram.Bracket{Quantile: 50, Count: 500224, ValueAt: 500223}, + hdrhistogram.Bracket{Quantile: 75, Count: 750080, ValueAt: 750079}, + hdrhistogram.Bracket{Quantile: 87.5, Count: 875008, ValueAt: 875007}, + hdrhistogram.Bracket{Quantile: 93.75, Count: 937984, ValueAt: 937983}, + hdrhistogram.Bracket{Quantile: 96.875, Count: 969216, ValueAt: 969215}, + hdrhistogram.Bracket{Quantile: 98.4375, Count: 984576, ValueAt: 984575}, + hdrhistogram.Bracket{Quantile: 99.21875, Count: 992256, ValueAt: 992255}, + hdrhistogram.Bracket{Quantile: 99.609375, Count: 996352, ValueAt: 996351}, + hdrhistogram.Bracket{Quantile: 99.8046875, Count: 998400, ValueAt: 998399}, + hdrhistogram.Bracket{Quantile: 99.90234375, Count: 999424, ValueAt: 999423}, + hdrhistogram.Bracket{Quantile: 99.951171875, Count: 999936, ValueAt: 999935}, + hdrhistogram.Bracket{Quantile: 99.9755859375, Count: 999936, ValueAt: 999935}, + hdrhistogram.Bracket{Quantile: 99.98779296875, Count: 999936, ValueAt: 999935}, + hdrhistogram.Bracket{Quantile: 99.993896484375, Count: 1000000, ValueAt: 1000447}, + hdrhistogram.Bracket{Quantile: 100, Count: 1000000, ValueAt: 1000447}, + } + + if !reflect.DeepEqual(actual, expected) { + t.Errorf("CF was %#v, but expected %#v", actual, expected) + } +} + +func TestDistribution(t *testing.T) { + h := hdrhistogram.New(8, 1024, 3) + + for i := 0; i < 1024; i++ { + if err := h.RecordValue(int64(i)); err != nil { + t.Fatal(err) + } + } + + actual := h.Distribution() + if len(actual) != 128 { + t.Errorf("Number of bars seen was %v, expected was 128", len(actual)) + } + for _, b := range actual { + if b.Count != 8 { + t.Errorf("Count per bar seen was %v, expected was 8", b.Count) + } + } +} + +func TestNaN(t *testing.T) { + h := hdrhistogram.New(1, 100000, 3) + if math.IsNaN(h.Mean()) { + t.Error("mean is NaN") + } + if math.IsNaN(h.StdDev()) { + t.Error("stddev is NaN") + } +} + +func TestSignificantFigures(t *testing.T) { + const sigFigs = 4 + h := hdrhistogram.New(1, 10, sigFigs) + if h.SignificantFigures() != sigFigs { + t.Errorf("Significant figures was %v, expected %d", h.SignificantFigures(), sigFigs) + } +} + +func TestLowestTrackableValue(t *testing.T) { + const minVal = 2 + h := hdrhistogram.New(minVal, 10, 3) + if h.LowestTrackableValue() != minVal { + t.Errorf("LowestTrackableValue figures was %v, expected %d", h.LowestTrackableValue(), minVal) + } +} + +func TestHighestTrackableValue(t *testing.T) { + const maxVal = 11 + h := hdrhistogram.New(1, maxVal, 3) + if h.HighestTrackableValue() != maxVal { + t.Errorf("HighestTrackableValue figures was %v, expected %d", h.HighestTrackableValue(), maxVal) + } +} + +func BenchmarkHistogramRecordValue(b *testing.B) { + h := hdrhistogram.New(1, 10000000, 3) + for i := 0; i < 1000000; i++ { + if err := h.RecordValue(int64(i)); err != nil { + b.Fatal(err) + } + } + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + h.RecordValue(100) + } +} + +func BenchmarkNew(b *testing.B) { + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + hdrhistogram.New(1, 120000, 3) // this could track 1ms-2min + } +} + +func TestUnitMagnitudeOverflow(t *testing.T) { + h := hdrhistogram.New(0, 200, 4) + if err := h.RecordValue(11); err != nil { + t.Fatal(err) + } +} + +func TestSubBucketMaskOverflow(t *testing.T) { + hist := hdrhistogram.New(2e7, 1e8, 5) + for _, sample := range [...]int64{1e8, 2e7, 3e7} { + hist.RecordValue(sample) + } + + for q, want := range map[float64]int64{ + 50: 33554431, + 83.33: 33554431, + 83.34: 100663295, + 99: 100663295, + } { + if got := hist.ValueAtQuantile(q); got != want { + t.Errorf("got %d for %fth percentile. want: %d", got, q, want) + } + } +} + +func TestExportImport(t *testing.T) { + min := int64(1) + max := int64(10000000) + sigfigs := 3 + h := hdrhistogram.New(min, max, sigfigs) + for i := 0; i < 1000000; i++ { + if err := h.RecordValue(int64(i)); err != nil { + t.Fatal(err) + } + } + + s := h.Export() + + if v := s.LowestTrackableValue; v != min { + t.Errorf("LowestTrackableValue was %v, but expected %v", v, min) + } + + if v := s.HighestTrackableValue; v != max { + t.Errorf("HighestTrackableValue was %v, but expected %v", v, max) + } + + if v := int(s.SignificantFigures); v != sigfigs { + t.Errorf("SignificantFigures was %v, but expected %v", v, sigfigs) + } + + if imported := hdrhistogram.Import(s); !imported.Equals(h) { + t.Error("Expected Histograms to be equivalent") + } + +} + +func TestEquals(t *testing.T) { + h1 := hdrhistogram.New(1, 10000000, 3) + for i := 0; i < 1000000; i++ { + if err := h1.RecordValue(int64(i)); err != nil { + t.Fatal(err) + } + } + + h2 := hdrhistogram.New(1, 10000000, 3) + for i := 0; i < 10000; i++ { + if err := h1.RecordValue(int64(i)); err != nil { + t.Fatal(err) + } + } + + if h1.Equals(h2) { + t.Error("Expected Histograms to not be equivalent") + } + + h1.Reset() + h2.Reset() + + if !h1.Equals(h2) { + t.Error("Expected Histograms to be equivalent") + } +} diff --git a/template/faaschain/vendor/github.com/codahale/hdrhistogram/window.go b/template/faaschain/vendor/github.com/codahale/hdrhistogram/window.go new file mode 100644 index 00000000..dc43612a --- /dev/null +++ b/template/faaschain/vendor/github.com/codahale/hdrhistogram/window.go @@ -0,0 +1,45 @@ +package hdrhistogram + +// A WindowedHistogram combines histograms to provide windowed statistics. +type WindowedHistogram struct { + idx int + h []Histogram + m *Histogram + + Current *Histogram +} + +// NewWindowed creates a new WindowedHistogram with N underlying histograms with +// the given parameters. +func NewWindowed(n int, minValue, maxValue int64, sigfigs int) *WindowedHistogram { + w := WindowedHistogram{ + idx: -1, + h: make([]Histogram, n), + m: New(minValue, maxValue, sigfigs), + } + + for i := range w.h { + w.h[i] = *New(minValue, maxValue, sigfigs) + } + w.Rotate() + + return &w +} + +// Merge returns a histogram which includes the recorded values from all the +// sections of the window. +func (w *WindowedHistogram) Merge() *Histogram { + w.m.Reset() + for _, h := range w.h { + w.m.Merge(&h) + } + return w.m +} + +// Rotate resets the oldest histogram and rotates it to be used as the current +// histogram. +func (w *WindowedHistogram) Rotate() { + w.idx++ + w.Current = &w.h[w.idx%len(w.h)] + w.Current.Reset() +} diff --git a/template/faaschain/vendor/github.com/codahale/hdrhistogram/window_test.go b/template/faaschain/vendor/github.com/codahale/hdrhistogram/window_test.go new file mode 100644 index 00000000..7e787588 --- /dev/null +++ b/template/faaschain/vendor/github.com/codahale/hdrhistogram/window_test.go @@ -0,0 +1,64 @@ +package hdrhistogram_test + +import ( + "testing" + + "github.com/codahale/hdrhistogram" +) + +func TestWindowedHistogram(t *testing.T) { + w := hdrhistogram.NewWindowed(2, 1, 1000, 3) + + for i := 0; i < 100; i++ { + w.Current.RecordValue(int64(i)) + } + w.Rotate() + + for i := 100; i < 200; i++ { + w.Current.RecordValue(int64(i)) + } + w.Rotate() + + for i := 200; i < 300; i++ { + w.Current.RecordValue(int64(i)) + } + + if v, want := w.Merge().ValueAtQuantile(50), int64(199); v != want { + t.Errorf("Median was %v, but expected %v", v, want) + } +} + +func BenchmarkWindowedHistogramRecordAndRotate(b *testing.B) { + w := hdrhistogram.NewWindowed(3, 1, 10000000, 3) + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + if err := w.Current.RecordValue(100); err != nil { + b.Fatal(err) + } + + if i%100000 == 1 { + w.Rotate() + } + } +} + +func BenchmarkWindowedHistogramMerge(b *testing.B) { + w := hdrhistogram.NewWindowed(3, 1, 10000000, 3) + for i := 0; i < 10000000; i++ { + if err := w.Current.RecordValue(100); err != nil { + b.Fatal(err) + } + + if i%100000 == 1 { + w.Rotate() + } + } + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + w.Merge() + } +} diff --git a/template/faaschain/vendor/github.com/opentracing/opentracing-go/.github/ISSUE_TEMPLATE.md b/template/faaschain/vendor/github.com/opentracing/opentracing-go/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..99a3cc62 --- /dev/null +++ b/template/faaschain/vendor/github.com/opentracing/opentracing-go/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,23 @@ + + + +## Use Case +Please explain your user story, what you are trying to do, which problem you are trying to solve. + +## Problem +What prevents you from solving your use case. + +## Proposal +A proposal that from your POV would solve the problem or improve the existing situation. If you don't have a proposed +solution, that's fine too. + +## Questions to address (if any) +Questions that should be answered during the discussion of this issue before +jumping into code. diff --git a/template/faaschain/vendor/github.com/opentracing/opentracing-go/.gitignore b/template/faaschain/vendor/github.com/opentracing/opentracing-go/.gitignore new file mode 100644 index 00000000..565f0f73 --- /dev/null +++ b/template/faaschain/vendor/github.com/opentracing/opentracing-go/.gitignore @@ -0,0 +1,13 @@ +# IntelliJ project files +.idea/ +opentracing-go.iml +opentracing-go.ipr +opentracing-go.iws + +# Test results +*.cov +*.html +test.log + +# Build dir +build/ diff --git a/template/faaschain/vendor/github.com/opentracing/opentracing-go/.travis.yml b/template/faaschain/vendor/github.com/opentracing/opentracing-go/.travis.yml new file mode 100644 index 00000000..8a4d4cdf --- /dev/null +++ b/template/faaschain/vendor/github.com/opentracing/opentracing-go/.travis.yml @@ -0,0 +1,15 @@ +language: go + +go: + - 1.7.x + - 1.8.x + - 1.9.x + - tip + +install: + - go get -u github.com/golang/lint/... + - go get -u github.com/stretchr/testify/... + - go get -u golang.org/x/net/context +script: + - make test lint + - go build ./... diff --git a/template/faaschain/vendor/github.com/opentracing/opentracing-go/CHANGELOG.md b/template/faaschain/vendor/github.com/opentracing/opentracing-go/CHANGELOG.md new file mode 100644 index 00000000..1fc9fdf7 --- /dev/null +++ b/template/faaschain/vendor/github.com/opentracing/opentracing-go/CHANGELOG.md @@ -0,0 +1,14 @@ +Changes by Version +================== + +1.1.0 (unreleased) +------------------- + +- Deprecate InitGlobalTracer() in favor of SetGlobalTracer() + + +1.0.0 (2016-09-26) +------------------- + +- This release implements OpenTracing Specification 1.0 (http://opentracing.io/spec) + diff --git a/template/faaschain/vendor/github.com/opentracing/opentracing-go/LICENSE b/template/faaschain/vendor/github.com/opentracing/opentracing-go/LICENSE new file mode 100644 index 00000000..f0027349 --- /dev/null +++ b/template/faaschain/vendor/github.com/opentracing/opentracing-go/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor 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, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2016 The OpenTracing Authors + + 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/template/faaschain/vendor/github.com/opentracing/opentracing-go/Makefile b/template/faaschain/vendor/github.com/opentracing/opentracing-go/Makefile new file mode 100644 index 00000000..d49a5c0d --- /dev/null +++ b/template/faaschain/vendor/github.com/opentracing/opentracing-go/Makefile @@ -0,0 +1,32 @@ +PACKAGES := . ./mocktracer/... ./ext/... + +.DEFAULT_GOAL := test-and-lint + +.PHONE: test-and-lint + +test-and-lint: test lint + +.PHONY: test +test: + go test -v -cover -race ./... + +cover: + @rm -rf cover-all.out + $(foreach pkg, $(PACKAGES), $(MAKE) cover-pkg PKG=$(pkg) || true;) + @grep mode: cover.out > coverage.out + @cat cover-all.out >> coverage.out + go tool cover -html=coverage.out -o cover.html + @rm -rf cover.out cover-all.out coverage.out + +cover-pkg: + go test -coverprofile cover.out $(PKG) + @grep -v mode: cover.out >> cover-all.out + +.PHONY: lint +lint: + go fmt ./... + golint ./... + @# Run again with magic to exit non-zero if golint outputs anything. + @! (golint ./... | read dummy) + go vet ./... + diff --git a/template/faaschain/vendor/github.com/opentracing/opentracing-go/README.md b/template/faaschain/vendor/github.com/opentracing/opentracing-go/README.md new file mode 100644 index 00000000..007ee237 --- /dev/null +++ b/template/faaschain/vendor/github.com/opentracing/opentracing-go/README.md @@ -0,0 +1,171 @@ +[![Gitter chat](http://img.shields.io/badge/gitter-join%20chat%20%E2%86%92-brightgreen.svg)](https://gitter.im/opentracing/public) [![Build Status](https://travis-ci.org/opentracing/opentracing-go.svg?branch=master)](https://travis-ci.org/opentracing/opentracing-go) [![GoDoc](https://godoc.org/github.com/opentracing/opentracing-go?status.svg)](http://godoc.org/github.com/opentracing/opentracing-go) +[![Sourcegraph Badge](https://sourcegraph.com/github.com/opentracing/opentracing-go/-/badge.svg)](https://sourcegraph.com/github.com/opentracing/opentracing-go?badge) + +# OpenTracing API for Go + +This package is a Go platform API for OpenTracing. + +## Required Reading + +In order to understand the Go platform API, one must first be familiar with the +[OpenTracing project](http://opentracing.io) and +[terminology](http://opentracing.io/documentation/pages/spec.html) more specifically. + +## API overview for those adding instrumentation + +Everyday consumers of this `opentracing` package really only need to worry +about a couple of key abstractions: the `StartSpan` function, the `Span` +interface, and binding a `Tracer` at `main()`-time. Here are code snippets +demonstrating some important use cases. + +#### Singleton initialization + +The simplest starting point is `./default_tracer.go`. As early as possible, call + +```go + import "github.com/opentracing/opentracing-go" + import ".../some_tracing_impl" + + func main() { + opentracing.SetGlobalTracer( + // tracing impl specific: + some_tracing_impl.New(...), + ) + ... + } +``` + +#### Non-Singleton initialization + +If you prefer direct control to singletons, manage ownership of the +`opentracing.Tracer` implementation explicitly. + +#### Creating a Span given an existing Go `context.Context` + +If you use `context.Context` in your application, OpenTracing's Go library will +happily rely on it for `Span` propagation. To start a new (blocking child) +`Span`, you can use `StartSpanFromContext`. + +```go + func xyz(ctx context.Context, ...) { + ... + span, ctx := opentracing.StartSpanFromContext(ctx, "operation_name") + defer span.Finish() + span.LogFields( + log.String("event", "soft error"), + log.String("type", "cache timeout"), + log.Int("waited.millis", 1500)) + ... + } +``` + +#### Starting an empty trace by creating a "root span" + +It's always possible to create a "root" `Span` with no parent or other causal +reference. + +```go + func xyz() { + ... + sp := opentracing.StartSpan("operation_name") + defer sp.Finish() + ... + } +``` + +#### Creating a (child) Span given an existing (parent) Span + +```go + func xyz(parentSpan opentracing.Span, ...) { + ... + sp := opentracing.StartSpan( + "operation_name", + opentracing.ChildOf(parentSpan.Context())) + defer sp.Finish() + ... + } +``` + +#### Serializing to the wire + +```go + func makeSomeRequest(ctx context.Context) ... { + if span := opentracing.SpanFromContext(ctx); span != nil { + httpClient := &http.Client{} + httpReq, _ := http.NewRequest("GET", "http://myservice/", nil) + + // Transmit the span's TraceContext as HTTP headers on our + // outbound request. + opentracing.GlobalTracer().Inject( + span.Context(), + opentracing.HTTPHeaders, + opentracing.HTTPHeadersCarrier(httpReq.Header)) + + resp, err := httpClient.Do(httpReq) + ... + } + ... + } +``` + +#### Deserializing from the wire + +```go + http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { + var serverSpan opentracing.Span + appSpecificOperationName := ... + wireContext, err := opentracing.GlobalTracer().Extract( + opentracing.HTTPHeaders, + opentracing.HTTPHeadersCarrier(req.Header)) + if err != nil { + // Optionally record something about err here + } + + // Create the span referring to the RPC client if available. + // If wireContext == nil, a root span will be created. + serverSpan = opentracing.StartSpan( + appSpecificOperationName, + ext.RPCServerOption(wireContext)) + + defer serverSpan.Finish() + + ctx := opentracing.ContextWithSpan(context.Background(), serverSpan) + ... + } +``` + +#### Conditionally capture a field using `log.Noop` + +In some situations, you may want to dynamically decide whether or not +to log a field. For example, you may want to capture additional data, +such as a customer ID, in non-production environments: + +```go + func Customer(order *Order) log.Field { + if os.Getenv("ENVIRONMENT") == "dev" { + return log.String("customer", order.Customer.ID) + } + return log.Noop() + } +``` + +#### Goroutine-safety + +The entire public API is goroutine-safe and does not require external +synchronization. + +## API pointers for those implementing a tracing system + +Tracing system implementors may be able to reuse or copy-paste-modify the `basictracer` package, found [here](https://github.com/opentracing/basictracer-go). In particular, see `basictracer.New(...)`. + +## API compatibility + +For the time being, "mild" backwards-incompatible changes may be made without changing the major version number. As OpenTracing and `opentracing-go` mature, backwards compatibility will become more of a priority. + +## Tracer test suite + +A test suite is available in the [harness](https://godoc.org/github.com/opentracing/opentracing-go/harness) package that can assist Tracer implementors to assert that their Tracer is working correctly. + +## Licensing + +[Apache 2.0 License](./LICENSE). diff --git a/template/faaschain/vendor/github.com/opentracing/opentracing-go/ext/tags.go b/template/faaschain/vendor/github.com/opentracing/opentracing-go/ext/tags.go new file mode 100644 index 00000000..8800129a --- /dev/null +++ b/template/faaschain/vendor/github.com/opentracing/opentracing-go/ext/tags.go @@ -0,0 +1,210 @@ +package ext + +import opentracing "github.com/opentracing/opentracing-go" + +// These constants define common tag names recommended for better portability across +// tracing systems and languages/platforms. +// +// The tag names are defined as typed strings, so that in addition to the usual use +// +// span.setTag(TagName, value) +// +// they also support value type validation via this additional syntax: +// +// TagName.Set(span, value) +// +var ( + ////////////////////////////////////////////////////////////////////// + // SpanKind (client/server or producer/consumer) + ////////////////////////////////////////////////////////////////////// + + // SpanKind hints at relationship between spans, e.g. client/server + SpanKind = spanKindTagName("span.kind") + + // SpanKindRPCClient marks a span representing the client-side of an RPC + // or other remote call + SpanKindRPCClientEnum = SpanKindEnum("client") + SpanKindRPCClient = opentracing.Tag{Key: string(SpanKind), Value: SpanKindRPCClientEnum} + + // SpanKindRPCServer marks a span representing the server-side of an RPC + // or other remote call + SpanKindRPCServerEnum = SpanKindEnum("server") + SpanKindRPCServer = opentracing.Tag{Key: string(SpanKind), Value: SpanKindRPCServerEnum} + + // SpanKindProducer marks a span representing the producer-side of a + // message bus + SpanKindProducerEnum = SpanKindEnum("producer") + SpanKindProducer = opentracing.Tag{Key: string(SpanKind), Value: SpanKindProducerEnum} + + // SpanKindConsumer marks a span representing the consumer-side of a + // message bus + SpanKindConsumerEnum = SpanKindEnum("consumer") + SpanKindConsumer = opentracing.Tag{Key: string(SpanKind), Value: SpanKindConsumerEnum} + + ////////////////////////////////////////////////////////////////////// + // Component name + ////////////////////////////////////////////////////////////////////// + + // Component is a low-cardinality identifier of the module, library, + // or package that is generating a span. + Component = stringTagName("component") + + ////////////////////////////////////////////////////////////////////// + // Sampling hint + ////////////////////////////////////////////////////////////////////// + + // SamplingPriority determines the priority of sampling this Span. + SamplingPriority = uint16TagName("sampling.priority") + + ////////////////////////////////////////////////////////////////////// + // Peer tags. These tags can be emitted by either client-side of + // server-side to describe the other side/service in a peer-to-peer + // communications, like an RPC call. + ////////////////////////////////////////////////////////////////////// + + // PeerService records the service name of the peer. + PeerService = stringTagName("peer.service") + + // PeerAddress records the address name of the peer. This may be a "ip:port", + // a bare "hostname", a FQDN or even a database DSN substring + // like "mysql://username@127.0.0.1:3306/dbname" + PeerAddress = stringTagName("peer.address") + + // PeerHostname records the host name of the peer + PeerHostname = stringTagName("peer.hostname") + + // PeerHostIPv4 records IP v4 host address of the peer + PeerHostIPv4 = ipv4Tag("peer.ipv4") + + // PeerHostIPv6 records IP v6 host address of the peer + PeerHostIPv6 = stringTagName("peer.ipv6") + + // PeerPort records port number of the peer + PeerPort = uint16TagName("peer.port") + + ////////////////////////////////////////////////////////////////////// + // HTTP Tags + ////////////////////////////////////////////////////////////////////// + + // HTTPUrl should be the URL of the request being handled in this segment + // of the trace, in standard URI format. The protocol is optional. + HTTPUrl = stringTagName("http.url") + + // HTTPMethod is the HTTP method of the request, and is case-insensitive. + HTTPMethod = stringTagName("http.method") + + // HTTPStatusCode is the numeric HTTP status code (200, 404, etc) of the + // HTTP response. + HTTPStatusCode = uint16TagName("http.status_code") + + ////////////////////////////////////////////////////////////////////// + // DB Tags + ////////////////////////////////////////////////////////////////////// + + // DBInstance is database instance name. + DBInstance = stringTagName("db.instance") + + // DBStatement is a database statement for the given database type. + // It can be a query or a prepared statement (i.e., before substitution). + DBStatement = stringTagName("db.statement") + + // DBType is a database type. For any SQL database, "sql". + // For others, the lower-case database category, e.g. "redis" + DBType = stringTagName("db.type") + + // DBUser is a username for accessing database. + DBUser = stringTagName("db.user") + + ////////////////////////////////////////////////////////////////////// + // Message Bus Tag + ////////////////////////////////////////////////////////////////////// + + // MessageBusDestination is an address at which messages can be exchanged + MessageBusDestination = stringTagName("message_bus.destination") + + ////////////////////////////////////////////////////////////////////// + // Error Tag + ////////////////////////////////////////////////////////////////////// + + // Error indicates that operation represented by the span resulted in an error. + Error = boolTagName("error") +) + +// --- + +// SpanKindEnum represents common span types +type SpanKindEnum string + +type spanKindTagName string + +// Set adds a string tag to the `span` +func (tag spanKindTagName) Set(span opentracing.Span, value SpanKindEnum) { + span.SetTag(string(tag), value) +} + +type rpcServerOption struct { + clientContext opentracing.SpanContext +} + +func (r rpcServerOption) Apply(o *opentracing.StartSpanOptions) { + if r.clientContext != nil { + opentracing.ChildOf(r.clientContext).Apply(o) + } + SpanKindRPCServer.Apply(o) +} + +// RPCServerOption returns a StartSpanOption appropriate for an RPC server span +// with `client` representing the metadata for the remote peer Span if available. +// In case client == nil, due to the client not being instrumented, this RPC +// server span will be a root span. +func RPCServerOption(client opentracing.SpanContext) opentracing.StartSpanOption { + return rpcServerOption{client} +} + +// --- + +type stringTagName string + +// Set adds a string tag to the `span` +func (tag stringTagName) Set(span opentracing.Span, value string) { + span.SetTag(string(tag), value) +} + +// --- + +type uint32TagName string + +// Set adds a uint32 tag to the `span` +func (tag uint32TagName) Set(span opentracing.Span, value uint32) { + span.SetTag(string(tag), value) +} + +// --- + +type uint16TagName string + +// Set adds a uint16 tag to the `span` +func (tag uint16TagName) Set(span opentracing.Span, value uint16) { + span.SetTag(string(tag), value) +} + +// --- + +type boolTagName string + +// Add adds a bool tag to the `span` +func (tag boolTagName) Set(span opentracing.Span, value bool) { + span.SetTag(string(tag), value) +} + +type ipv4Tag string + +// Set adds IP v4 host address of the peer as an uint32 value to the `span`, keep this for backward and zipkin compatibility +func (tag ipv4Tag) Set(span opentracing.Span, value uint32) { + span.SetTag(string(tag), value) +} + +// SetString records IP v4 host address of the peer as a .-separated tuple to the `span`. E.g., "127.0.0.1" +func (tag ipv4Tag) SetString(span opentracing.Span, value string) { + span.SetTag(string(tag), value) +} diff --git a/template/faaschain/vendor/github.com/opentracing/opentracing-go/ext/tags_test.go b/template/faaschain/vendor/github.com/opentracing/opentracing-go/ext/tags_test.go new file mode 100644 index 00000000..ea9af335 --- /dev/null +++ b/template/faaschain/vendor/github.com/opentracing/opentracing-go/ext/tags_test.go @@ -0,0 +1,148 @@ +package ext_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/opentracing/opentracing-go/mocktracer" +) + +func TestPeerTags(t *testing.T) { + if ext.PeerService != "peer.service" { + t.Fatalf("Invalid PeerService %v", ext.PeerService) + } + tracer := mocktracer.New() + span := tracer.StartSpan("my-trace") + ext.PeerService.Set(span, "my-service") + ext.PeerAddress.Set(span, "my-hostname:8080") + ext.PeerHostname.Set(span, "my-hostname") + ext.PeerHostIPv4.Set(span, uint32(127<<24|1)) + ext.PeerHostIPv6.Set(span, "::") + ext.PeerPort.Set(span, uint16(8080)) + ext.SamplingPriority.Set(span, uint16(1)) + ext.SpanKind.Set(span, ext.SpanKindRPCServerEnum) + ext.SpanKindRPCClient.Set(span) + span.Finish() + + rawSpan := tracer.FinishedSpans()[0] + assert.Equal(t, map[string]interface{}{ + "peer.service": "my-service", + "peer.address": "my-hostname:8080", + "peer.hostname": "my-hostname", + "peer.ipv4": uint32(127<<24 | 1), + "peer.ipv6": "::", + "peer.port": uint16(8080), + "span.kind": ext.SpanKindRPCClientEnum, + }, rawSpan.Tags()) + assert.True(t, span.Context().(mocktracer.MockSpanContext).Sampled) + ext.SamplingPriority.Set(span, uint16(0)) + assert.False(t, span.Context().(mocktracer.MockSpanContext).Sampled) +} + +func TestHTTPTags(t *testing.T) { + tracer := mocktracer.New() + span := tracer.StartSpan("my-trace", ext.SpanKindRPCServer) + ext.HTTPUrl.Set(span, "test.biz/uri?protocol=false") + ext.HTTPMethod.Set(span, "GET") + ext.HTTPStatusCode.Set(span, 301) + span.Finish() + + rawSpan := tracer.FinishedSpans()[0] + assert.Equal(t, map[string]interface{}{ + "http.url": "test.biz/uri?protocol=false", + "http.method": "GET", + "http.status_code": uint16(301), + "span.kind": ext.SpanKindRPCServerEnum, + }, rawSpan.Tags()) +} + +func TestDBTags(t *testing.T) { + tracer := mocktracer.New() + span := tracer.StartSpan("my-trace", ext.SpanKindRPCClient) + ext.DBInstance.Set(span, "127.0.0.1:3306/customers") + ext.DBStatement.Set(span, "SELECT * FROM user_table") + ext.DBType.Set(span, "sql") + ext.DBUser.Set(span, "customer_user") + span.Finish() + + rawSpan := tracer.FinishedSpans()[0] + assert.Equal(t, map[string]interface{}{ + "db.instance": "127.0.0.1:3306/customers", + "db.statement": "SELECT * FROM user_table", + "db.type": "sql", + "db.user": "customer_user", + "span.kind": ext.SpanKindRPCClientEnum, + }, rawSpan.Tags()) +} + +func TestMiscTags(t *testing.T) { + tracer := mocktracer.New() + span := tracer.StartSpan("my-trace") + ext.Component.Set(span, "my-awesome-library") + ext.SamplingPriority.Set(span, 1) + ext.Error.Set(span, true) + + span.Finish() + + rawSpan := tracer.FinishedSpans()[0] + assert.Equal(t, map[string]interface{}{ + "component": "my-awesome-library", + "error": true, + }, rawSpan.Tags()) +} + +func TestRPCServerOption(t *testing.T) { + tracer := mocktracer.New() + parent := tracer.StartSpan("my-trace") + parent.SetBaggageItem("bag", "gage") + + carrier := opentracing.HTTPHeadersCarrier{} + err := tracer.Inject(parent.Context(), opentracing.HTTPHeaders, carrier) + if err != nil { + t.Fatal(err) + } + + parCtx, err := tracer.Extract(opentracing.HTTPHeaders, carrier) + if err != nil { + t.Fatal(err) + } + + tracer.StartSpan("my-child", ext.RPCServerOption(parCtx)).Finish() + + rawSpan := tracer.FinishedSpans()[0] + assert.Equal(t, map[string]interface{}{ + "span.kind": ext.SpanKindRPCServerEnum, + }, rawSpan.Tags()) + assert.Equal(t, map[string]string{ + "bag": "gage", + }, rawSpan.Context().(mocktracer.MockSpanContext).Baggage) +} + +func TestMessageBusProducerTags(t *testing.T) { + tracer := mocktracer.New() + span := tracer.StartSpan("my-trace", ext.SpanKindProducer) + ext.MessageBusDestination.Set(span, "topic name") + span.Finish() + + rawSpan := tracer.FinishedSpans()[0] + assert.Equal(t, map[string]interface{}{ + "message_bus.destination": "topic name", + "span.kind": ext.SpanKindProducerEnum, + }, rawSpan.Tags()) +} + +func TestMessageBusConsumerTags(t *testing.T) { + tracer := mocktracer.New() + span := tracer.StartSpan("my-trace", ext.SpanKindConsumer) + ext.MessageBusDestination.Set(span, "topic name") + span.Finish() + + rawSpan := tracer.FinishedSpans()[0] + assert.Equal(t, map[string]interface{}{ + "message_bus.destination": "topic name", + "span.kind": ext.SpanKindConsumerEnum, + }, rawSpan.Tags()) +} diff --git a/template/faaschain/vendor/github.com/opentracing/opentracing-go/globaltracer.go b/template/faaschain/vendor/github.com/opentracing/opentracing-go/globaltracer.go new file mode 100644 index 00000000..8c8e793f --- /dev/null +++ b/template/faaschain/vendor/github.com/opentracing/opentracing-go/globaltracer.go @@ -0,0 +1,32 @@ +package opentracing + +var ( + globalTracer Tracer = NoopTracer{} +) + +// SetGlobalTracer sets the [singleton] opentracing.Tracer returned by +// GlobalTracer(). Those who use GlobalTracer (rather than directly manage an +// opentracing.Tracer instance) should call SetGlobalTracer as early as +// possible in main(), prior to calling the `StartSpan` global func below. +// Prior to calling `SetGlobalTracer`, any Spans started via the `StartSpan` +// (etc) globals are noops. +func SetGlobalTracer(tracer Tracer) { + globalTracer = tracer +} + +// GlobalTracer returns the global singleton `Tracer` implementation. +// Before `SetGlobalTracer()` is called, the `GlobalTracer()` is a noop +// implementation that drops all data handed to it. +func GlobalTracer() Tracer { + return globalTracer +} + +// StartSpan defers to `Tracer.StartSpan`. See `GlobalTracer()`. +func StartSpan(operationName string, opts ...StartSpanOption) Span { + return globalTracer.StartSpan(operationName, opts...) +} + +// InitGlobalTracer is deprecated. Please use SetGlobalTracer. +func InitGlobalTracer(tracer Tracer) { + SetGlobalTracer(tracer) +} diff --git a/template/faaschain/vendor/github.com/opentracing/opentracing-go/gocontext.go b/template/faaschain/vendor/github.com/opentracing/opentracing-go/gocontext.go new file mode 100644 index 00000000..05a62e70 --- /dev/null +++ b/template/faaschain/vendor/github.com/opentracing/opentracing-go/gocontext.go @@ -0,0 +1,54 @@ +package opentracing + +import "context" + +type contextKey struct{} + +var activeSpanKey = contextKey{} + +// ContextWithSpan returns a new `context.Context` that holds a reference to +// `span`'s SpanContext. +func ContextWithSpan(ctx context.Context, span Span) context.Context { + return context.WithValue(ctx, activeSpanKey, span) +} + +// SpanFromContext returns the `Span` previously associated with `ctx`, or +// `nil` if no such `Span` could be found. +// +// NOTE: context.Context != SpanContext: the former is Go's intra-process +// context propagation mechanism, and the latter houses OpenTracing's per-Span +// identity and baggage information. +func SpanFromContext(ctx context.Context) Span { + val := ctx.Value(activeSpanKey) + if sp, ok := val.(Span); ok { + return sp + } + return nil +} + +// StartSpanFromContext starts and returns a Span with `operationName`, using +// any Span found within `ctx` as a ChildOfRef. If no such parent could be +// found, StartSpanFromContext creates a root (parentless) Span. +// +// The second return value is a context.Context object built around the +// returned Span. +// +// Example usage: +// +// SomeFunction(ctx context.Context, ...) { +// sp, ctx := opentracing.StartSpanFromContext(ctx, "SomeFunction") +// defer sp.Finish() +// ... +// } +func StartSpanFromContext(ctx context.Context, operationName string, opts ...StartSpanOption) (Span, context.Context) { + return startSpanFromContextWithTracer(ctx, GlobalTracer(), operationName, opts...) +} + +// startSpanFromContextWithTracer is factored out for testing purposes. +func startSpanFromContextWithTracer(ctx context.Context, tracer Tracer, operationName string, opts ...StartSpanOption) (Span, context.Context) { + if parentSpan := SpanFromContext(ctx); parentSpan != nil { + opts = append(opts, ChildOf(parentSpan.Context())) + } + span := tracer.StartSpan(operationName, opts...) + return span, ContextWithSpan(ctx, span) +} diff --git a/template/faaschain/vendor/github.com/opentracing/opentracing-go/gocontext_test.go b/template/faaschain/vendor/github.com/opentracing/opentracing-go/gocontext_test.go new file mode 100644 index 00000000..1e99deaa --- /dev/null +++ b/template/faaschain/vendor/github.com/opentracing/opentracing-go/gocontext_test.go @@ -0,0 +1,81 @@ +package opentracing + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestContextWithSpan(t *testing.T) { + span := &noopSpan{} + ctx := ContextWithSpan(context.Background(), span) + span2 := SpanFromContext(ctx) + if span != span2 { + t.Errorf("Not the same span returned from context, expected=%+v, actual=%+v", span, span2) + } + + ctx = context.Background() + span2 = SpanFromContext(ctx) + if span2 != nil { + t.Errorf("Expected nil span, found %+v", span2) + } + + ctx = ContextWithSpan(ctx, span) + span2 = SpanFromContext(ctx) + if span != span2 { + t.Errorf("Not the same span returned from context, expected=%+v, actual=%+v", span, span2) + } +} + +func TestStartSpanFromContext(t *testing.T) { + testTracer := testTracer{} + + // Test the case where there *is* a Span in the Context. + { + parentSpan := &testSpan{} + parentCtx := ContextWithSpan(context.Background(), parentSpan) + childSpan, childCtx := startSpanFromContextWithTracer(parentCtx, testTracer, "child") + if !childSpan.Context().(testSpanContext).HasParent { + t.Errorf("Failed to find parent: %v", childSpan) + } + if !childSpan.(testSpan).Equal(SpanFromContext(childCtx)) { + t.Errorf("Unable to find child span in context: %v", childCtx) + } + } + + // Test the case where there *is not* a Span in the Context. + { + emptyCtx := context.Background() + childSpan, childCtx := startSpanFromContextWithTracer(emptyCtx, testTracer, "child") + if childSpan.Context().(testSpanContext).HasParent { + t.Errorf("Should not have found parent: %v", childSpan) + } + if !childSpan.(testSpan).Equal(SpanFromContext(childCtx)) { + t.Errorf("Unable to find child span in context: %v", childCtx) + } + } +} + +func TestStartSpanFromContextOptions(t *testing.T) { + testTracer := testTracer{} + + // Test options are passed to tracer + + startTime := time.Now().Add(-10 * time.Second) // ten seconds ago + span, ctx := startSpanFromContextWithTracer( + context.Background(), testTracer, "parent", StartTime(startTime), Tag{"component", "test"}) + + assert.Equal(t, "test", span.(testSpan).Tags["component"]) + assert.Equal(t, startTime, span.(testSpan).StartTime) + + // Test it also works for a child span + + childStartTime := startTime.Add(3 * time.Second) + childSpan, _ := startSpanFromContextWithTracer( + ctx, testTracer, "child", StartTime(childStartTime)) + + assert.Equal(t, childSpan.(testSpan).Tags["component"], nil) + assert.Equal(t, childSpan.(testSpan).StartTime, childStartTime) +} diff --git a/template/faaschain/vendor/github.com/opentracing/opentracing-go/harness/api_checkers.go b/template/faaschain/vendor/github.com/opentracing/opentracing-go/harness/api_checkers.go new file mode 100644 index 00000000..ce7950bc --- /dev/null +++ b/template/faaschain/vendor/github.com/opentracing/opentracing-go/harness/api_checkers.go @@ -0,0 +1,472 @@ +/* + +Package harness provides a suite of API compatibility checks. They were originally ported from the +OpenTracing Python library's "harness" module. + +To run this test suite against your tracer, call harness.RunAPIChecks and provide it a function +that returns a Tracer implementation and a function to call to close it. The function will be +called to create a new tracer before each test in the suite is run, and the returned closer function +will be called after each test is finished. + +Several options provide additional checks for your Tracer's behavior: CheckBaggageValues(true) +indicates your tracer supports baggage propagation, CheckExtract(true) tells the suite to test if +the Tracer can extract a trace context from text and binary carriers, and CheckInject(true) tests +if the Tracer can inject the trace context into a carrier. + +The UseProbe option provides an APICheckProbe implementation that allows the test suite to +additionally check if two Spans are part of the same trace, and if a Span and a SpanContext +are part of the same trace. Implementing an APICheckProbe provides additional assertions that +your tracer is working properly. + +*/ +package harness + +import ( + "bytes" + "testing" + "time" + + opentracing "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/log" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +// APICheckCapabilities describes capabilities of a Tracer that should be checked by APICheckSuite. +type APICheckCapabilities struct { + CheckBaggageValues bool // whether to check for propagation of baggage values + CheckExtract bool // whether to check if extracting contexts from carriers works + CheckInject bool // whether to check if injecting contexts works + Probe APICheckProbe // optional interface providing methods to check recorded data +} + +// APICheckProbe exposes methods for testing data recorded by a Tracer. +type APICheckProbe interface { + // SameTrace helps tests assert that this tracer's spans are from the same trace. + SameTrace(first, second opentracing.Span) bool + // SameSpanContext helps tests assert that a span and a context are from the same trace and span. + SameSpanContext(opentracing.Span, opentracing.SpanContext) bool +} + +// APICheckSuite is a testify suite for checking a Tracer against the OpenTracing API. +type APICheckSuite struct { + suite.Suite + opts APICheckCapabilities + newTracer func() (tracer opentracing.Tracer, closer func()) + tracer opentracing.Tracer + closer func() +} + +// RunAPIChecks runs a test suite to check a Tracer against the OpenTracing API. +// It is provided a function that will be executed to create and destroy a tracer for each test +// in the suite, and the given APICheckOption functional options `opts`. +func RunAPIChecks( + t *testing.T, + newTracer func() (tracer opentracing.Tracer, closer func()), + opts ...APICheckOption, +) { + s := &APICheckSuite{newTracer: newTracer} + for _, opt := range opts { + opt(s) + } + suite.Run(t, s) +} + +// APICheckOption instances may be passed to NewAPICheckSuite. +type APICheckOption func(*APICheckSuite) + +// CheckBaggageValues returns an option that sets whether to check for propagation of baggage values. +func CheckBaggageValues(val bool) APICheckOption { + return func(s *APICheckSuite) { + s.opts.CheckBaggageValues = val + } +} + +// CheckExtract returns an option that sets whether to check if extracting contexts from carriers works. +func CheckExtract(val bool) APICheckOption { + return func(s *APICheckSuite) { + s.opts.CheckExtract = val + } +} + +// CheckInject returns an option that sets whether to check if injecting contexts works. +func CheckInject(val bool) APICheckOption { + return func(s *APICheckSuite) { + s.opts.CheckInject = val + } +} + +// CheckEverything returns an option that enables all API checks. +func CheckEverything() APICheckOption { + return func(s *APICheckSuite) { + s.opts.CheckBaggageValues = true + s.opts.CheckExtract = true + s.opts.CheckInject = true + } +} + +// UseProbe returns an option that specifies an APICheckProbe implementation to use. +func UseProbe(probe APICheckProbe) APICheckOption { + return func(s *APICheckSuite) { + s.opts.Probe = probe + } +} + +// SetupTest creates a tracer for this specific test invocation. +func (s *APICheckSuite) SetupTest() { + s.tracer, s.closer = s.newTracer() + if s.tracer == nil { + s.T().Fatalf("newTracer returned nil Tracer") + } +} + +// TearDownTest closes the tracer, and clears the test-specific tracer. +func (s *APICheckSuite) TearDownTest() { + if s.closer != nil { + s.closer() + } + s.tracer, s.closer = nil, nil +} + +// TestStartSpan checks if a Tracer can start a span and calls some span API methods. +func (s *APICheckSuite) TestStartSpan() { + span := s.tracer.StartSpan( + "Fry", + opentracing.Tag{Key: "birthday", Value: "August 14 1974"}) + span.LogFields( + log.String("hospital", "Brooklyn Pre-Med Hospital"), + log.String("city", "Old New York")) + span.Finish() +} + +// TestStartSpanWithParent checks if a Tracer can start a span with a specified parent. +func (s *APICheckSuite) TestStartSpanWithParent() { + parentSpan := s.tracer.StartSpan("Turanga Munda") + s.NotNil(parentSpan) + + childFns := []func(opentracing.SpanContext) opentracing.SpanReference{ + opentracing.ChildOf, + opentracing.FollowsFrom, + } + for _, childFn := range childFns { + span := s.tracer.StartSpan( + "Leela", + childFn(parentSpan.Context()), + opentracing.Tag{Key: "birthplace", Value: "sewers"}) + span.Finish() + if s.opts.Probe != nil { + s.True(s.opts.Probe.SameTrace(parentSpan, span)) + } else { + s.T().Log("harness.Probe not specified, skipping") + } + } + + parentSpan.Finish() +} + +// TestSetOperationName attempts to set the operation name on a span after it has been created. +func (s *APICheckSuite) TestSetOperationName() { + span := s.tracer.StartSpan("").SetOperationName("Farnsworth") + span.Finish() +} + +// TestSpanTagValueTypes sets tags using values of different types. +func (s *APICheckSuite) TestSpanTagValueTypes() { + span := s.tracer.StartSpan("ManyTypes") + span. + SetTag("an_int", 9). + SetTag("a_bool", true). + SetTag("a_string", "aoeuidhtns") +} + +// TestSpanTagsWithChaining tests chaining of calls to SetTag. +func (s *APICheckSuite) TestSpanTagsWithChaining() { + span := s.tracer.StartSpan("Farnsworth") + span. + SetTag("birthday", "9 April, 2841"). + SetTag("loves", "different lengths of wires") + span. + SetTag("unicode_val", "non-ascii: \u200b"). + SetTag("unicode_key_\u200b", "ascii val") + span.Finish() +} + +// TestSpanLogs tests calls to log keys and values with spans. +func (s *APICheckSuite) TestSpanLogs() { + span := s.tracer.StartSpan("Fry") + span.LogKV( + "event", "frozen", + "year", 1999, + "place", "Cryogenics Labs") + span.LogKV( + "event", "defrosted", + "year", 2999, + "place", "Cryogenics Labs") + + ts := time.Now() + span.FinishWithOptions(opentracing.FinishOptions{ + LogRecords: []opentracing.LogRecord{ + { + Timestamp: ts, + Fields: []log.Field{ + log.String("event", "job-assignment"), + log.String("type", "delivery boy"), + }, + }, + }}) + + // Test deprecated log methods + span.LogEvent("an arbitrary event") + span.LogEventWithPayload("y", "z") + span.Log(opentracing.LogData{Event: "y", Payload: "z"}) +} + +func assertEmptyBaggage(t *testing.T, spanContext opentracing.SpanContext) { + if !assert.NotNil(t, spanContext, "assertEmptyBaggage got empty context") { + return + } + spanContext.ForeachBaggageItem(func(k, v string) bool { + assert.Fail(t, "new span shouldn't have baggage") + return false + }) +} + +// TestSpanBaggage tests calls to set and get span baggage, and if the CheckBaggageValues option +// is set, asserts that baggage values were successfully retrieved. +func (s *APICheckSuite) TestSpanBaggage() { + span := s.tracer.StartSpan("Fry") + assertEmptyBaggage(s.T(), span.Context()) + + spanRef := span.SetBaggageItem("Kiff-loves", "Amy") + s.Exactly(spanRef, span) + + val := span.BaggageItem("Kiff-loves") + if s.opts.CheckBaggageValues { + s.Equal("Amy", val) + } else { + s.T().Log("CheckBaggageValues capability not set, skipping") + } + span.Finish() +} + +// TestContextBaggage tests calls to set and get span baggage, and if the CheckBaggageValues option +// is set, asserts that baggage values were successfully retrieved from the span's SpanContext. +func (s *APICheckSuite) TestContextBaggage() { + span := s.tracer.StartSpan("Fry") + assertEmptyBaggage(s.T(), span.Context()) + + span.SetBaggageItem("Kiff-loves", "Amy") + if s.opts.CheckBaggageValues { + called := false + span.Context().ForeachBaggageItem(func(k, v string) bool { + s.False(called) + called = true + s.Equal("Kiff-loves", k) + s.Equal("Amy", v) + return true + }) + } else { + s.T().Log("CheckBaggageValues capability not set, skipping") + } + span.Finish() +} + +// TestTextPropagation tests if the Tracer can Inject a span into a TextMapCarrier, and later Extract it. +// If CheckExtract is set, it will check if Extract was successful (returned no error). If a Probe is set, +// it will check if the extracted context is in the same trace as the original span. +func (s *APICheckSuite) TestTextPropagation() { + span := s.tracer.StartSpan("Bender") + textCarrier := opentracing.TextMapCarrier{} + err := span.Tracer().Inject(span.Context(), opentracing.TextMap, textCarrier) + assert.NoError(s.T(), err) + + extractedContext, err := s.tracer.Extract(opentracing.TextMap, textCarrier) + if s.opts.CheckExtract { + s.NoError(err) + assertEmptyBaggage(s.T(), extractedContext) + } else { + s.T().Log("CheckExtract capability not set, skipping") + } + if s.opts.Probe != nil { + s.True(s.opts.Probe.SameSpanContext(span, extractedContext)) + } else { + s.T().Log("harness.Probe not specified, skipping") + } + span.Finish() +} + +// TestHTTPPropagation tests if the Tracer can Inject a span into HTTP headers, and later Extract it. +// If CheckExtract is set, it will check if Extract was successful (returned no error). If a Probe is set, +// it will check if the extracted context is in the same trace as the original span. +func (s *APICheckSuite) TestHTTPPropagation() { + span := s.tracer.StartSpan("Bender") + textCarrier := opentracing.HTTPHeadersCarrier{} + err := span.Tracer().Inject(span.Context(), opentracing.HTTPHeaders, textCarrier) + s.NoError(err) + + extractedContext, err := s.tracer.Extract(opentracing.HTTPHeaders, textCarrier) + if s.opts.CheckExtract { + s.NoError(err) + assertEmptyBaggage(s.T(), extractedContext) + } else { + s.T().Log("CheckExtract capability not set, skipping") + } + if s.opts.Probe != nil { + s.True(s.opts.Probe.SameSpanContext(span, extractedContext)) + } else { + s.T().Log("harness.Probe not specified, skipping") + } + span.Finish() +} + +// TestBinaryPropagation tests if the Tracer can Inject a span into a binary buffer, and later Extract it. +// If CheckExtract is set, it will check if Extract was successful (returned no error). If a Probe is set, +// it will check if the extracted context is in the same trace as the original span. +func (s *APICheckSuite) TestBinaryPropagation() { + span := s.tracer.StartSpan("Bender") + buf := new(bytes.Buffer) + err := span.Tracer().Inject(span.Context(), opentracing.Binary, buf) + s.NoError(err) + + extractedContext, err := s.tracer.Extract(opentracing.Binary, buf) + if s.opts.CheckExtract { + s.NoError(err) + assertEmptyBaggage(s.T(), extractedContext) + } else { + s.T().Log("CheckExtract capability not set, skipping") + } + if s.opts.Probe != nil { + s.True(s.opts.Probe.SameSpanContext(span, extractedContext)) + } else { + s.T().Log("harness.Probe not specified, skipping") + } + span.Finish() +} + +// TestMandatoryFormats tests if all mandatory carrier formats are supported. If CheckExtract is set, it +// will check if the call to Extract was successful (returned no error such as ErrUnsupportedFormat). +func (s *APICheckSuite) TestMandatoryFormats() { + formats := []struct{ Format, Carrier interface{} }{ + {opentracing.TextMap, opentracing.TextMapCarrier{}}, + {opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier{}}, + {opentracing.Binary, new(bytes.Buffer)}, + } + span := s.tracer.StartSpan("Bender") + for _, fmtCarrier := range formats { + err := span.Tracer().Inject(span.Context(), fmtCarrier.Format, fmtCarrier.Carrier) + s.NoError(err) + spanCtx, err := s.tracer.Extract(fmtCarrier.Format, fmtCarrier.Carrier) + if s.opts.CheckExtract { + s.NoError(err) + assertEmptyBaggage(s.T(), spanCtx) + } else { + s.T().Log("CheckExtract capability not set, skipping") + } + } +} + +// TestUnknownFormat checks if attempting to Inject or Extract using an unsupported format +// returns ErrUnsupportedFormat, if CheckInject and CheckExtract are set. +func (s *APICheckSuite) TestUnknownFormat() { + customFormat := "kiss my shiny metal ..." + span := s.tracer.StartSpan("Bender") + + err := span.Tracer().Inject(span.Context(), customFormat, nil) + if s.opts.CheckInject { + s.Equal(opentracing.ErrUnsupportedFormat, err) + } else { + s.T().Log("CheckInject capability not set, skipping") + } + ctx, err := s.tracer.Extract(customFormat, nil) + s.Nil(ctx) + if s.opts.CheckExtract { + s.Equal(opentracing.ErrUnsupportedFormat, err) + } else { + s.T().Log("CheckExtract capability not set, skipping") + } +} + +// ForeignSpanContext satisfies the opentracing.SpanContext interface, but otherwise does nothing. +type ForeignSpanContext struct{} + +// ForeachBaggageItem could call handler for each baggage KV, but does nothing. +func (f ForeignSpanContext) ForeachBaggageItem(handler func(k, v string) bool) {} + +// NotACarrier does not satisfy any of the opentracing carrier interfaces. +type NotACarrier struct{} + +// TestInvalidInject checks if errors are returned when Inject is called with invalid inputs. +func (s *APICheckSuite) TestInvalidInject() { + if !s.opts.CheckInject { + s.T().Skip("CheckInject capability not set, skipping") + } + span := s.tracer.StartSpan("op") + + // binary inject + err := span.Tracer().Inject(ForeignSpanContext{}, opentracing.Binary, new(bytes.Buffer)) + s.Equal(opentracing.ErrInvalidSpanContext, err, "Foreign SpanContext should return invalid error") + err = span.Tracer().Inject(span.Context(), opentracing.Binary, NotACarrier{}) + s.Equal(opentracing.ErrInvalidCarrier, err, "Carrier that's not io.Writer should return error") + + // text inject + err = span.Tracer().Inject(ForeignSpanContext{}, opentracing.TextMap, opentracing.TextMapCarrier{}) + s.Equal(opentracing.ErrInvalidSpanContext, err, "Foreign SpanContext should return invalid error") + err = span.Tracer().Inject(span.Context(), opentracing.TextMap, NotACarrier{}) + s.Equal(opentracing.ErrInvalidCarrier, err, "Carrier that's not TextMapWriter should return error") + + // HTTP inject + err = span.Tracer().Inject(ForeignSpanContext{}, opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier{}) + s.Equal(opentracing.ErrInvalidSpanContext, err, "Foreign SpanContext should return invalid error") + err = span.Tracer().Inject(span.Context(), opentracing.HTTPHeaders, NotACarrier{}) + s.Equal(opentracing.ErrInvalidCarrier, err, "Carrier that's not TextMapWriter should return error") +} + +// TestInvalidExtract checks if errors are returned when Extract is called with invalid inputs. +func (s *APICheckSuite) TestInvalidExtract() { + if !s.opts.CheckExtract { + s.T().Skip("CheckExtract capability not set, skipping") + } + span := s.tracer.StartSpan("op") + + // binary extract + ctx, err := span.Tracer().Extract(opentracing.Binary, NotACarrier{}) + s.Equal(opentracing.ErrInvalidCarrier, err, "Carrier that's not io.Reader should return error") + s.Nil(ctx) + + // text extract + ctx, err = span.Tracer().Extract(opentracing.TextMap, NotACarrier{}) + s.Equal(opentracing.ErrInvalidCarrier, err, "Carrier that's not TextMapReader should return error") + s.Nil(ctx) + + // HTTP extract + ctx, err = span.Tracer().Extract(opentracing.HTTPHeaders, NotACarrier{}) + s.Equal(opentracing.ErrInvalidCarrier, err, "Carrier that's not TextMapReader should return error") + s.Nil(ctx) + + span.Finish() +} + +// TestMultiBaggage tests calls to set multiple baggage items, and if the CheckBaggageValues option +// is set, asserts that a baggage value was successfully retrieved from the span's SpanContext. +// It also ensures that returning false from the ForeachBaggageItem handler aborts iteration. +func (s *APICheckSuite) TestMultiBaggage() { + span := s.tracer.StartSpan("op") + assertEmptyBaggage(s.T(), span.Context()) + + span.SetBaggageItem("Bag1", "BaggageVal1") + span.SetBaggageItem("Bag2", "BaggageVal2") + if s.opts.CheckBaggageValues { + s.Equal("BaggageVal1", span.BaggageItem("Bag1")) + s.Equal("BaggageVal2", span.BaggageItem("Bag2")) + called := false + span.Context().ForeachBaggageItem(func(k, v string) bool { + s.False(called) // should only be called once + called = true + return false + }) + s.True(called) + } else { + s.T().Log("CheckBaggageValues capability not set, skipping") + } + span.Finish() +} diff --git a/template/faaschain/vendor/github.com/opentracing/opentracing-go/harness/noop_api_test.go b/template/faaschain/vendor/github.com/opentracing/opentracing-go/harness/noop_api_test.go new file mode 100644 index 00000000..0ac8e8af --- /dev/null +++ b/template/faaschain/vendor/github.com/opentracing/opentracing-go/harness/noop_api_test.go @@ -0,0 +1,17 @@ +package harness + +import ( + "testing" + + opentracing "github.com/opentracing/opentracing-go" +) + +func TestAPI(t *testing.T) { + RunAPIChecks(t, func() (tracer opentracing.Tracer, closer func()) { + return opentracing.NoopTracer{}, nil + }, // NoopTracer doesn't do much + CheckBaggageValues(false), + CheckInject(false), + CheckExtract(false), + ) +} diff --git a/template/faaschain/vendor/github.com/opentracing/opentracing-go/log/field.go b/template/faaschain/vendor/github.com/opentracing/opentracing-go/log/field.go new file mode 100644 index 00000000..50feea34 --- /dev/null +++ b/template/faaschain/vendor/github.com/opentracing/opentracing-go/log/field.go @@ -0,0 +1,269 @@ +package log + +import ( + "fmt" + "math" +) + +type fieldType int + +const ( + stringType fieldType = iota + boolType + intType + int32Type + uint32Type + int64Type + uint64Type + float32Type + float64Type + errorType + objectType + lazyLoggerType + noopType +) + +// Field instances are constructed via LogBool, LogString, and so on. +// Tracing implementations may then handle them via the Field.Marshal +// method. +// +// "heavily influenced by" (i.e., partially stolen from) +// https://github.com/uber-go/zap +type Field struct { + key string + fieldType fieldType + numericVal int64 + stringVal string + interfaceVal interface{} +} + +// String adds a string-valued key:value pair to a Span.LogFields() record +func String(key, val string) Field { + return Field{ + key: key, + fieldType: stringType, + stringVal: val, + } +} + +// Bool adds a bool-valued key:value pair to a Span.LogFields() record +func Bool(key string, val bool) Field { + var numericVal int64 + if val { + numericVal = 1 + } + return Field{ + key: key, + fieldType: boolType, + numericVal: numericVal, + } +} + +// Int adds an int-valued key:value pair to a Span.LogFields() record +func Int(key string, val int) Field { + return Field{ + key: key, + fieldType: intType, + numericVal: int64(val), + } +} + +// Int32 adds an int32-valued key:value pair to a Span.LogFields() record +func Int32(key string, val int32) Field { + return Field{ + key: key, + fieldType: int32Type, + numericVal: int64(val), + } +} + +// Int64 adds an int64-valued key:value pair to a Span.LogFields() record +func Int64(key string, val int64) Field { + return Field{ + key: key, + fieldType: int64Type, + numericVal: val, + } +} + +// Uint32 adds a uint32-valued key:value pair to a Span.LogFields() record +func Uint32(key string, val uint32) Field { + return Field{ + key: key, + fieldType: uint32Type, + numericVal: int64(val), + } +} + +// Uint64 adds a uint64-valued key:value pair to a Span.LogFields() record +func Uint64(key string, val uint64) Field { + return Field{ + key: key, + fieldType: uint64Type, + numericVal: int64(val), + } +} + +// Float32 adds a float32-valued key:value pair to a Span.LogFields() record +func Float32(key string, val float32) Field { + return Field{ + key: key, + fieldType: float32Type, + numericVal: int64(math.Float32bits(val)), + } +} + +// Float64 adds a float64-valued key:value pair to a Span.LogFields() record +func Float64(key string, val float64) Field { + return Field{ + key: key, + fieldType: float64Type, + numericVal: int64(math.Float64bits(val)), + } +} + +// Error adds an error with the key "error" to a Span.LogFields() record +func Error(err error) Field { + return Field{ + key: "error", + fieldType: errorType, + interfaceVal: err, + } +} + +// Object adds an object-valued key:value pair to a Span.LogFields() record +func Object(key string, obj interface{}) Field { + return Field{ + key: key, + fieldType: objectType, + interfaceVal: obj, + } +} + +// LazyLogger allows for user-defined, late-bound logging of arbitrary data +type LazyLogger func(fv Encoder) + +// Lazy adds a LazyLogger to a Span.LogFields() record; the tracing +// implementation will call the LazyLogger function at an indefinite time in +// the future (after Lazy() returns). +func Lazy(ll LazyLogger) Field { + return Field{ + fieldType: lazyLoggerType, + interfaceVal: ll, + } +} + +// Noop creates a no-op log field that should be ignored by the tracer. +// It can be used to capture optional fields, for example those that should +// only be logged in non-production environment: +// +// func customerField(order *Order) log.Field { +// if os.Getenv("ENVIRONMENT") == "dev" { +// return log.String("customer", order.Customer.ID) +// } +// return log.Noop() +// } +// +// span.LogFields(log.String("event", "purchase"), customerField(order)) +// +func Noop() Field { + return Field{ + fieldType: noopType, + } +} + +// Encoder allows access to the contents of a Field (via a call to +// Field.Marshal). +// +// Tracer implementations typically provide an implementation of Encoder; +// OpenTracing callers typically do not need to concern themselves with it. +type Encoder interface { + EmitString(key, value string) + EmitBool(key string, value bool) + EmitInt(key string, value int) + EmitInt32(key string, value int32) + EmitInt64(key string, value int64) + EmitUint32(key string, value uint32) + EmitUint64(key string, value uint64) + EmitFloat32(key string, value float32) + EmitFloat64(key string, value float64) + EmitObject(key string, value interface{}) + EmitLazyLogger(value LazyLogger) +} + +// Marshal passes a Field instance through to the appropriate +// field-type-specific method of an Encoder. +func (lf Field) Marshal(visitor Encoder) { + switch lf.fieldType { + case stringType: + visitor.EmitString(lf.key, lf.stringVal) + case boolType: + visitor.EmitBool(lf.key, lf.numericVal != 0) + case intType: + visitor.EmitInt(lf.key, int(lf.numericVal)) + case int32Type: + visitor.EmitInt32(lf.key, int32(lf.numericVal)) + case int64Type: + visitor.EmitInt64(lf.key, int64(lf.numericVal)) + case uint32Type: + visitor.EmitUint32(lf.key, uint32(lf.numericVal)) + case uint64Type: + visitor.EmitUint64(lf.key, uint64(lf.numericVal)) + case float32Type: + visitor.EmitFloat32(lf.key, math.Float32frombits(uint32(lf.numericVal))) + case float64Type: + visitor.EmitFloat64(lf.key, math.Float64frombits(uint64(lf.numericVal))) + case errorType: + if err, ok := lf.interfaceVal.(error); ok { + visitor.EmitString(lf.key, err.Error()) + } else { + visitor.EmitString(lf.key, "") + } + case objectType: + visitor.EmitObject(lf.key, lf.interfaceVal) + case lazyLoggerType: + visitor.EmitLazyLogger(lf.interfaceVal.(LazyLogger)) + case noopType: + // intentionally left blank + } +} + +// Key returns the field's key. +func (lf Field) Key() string { + return lf.key +} + +// Value returns the field's value as interface{}. +func (lf Field) Value() interface{} { + switch lf.fieldType { + case stringType: + return lf.stringVal + case boolType: + return lf.numericVal != 0 + case intType: + return int(lf.numericVal) + case int32Type: + return int32(lf.numericVal) + case int64Type: + return int64(lf.numericVal) + case uint32Type: + return uint32(lf.numericVal) + case uint64Type: + return uint64(lf.numericVal) + case float32Type: + return math.Float32frombits(uint32(lf.numericVal)) + case float64Type: + return math.Float64frombits(uint64(lf.numericVal)) + case errorType, objectType, lazyLoggerType: + return lf.interfaceVal + case noopType: + return nil + default: + return nil + } +} + +// String returns a string representation of the key and value. +func (lf Field) String() string { + return fmt.Sprint(lf.key, ":", lf.Value()) +} diff --git a/template/faaschain/vendor/github.com/opentracing/opentracing-go/log/field_test.go b/template/faaschain/vendor/github.com/opentracing/opentracing-go/log/field_test.go new file mode 100644 index 00000000..73ab172d --- /dev/null +++ b/template/faaschain/vendor/github.com/opentracing/opentracing-go/log/field_test.go @@ -0,0 +1,51 @@ +package log + +import ( + "fmt" + "testing" +) + +func TestFieldString(t *testing.T) { + testCases := []struct { + field Field + expected string + }{ + { + field: String("key", "value"), + expected: "key:value", + }, + { + field: Bool("key", true), + expected: "key:true", + }, + { + field: Int("key", 5), + expected: "key:5", + }, + { + field: Error(fmt.Errorf("err msg")), + expected: "error:err msg", + }, + { + field: Error(nil), + expected: "error:", + }, + { + field: Noop(), + expected: ":", + }, + } + for i, tc := range testCases { + if str := tc.field.String(); str != tc.expected { + t.Errorf("%d: expected '%s', got '%s'", i, tc.expected, str) + } + } +} + +func TestNoopDoesNotMarshal(t *testing.T) { + mockEncoder := struct { + Encoder + }{} + f := Noop() + f.Marshal(mockEncoder) // panics if any Encoder method is invoked +} diff --git a/template/faaschain/vendor/github.com/opentracing/opentracing-go/log/util.go b/template/faaschain/vendor/github.com/opentracing/opentracing-go/log/util.go new file mode 100644 index 00000000..3832feb5 --- /dev/null +++ b/template/faaschain/vendor/github.com/opentracing/opentracing-go/log/util.go @@ -0,0 +1,54 @@ +package log + +import "fmt" + +// InterleavedKVToFields converts keyValues a la Span.LogKV() to a Field slice +// a la Span.LogFields(). +func InterleavedKVToFields(keyValues ...interface{}) ([]Field, error) { + if len(keyValues)%2 != 0 { + return nil, fmt.Errorf("non-even keyValues len: %d", len(keyValues)) + } + fields := make([]Field, len(keyValues)/2) + for i := 0; i*2 < len(keyValues); i++ { + key, ok := keyValues[i*2].(string) + if !ok { + return nil, fmt.Errorf( + "non-string key (pair #%d): %T", + i, keyValues[i*2]) + } + switch typedVal := keyValues[i*2+1].(type) { + case bool: + fields[i] = Bool(key, typedVal) + case string: + fields[i] = String(key, typedVal) + case int: + fields[i] = Int(key, typedVal) + case int8: + fields[i] = Int32(key, int32(typedVal)) + case int16: + fields[i] = Int32(key, int32(typedVal)) + case int32: + fields[i] = Int32(key, typedVal) + case int64: + fields[i] = Int64(key, typedVal) + case uint: + fields[i] = Uint64(key, uint64(typedVal)) + case uint64: + fields[i] = Uint64(key, typedVal) + case uint8: + fields[i] = Uint32(key, uint32(typedVal)) + case uint16: + fields[i] = Uint32(key, uint32(typedVal)) + case uint32: + fields[i] = Uint32(key, typedVal) + case float32: + fields[i] = Float32(key, typedVal) + case float64: + fields[i] = Float64(key, typedVal) + default: + // When in doubt, coerce to a string + fields[i] = String(key, fmt.Sprint(typedVal)) + } + } + return fields, nil +} diff --git a/template/faaschain/vendor/github.com/opentracing/opentracing-go/mocktracer/mocklogrecord.go b/template/faaschain/vendor/github.com/opentracing/opentracing-go/mocktracer/mocklogrecord.go new file mode 100644 index 00000000..2ce96d9d --- /dev/null +++ b/template/faaschain/vendor/github.com/opentracing/opentracing-go/mocktracer/mocklogrecord.go @@ -0,0 +1,105 @@ +package mocktracer + +import ( + "fmt" + "reflect" + "time" + + "github.com/opentracing/opentracing-go/log" +) + +// MockLogRecord represents data logged to a Span via Span.LogFields or +// Span.LogKV. +type MockLogRecord struct { + Timestamp time.Time + Fields []MockKeyValue +} + +// MockKeyValue represents a single key:value pair. +type MockKeyValue struct { + Key string + + // All MockLogRecord values are coerced to strings via fmt.Sprint(), though + // we retain their type separately. + ValueKind reflect.Kind + ValueString string +} + +// EmitString belongs to the log.Encoder interface +func (m *MockKeyValue) EmitString(key, value string) { + m.Key = key + m.ValueKind = reflect.TypeOf(value).Kind() + m.ValueString = fmt.Sprint(value) +} + +// EmitBool belongs to the log.Encoder interface +func (m *MockKeyValue) EmitBool(key string, value bool) { + m.Key = key + m.ValueKind = reflect.TypeOf(value).Kind() + m.ValueString = fmt.Sprint(value) +} + +// EmitInt belongs to the log.Encoder interface +func (m *MockKeyValue) EmitInt(key string, value int) { + m.Key = key + m.ValueKind = reflect.TypeOf(value).Kind() + m.ValueString = fmt.Sprint(value) +} + +// EmitInt32 belongs to the log.Encoder interface +func (m *MockKeyValue) EmitInt32(key string, value int32) { + m.Key = key + m.ValueKind = reflect.TypeOf(value).Kind() + m.ValueString = fmt.Sprint(value) +} + +// EmitInt64 belongs to the log.Encoder interface +func (m *MockKeyValue) EmitInt64(key string, value int64) { + m.Key = key + m.ValueKind = reflect.TypeOf(value).Kind() + m.ValueString = fmt.Sprint(value) +} + +// EmitUint32 belongs to the log.Encoder interface +func (m *MockKeyValue) EmitUint32(key string, value uint32) { + m.Key = key + m.ValueKind = reflect.TypeOf(value).Kind() + m.ValueString = fmt.Sprint(value) +} + +// EmitUint64 belongs to the log.Encoder interface +func (m *MockKeyValue) EmitUint64(key string, value uint64) { + m.Key = key + m.ValueKind = reflect.TypeOf(value).Kind() + m.ValueString = fmt.Sprint(value) +} + +// EmitFloat32 belongs to the log.Encoder interface +func (m *MockKeyValue) EmitFloat32(key string, value float32) { + m.Key = key + m.ValueKind = reflect.TypeOf(value).Kind() + m.ValueString = fmt.Sprint(value) +} + +// EmitFloat64 belongs to the log.Encoder interface +func (m *MockKeyValue) EmitFloat64(key string, value float64) { + m.Key = key + m.ValueKind = reflect.TypeOf(value).Kind() + m.ValueString = fmt.Sprint(value) +} + +// EmitObject belongs to the log.Encoder interface +func (m *MockKeyValue) EmitObject(key string, value interface{}) { + m.Key = key + m.ValueKind = reflect.TypeOf(value).Kind() + m.ValueString = fmt.Sprint(value) +} + +// EmitLazyLogger belongs to the log.Encoder interface +func (m *MockKeyValue) EmitLazyLogger(value log.LazyLogger) { + var meta MockKeyValue + value(&meta) + m.Key = meta.Key + m.ValueKind = meta.ValueKind + m.ValueString = meta.ValueString +} diff --git a/template/faaschain/vendor/github.com/opentracing/opentracing-go/mocktracer/mockspan.go b/template/faaschain/vendor/github.com/opentracing/opentracing-go/mocktracer/mockspan.go new file mode 100644 index 00000000..8c7932ce --- /dev/null +++ b/template/faaschain/vendor/github.com/opentracing/opentracing-go/mocktracer/mockspan.go @@ -0,0 +1,284 @@ +package mocktracer + +import ( + "fmt" + "sync" + "sync/atomic" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/opentracing/opentracing-go/log" +) + +// MockSpanContext is an opentracing.SpanContext implementation. +// +// It is entirely unsuitable for production use, but appropriate for tests +// that want to verify tracing behavior in other frameworks/applications. +// +// By default all spans have Sampled=true flag, unless {"sampling.priority": 0} +// tag is set. +type MockSpanContext struct { + TraceID int + SpanID int + Sampled bool + Baggage map[string]string +} + +var mockIDSource = uint32(42) + +func nextMockID() int { + return int(atomic.AddUint32(&mockIDSource, 1)) +} + +// ForeachBaggageItem belongs to the SpanContext interface +func (c MockSpanContext) ForeachBaggageItem(handler func(k, v string) bool) { + for k, v := range c.Baggage { + if !handler(k, v) { + break + } + } +} + +// WithBaggageItem creates a new context with an extra baggage item. +func (c MockSpanContext) WithBaggageItem(key, value string) MockSpanContext { + var newBaggage map[string]string + if c.Baggage == nil { + newBaggage = map[string]string{key: value} + } else { + newBaggage = make(map[string]string, len(c.Baggage)+1) + for k, v := range c.Baggage { + newBaggage[k] = v + } + newBaggage[key] = value + } + // Use positional parameters so the compiler will help catch new fields. + return MockSpanContext{c.TraceID, c.SpanID, c.Sampled, newBaggage} +} + +// MockSpan is an opentracing.Span implementation that exports its internal +// state for testing purposes. +type MockSpan struct { + sync.RWMutex + + ParentID int + + OperationName string + StartTime time.Time + FinishTime time.Time + + // All of the below are protected by the embedded RWMutex. + SpanContext MockSpanContext + tags map[string]interface{} + logs []MockLogRecord + tracer *MockTracer +} + +func newMockSpan(t *MockTracer, name string, opts opentracing.StartSpanOptions) *MockSpan { + tags := opts.Tags + if tags == nil { + tags = map[string]interface{}{} + } + traceID := nextMockID() + parentID := int(0) + var baggage map[string]string + sampled := true + if len(opts.References) > 0 { + traceID = opts.References[0].ReferencedContext.(MockSpanContext).TraceID + parentID = opts.References[0].ReferencedContext.(MockSpanContext).SpanID + sampled = opts.References[0].ReferencedContext.(MockSpanContext).Sampled + baggage = opts.References[0].ReferencedContext.(MockSpanContext).Baggage + } + spanContext := MockSpanContext{traceID, nextMockID(), sampled, baggage} + startTime := opts.StartTime + if startTime.IsZero() { + startTime = time.Now() + } + return &MockSpan{ + ParentID: parentID, + OperationName: name, + StartTime: startTime, + tags: tags, + logs: []MockLogRecord{}, + SpanContext: spanContext, + + tracer: t, + } +} + +// Tags returns a copy of tags accumulated by the span so far +func (s *MockSpan) Tags() map[string]interface{} { + s.RLock() + defer s.RUnlock() + tags := make(map[string]interface{}) + for k, v := range s.tags { + tags[k] = v + } + return tags +} + +// Tag returns a single tag +func (s *MockSpan) Tag(k string) interface{} { + s.RLock() + defer s.RUnlock() + return s.tags[k] +} + +// Logs returns a copy of logs accumulated in the span so far +func (s *MockSpan) Logs() []MockLogRecord { + s.RLock() + defer s.RUnlock() + logs := make([]MockLogRecord, len(s.logs)) + copy(logs, s.logs) + return logs +} + +// Context belongs to the Span interface +func (s *MockSpan) Context() opentracing.SpanContext { + s.Lock() + defer s.Unlock() + return s.SpanContext +} + +// SetTag belongs to the Span interface +func (s *MockSpan) SetTag(key string, value interface{}) opentracing.Span { + s.Lock() + defer s.Unlock() + if key == string(ext.SamplingPriority) { + if v, ok := value.(uint16); ok { + s.SpanContext.Sampled = v > 0 + return s + } + if v, ok := value.(int); ok { + s.SpanContext.Sampled = v > 0 + return s + } + } + s.tags[key] = value + return s +} + +// SetBaggageItem belongs to the Span interface +func (s *MockSpan) SetBaggageItem(key, val string) opentracing.Span { + s.Lock() + defer s.Unlock() + s.SpanContext = s.SpanContext.WithBaggageItem(key, val) + return s +} + +// BaggageItem belongs to the Span interface +func (s *MockSpan) BaggageItem(key string) string { + s.RLock() + defer s.RUnlock() + return s.SpanContext.Baggage[key] +} + +// Finish belongs to the Span interface +func (s *MockSpan) Finish() { + s.Lock() + s.FinishTime = time.Now() + s.Unlock() + s.tracer.recordSpan(s) +} + +// FinishWithOptions belongs to the Span interface +func (s *MockSpan) FinishWithOptions(opts opentracing.FinishOptions) { + s.Lock() + s.FinishTime = opts.FinishTime + s.Unlock() + + // Handle any late-bound LogRecords. + for _, lr := range opts.LogRecords { + s.logFieldsWithTimestamp(lr.Timestamp, lr.Fields...) + } + // Handle (deprecated) BulkLogData. + for _, ld := range opts.BulkLogData { + if ld.Payload != nil { + s.logFieldsWithTimestamp( + ld.Timestamp, + log.String("event", ld.Event), + log.Object("payload", ld.Payload)) + } else { + s.logFieldsWithTimestamp( + ld.Timestamp, + log.String("event", ld.Event)) + } + } + + s.tracer.recordSpan(s) +} + +// String allows printing span for debugging +func (s *MockSpan) String() string { + return fmt.Sprintf( + "traceId=%d, spanId=%d, parentId=%d, sampled=%t, name=%s", + s.SpanContext.TraceID, s.SpanContext.SpanID, s.ParentID, + s.SpanContext.Sampled, s.OperationName) +} + +// LogFields belongs to the Span interface +func (s *MockSpan) LogFields(fields ...log.Field) { + s.logFieldsWithTimestamp(time.Now(), fields...) +} + +// The caller MUST NOT hold s.Lock +func (s *MockSpan) logFieldsWithTimestamp(ts time.Time, fields ...log.Field) { + lr := MockLogRecord{ + Timestamp: ts, + Fields: make([]MockKeyValue, len(fields)), + } + for i, f := range fields { + outField := &(lr.Fields[i]) + f.Marshal(outField) + } + + s.Lock() + defer s.Unlock() + s.logs = append(s.logs, lr) +} + +// LogKV belongs to the Span interface. +// +// This implementations coerces all "values" to strings, though that is not +// something all implementations need to do. Indeed, a motivated person can and +// probably should have this do a typed switch on the values. +func (s *MockSpan) LogKV(keyValues ...interface{}) { + if len(keyValues)%2 != 0 { + s.LogFields(log.Error(fmt.Errorf("Non-even keyValues len: %v", len(keyValues)))) + return + } + fields, err := log.InterleavedKVToFields(keyValues...) + if err != nil { + s.LogFields(log.Error(err), log.String("function", "LogKV")) + return + } + s.LogFields(fields...) +} + +// LogEvent belongs to the Span interface +func (s *MockSpan) LogEvent(event string) { + s.LogFields(log.String("event", event)) +} + +// LogEventWithPayload belongs to the Span interface +func (s *MockSpan) LogEventWithPayload(event string, payload interface{}) { + s.LogFields(log.String("event", event), log.Object("payload", payload)) +} + +// Log belongs to the Span interface +func (s *MockSpan) Log(data opentracing.LogData) { + panic("MockSpan.Log() no longer supported") +} + +// SetOperationName belongs to the Span interface +func (s *MockSpan) SetOperationName(operationName string) opentracing.Span { + s.Lock() + defer s.Unlock() + s.OperationName = operationName + return s +} + +// Tracer belongs to the Span interface +func (s *MockSpan) Tracer() opentracing.Tracer { + return s.tracer +} diff --git a/template/faaschain/vendor/github.com/opentracing/opentracing-go/mocktracer/mocktracer.go b/template/faaschain/vendor/github.com/opentracing/opentracing-go/mocktracer/mocktracer.go new file mode 100644 index 00000000..a74c1458 --- /dev/null +++ b/template/faaschain/vendor/github.com/opentracing/opentracing-go/mocktracer/mocktracer.go @@ -0,0 +1,105 @@ +package mocktracer + +import ( + "sync" + + "github.com/opentracing/opentracing-go" +) + +// New returns a MockTracer opentracing.Tracer implementation that's intended +// to facilitate tests of OpenTracing instrumentation. +func New() *MockTracer { + t := &MockTracer{ + finishedSpans: []*MockSpan{}, + injectors: make(map[interface{}]Injector), + extractors: make(map[interface{}]Extractor), + } + + // register default injectors/extractors + textPropagator := new(TextMapPropagator) + t.RegisterInjector(opentracing.TextMap, textPropagator) + t.RegisterExtractor(opentracing.TextMap, textPropagator) + + httpPropagator := &TextMapPropagator{HTTPHeaders: true} + t.RegisterInjector(opentracing.HTTPHeaders, httpPropagator) + t.RegisterExtractor(opentracing.HTTPHeaders, httpPropagator) + + return t +} + +// MockTracer is only intended for testing OpenTracing instrumentation. +// +// It is entirely unsuitable for production use, but appropriate for tests +// that want to verify tracing behavior in other frameworks/applications. +type MockTracer struct { + sync.RWMutex + finishedSpans []*MockSpan + injectors map[interface{}]Injector + extractors map[interface{}]Extractor +} + +// FinishedSpans returns all spans that have been Finish()'ed since the +// MockTracer was constructed or since the last call to its Reset() method. +func (t *MockTracer) FinishedSpans() []*MockSpan { + t.RLock() + defer t.RUnlock() + spans := make([]*MockSpan, len(t.finishedSpans)) + copy(spans, t.finishedSpans) + return spans +} + +// Reset clears the internally accumulated finished spans. Note that any +// extant MockSpans will still append to finishedSpans when they Finish(), +// even after a call to Reset(). +func (t *MockTracer) Reset() { + t.Lock() + defer t.Unlock() + t.finishedSpans = []*MockSpan{} +} + +// StartSpan belongs to the Tracer interface. +func (t *MockTracer) StartSpan(operationName string, opts ...opentracing.StartSpanOption) opentracing.Span { + sso := opentracing.StartSpanOptions{} + for _, o := range opts { + o.Apply(&sso) + } + return newMockSpan(t, operationName, sso) +} + +// RegisterInjector registers injector for given format +func (t *MockTracer) RegisterInjector(format interface{}, injector Injector) { + t.injectors[format] = injector +} + +// RegisterExtractor registers extractor for given format +func (t *MockTracer) RegisterExtractor(format interface{}, extractor Extractor) { + t.extractors[format] = extractor +} + +// Inject belongs to the Tracer interface. +func (t *MockTracer) Inject(sm opentracing.SpanContext, format interface{}, carrier interface{}) error { + spanContext, ok := sm.(MockSpanContext) + if !ok { + return opentracing.ErrInvalidCarrier + } + injector, ok := t.injectors[format] + if !ok { + return opentracing.ErrUnsupportedFormat + } + return injector.Inject(spanContext, carrier) +} + +// Extract belongs to the Tracer interface. +func (t *MockTracer) Extract(format interface{}, carrier interface{}) (opentracing.SpanContext, error) { + extractor, ok := t.extractors[format] + if !ok { + return nil, opentracing.ErrUnsupportedFormat + } + return extractor.Extract(carrier) +} + +func (t *MockTracer) recordSpan(span *MockSpan) { + t.Lock() + defer t.Unlock() + t.finishedSpans = append(t.finishedSpans, span) +} diff --git a/template/faaschain/vendor/github.com/opentracing/opentracing-go/mocktracer/mocktracer_test.go b/template/faaschain/vendor/github.com/opentracing/opentracing-go/mocktracer/mocktracer_test.go new file mode 100644 index 00000000..14c04d8c --- /dev/null +++ b/template/faaschain/vendor/github.com/opentracing/opentracing-go/mocktracer/mocktracer_test.go @@ -0,0 +1,284 @@ +package mocktracer + +import ( + "net/http" + "reflect" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/opentracing/opentracing-go/log" +) + +func TestMockTracer_StartSpan(t *testing.T) { + tracer := New() + span1 := tracer.StartSpan( + "a", + opentracing.Tags(map[string]interface{}{"x": "y"})) + + span2 := span1.Tracer().StartSpan( + "", opentracing.ChildOf(span1.Context())) + span2.Finish() + span1.Finish() + spans := tracer.FinishedSpans() + assert.Equal(t, 2, len(spans)) + + parent := spans[1] + child := spans[0] + assert.Equal(t, map[string]interface{}{"x": "y"}, parent.Tags()) + assert.Equal(t, child.ParentID, parent.Context().(MockSpanContext).SpanID) +} + +func TestMockSpan_SetOperationName(t *testing.T) { + tracer := New() + span := tracer.StartSpan("") + span.SetOperationName("x") + assert.Equal(t, "x", span.(*MockSpan).OperationName) +} + +func TestMockSpanContext_Baggage(t *testing.T) { + tracer := New() + span := tracer.StartSpan("x") + span.SetBaggageItem("x", "y") + assert.Equal(t, "y", span.BaggageItem("x")) + assert.Equal(t, map[string]string{"x": "y"}, span.Context().(MockSpanContext).Baggage) + + baggage := make(map[string]string) + span.Context().ForeachBaggageItem(func(k, v string) bool { + baggage[k] = v + return true + }) + assert.Equal(t, map[string]string{"x": "y"}, baggage) + + span.SetBaggageItem("a", "b") + baggage = make(map[string]string) + span.Context().ForeachBaggageItem(func(k, v string) bool { + baggage[k] = v + return false // exit early + }) + assert.Equal(t, 2, len(span.Context().(MockSpanContext).Baggage)) + assert.Equal(t, 1, len(baggage)) +} + +func TestMockSpan_Tag(t *testing.T) { + tracer := New() + span := tracer.StartSpan("x") + span.SetTag("x", "y") + assert.Equal(t, "y", span.(*MockSpan).Tag("x")) +} + +func TestMockSpan_Tags(t *testing.T) { + tracer := New() + span := tracer.StartSpan("x") + span.SetTag("x", "y") + assert.Equal(t, map[string]interface{}{"x": "y"}, span.(*MockSpan).Tags()) +} + +func TestMockTracer_FinishedSpans_and_Reset(t *testing.T) { + tracer := New() + span := tracer.StartSpan("x") + span.SetTag("x", "y") + span.Finish() + spans := tracer.FinishedSpans() + assert.Equal(t, 1, len(spans)) + assert.Equal(t, map[string]interface{}{"x": "y"}, spans[0].Tags()) + + tracer.Reset() + spans = tracer.FinishedSpans() + assert.Equal(t, 0, len(spans)) +} + +func zeroOutTimestamps(recs []MockLogRecord) { + for i := range recs { + recs[i].Timestamp = time.Time{} + } +} + +func TestMockSpan_LogKV(t *testing.T) { + tracer := New() + span := tracer.StartSpan("s") + span.LogKV("key0", "string0") + span.LogKV("key1", "string1", "key2", uint32(42)) + span.Finish() + spans := tracer.FinishedSpans() + assert.Equal(t, 1, len(spans)) + actual := spans[0].Logs() + zeroOutTimestamps(actual) + assert.Equal(t, []MockLogRecord{ + MockLogRecord{ + Fields: []MockKeyValue{ + MockKeyValue{Key: "key0", ValueKind: reflect.String, ValueString: "string0"}, + }, + }, + MockLogRecord{ + Fields: []MockKeyValue{ + MockKeyValue{Key: "key1", ValueKind: reflect.String, ValueString: "string1"}, + MockKeyValue{Key: "key2", ValueKind: reflect.Uint32, ValueString: "42"}, + }, + }, + }, actual) +} + +func TestMockSpan_LogFields(t *testing.T) { + tracer := New() + span := tracer.StartSpan("s") + span.LogFields(log.String("key0", "string0")) + span.LogFields(log.String("key1", "string1"), log.Uint32("key2", uint32(42))) + span.LogFields(log.Lazy(func(fv log.Encoder) { + fv.EmitInt("key_lazy", 12) + })) + span.FinishWithOptions(opentracing.FinishOptions{ + LogRecords: []opentracing.LogRecord{ + {Timestamp: time.Now(), Fields: []log.Field{log.String("key9", "finish")}}, + }}) + spans := tracer.FinishedSpans() + assert.Equal(t, 1, len(spans)) + actual := spans[0].Logs() + zeroOutTimestamps(actual) + assert.Equal(t, []MockLogRecord{ + MockLogRecord{ + Fields: []MockKeyValue{ + MockKeyValue{Key: "key0", ValueKind: reflect.String, ValueString: "string0"}, + }, + }, + MockLogRecord{ + Fields: []MockKeyValue{ + MockKeyValue{Key: "key1", ValueKind: reflect.String, ValueString: "string1"}, + MockKeyValue{Key: "key2", ValueKind: reflect.Uint32, ValueString: "42"}, + }, + }, + MockLogRecord{ + Fields: []MockKeyValue{ + // Note that the LazyLogger gets to control the key as well as the value. + MockKeyValue{Key: "key_lazy", ValueKind: reflect.Int, ValueString: "12"}, + }, + }, + MockLogRecord{ + Fields: []MockKeyValue{ + MockKeyValue{Key: "key9", ValueKind: reflect.String, ValueString: "finish"}, + }, + }, + }, actual) +} + +func TestMockSpan_DeprecatedLogs(t *testing.T) { + tracer := New() + span := tracer.StartSpan("x") + span.LogEvent("x") + span.LogEventWithPayload("y", "z") + span.LogEvent("a") + span.FinishWithOptions(opentracing.FinishOptions{ + BulkLogData: []opentracing.LogData{{Event: "f"}}}) + spans := tracer.FinishedSpans() + assert.Equal(t, 1, len(spans)) + actual := spans[0].Logs() + zeroOutTimestamps(actual) + assert.Equal(t, []MockLogRecord{ + MockLogRecord{ + Fields: []MockKeyValue{ + MockKeyValue{Key: "event", ValueKind: reflect.String, ValueString: "x"}, + }, + }, + MockLogRecord{ + Fields: []MockKeyValue{ + MockKeyValue{Key: "event", ValueKind: reflect.String, ValueString: "y"}, + MockKeyValue{Key: "payload", ValueKind: reflect.String, ValueString: "z"}, + }, + }, + MockLogRecord{ + Fields: []MockKeyValue{ + MockKeyValue{Key: "event", ValueKind: reflect.String, ValueString: "a"}, + }, + }, + MockLogRecord{ + Fields: []MockKeyValue{ + MockKeyValue{Key: "event", ValueKind: reflect.String, ValueString: "f"}, + }, + }, + }, actual) +} + +func TestMockTracer_Propagation(t *testing.T) { + textCarrier := func() interface{} { + return opentracing.TextMapCarrier(make(map[string]string)) + } + textLen := func(c interface{}) int { + return len(c.(opentracing.TextMapCarrier)) + } + + httpCarrier := func() interface{} { + httpHeaders := http.Header(make(map[string][]string)) + return opentracing.HTTPHeadersCarrier(httpHeaders) + } + httpLen := func(c interface{}) int { + return len(c.(opentracing.HTTPHeadersCarrier)) + } + + tests := []struct { + sampled bool + format opentracing.BuiltinFormat + carrier func() interface{} + len func(interface{}) int + }{ + {sampled: true, format: opentracing.TextMap, carrier: textCarrier, len: textLen}, + {sampled: false, format: opentracing.TextMap, carrier: textCarrier, len: textLen}, + {sampled: true, format: opentracing.HTTPHeaders, carrier: httpCarrier, len: httpLen}, + {sampled: false, format: opentracing.HTTPHeaders, carrier: httpCarrier, len: httpLen}, + } + for _, test := range tests { + tracer := New() + span := tracer.StartSpan("x") + span.SetBaggageItem("x", "y:z") // colon should be URL encoded as %3A + if !test.sampled { + ext.SamplingPriority.Set(span, 0) + } + mSpan := span.(*MockSpan) + + assert.Equal(t, opentracing.ErrUnsupportedFormat, + tracer.Inject(span.Context(), opentracing.Binary, nil)) + assert.Equal(t, opentracing.ErrInvalidCarrier, + tracer.Inject(span.Context(), opentracing.TextMap, span)) + + carrier := test.carrier() + + err := tracer.Inject(span.Context(), test.format, carrier) + require.NoError(t, err) + assert.Equal(t, 4, test.len(carrier), "expect baggage + 2 ids + sampled") + if test.format == opentracing.HTTPHeaders { + c := carrier.(opentracing.HTTPHeadersCarrier) + assert.Equal(t, "y%3Az", c["Mockpfx-Baggage-X"][0]) + } + + _, err = tracer.Extract(opentracing.Binary, nil) + assert.Equal(t, opentracing.ErrUnsupportedFormat, err) + _, err = tracer.Extract(opentracing.TextMap, tracer) + assert.Equal(t, opentracing.ErrInvalidCarrier, err) + + extractedContext, err := tracer.Extract(test.format, carrier) + require.NoError(t, err) + assert.Equal(t, mSpan.SpanContext.TraceID, extractedContext.(MockSpanContext).TraceID) + assert.Equal(t, mSpan.SpanContext.SpanID, extractedContext.(MockSpanContext).SpanID) + assert.Equal(t, test.sampled, extractedContext.(MockSpanContext).Sampled) + assert.Equal(t, "y:z", extractedContext.(MockSpanContext).Baggage["x"]) + } +} + +func TestMockSpan_Races(t *testing.T) { + span := New().StartSpan("x") + var wg sync.WaitGroup + wg.Add(2) + go func() { + defer wg.Done() + span.SetBaggageItem("test_key", "test_value") + }() + go func() { + defer wg.Done() + span.Context() + }() + wg.Wait() +} diff --git a/template/faaschain/vendor/github.com/opentracing/opentracing-go/mocktracer/propagation.go b/template/faaschain/vendor/github.com/opentracing/opentracing-go/mocktracer/propagation.go new file mode 100644 index 00000000..8364f1d1 --- /dev/null +++ b/template/faaschain/vendor/github.com/opentracing/opentracing-go/mocktracer/propagation.go @@ -0,0 +1,120 @@ +package mocktracer + +import ( + "fmt" + "net/url" + "strconv" + "strings" + + "github.com/opentracing/opentracing-go" +) + +const mockTextMapIdsPrefix = "mockpfx-ids-" +const mockTextMapBaggagePrefix = "mockpfx-baggage-" + +var emptyContext = MockSpanContext{} + +// Injector is responsible for injecting SpanContext instances in a manner suitable +// for propagation via a format-specific "carrier" object. Typically the +// injection will take place across an RPC boundary, but message queues and +// other IPC mechanisms are also reasonable places to use an Injector. +type Injector interface { + // Inject takes `SpanContext` and injects it into `carrier`. The actual type + // of `carrier` depends on the `format` passed to `Tracer.Inject()`. + // + // Implementations may return opentracing.ErrInvalidCarrier or any other + // implementation-specific error if injection fails. + Inject(ctx MockSpanContext, carrier interface{}) error +} + +// Extractor is responsible for extracting SpanContext instances from a +// format-specific "carrier" object. Typically the extraction will take place +// on the server side of an RPC boundary, but message queues and other IPC +// mechanisms are also reasonable places to use an Extractor. +type Extractor interface { + // Extract decodes a SpanContext instance from the given `carrier`, + // or (nil, opentracing.ErrSpanContextNotFound) if no context could + // be found in the `carrier`. + Extract(carrier interface{}) (MockSpanContext, error) +} + +// TextMapPropagator implements Injector/Extractor for TextMap and HTTPHeaders formats. +type TextMapPropagator struct { + HTTPHeaders bool +} + +// Inject implements the Injector interface +func (t *TextMapPropagator) Inject(spanContext MockSpanContext, carrier interface{}) error { + writer, ok := carrier.(opentracing.TextMapWriter) + if !ok { + return opentracing.ErrInvalidCarrier + } + // Ids: + writer.Set(mockTextMapIdsPrefix+"traceid", strconv.Itoa(spanContext.TraceID)) + writer.Set(mockTextMapIdsPrefix+"spanid", strconv.Itoa(spanContext.SpanID)) + writer.Set(mockTextMapIdsPrefix+"sampled", fmt.Sprint(spanContext.Sampled)) + // Baggage: + for baggageKey, baggageVal := range spanContext.Baggage { + safeVal := baggageVal + if t.HTTPHeaders { + safeVal = url.QueryEscape(baggageVal) + } + writer.Set(mockTextMapBaggagePrefix+baggageKey, safeVal) + } + return nil +} + +// Extract implements the Extractor interface +func (t *TextMapPropagator) Extract(carrier interface{}) (MockSpanContext, error) { + reader, ok := carrier.(opentracing.TextMapReader) + if !ok { + return emptyContext, opentracing.ErrInvalidCarrier + } + rval := MockSpanContext{0, 0, true, nil} + err := reader.ForeachKey(func(key, val string) error { + lowerKey := strings.ToLower(key) + switch { + case lowerKey == mockTextMapIdsPrefix+"traceid": + // Ids: + i, err := strconv.Atoi(val) + if err != nil { + return err + } + rval.TraceID = i + case lowerKey == mockTextMapIdsPrefix+"spanid": + // Ids: + i, err := strconv.Atoi(val) + if err != nil { + return err + } + rval.SpanID = i + case lowerKey == mockTextMapIdsPrefix+"sampled": + b, err := strconv.ParseBool(val) + if err != nil { + return err + } + rval.Sampled = b + case strings.HasPrefix(lowerKey, mockTextMapBaggagePrefix): + // Baggage: + if rval.Baggage == nil { + rval.Baggage = make(map[string]string) + } + safeVal := val + if t.HTTPHeaders { + // unescape errors are ignored, nothing can be done + if rawVal, err := url.QueryUnescape(val); err == nil { + safeVal = rawVal + } + } + rval.Baggage[lowerKey[len(mockTextMapBaggagePrefix):]] = safeVal + } + return nil + }) + if rval.TraceID == 0 || rval.SpanID == 0 { + return emptyContext, opentracing.ErrSpanContextNotFound + } + if err != nil { + return emptyContext, err + } + return rval, nil +} diff --git a/template/faaschain/vendor/github.com/opentracing/opentracing-go/noop.go b/template/faaschain/vendor/github.com/opentracing/opentracing-go/noop.go new file mode 100644 index 00000000..0d32f692 --- /dev/null +++ b/template/faaschain/vendor/github.com/opentracing/opentracing-go/noop.go @@ -0,0 +1,64 @@ +package opentracing + +import "github.com/opentracing/opentracing-go/log" + +// A NoopTracer is a trivial, minimum overhead implementation of Tracer +// for which all operations are no-ops. +// +// The primary use of this implementation is in libraries, such as RPC +// frameworks, that make tracing an optional feature controlled by the +// end user. A no-op implementation allows said libraries to use it +// as the default Tracer and to write instrumentation that does +// not need to keep checking if the tracer instance is nil. +// +// For the same reason, the NoopTracer is the default "global" tracer +// (see GlobalTracer and SetGlobalTracer functions). +// +// WARNING: NoopTracer does not support baggage propagation. +type NoopTracer struct{} + +type noopSpan struct{} +type noopSpanContext struct{} + +var ( + defaultNoopSpanContext = noopSpanContext{} + defaultNoopSpan = noopSpan{} + defaultNoopTracer = NoopTracer{} +) + +const ( + emptyString = "" +) + +// noopSpanContext: +func (n noopSpanContext) ForeachBaggageItem(handler func(k, v string) bool) {} + +// noopSpan: +func (n noopSpan) Context() SpanContext { return defaultNoopSpanContext } +func (n noopSpan) SetBaggageItem(key, val string) Span { return defaultNoopSpan } +func (n noopSpan) BaggageItem(key string) string { return emptyString } +func (n noopSpan) SetTag(key string, value interface{}) Span { return n } +func (n noopSpan) LogFields(fields ...log.Field) {} +func (n noopSpan) LogKV(keyVals ...interface{}) {} +func (n noopSpan) Finish() {} +func (n noopSpan) FinishWithOptions(opts FinishOptions) {} +func (n noopSpan) SetOperationName(operationName string) Span { return n } +func (n noopSpan) Tracer() Tracer { return defaultNoopTracer } +func (n noopSpan) LogEvent(event string) {} +func (n noopSpan) LogEventWithPayload(event string, payload interface{}) {} +func (n noopSpan) Log(data LogData) {} + +// StartSpan belongs to the Tracer interface. +func (n NoopTracer) StartSpan(operationName string, opts ...StartSpanOption) Span { + return defaultNoopSpan +} + +// Inject belongs to the Tracer interface. +func (n NoopTracer) Inject(sp SpanContext, format interface{}, carrier interface{}) error { + return nil +} + +// Extract belongs to the Tracer interface. +func (n NoopTracer) Extract(format interface{}, carrier interface{}) (SpanContext, error) { + return nil, ErrSpanContextNotFound +} diff --git a/template/faaschain/vendor/github.com/opentracing/opentracing-go/options_test.go b/template/faaschain/vendor/github.com/opentracing/opentracing-go/options_test.go new file mode 100644 index 00000000..56a543bf --- /dev/null +++ b/template/faaschain/vendor/github.com/opentracing/opentracing-go/options_test.go @@ -0,0 +1,31 @@ +package opentracing + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestChildOfAndFollowsFrom(t *testing.T) { + tests := []struct { + newOpt func(SpanContext) SpanReference + refType SpanReferenceType + name string + }{ + {ChildOf, ChildOfRef, "ChildOf"}, + {FollowsFrom, FollowsFromRef, "FollowsFrom"}, + } + + for _, test := range tests { + opts := new(StartSpanOptions) + + test.newOpt(nil).Apply(opts) + require.Nil(t, opts.References, "%s(nil) must not append a reference", test.name) + + ctx := new(noopSpanContext) + test.newOpt(ctx).Apply(opts) + require.Equal(t, []SpanReference{ + SpanReference{ReferencedContext: ctx, Type: test.refType}, + }, opts.References, "%s(ctx) must append a reference", test.name) + } +} diff --git a/template/faaschain/vendor/github.com/opentracing/opentracing-go/propagation.go b/template/faaschain/vendor/github.com/opentracing/opentracing-go/propagation.go new file mode 100644 index 00000000..0dd466a3 --- /dev/null +++ b/template/faaschain/vendor/github.com/opentracing/opentracing-go/propagation.go @@ -0,0 +1,176 @@ +package opentracing + +import ( + "errors" + "net/http" +) + +/////////////////////////////////////////////////////////////////////////////// +// CORE PROPAGATION INTERFACES: +/////////////////////////////////////////////////////////////////////////////// + +var ( + // ErrUnsupportedFormat occurs when the `format` passed to Tracer.Inject() or + // Tracer.Extract() is not recognized by the Tracer implementation. + ErrUnsupportedFormat = errors.New("opentracing: Unknown or unsupported Inject/Extract format") + + // ErrSpanContextNotFound occurs when the `carrier` passed to + // Tracer.Extract() is valid and uncorrupted but has insufficient + // information to extract a SpanContext. + ErrSpanContextNotFound = errors.New("opentracing: SpanContext not found in Extract carrier") + + // ErrInvalidSpanContext errors occur when Tracer.Inject() is asked to + // operate on a SpanContext which it is not prepared to handle (for + // example, since it was created by a different tracer implementation). + ErrInvalidSpanContext = errors.New("opentracing: SpanContext type incompatible with tracer") + + // ErrInvalidCarrier errors occur when Tracer.Inject() or Tracer.Extract() + // implementations expect a different type of `carrier` than they are + // given. + ErrInvalidCarrier = errors.New("opentracing: Invalid Inject/Extract carrier") + + // ErrSpanContextCorrupted occurs when the `carrier` passed to + // Tracer.Extract() is of the expected type but is corrupted. + ErrSpanContextCorrupted = errors.New("opentracing: SpanContext data corrupted in Extract carrier") +) + +/////////////////////////////////////////////////////////////////////////////// +// BUILTIN PROPAGATION FORMATS: +/////////////////////////////////////////////////////////////////////////////// + +// BuiltinFormat is used to demarcate the values within package `opentracing` +// that are intended for use with the Tracer.Inject() and Tracer.Extract() +// methods. +type BuiltinFormat byte + +const ( + // Binary represents SpanContexts as opaque binary data. + // + // For Tracer.Inject(): the carrier must be an `io.Writer`. + // + // For Tracer.Extract(): the carrier must be an `io.Reader`. + Binary BuiltinFormat = iota + + // TextMap represents SpanContexts as key:value string pairs. + // + // Unlike HTTPHeaders, the TextMap format does not restrict the key or + // value character sets in any way. + // + // For Tracer.Inject(): the carrier must be a `TextMapWriter`. + // + // For Tracer.Extract(): the carrier must be a `TextMapReader`. + TextMap + + // HTTPHeaders represents SpanContexts as HTTP header string pairs. + // + // Unlike TextMap, the HTTPHeaders format requires that the keys and values + // be valid as HTTP headers as-is (i.e., character casing may be unstable + // and special characters are disallowed in keys, values should be + // URL-escaped, etc). + // + // For Tracer.Inject(): the carrier must be a `TextMapWriter`. + // + // For Tracer.Extract(): the carrier must be a `TextMapReader`. + // + // See HTTPHeadersCarrier for an implementation of both TextMapWriter + // and TextMapReader that defers to an http.Header instance for storage. + // For example, Inject(): + // + // carrier := opentracing.HTTPHeadersCarrier(httpReq.Header) + // err := span.Tracer().Inject( + // span.Context(), opentracing.HTTPHeaders, carrier) + // + // Or Extract(): + // + // carrier := opentracing.HTTPHeadersCarrier(httpReq.Header) + // clientContext, err := tracer.Extract( + // opentracing.HTTPHeaders, carrier) + // + HTTPHeaders +) + +// TextMapWriter is the Inject() carrier for the TextMap builtin format. With +// it, the caller can encode a SpanContext for propagation as entries in a map +// of unicode strings. +type TextMapWriter interface { + // Set a key:value pair to the carrier. Multiple calls to Set() for the + // same key leads to undefined behavior. + // + // NOTE: The backing store for the TextMapWriter may contain data unrelated + // to SpanContext. As such, Inject() and Extract() implementations that + // call the TextMapWriter and TextMapReader interfaces must agree on a + // prefix or other convention to distinguish their own key:value pairs. + Set(key, val string) +} + +// TextMapReader is the Extract() carrier for the TextMap builtin format. With it, +// the caller can decode a propagated SpanContext as entries in a map of +// unicode strings. +type TextMapReader interface { + // ForeachKey returns TextMap contents via repeated calls to the `handler` + // function. If any call to `handler` returns a non-nil error, ForeachKey + // terminates and returns that error. + // + // NOTE: The backing store for the TextMapReader may contain data unrelated + // to SpanContext. As such, Inject() and Extract() implementations that + // call the TextMapWriter and TextMapReader interfaces must agree on a + // prefix or other convention to distinguish their own key:value pairs. + // + // The "foreach" callback pattern reduces unnecessary copying in some cases + // and also allows implementations to hold locks while the map is read. + ForeachKey(handler func(key, val string) error) error +} + +// TextMapCarrier allows the use of regular map[string]string +// as both TextMapWriter and TextMapReader. +type TextMapCarrier map[string]string + +// ForeachKey conforms to the TextMapReader interface. +func (c TextMapCarrier) ForeachKey(handler func(key, val string) error) error { + for k, v := range c { + if err := handler(k, v); err != nil { + return err + } + } + return nil +} + +// Set implements Set() of opentracing.TextMapWriter +func (c TextMapCarrier) Set(key, val string) { + c[key] = val +} + +// HTTPHeadersCarrier satisfies both TextMapWriter and TextMapReader. +// +// Example usage for server side: +// +// carrier := opentracing.HTTPHeadersCarrier(httpReq.Header) +// clientContext, err := tracer.Extract(opentracing.HTTPHeaders, carrier) +// +// Example usage for client side: +// +// carrier := opentracing.HTTPHeadersCarrier(httpReq.Header) +// err := tracer.Inject( +// span.Context(), +// opentracing.HTTPHeaders, +// carrier) +// +type HTTPHeadersCarrier http.Header + +// Set conforms to the TextMapWriter interface. +func (c HTTPHeadersCarrier) Set(key, val string) { + h := http.Header(c) + h.Add(key, val) +} + +// ForeachKey conforms to the TextMapReader interface. +func (c HTTPHeadersCarrier) ForeachKey(handler func(key, val string) error) error { + for k, vals := range c { + for _, v := range vals { + if err := handler(k, v); err != nil { + return err + } + } + } + return nil +} diff --git a/template/faaschain/vendor/github.com/opentracing/opentracing-go/propagation_test.go b/template/faaschain/vendor/github.com/opentracing/opentracing-go/propagation_test.go new file mode 100644 index 00000000..e3dad559 --- /dev/null +++ b/template/faaschain/vendor/github.com/opentracing/opentracing-go/propagation_test.go @@ -0,0 +1,93 @@ +package opentracing + +import ( + "net/http" + "strconv" + "testing" +) + +const testHeaderPrefix = "testprefix-" + +func TestTextMapCarrierInject(t *testing.T) { + m := make(map[string]string) + m["NotOT"] = "blah" + m["opname"] = "AlsoNotOT" + tracer := testTracer{} + span := tracer.StartSpan("someSpan") + fakeID := span.Context().(testSpanContext).FakeID + + carrier := TextMapCarrier(m) + if err := span.Tracer().Inject(span.Context(), TextMap, carrier); err != nil { + t.Fatal(err) + } + + if len(m) != 3 { + t.Errorf("Unexpected header length: %v", len(m)) + } + // The prefix comes from just above; the suffix comes from + // testTracer.Inject(). + if m["testprefix-fakeid"] != strconv.Itoa(fakeID) { + t.Errorf("Could not find fakeid at expected key") + } +} + +func TestTextMapCarrierExtract(t *testing.T) { + m := make(map[string]string) + m["NotOT"] = "blah" + m["opname"] = "AlsoNotOT" + m["testprefix-fakeid"] = "42" + tracer := testTracer{} + + carrier := TextMapCarrier(m) + extractedContext, err := tracer.Extract(TextMap, carrier) + if err != nil { + t.Fatal(err) + } + + if extractedContext.(testSpanContext).FakeID != 42 { + t.Errorf("Failed to read testprefix-fakeid correctly") + } +} + +func TestHTTPHeaderInject(t *testing.T) { + h := http.Header{} + h.Add("NotOT", "blah") + h.Add("opname", "AlsoNotOT") + tracer := testTracer{} + span := tracer.StartSpan("someSpan") + fakeID := span.Context().(testSpanContext).FakeID + + // Use HTTPHeadersCarrier to wrap around `h`. + carrier := HTTPHeadersCarrier(h) + if err := span.Tracer().Inject(span.Context(), HTTPHeaders, carrier); err != nil { + t.Fatal(err) + } + + if len(h) != 3 { + t.Errorf("Unexpected header length: %v", len(h)) + } + // The prefix comes from just above; the suffix comes from + // testTracer.Inject(). + if h.Get("testprefix-fakeid") != strconv.Itoa(fakeID) { + t.Errorf("Could not find fakeid at expected key") + } +} + +func TestHTTPHeaderExtract(t *testing.T) { + h := http.Header{} + h.Add("NotOT", "blah") + h.Add("opname", "AlsoNotOT") + h.Add("testprefix-fakeid", "42") + tracer := testTracer{} + + // Use HTTPHeadersCarrier to wrap around `h`. + carrier := HTTPHeadersCarrier(h) + spanContext, err := tracer.Extract(HTTPHeaders, carrier) + if err != nil { + t.Fatal(err) + } + + if spanContext.(testSpanContext).FakeID != 42 { + t.Errorf("Failed to read testprefix-fakeid correctly") + } +} diff --git a/template/faaschain/vendor/github.com/opentracing/opentracing-go/span.go b/template/faaschain/vendor/github.com/opentracing/opentracing-go/span.go new file mode 100644 index 00000000..0d3fb534 --- /dev/null +++ b/template/faaschain/vendor/github.com/opentracing/opentracing-go/span.go @@ -0,0 +1,189 @@ +package opentracing + +import ( + "time" + + "github.com/opentracing/opentracing-go/log" +) + +// SpanContext represents Span state that must propagate to descendant Spans and across process +// boundaries (e.g., a tuple). +type SpanContext interface { + // ForeachBaggageItem grants access to all baggage items stored in the + // SpanContext. + // The handler function will be called for each baggage key/value pair. + // The ordering of items is not guaranteed. + // + // The bool return value indicates if the handler wants to continue iterating + // through the rest of the baggage items; for example if the handler is trying to + // find some baggage item by pattern matching the name, it can return false + // as soon as the item is found to stop further iterations. + ForeachBaggageItem(handler func(k, v string) bool) +} + +// Span represents an active, un-finished span in the OpenTracing system. +// +// Spans are created by the Tracer interface. +type Span interface { + // Sets the end timestamp and finalizes Span state. + // + // With the exception of calls to Context() (which are always allowed), + // Finish() must be the last call made to any span instance, and to do + // otherwise leads to undefined behavior. + Finish() + // FinishWithOptions is like Finish() but with explicit control over + // timestamps and log data. + FinishWithOptions(opts FinishOptions) + + // Context() yields the SpanContext for this Span. Note that the return + // value of Context() is still valid after a call to Span.Finish(), as is + // a call to Span.Context() after a call to Span.Finish(). + Context() SpanContext + + // Sets or changes the operation name. + // + // Returns a reference to this Span for chaining. + SetOperationName(operationName string) Span + + // Adds a tag to the span. + // + // If there is a pre-existing tag set for `key`, it is overwritten. + // + // Tag values can be numeric types, strings, or bools. The behavior of + // other tag value types is undefined at the OpenTracing level. If a + // tracing system does not know how to handle a particular value type, it + // may ignore the tag, but shall not panic. + // + // Returns a reference to this Span for chaining. + SetTag(key string, value interface{}) Span + + // LogFields is an efficient and type-checked way to record key:value + // logging data about a Span, though the programming interface is a little + // more verbose than LogKV(). Here's an example: + // + // span.LogFields( + // log.String("event", "soft error"), + // log.String("type", "cache timeout"), + // log.Int("waited.millis", 1500)) + // + // Also see Span.FinishWithOptions() and FinishOptions.BulkLogData. + LogFields(fields ...log.Field) + + // LogKV is a concise, readable way to record key:value logging data about + // a Span, though unfortunately this also makes it less efficient and less + // type-safe than LogFields(). Here's an example: + // + // span.LogKV( + // "event", "soft error", + // "type", "cache timeout", + // "waited.millis", 1500) + // + // For LogKV (as opposed to LogFields()), the parameters must appear as + // key-value pairs, like + // + // span.LogKV(key1, val1, key2, val2, key3, val3, ...) + // + // The keys must all be strings. The values may be strings, numeric types, + // bools, Go error instances, or arbitrary structs. + // + // (Note to implementors: consider the log.InterleavedKVToFields() helper) + LogKV(alternatingKeyValues ...interface{}) + + // SetBaggageItem sets a key:value pair on this Span and its SpanContext + // that also propagates to descendants of this Span. + // + // SetBaggageItem() enables powerful functionality given a full-stack + // opentracing integration (e.g., arbitrary application data from a mobile + // app can make it, transparently, all the way into the depths of a storage + // system), and with it some powerful costs: use this feature with care. + // + // IMPORTANT NOTE #1: SetBaggageItem() will only propagate baggage items to + // *future* causal descendants of the associated Span. + // + // IMPORTANT NOTE #2: Use this thoughtfully and with care. Every key and + // value is copied into every local *and remote* child of the associated + // Span, and that can add up to a lot of network and cpu overhead. + // + // Returns a reference to this Span for chaining. + SetBaggageItem(restrictedKey, value string) Span + + // Gets the value for a baggage item given its key. Returns the empty string + // if the value isn't found in this Span. + BaggageItem(restrictedKey string) string + + // Provides access to the Tracer that created this Span. + Tracer() Tracer + + // Deprecated: use LogFields or LogKV + LogEvent(event string) + // Deprecated: use LogFields or LogKV + LogEventWithPayload(event string, payload interface{}) + // Deprecated: use LogFields or LogKV + Log(data LogData) +} + +// LogRecord is data associated with a single Span log. Every LogRecord +// instance must specify at least one Field. +type LogRecord struct { + Timestamp time.Time + Fields []log.Field +} + +// FinishOptions allows Span.FinishWithOptions callers to override the finish +// timestamp and provide log data via a bulk interface. +type FinishOptions struct { + // FinishTime overrides the Span's finish time, or implicitly becomes + // time.Now() if FinishTime.IsZero(). + // + // FinishTime must resolve to a timestamp that's >= the Span's StartTime + // (per StartSpanOptions). + FinishTime time.Time + + // LogRecords allows the caller to specify the contents of many LogFields() + // calls with a single slice. May be nil. + // + // None of the LogRecord.Timestamp values may be .IsZero() (i.e., they must + // be set explicitly). Also, they must be >= the Span's start timestamp and + // <= the FinishTime (or time.Now() if FinishTime.IsZero()). Otherwise the + // behavior of FinishWithOptions() is undefined. + // + // If specified, the caller hands off ownership of LogRecords at + // FinishWithOptions() invocation time. + // + // If specified, the (deprecated) BulkLogData must be nil or empty. + LogRecords []LogRecord + + // BulkLogData is DEPRECATED. + BulkLogData []LogData +} + +// LogData is DEPRECATED +type LogData struct { + Timestamp time.Time + Event string + Payload interface{} +} + +// ToLogRecord converts a deprecated LogData to a non-deprecated LogRecord +func (ld *LogData) ToLogRecord() LogRecord { + var literalTimestamp time.Time + if ld.Timestamp.IsZero() { + literalTimestamp = time.Now() + } else { + literalTimestamp = ld.Timestamp + } + rval := LogRecord{ + Timestamp: literalTimestamp, + } + if ld.Payload == nil { + rval.Fields = []log.Field{ + log.String("event", ld.Event), + } + } else { + rval.Fields = []log.Field{ + log.String("event", ld.Event), + log.Object("payload", ld.Payload), + } + } + return rval +} diff --git a/template/faaschain/vendor/github.com/opentracing/opentracing-go/testtracer_test.go b/template/faaschain/vendor/github.com/opentracing/opentracing-go/testtracer_test.go new file mode 100644 index 00000000..dd13788c --- /dev/null +++ b/template/faaschain/vendor/github.com/opentracing/opentracing-go/testtracer_test.go @@ -0,0 +1,138 @@ +package opentracing + +import ( + "strconv" + "strings" + "time" + + "github.com/opentracing/opentracing-go/log" +) + +const testHTTPHeaderPrefix = "testprefix-" + +// testTracer is a most-noop Tracer implementation that makes it possible for +// unittests to verify whether certain methods were / were not called. +type testTracer struct{} + +var fakeIDSource = 1 + +func nextFakeID() int { + fakeIDSource++ + return fakeIDSource +} + +type testSpanContext struct { + HasParent bool + FakeID int +} + +func (n testSpanContext) ForeachBaggageItem(handler func(k, v string) bool) {} + +type testSpan struct { + spanContext testSpanContext + OperationName string + StartTime time.Time + Tags map[string]interface{} +} + +func (n testSpan) Equal(os Span) bool { + other, ok := os.(testSpan) + if !ok { + return false + } + if n.spanContext != other.spanContext { + return false + } + if n.OperationName != other.OperationName { + return false + } + if !n.StartTime.Equal(other.StartTime) { + return false + } + if len(n.Tags) != len(other.Tags) { + return false + } + + for k, v := range n.Tags { + if ov, ok := other.Tags[k]; !ok || ov != v { + return false + } + } + + return true +} + +// testSpan: +func (n testSpan) Context() SpanContext { return n.spanContext } +func (n testSpan) SetTag(key string, value interface{}) Span { return n } +func (n testSpan) Finish() {} +func (n testSpan) FinishWithOptions(opts FinishOptions) {} +func (n testSpan) LogFields(fields ...log.Field) {} +func (n testSpan) LogKV(kvs ...interface{}) {} +func (n testSpan) SetOperationName(operationName string) Span { return n } +func (n testSpan) Tracer() Tracer { return testTracer{} } +func (n testSpan) SetBaggageItem(key, val string) Span { return n } +func (n testSpan) BaggageItem(key string) string { return "" } +func (n testSpan) LogEvent(event string) {} +func (n testSpan) LogEventWithPayload(event string, payload interface{}) {} +func (n testSpan) Log(data LogData) {} + +// StartSpan belongs to the Tracer interface. +func (n testTracer) StartSpan(operationName string, opts ...StartSpanOption) Span { + sso := StartSpanOptions{} + for _, o := range opts { + o.Apply(&sso) + } + return n.startSpanWithOptions(operationName, sso) +} + +func (n testTracer) startSpanWithOptions(name string, opts StartSpanOptions) Span { + fakeID := nextFakeID() + if len(opts.References) > 0 { + fakeID = opts.References[0].ReferencedContext.(testSpanContext).FakeID + } + + return testSpan{ + OperationName: name, + StartTime: opts.StartTime, + Tags: opts.Tags, + spanContext: testSpanContext{ + HasParent: len(opts.References) > 0, + FakeID: fakeID, + }, + } +} + +// Inject belongs to the Tracer interface. +func (n testTracer) Inject(sp SpanContext, format interface{}, carrier interface{}) error { + spanContext := sp.(testSpanContext) + switch format { + case HTTPHeaders, TextMap: + carrier.(TextMapWriter).Set(testHTTPHeaderPrefix+"fakeid", strconv.Itoa(spanContext.FakeID)) + return nil + } + return ErrUnsupportedFormat +} + +// Extract belongs to the Tracer interface. +func (n testTracer) Extract(format interface{}, carrier interface{}) (SpanContext, error) { + switch format { + case HTTPHeaders, TextMap: + // Just for testing purposes... generally not a worthwhile thing to + // propagate. + sm := testSpanContext{} + err := carrier.(TextMapReader).ForeachKey(func(key, val string) error { + switch strings.ToLower(key) { + case testHTTPHeaderPrefix + "fakeid": + i, err := strconv.Atoi(val) + if err != nil { + return err + } + sm.FakeID = i + } + return nil + }) + return sm, err + } + return nil, ErrSpanContextNotFound +} diff --git a/template/faaschain/vendor/github.com/opentracing/opentracing-go/tracer.go b/template/faaschain/vendor/github.com/opentracing/opentracing-go/tracer.go new file mode 100644 index 00000000..7bca1f73 --- /dev/null +++ b/template/faaschain/vendor/github.com/opentracing/opentracing-go/tracer.go @@ -0,0 +1,305 @@ +package opentracing + +import "time" + +// Tracer is a simple, thin interface for Span creation and SpanContext +// propagation. +type Tracer interface { + + // Create, start, and return a new Span with the given `operationName` and + // incorporate the given StartSpanOption `opts`. (Note that `opts` borrows + // from the "functional options" pattern, per + // http://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis) + // + // A Span with no SpanReference options (e.g., opentracing.ChildOf() or + // opentracing.FollowsFrom()) becomes the root of its own trace. + // + // Examples: + // + // var tracer opentracing.Tracer = ... + // + // // The root-span case: + // sp := tracer.StartSpan("GetFeed") + // + // // The vanilla child span case: + // sp := tracer.StartSpan( + // "GetFeed", + // opentracing.ChildOf(parentSpan.Context())) + // + // // All the bells and whistles: + // sp := tracer.StartSpan( + // "GetFeed", + // opentracing.ChildOf(parentSpan.Context()), + // opentracing.Tag{"user_agent", loggedReq.UserAgent}, + // opentracing.StartTime(loggedReq.Timestamp), + // ) + // + StartSpan(operationName string, opts ...StartSpanOption) Span + + // Inject() takes the `sm` SpanContext instance and injects it for + // propagation within `carrier`. The actual type of `carrier` depends on + // the value of `format`. + // + // OpenTracing defines a common set of `format` values (see BuiltinFormat), + // and each has an expected carrier type. + // + // Other packages may declare their own `format` values, much like the keys + // used by `context.Context` (see + // https://godoc.org/golang.org/x/net/context#WithValue). + // + // Example usage (sans error handling): + // + // carrier := opentracing.HTTPHeadersCarrier(httpReq.Header) + // err := tracer.Inject( + // span.Context(), + // opentracing.HTTPHeaders, + // carrier) + // + // NOTE: All opentracing.Tracer implementations MUST support all + // BuiltinFormats. + // + // Implementations may return opentracing.ErrUnsupportedFormat if `format` + // is not supported by (or not known by) the implementation. + // + // Implementations may return opentracing.ErrInvalidCarrier or any other + // implementation-specific error if the format is supported but injection + // fails anyway. + // + // See Tracer.Extract(). + Inject(sm SpanContext, format interface{}, carrier interface{}) error + + // Extract() returns a SpanContext instance given `format` and `carrier`. + // + // OpenTracing defines a common set of `format` values (see BuiltinFormat), + // and each has an expected carrier type. + // + // Other packages may declare their own `format` values, much like the keys + // used by `context.Context` (see + // https://godoc.org/golang.org/x/net/context#WithValue). + // + // Example usage (with StartSpan): + // + // + // carrier := opentracing.HTTPHeadersCarrier(httpReq.Header) + // clientContext, err := tracer.Extract(opentracing.HTTPHeaders, carrier) + // + // // ... assuming the ultimate goal here is to resume the trace with a + // // server-side Span: + // var serverSpan opentracing.Span + // if err == nil { + // span = tracer.StartSpan( + // rpcMethodName, ext.RPCServerOption(clientContext)) + // } else { + // span = tracer.StartSpan(rpcMethodName) + // } + // + // + // NOTE: All opentracing.Tracer implementations MUST support all + // BuiltinFormats. + // + // Return values: + // - A successful Extract returns a SpanContext instance and a nil error + // - If there was simply no SpanContext to extract in `carrier`, Extract() + // returns (nil, opentracing.ErrSpanContextNotFound) + // - If `format` is unsupported or unrecognized, Extract() returns (nil, + // opentracing.ErrUnsupportedFormat) + // - If there are more fundamental problems with the `carrier` object, + // Extract() may return opentracing.ErrInvalidCarrier, + // opentracing.ErrSpanContextCorrupted, or implementation-specific + // errors. + // + // See Tracer.Inject(). + Extract(format interface{}, carrier interface{}) (SpanContext, error) +} + +// StartSpanOptions allows Tracer.StartSpan() callers and implementors a +// mechanism to override the start timestamp, specify Span References, and make +// a single Tag or multiple Tags available at Span start time. +// +// StartSpan() callers should look at the StartSpanOption interface and +// implementations available in this package. +// +// Tracer implementations can convert a slice of `StartSpanOption` instances +// into a `StartSpanOptions` struct like so: +// +// func StartSpan(opName string, opts ...opentracing.StartSpanOption) { +// sso := opentracing.StartSpanOptions{} +// for _, o := range opts { +// o.Apply(&sso) +// } +// ... +// } +// +type StartSpanOptions struct { + // Zero or more causal references to other Spans (via their SpanContext). + // If empty, start a "root" Span (i.e., start a new trace). + References []SpanReference + + // StartTime overrides the Span's start time, or implicitly becomes + // time.Now() if StartTime.IsZero(). + StartTime time.Time + + // Tags may have zero or more entries; the restrictions on map values are + // identical to those for Span.SetTag(). May be nil. + // + // If specified, the caller hands off ownership of Tags at + // StartSpan() invocation time. + Tags map[string]interface{} +} + +// StartSpanOption instances (zero or more) may be passed to Tracer.StartSpan. +// +// StartSpanOption borrows from the "functional options" pattern, per +// http://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis +type StartSpanOption interface { + Apply(*StartSpanOptions) +} + +// SpanReferenceType is an enum type describing different categories of +// relationships between two Spans. If Span-2 refers to Span-1, the +// SpanReferenceType describes Span-1 from Span-2's perspective. For example, +// ChildOfRef means that Span-1 created Span-2. +// +// NOTE: Span-1 and Span-2 do *not* necessarily depend on each other for +// completion; e.g., Span-2 may be part of a background job enqueued by Span-1, +// or Span-2 may be sitting in a distributed queue behind Span-1. +type SpanReferenceType int + +const ( + // ChildOfRef refers to a parent Span that caused *and* somehow depends + // upon the new child Span. Often (but not always), the parent Span cannot + // finish until the child Span does. + // + // An timing diagram for a ChildOfRef that's blocked on the new Span: + // + // [-Parent Span---------] + // [-Child Span----] + // + // See http://opentracing.io/spec/ + // + // See opentracing.ChildOf() + ChildOfRef SpanReferenceType = iota + + // FollowsFromRef refers to a parent Span that does not depend in any way + // on the result of the new child Span. For instance, one might use + // FollowsFromRefs to describe pipeline stages separated by queues, + // or a fire-and-forget cache insert at the tail end of a web request. + // + // A FollowsFromRef Span is part of the same logical trace as the new Span: + // i.e., the new Span is somehow caused by the work of its FollowsFromRef. + // + // All of the following could be valid timing diagrams for children that + // "FollowFrom" a parent. + // + // [-Parent Span-] [-Child Span-] + // + // + // [-Parent Span--] + // [-Child Span-] + // + // + // [-Parent Span-] + // [-Child Span-] + // + // See http://opentracing.io/spec/ + // + // See opentracing.FollowsFrom() + FollowsFromRef +) + +// SpanReference is a StartSpanOption that pairs a SpanReferenceType and a +// referenced SpanContext. See the SpanReferenceType documentation for +// supported relationships. If SpanReference is created with +// ReferencedContext==nil, it has no effect. Thus it allows for a more concise +// syntax for starting spans: +// +// sc, _ := tracer.Extract(someFormat, someCarrier) +// span := tracer.StartSpan("operation", opentracing.ChildOf(sc)) +// +// The `ChildOf(sc)` option above will not panic if sc == nil, it will just +// not add the parent span reference to the options. +type SpanReference struct { + Type SpanReferenceType + ReferencedContext SpanContext +} + +// Apply satisfies the StartSpanOption interface. +func (r SpanReference) Apply(o *StartSpanOptions) { + if r.ReferencedContext != nil { + o.References = append(o.References, r) + } +} + +// ChildOf returns a StartSpanOption pointing to a dependent parent span. +// If sc == nil, the option has no effect. +// +// See ChildOfRef, SpanReference +func ChildOf(sc SpanContext) SpanReference { + return SpanReference{ + Type: ChildOfRef, + ReferencedContext: sc, + } +} + +// FollowsFrom returns a StartSpanOption pointing to a parent Span that caused +// the child Span but does not directly depend on its result in any way. +// If sc == nil, the option has no effect. +// +// See FollowsFromRef, SpanReference +func FollowsFrom(sc SpanContext) SpanReference { + return SpanReference{ + Type: FollowsFromRef, + ReferencedContext: sc, + } +} + +// StartTime is a StartSpanOption that sets an explicit start timestamp for the +// new Span. +type StartTime time.Time + +// Apply satisfies the StartSpanOption interface. +func (t StartTime) Apply(o *StartSpanOptions) { + o.StartTime = time.Time(t) +} + +// Tags are a generic map from an arbitrary string key to an opaque value type. +// The underlying tracing system is responsible for interpreting and +// serializing the values. +type Tags map[string]interface{} + +// Apply satisfies the StartSpanOption interface. +func (t Tags) Apply(o *StartSpanOptions) { + if o.Tags == nil { + o.Tags = make(map[string]interface{}) + } + for k, v := range t { + o.Tags[k] = v + } +} + +// Tag may be passed as a StartSpanOption to add a tag to new spans, +// or its Set method may be used to apply the tag to an existing Span, +// for example: +// +// tracer.StartSpan("opName", Tag{"Key", value}) +// +// or +// +// Tag{"key", value}.Set(span) +type Tag struct { + Key string + Value interface{} +} + +// Apply satisfies the StartSpanOption interface. +func (t Tag) Apply(o *StartSpanOptions) { + if o.Tags == nil { + o.Tags = make(map[string]interface{}) + } + o.Tags[t.Key] = t.Value +} + +// Set applies the tag to an existing Span. +func (t Tag) Set(s Span) { + s.SetTag(t.Key, t.Value) +} diff --git a/template/faaschain/vendor/github.com/pkg/errors/.gitignore b/template/faaschain/vendor/github.com/pkg/errors/.gitignore new file mode 100644 index 00000000..daf913b1 --- /dev/null +++ b/template/faaschain/vendor/github.com/pkg/errors/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/template/faaschain/vendor/github.com/pkg/errors/.travis.yml b/template/faaschain/vendor/github.com/pkg/errors/.travis.yml new file mode 100644 index 00000000..15e5a192 --- /dev/null +++ b/template/faaschain/vendor/github.com/pkg/errors/.travis.yml @@ -0,0 +1,14 @@ +language: go +go_import_path: github.com/pkg/errors +go: + - 1.4.x + - 1.5.x + - 1.6.x + - 1.7.x + - 1.8.x + - 1.9.x + - 1.10.x + - tip + +script: + - go test -v ./... diff --git a/template/faaschain/vendor/github.com/pkg/errors/LICENSE b/template/faaschain/vendor/github.com/pkg/errors/LICENSE new file mode 100644 index 00000000..835ba3e7 --- /dev/null +++ b/template/faaschain/vendor/github.com/pkg/errors/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2015, Dave Cheney +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. + +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 HOLDER 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/template/faaschain/vendor/github.com/pkg/errors/README.md b/template/faaschain/vendor/github.com/pkg/errors/README.md new file mode 100644 index 00000000..6483ba2a --- /dev/null +++ b/template/faaschain/vendor/github.com/pkg/errors/README.md @@ -0,0 +1,52 @@ +# errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) [![Sourcegraph](https://sourcegraph.com/github.com/pkg/errors/-/badge.svg)](https://sourcegraph.com/github.com/pkg/errors?badge) + +Package errors provides simple error handling primitives. + +`go get github.com/pkg/errors` + +The traditional error handling idiom in Go is roughly akin to +```go +if err != nil { + return err +} +``` +which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error. + +## Adding context to an error + +The errors.Wrap function returns a new error that adds context to the original error. For example +```go +_, err := ioutil.ReadAll(r) +if err != nil { + return errors.Wrap(err, "read failed") +} +``` +## Retrieving the cause of an error + +Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`. +```go +type causer interface { + Cause() error +} +``` +`errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example: +```go +switch err := errors.Cause(err).(type) { +case *MyError: + // handle specifically +default: + // unknown error +} +``` + +[Read the package documentation for more information](https://godoc.org/github.com/pkg/errors). + +## Contributing + +We welcome pull requests, bug fixes and issue reports. With that said, the bar for adding new symbols to this package is intentionally set high. + +Before proposing a change, please discuss your change by raising an issue. + +## License + +BSD-2-Clause diff --git a/template/faaschain/vendor/github.com/pkg/errors/appveyor.yml b/template/faaschain/vendor/github.com/pkg/errors/appveyor.yml new file mode 100644 index 00000000..a932eade --- /dev/null +++ b/template/faaschain/vendor/github.com/pkg/errors/appveyor.yml @@ -0,0 +1,32 @@ +version: build-{build}.{branch} + +clone_folder: C:\gopath\src\github.com\pkg\errors +shallow_clone: true # for startup speed + +environment: + GOPATH: C:\gopath + +platform: + - x64 + +# http://www.appveyor.com/docs/installed-software +install: + # some helpful output for debugging builds + - go version + - go env + # pre-installed MinGW at C:\MinGW is 32bit only + # but MSYS2 at C:\msys64 has mingw64 + - set PATH=C:\msys64\mingw64\bin;%PATH% + - gcc --version + - g++ --version + +build_script: + - go install -v ./... + +test_script: + - set PATH=C:\gopath\bin;%PATH% + - go test -v ./... + +#artifacts: +# - path: '%GOPATH%\bin\*.exe' +deploy: off diff --git a/template/faaschain/vendor/github.com/pkg/errors/bench_test.go b/template/faaschain/vendor/github.com/pkg/errors/bench_test.go new file mode 100644 index 00000000..903b5f2d --- /dev/null +++ b/template/faaschain/vendor/github.com/pkg/errors/bench_test.go @@ -0,0 +1,63 @@ +// +build go1.7 + +package errors + +import ( + "fmt" + "testing" + + stderrors "errors" +) + +func noErrors(at, depth int) error { + if at >= depth { + return stderrors.New("no error") + } + return noErrors(at+1, depth) +} + +func yesErrors(at, depth int) error { + if at >= depth { + return New("ye error") + } + return yesErrors(at+1, depth) +} + +// GlobalE is an exported global to store the result of benchmark results, +// preventing the compiler from optimising the benchmark functions away. +var GlobalE error + +func BenchmarkErrors(b *testing.B) { + type run struct { + stack int + std bool + } + runs := []run{ + {10, false}, + {10, true}, + {100, false}, + {100, true}, + {1000, false}, + {1000, true}, + } + for _, r := range runs { + part := "pkg/errors" + if r.std { + part = "errors" + } + name := fmt.Sprintf("%s-stack-%d", part, r.stack) + b.Run(name, func(b *testing.B) { + var err error + f := yesErrors + if r.std { + f = noErrors + } + b.ReportAllocs() + for i := 0; i < b.N; i++ { + err = f(0, r.stack) + } + b.StopTimer() + GlobalE = err + }) + } +} diff --git a/template/faaschain/vendor/github.com/pkg/errors/errors.go b/template/faaschain/vendor/github.com/pkg/errors/errors.go new file mode 100644 index 00000000..842ee804 --- /dev/null +++ b/template/faaschain/vendor/github.com/pkg/errors/errors.go @@ -0,0 +1,269 @@ +// Package errors provides simple error handling primitives. +// +// The traditional error handling idiom in Go is roughly akin to +// +// if err != nil { +// return err +// } +// +// which applied recursively up the call stack results in error reports +// without context or debugging information. The errors package allows +// programmers to add context to the failure path in their code in a way +// that does not destroy the original value of the error. +// +// Adding context to an error +// +// The errors.Wrap function returns a new error that adds context to the +// original error by recording a stack trace at the point Wrap is called, +// and the supplied message. For example +// +// _, err := ioutil.ReadAll(r) +// if err != nil { +// return errors.Wrap(err, "read failed") +// } +// +// If additional control is required the errors.WithStack and errors.WithMessage +// functions destructure errors.Wrap into its component operations of annotating +// an error with a stack trace and an a message, respectively. +// +// Retrieving the cause of an error +// +// Using errors.Wrap constructs a stack of errors, adding context to the +// preceding error. Depending on the nature of the error it may be necessary +// to reverse the operation of errors.Wrap to retrieve the original error +// for inspection. Any error value which implements this interface +// +// type causer interface { +// Cause() error +// } +// +// can be inspected by errors.Cause. errors.Cause will recursively retrieve +// the topmost error which does not implement causer, which is assumed to be +// the original cause. For example: +// +// switch err := errors.Cause(err).(type) { +// case *MyError: +// // handle specifically +// default: +// // unknown error +// } +// +// causer interface is not exported by this package, but is considered a part +// of stable public API. +// +// Formatted printing of errors +// +// All error values returned from this package implement fmt.Formatter and can +// be formatted by the fmt package. The following verbs are supported +// +// %s print the error. If the error has a Cause it will be +// printed recursively +// %v see %s +// %+v extended format. Each Frame of the error's StackTrace will +// be printed in detail. +// +// Retrieving the stack trace of an error or wrapper +// +// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are +// invoked. This information can be retrieved with the following interface. +// +// type stackTracer interface { +// StackTrace() errors.StackTrace +// } +// +// Where errors.StackTrace is defined as +// +// type StackTrace []Frame +// +// The Frame type represents a call site in the stack trace. Frame supports +// the fmt.Formatter interface that can be used for printing information about +// the stack trace of this error. For example: +// +// if err, ok := err.(stackTracer); ok { +// for _, f := range err.StackTrace() { +// fmt.Printf("%+s:%d", f) +// } +// } +// +// stackTracer interface is not exported by this package, but is considered a part +// of stable public API. +// +// See the documentation for Frame.Format for more details. +package errors + +import ( + "fmt" + "io" +) + +// New returns an error with the supplied message. +// New also records the stack trace at the point it was called. +func New(message string) error { + return &fundamental{ + msg: message, + stack: callers(), + } +} + +// Errorf formats according to a format specifier and returns the string +// as a value that satisfies error. +// Errorf also records the stack trace at the point it was called. +func Errorf(format string, args ...interface{}) error { + return &fundamental{ + msg: fmt.Sprintf(format, args...), + stack: callers(), + } +} + +// fundamental is an error that has a message and a stack, but no caller. +type fundamental struct { + msg string + *stack +} + +func (f *fundamental) Error() string { return f.msg } + +func (f *fundamental) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + io.WriteString(s, f.msg) + f.stack.Format(s, verb) + return + } + fallthrough + case 's': + io.WriteString(s, f.msg) + case 'q': + fmt.Fprintf(s, "%q", f.msg) + } +} + +// WithStack annotates err with a stack trace at the point WithStack was called. +// If err is nil, WithStack returns nil. +func WithStack(err error) error { + if err == nil { + return nil + } + return &withStack{ + err, + callers(), + } +} + +type withStack struct { + error + *stack +} + +func (w *withStack) Cause() error { return w.error } + +func (w *withStack) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + fmt.Fprintf(s, "%+v", w.Cause()) + w.stack.Format(s, verb) + return + } + fallthrough + case 's': + io.WriteString(s, w.Error()) + case 'q': + fmt.Fprintf(s, "%q", w.Error()) + } +} + +// Wrap returns an error annotating err with a stack trace +// at the point Wrap is called, and the supplied message. +// If err is nil, Wrap returns nil. +func Wrap(err error, message string) error { + if err == nil { + return nil + } + err = &withMessage{ + cause: err, + msg: message, + } + return &withStack{ + err, + callers(), + } +} + +// Wrapf returns an error annotating err with a stack trace +// at the point Wrapf is call, and the format specifier. +// If err is nil, Wrapf returns nil. +func Wrapf(err error, format string, args ...interface{}) error { + if err == nil { + return nil + } + err = &withMessage{ + cause: err, + msg: fmt.Sprintf(format, args...), + } + return &withStack{ + err, + callers(), + } +} + +// WithMessage annotates err with a new message. +// If err is nil, WithMessage returns nil. +func WithMessage(err error, message string) error { + if err == nil { + return nil + } + return &withMessage{ + cause: err, + msg: message, + } +} + +type withMessage struct { + cause error + msg string +} + +func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() } +func (w *withMessage) Cause() error { return w.cause } + +func (w *withMessage) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + fmt.Fprintf(s, "%+v\n", w.Cause()) + io.WriteString(s, w.msg) + return + } + fallthrough + case 's', 'q': + io.WriteString(s, w.Error()) + } +} + +// Cause returns the underlying cause of the error, if possible. +// An error value has a cause if it implements the following +// interface: +// +// type causer interface { +// Cause() error +// } +// +// If the error does not implement Cause, the original error will +// be returned. If the error is nil, nil will be returned without further +// investigation. +func Cause(err error) error { + type causer interface { + Cause() error + } + + for err != nil { + cause, ok := err.(causer) + if !ok { + break + } + err = cause.Cause() + } + return err +} diff --git a/template/faaschain/vendor/github.com/pkg/errors/errors_test.go b/template/faaschain/vendor/github.com/pkg/errors/errors_test.go new file mode 100644 index 00000000..c4e6eef6 --- /dev/null +++ b/template/faaschain/vendor/github.com/pkg/errors/errors_test.go @@ -0,0 +1,225 @@ +package errors + +import ( + "errors" + "fmt" + "io" + "reflect" + "testing" +) + +func TestNew(t *testing.T) { + tests := []struct { + err string + want error + }{ + {"", fmt.Errorf("")}, + {"foo", fmt.Errorf("foo")}, + {"foo", New("foo")}, + {"string with format specifiers: %v", errors.New("string with format specifiers: %v")}, + } + + for _, tt := range tests { + got := New(tt.err) + if got.Error() != tt.want.Error() { + t.Errorf("New.Error(): got: %q, want %q", got, tt.want) + } + } +} + +func TestWrapNil(t *testing.T) { + got := Wrap(nil, "no error") + if got != nil { + t.Errorf("Wrap(nil, \"no error\"): got %#v, expected nil", got) + } +} + +func TestWrap(t *testing.T) { + tests := []struct { + err error + message string + want string + }{ + {io.EOF, "read error", "read error: EOF"}, + {Wrap(io.EOF, "read error"), "client error", "client error: read error: EOF"}, + } + + for _, tt := range tests { + got := Wrap(tt.err, tt.message).Error() + if got != tt.want { + t.Errorf("Wrap(%v, %q): got: %v, want %v", tt.err, tt.message, got, tt.want) + } + } +} + +type nilError struct{} + +func (nilError) Error() string { return "nil error" } + +func TestCause(t *testing.T) { + x := New("error") + tests := []struct { + err error + want error + }{{ + // nil error is nil + err: nil, + want: nil, + }, { + // explicit nil error is nil + err: (error)(nil), + want: nil, + }, { + // typed nil is nil + err: (*nilError)(nil), + want: (*nilError)(nil), + }, { + // uncaused error is unaffected + err: io.EOF, + want: io.EOF, + }, { + // caused error returns cause + err: Wrap(io.EOF, "ignored"), + want: io.EOF, + }, { + err: x, // return from errors.New + want: x, + }, { + WithMessage(nil, "whoops"), + nil, + }, { + WithMessage(io.EOF, "whoops"), + io.EOF, + }, { + WithStack(nil), + nil, + }, { + WithStack(io.EOF), + io.EOF, + }} + + for i, tt := range tests { + got := Cause(tt.err) + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("test %d: got %#v, want %#v", i+1, got, tt.want) + } + } +} + +func TestWrapfNil(t *testing.T) { + got := Wrapf(nil, "no error") + if got != nil { + t.Errorf("Wrapf(nil, \"no error\"): got %#v, expected nil", got) + } +} + +func TestWrapf(t *testing.T) { + tests := []struct { + err error + message string + want string + }{ + {io.EOF, "read error", "read error: EOF"}, + {Wrapf(io.EOF, "read error without format specifiers"), "client error", "client error: read error without format specifiers: EOF"}, + {Wrapf(io.EOF, "read error with %d format specifier", 1), "client error", "client error: read error with 1 format specifier: EOF"}, + } + + for _, tt := range tests { + got := Wrapf(tt.err, tt.message).Error() + if got != tt.want { + t.Errorf("Wrapf(%v, %q): got: %v, want %v", tt.err, tt.message, got, tt.want) + } + } +} + +func TestErrorf(t *testing.T) { + tests := []struct { + err error + want string + }{ + {Errorf("read error without format specifiers"), "read error without format specifiers"}, + {Errorf("read error with %d format specifier", 1), "read error with 1 format specifier"}, + } + + for _, tt := range tests { + got := tt.err.Error() + if got != tt.want { + t.Errorf("Errorf(%v): got: %q, want %q", tt.err, got, tt.want) + } + } +} + +func TestWithStackNil(t *testing.T) { + got := WithStack(nil) + if got != nil { + t.Errorf("WithStack(nil): got %#v, expected nil", got) + } +} + +func TestWithStack(t *testing.T) { + tests := []struct { + err error + want string + }{ + {io.EOF, "EOF"}, + {WithStack(io.EOF), "EOF"}, + } + + for _, tt := range tests { + got := WithStack(tt.err).Error() + if got != tt.want { + t.Errorf("WithStack(%v): got: %v, want %v", tt.err, got, tt.want) + } + } +} + +func TestWithMessageNil(t *testing.T) { + got := WithMessage(nil, "no error") + if got != nil { + t.Errorf("WithMessage(nil, \"no error\"): got %#v, expected nil", got) + } +} + +func TestWithMessage(t *testing.T) { + tests := []struct { + err error + message string + want string + }{ + {io.EOF, "read error", "read error: EOF"}, + {WithMessage(io.EOF, "read error"), "client error", "client error: read error: EOF"}, + } + + for _, tt := range tests { + got := WithMessage(tt.err, tt.message).Error() + if got != tt.want { + t.Errorf("WithMessage(%v, %q): got: %q, want %q", tt.err, tt.message, got, tt.want) + } + } +} + +// errors.New, etc values are not expected to be compared by value +// but the change in errors#27 made them incomparable. Assert that +// various kinds of errors have a functional equality operator, even +// if the result of that equality is always false. +func TestErrorEquality(t *testing.T) { + vals := []error{ + nil, + io.EOF, + errors.New("EOF"), + New("EOF"), + Errorf("EOF"), + Wrap(io.EOF, "EOF"), + Wrapf(io.EOF, "EOF%d", 2), + WithMessage(nil, "whoops"), + WithMessage(io.EOF, "whoops"), + WithStack(io.EOF), + WithStack(nil), + } + + for i := range vals { + for j := range vals { + _ = vals[i] == vals[j] // mustn't panic + } + } +} diff --git a/template/faaschain/vendor/github.com/pkg/errors/example_test.go b/template/faaschain/vendor/github.com/pkg/errors/example_test.go new file mode 100644 index 00000000..c1fc13e3 --- /dev/null +++ b/template/faaschain/vendor/github.com/pkg/errors/example_test.go @@ -0,0 +1,205 @@ +package errors_test + +import ( + "fmt" + + "github.com/pkg/errors" +) + +func ExampleNew() { + err := errors.New("whoops") + fmt.Println(err) + + // Output: whoops +} + +func ExampleNew_printf() { + err := errors.New("whoops") + fmt.Printf("%+v", err) + + // Example output: + // whoops + // github.com/pkg/errors_test.ExampleNew_printf + // /home/dfc/src/github.com/pkg/errors/example_test.go:17 + // testing.runExample + // /home/dfc/go/src/testing/example.go:114 + // testing.RunExamples + // /home/dfc/go/src/testing/example.go:38 + // testing.(*M).Run + // /home/dfc/go/src/testing/testing.go:744 + // main.main + // /github.com/pkg/errors/_test/_testmain.go:106 + // runtime.main + // /home/dfc/go/src/runtime/proc.go:183 + // runtime.goexit + // /home/dfc/go/src/runtime/asm_amd64.s:2059 +} + +func ExampleWithMessage() { + cause := errors.New("whoops") + err := errors.WithMessage(cause, "oh noes") + fmt.Println(err) + + // Output: oh noes: whoops +} + +func ExampleWithStack() { + cause := errors.New("whoops") + err := errors.WithStack(cause) + fmt.Println(err) + + // Output: whoops +} + +func ExampleWithStack_printf() { + cause := errors.New("whoops") + err := errors.WithStack(cause) + fmt.Printf("%+v", err) + + // Example Output: + // whoops + // github.com/pkg/errors_test.ExampleWithStack_printf + // /home/fabstu/go/src/github.com/pkg/errors/example_test.go:55 + // testing.runExample + // /usr/lib/go/src/testing/example.go:114 + // testing.RunExamples + // /usr/lib/go/src/testing/example.go:38 + // testing.(*M).Run + // /usr/lib/go/src/testing/testing.go:744 + // main.main + // github.com/pkg/errors/_test/_testmain.go:106 + // runtime.main + // /usr/lib/go/src/runtime/proc.go:183 + // runtime.goexit + // /usr/lib/go/src/runtime/asm_amd64.s:2086 + // github.com/pkg/errors_test.ExampleWithStack_printf + // /home/fabstu/go/src/github.com/pkg/errors/example_test.go:56 + // testing.runExample + // /usr/lib/go/src/testing/example.go:114 + // testing.RunExamples + // /usr/lib/go/src/testing/example.go:38 + // testing.(*M).Run + // /usr/lib/go/src/testing/testing.go:744 + // main.main + // github.com/pkg/errors/_test/_testmain.go:106 + // runtime.main + // /usr/lib/go/src/runtime/proc.go:183 + // runtime.goexit + // /usr/lib/go/src/runtime/asm_amd64.s:2086 +} + +func ExampleWrap() { + cause := errors.New("whoops") + err := errors.Wrap(cause, "oh noes") + fmt.Println(err) + + // Output: oh noes: whoops +} + +func fn() error { + e1 := errors.New("error") + e2 := errors.Wrap(e1, "inner") + e3 := errors.Wrap(e2, "middle") + return errors.Wrap(e3, "outer") +} + +func ExampleCause() { + err := fn() + fmt.Println(err) + fmt.Println(errors.Cause(err)) + + // Output: outer: middle: inner: error + // error +} + +func ExampleWrap_extended() { + err := fn() + fmt.Printf("%+v\n", err) + + // Example output: + // error + // github.com/pkg/errors_test.fn + // /home/dfc/src/github.com/pkg/errors/example_test.go:47 + // github.com/pkg/errors_test.ExampleCause_printf + // /home/dfc/src/github.com/pkg/errors/example_test.go:63 + // testing.runExample + // /home/dfc/go/src/testing/example.go:114 + // testing.RunExamples + // /home/dfc/go/src/testing/example.go:38 + // testing.(*M).Run + // /home/dfc/go/src/testing/testing.go:744 + // main.main + // /github.com/pkg/errors/_test/_testmain.go:104 + // runtime.main + // /home/dfc/go/src/runtime/proc.go:183 + // runtime.goexit + // /home/dfc/go/src/runtime/asm_amd64.s:2059 + // github.com/pkg/errors_test.fn + // /home/dfc/src/github.com/pkg/errors/example_test.go:48: inner + // github.com/pkg/errors_test.fn + // /home/dfc/src/github.com/pkg/errors/example_test.go:49: middle + // github.com/pkg/errors_test.fn + // /home/dfc/src/github.com/pkg/errors/example_test.go:50: outer +} + +func ExampleWrapf() { + cause := errors.New("whoops") + err := errors.Wrapf(cause, "oh noes #%d", 2) + fmt.Println(err) + + // Output: oh noes #2: whoops +} + +func ExampleErrorf_extended() { + err := errors.Errorf("whoops: %s", "foo") + fmt.Printf("%+v", err) + + // Example output: + // whoops: foo + // github.com/pkg/errors_test.ExampleErrorf + // /home/dfc/src/github.com/pkg/errors/example_test.go:101 + // testing.runExample + // /home/dfc/go/src/testing/example.go:114 + // testing.RunExamples + // /home/dfc/go/src/testing/example.go:38 + // testing.(*M).Run + // /home/dfc/go/src/testing/testing.go:744 + // main.main + // /github.com/pkg/errors/_test/_testmain.go:102 + // runtime.main + // /home/dfc/go/src/runtime/proc.go:183 + // runtime.goexit + // /home/dfc/go/src/runtime/asm_amd64.s:2059 +} + +func Example_stackTrace() { + type stackTracer interface { + StackTrace() errors.StackTrace + } + + err, ok := errors.Cause(fn()).(stackTracer) + if !ok { + panic("oops, err does not implement stackTracer") + } + + st := err.StackTrace() + fmt.Printf("%+v", st[0:2]) // top two frames + + // Example output: + // github.com/pkg/errors_test.fn + // /home/dfc/src/github.com/pkg/errors/example_test.go:47 + // github.com/pkg/errors_test.Example_stackTrace + // /home/dfc/src/github.com/pkg/errors/example_test.go:127 +} + +func ExampleCause_printf() { + err := errors.Wrap(func() error { + return func() error { + return errors.Errorf("hello %s", fmt.Sprintf("world")) + }() + }(), "failed") + + fmt.Printf("%v", err) + + // Output: failed: hello world +} diff --git a/template/faaschain/vendor/github.com/pkg/errors/format_test.go b/template/faaschain/vendor/github.com/pkg/errors/format_test.go new file mode 100644 index 00000000..c2eef5f0 --- /dev/null +++ b/template/faaschain/vendor/github.com/pkg/errors/format_test.go @@ -0,0 +1,535 @@ +package errors + +import ( + "errors" + "fmt" + "io" + "regexp" + "strings" + "testing" +) + +func TestFormatNew(t *testing.T) { + tests := []struct { + error + format string + want string + }{{ + New("error"), + "%s", + "error", + }, { + New("error"), + "%v", + "error", + }, { + New("error"), + "%+v", + "error\n" + + "github.com/pkg/errors.TestFormatNew\n" + + "\t.+/github.com/pkg/errors/format_test.go:26", + }, { + New("error"), + "%q", + `"error"`, + }} + + for i, tt := range tests { + testFormatRegexp(t, i, tt.error, tt.format, tt.want) + } +} + +func TestFormatErrorf(t *testing.T) { + tests := []struct { + error + format string + want string + }{{ + Errorf("%s", "error"), + "%s", + "error", + }, { + Errorf("%s", "error"), + "%v", + "error", + }, { + Errorf("%s", "error"), + "%+v", + "error\n" + + "github.com/pkg/errors.TestFormatErrorf\n" + + "\t.+/github.com/pkg/errors/format_test.go:56", + }} + + for i, tt := range tests { + testFormatRegexp(t, i, tt.error, tt.format, tt.want) + } +} + +func TestFormatWrap(t *testing.T) { + tests := []struct { + error + format string + want string + }{{ + Wrap(New("error"), "error2"), + "%s", + "error2: error", + }, { + Wrap(New("error"), "error2"), + "%v", + "error2: error", + }, { + Wrap(New("error"), "error2"), + "%+v", + "error\n" + + "github.com/pkg/errors.TestFormatWrap\n" + + "\t.+/github.com/pkg/errors/format_test.go:82", + }, { + Wrap(io.EOF, "error"), + "%s", + "error: EOF", + }, { + Wrap(io.EOF, "error"), + "%v", + "error: EOF", + }, { + Wrap(io.EOF, "error"), + "%+v", + "EOF\n" + + "error\n" + + "github.com/pkg/errors.TestFormatWrap\n" + + "\t.+/github.com/pkg/errors/format_test.go:96", + }, { + Wrap(Wrap(io.EOF, "error1"), "error2"), + "%+v", + "EOF\n" + + "error1\n" + + "github.com/pkg/errors.TestFormatWrap\n" + + "\t.+/github.com/pkg/errors/format_test.go:103\n", + }, { + Wrap(New("error with space"), "context"), + "%q", + `"context: error with space"`, + }} + + for i, tt := range tests { + testFormatRegexp(t, i, tt.error, tt.format, tt.want) + } +} + +func TestFormatWrapf(t *testing.T) { + tests := []struct { + error + format string + want string + }{{ + Wrapf(io.EOF, "error%d", 2), + "%s", + "error2: EOF", + }, { + Wrapf(io.EOF, "error%d", 2), + "%v", + "error2: EOF", + }, { + Wrapf(io.EOF, "error%d", 2), + "%+v", + "EOF\n" + + "error2\n" + + "github.com/pkg/errors.TestFormatWrapf\n" + + "\t.+/github.com/pkg/errors/format_test.go:134", + }, { + Wrapf(New("error"), "error%d", 2), + "%s", + "error2: error", + }, { + Wrapf(New("error"), "error%d", 2), + "%v", + "error2: error", + }, { + Wrapf(New("error"), "error%d", 2), + "%+v", + "error\n" + + "github.com/pkg/errors.TestFormatWrapf\n" + + "\t.+/github.com/pkg/errors/format_test.go:149", + }} + + for i, tt := range tests { + testFormatRegexp(t, i, tt.error, tt.format, tt.want) + } +} + +func TestFormatWithStack(t *testing.T) { + tests := []struct { + error + format string + want []string + }{{ + WithStack(io.EOF), + "%s", + []string{"EOF"}, + }, { + WithStack(io.EOF), + "%v", + []string{"EOF"}, + }, { + WithStack(io.EOF), + "%+v", + []string{"EOF", + "github.com/pkg/errors.TestFormatWithStack\n" + + "\t.+/github.com/pkg/errors/format_test.go:175"}, + }, { + WithStack(New("error")), + "%s", + []string{"error"}, + }, { + WithStack(New("error")), + "%v", + []string{"error"}, + }, { + WithStack(New("error")), + "%+v", + []string{"error", + "github.com/pkg/errors.TestFormatWithStack\n" + + "\t.+/github.com/pkg/errors/format_test.go:189", + "github.com/pkg/errors.TestFormatWithStack\n" + + "\t.+/github.com/pkg/errors/format_test.go:189"}, + }, { + WithStack(WithStack(io.EOF)), + "%+v", + []string{"EOF", + "github.com/pkg/errors.TestFormatWithStack\n" + + "\t.+/github.com/pkg/errors/format_test.go:197", + "github.com/pkg/errors.TestFormatWithStack\n" + + "\t.+/github.com/pkg/errors/format_test.go:197"}, + }, { + WithStack(WithStack(Wrapf(io.EOF, "message"))), + "%+v", + []string{"EOF", + "message", + "github.com/pkg/errors.TestFormatWithStack\n" + + "\t.+/github.com/pkg/errors/format_test.go:205", + "github.com/pkg/errors.TestFormatWithStack\n" + + "\t.+/github.com/pkg/errors/format_test.go:205", + "github.com/pkg/errors.TestFormatWithStack\n" + + "\t.+/github.com/pkg/errors/format_test.go:205"}, + }, { + WithStack(Errorf("error%d", 1)), + "%+v", + []string{"error1", + "github.com/pkg/errors.TestFormatWithStack\n" + + "\t.+/github.com/pkg/errors/format_test.go:216", + "github.com/pkg/errors.TestFormatWithStack\n" + + "\t.+/github.com/pkg/errors/format_test.go:216"}, + }} + + for i, tt := range tests { + testFormatCompleteCompare(t, i, tt.error, tt.format, tt.want, true) + } +} + +func TestFormatWithMessage(t *testing.T) { + tests := []struct { + error + format string + want []string + }{{ + WithMessage(New("error"), "error2"), + "%s", + []string{"error2: error"}, + }, { + WithMessage(New("error"), "error2"), + "%v", + []string{"error2: error"}, + }, { + WithMessage(New("error"), "error2"), + "%+v", + []string{ + "error", + "github.com/pkg/errors.TestFormatWithMessage\n" + + "\t.+/github.com/pkg/errors/format_test.go:244", + "error2"}, + }, { + WithMessage(io.EOF, "addition1"), + "%s", + []string{"addition1: EOF"}, + }, { + WithMessage(io.EOF, "addition1"), + "%v", + []string{"addition1: EOF"}, + }, { + WithMessage(io.EOF, "addition1"), + "%+v", + []string{"EOF", "addition1"}, + }, { + WithMessage(WithMessage(io.EOF, "addition1"), "addition2"), + "%v", + []string{"addition2: addition1: EOF"}, + }, { + WithMessage(WithMessage(io.EOF, "addition1"), "addition2"), + "%+v", + []string{"EOF", "addition1", "addition2"}, + }, { + Wrap(WithMessage(io.EOF, "error1"), "error2"), + "%+v", + []string{"EOF", "error1", "error2", + "github.com/pkg/errors.TestFormatWithMessage\n" + + "\t.+/github.com/pkg/errors/format_test.go:272"}, + }, { + WithMessage(Errorf("error%d", 1), "error2"), + "%+v", + []string{"error1", + "github.com/pkg/errors.TestFormatWithMessage\n" + + "\t.+/github.com/pkg/errors/format_test.go:278", + "error2"}, + }, { + WithMessage(WithStack(io.EOF), "error"), + "%+v", + []string{ + "EOF", + "github.com/pkg/errors.TestFormatWithMessage\n" + + "\t.+/github.com/pkg/errors/format_test.go:285", + "error"}, + }, { + WithMessage(Wrap(WithStack(io.EOF), "inside-error"), "outside-error"), + "%+v", + []string{ + "EOF", + "github.com/pkg/errors.TestFormatWithMessage\n" + + "\t.+/github.com/pkg/errors/format_test.go:293", + "inside-error", + "github.com/pkg/errors.TestFormatWithMessage\n" + + "\t.+/github.com/pkg/errors/format_test.go:293", + "outside-error"}, + }} + + for i, tt := range tests { + testFormatCompleteCompare(t, i, tt.error, tt.format, tt.want, true) + } +} + +func TestFormatGeneric(t *testing.T) { + starts := []struct { + err error + want []string + }{ + {New("new-error"), []string{ + "new-error", + "github.com/pkg/errors.TestFormatGeneric\n" + + "\t.+/github.com/pkg/errors/format_test.go:315"}, + }, {Errorf("errorf-error"), []string{ + "errorf-error", + "github.com/pkg/errors.TestFormatGeneric\n" + + "\t.+/github.com/pkg/errors/format_test.go:319"}, + }, {errors.New("errors-new-error"), []string{ + "errors-new-error"}, + }, + } + + wrappers := []wrapper{ + { + func(err error) error { return WithMessage(err, "with-message") }, + []string{"with-message"}, + }, { + func(err error) error { return WithStack(err) }, + []string{ + "github.com/pkg/errors.(func·002|TestFormatGeneric.func2)\n\t" + + ".+/github.com/pkg/errors/format_test.go:333", + }, + }, { + func(err error) error { return Wrap(err, "wrap-error") }, + []string{ + "wrap-error", + "github.com/pkg/errors.(func·003|TestFormatGeneric.func3)\n\t" + + ".+/github.com/pkg/errors/format_test.go:339", + }, + }, { + func(err error) error { return Wrapf(err, "wrapf-error%d", 1) }, + []string{ + "wrapf-error1", + "github.com/pkg/errors.(func·004|TestFormatGeneric.func4)\n\t" + + ".+/github.com/pkg/errors/format_test.go:346", + }, + }, + } + + for s := range starts { + err := starts[s].err + want := starts[s].want + testFormatCompleteCompare(t, s, err, "%+v", want, false) + testGenericRecursive(t, err, want, wrappers, 3) + } +} + +func testFormatRegexp(t *testing.T, n int, arg interface{}, format, want string) { + got := fmt.Sprintf(format, arg) + gotLines := strings.SplitN(got, "\n", -1) + wantLines := strings.SplitN(want, "\n", -1) + + if len(wantLines) > len(gotLines) { + t.Errorf("test %d: wantLines(%d) > gotLines(%d):\n got: %q\nwant: %q", n+1, len(wantLines), len(gotLines), got, want) + return + } + + for i, w := range wantLines { + match, err := regexp.MatchString(w, gotLines[i]) + if err != nil { + t.Fatal(err) + } + if !match { + t.Errorf("test %d: line %d: fmt.Sprintf(%q, err):\n got: %q\nwant: %q", n+1, i+1, format, got, want) + } + } +} + +var stackLineR = regexp.MustCompile(`\.`) + +// parseBlocks parses input into a slice, where: +// - incase entry contains a newline, its a stacktrace +// - incase entry contains no newline, its a solo line. +// +// Detecting stack boundaries only works incase the WithStack-calls are +// to be found on the same line, thats why it is optionally here. +// +// Example use: +// +// for _, e := range blocks { +// if strings.ContainsAny(e, "\n") { +// // Match as stack +// } else { +// // Match as line +// } +// } +// +func parseBlocks(input string, detectStackboundaries bool) ([]string, error) { + var blocks []string + + stack := "" + wasStack := false + lines := map[string]bool{} // already found lines + + for _, l := range strings.Split(input, "\n") { + isStackLine := stackLineR.MatchString(l) + + switch { + case !isStackLine && wasStack: + blocks = append(blocks, stack, l) + stack = "" + lines = map[string]bool{} + case isStackLine: + if wasStack { + // Detecting two stacks after another, possible cause lines match in + // our tests due to WithStack(WithStack(io.EOF)) on same line. + if detectStackboundaries { + if lines[l] { + if len(stack) == 0 { + return nil, errors.New("len of block must not be zero here") + } + + blocks = append(blocks, stack) + stack = l + lines = map[string]bool{l: true} + continue + } + } + + stack = stack + "\n" + l + } else { + stack = l + } + lines[l] = true + case !isStackLine && !wasStack: + blocks = append(blocks, l) + default: + return nil, errors.New("must not happen") + } + + wasStack = isStackLine + } + + // Use up stack + if stack != "" { + blocks = append(blocks, stack) + } + return blocks, nil +} + +func testFormatCompleteCompare(t *testing.T, n int, arg interface{}, format string, want []string, detectStackBoundaries bool) { + gotStr := fmt.Sprintf(format, arg) + + got, err := parseBlocks(gotStr, detectStackBoundaries) + if err != nil { + t.Fatal(err) + } + + if len(got) != len(want) { + t.Fatalf("test %d: fmt.Sprintf(%s, err) -> wrong number of blocks: got(%d) want(%d)\n got: %s\nwant: %s\ngotStr: %q", + n+1, format, len(got), len(want), prettyBlocks(got), prettyBlocks(want), gotStr) + } + + for i := range got { + if strings.ContainsAny(want[i], "\n") { + // Match as stack + match, err := regexp.MatchString(want[i], got[i]) + if err != nil { + t.Fatal(err) + } + if !match { + t.Fatalf("test %d: block %d: fmt.Sprintf(%q, err):\ngot:\n%q\nwant:\n%q\nall-got:\n%s\nall-want:\n%s\n", + n+1, i+1, format, got[i], want[i], prettyBlocks(got), prettyBlocks(want)) + } + } else { + // Match as message + if got[i] != want[i] { + t.Fatalf("test %d: fmt.Sprintf(%s, err) at block %d got != want:\n got: %q\nwant: %q", n+1, format, i+1, got[i], want[i]) + } + } + } +} + +type wrapper struct { + wrap func(err error) error + want []string +} + +func prettyBlocks(blocks []string) string { + var out []string + + for _, b := range blocks { + out = append(out, fmt.Sprintf("%v", b)) + } + + return " " + strings.Join(out, "\n ") +} + +func testGenericRecursive(t *testing.T, beforeErr error, beforeWant []string, list []wrapper, maxDepth int) { + if len(beforeWant) == 0 { + panic("beforeWant must not be empty") + } + for _, w := range list { + if len(w.want) == 0 { + panic("want must not be empty") + } + + err := w.wrap(beforeErr) + + // Copy required cause append(beforeWant, ..) modified beforeWant subtly. + beforeCopy := make([]string, len(beforeWant)) + copy(beforeCopy, beforeWant) + + beforeWant := beforeCopy + last := len(beforeWant) - 1 + var want []string + + // Merge two stacks behind each other. + if strings.ContainsAny(beforeWant[last], "\n") && strings.ContainsAny(w.want[0], "\n") { + want = append(beforeWant[:last], append([]string{beforeWant[last] + "((?s).*)" + w.want[0]}, w.want[1:]...)...) + } else { + want = append(beforeWant, w.want...) + } + + testFormatCompleteCompare(t, maxDepth, err, "%+v", want, false) + if maxDepth > 0 { + testGenericRecursive(t, err, want, list, maxDepth-1) + } + } +} diff --git a/template/faaschain/vendor/github.com/pkg/errors/stack.go b/template/faaschain/vendor/github.com/pkg/errors/stack.go new file mode 100644 index 00000000..2874a048 --- /dev/null +++ b/template/faaschain/vendor/github.com/pkg/errors/stack.go @@ -0,0 +1,147 @@ +package errors + +import ( + "fmt" + "io" + "path" + "runtime" + "strings" +) + +// Frame represents a program counter inside a stack frame. +type Frame uintptr + +// pc returns the program counter for this frame; +// multiple frames may have the same PC value. +func (f Frame) pc() uintptr { return uintptr(f) - 1 } + +// file returns the full path to the file that contains the +// function for this Frame's pc. +func (f Frame) file() string { + fn := runtime.FuncForPC(f.pc()) + if fn == nil { + return "unknown" + } + file, _ := fn.FileLine(f.pc()) + return file +} + +// line returns the line number of source code of the +// function for this Frame's pc. +func (f Frame) line() int { + fn := runtime.FuncForPC(f.pc()) + if fn == nil { + return 0 + } + _, line := fn.FileLine(f.pc()) + return line +} + +// Format formats the frame according to the fmt.Formatter interface. +// +// %s source file +// %d source line +// %n function name +// %v equivalent to %s:%d +// +// Format accepts flags that alter the printing of some verbs, as follows: +// +// %+s function name and path of source file relative to the compile time +// GOPATH separated by \n\t (\n\t) +// %+v equivalent to %+s:%d +func (f Frame) Format(s fmt.State, verb rune) { + switch verb { + case 's': + switch { + case s.Flag('+'): + pc := f.pc() + fn := runtime.FuncForPC(pc) + if fn == nil { + io.WriteString(s, "unknown") + } else { + file, _ := fn.FileLine(pc) + fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file) + } + default: + io.WriteString(s, path.Base(f.file())) + } + case 'd': + fmt.Fprintf(s, "%d", f.line()) + case 'n': + name := runtime.FuncForPC(f.pc()).Name() + io.WriteString(s, funcname(name)) + case 'v': + f.Format(s, 's') + io.WriteString(s, ":") + f.Format(s, 'd') + } +} + +// StackTrace is stack of Frames from innermost (newest) to outermost (oldest). +type StackTrace []Frame + +// Format formats the stack of Frames according to the fmt.Formatter interface. +// +// %s lists source files for each Frame in the stack +// %v lists the source file and line number for each Frame in the stack +// +// Format accepts flags that alter the printing of some verbs, as follows: +// +// %+v Prints filename, function, and line number for each Frame in the stack. +func (st StackTrace) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + switch { + case s.Flag('+'): + for _, f := range st { + fmt.Fprintf(s, "\n%+v", f) + } + case s.Flag('#'): + fmt.Fprintf(s, "%#v", []Frame(st)) + default: + fmt.Fprintf(s, "%v", []Frame(st)) + } + case 's': + fmt.Fprintf(s, "%s", []Frame(st)) + } +} + +// stack represents a stack of program counters. +type stack []uintptr + +func (s *stack) Format(st fmt.State, verb rune) { + switch verb { + case 'v': + switch { + case st.Flag('+'): + for _, pc := range *s { + f := Frame(pc) + fmt.Fprintf(st, "\n%+v", f) + } + } + } +} + +func (s *stack) StackTrace() StackTrace { + f := make([]Frame, len(*s)) + for i := 0; i < len(f); i++ { + f[i] = Frame((*s)[i]) + } + return f +} + +func callers() *stack { + const depth = 32 + var pcs [depth]uintptr + n := runtime.Callers(3, pcs[:]) + var st stack = pcs[0:n] + return &st +} + +// funcname removes the path prefix component of a function's name reported by func.Name(). +func funcname(name string) string { + i := strings.LastIndex(name, "/") + name = name[i+1:] + i = strings.Index(name, ".") + return name[i+1:] +} diff --git a/template/faaschain/vendor/github.com/pkg/errors/stack_test.go b/template/faaschain/vendor/github.com/pkg/errors/stack_test.go new file mode 100644 index 00000000..85fc4195 --- /dev/null +++ b/template/faaschain/vendor/github.com/pkg/errors/stack_test.go @@ -0,0 +1,274 @@ +package errors + +import ( + "fmt" + "runtime" + "testing" +) + +var initpc, _, _, _ = runtime.Caller(0) + +func TestFrameLine(t *testing.T) { + var tests = []struct { + Frame + want int + }{{ + Frame(initpc), + 9, + }, { + func() Frame { + var pc, _, _, _ = runtime.Caller(0) + return Frame(pc) + }(), + 20, + }, { + func() Frame { + var pc, _, _, _ = runtime.Caller(1) + return Frame(pc) + }(), + 28, + }, { + Frame(0), // invalid PC + 0, + }} + + for _, tt := range tests { + got := tt.Frame.line() + want := tt.want + if want != got { + t.Errorf("Frame(%v): want: %v, got: %v", uintptr(tt.Frame), want, got) + } + } +} + +type X struct{} + +func (x X) val() Frame { + var pc, _, _, _ = runtime.Caller(0) + return Frame(pc) +} + +func (x *X) ptr() Frame { + var pc, _, _, _ = runtime.Caller(0) + return Frame(pc) +} + +func TestFrameFormat(t *testing.T) { + var tests = []struct { + Frame + format string + want string + }{{ + Frame(initpc), + "%s", + "stack_test.go", + }, { + Frame(initpc), + "%+s", + "github.com/pkg/errors.init\n" + + "\t.+/github.com/pkg/errors/stack_test.go", + }, { + Frame(0), + "%s", + "unknown", + }, { + Frame(0), + "%+s", + "unknown", + }, { + Frame(initpc), + "%d", + "9", + }, { + Frame(0), + "%d", + "0", + }, { + Frame(initpc), + "%n", + "init", + }, { + func() Frame { + var x X + return x.ptr() + }(), + "%n", + `\(\*X\).ptr`, + }, { + func() Frame { + var x X + return x.val() + }(), + "%n", + "X.val", + }, { + Frame(0), + "%n", + "", + }, { + Frame(initpc), + "%v", + "stack_test.go:9", + }, { + Frame(initpc), + "%+v", + "github.com/pkg/errors.init\n" + + "\t.+/github.com/pkg/errors/stack_test.go:9", + }, { + Frame(0), + "%v", + "unknown:0", + }} + + for i, tt := range tests { + testFormatRegexp(t, i, tt.Frame, tt.format, tt.want) + } +} + +func TestFuncname(t *testing.T) { + tests := []struct { + name, want string + }{ + {"", ""}, + {"runtime.main", "main"}, + {"github.com/pkg/errors.funcname", "funcname"}, + {"funcname", "funcname"}, + {"io.copyBuffer", "copyBuffer"}, + {"main.(*R).Write", "(*R).Write"}, + } + + for _, tt := range tests { + got := funcname(tt.name) + want := tt.want + if got != want { + t.Errorf("funcname(%q): want: %q, got %q", tt.name, want, got) + } + } +} + +func TestStackTrace(t *testing.T) { + tests := []struct { + err error + want []string + }{{ + New("ooh"), []string{ + "github.com/pkg/errors.TestStackTrace\n" + + "\t.+/github.com/pkg/errors/stack_test.go:154", + }, + }, { + Wrap(New("ooh"), "ahh"), []string{ + "github.com/pkg/errors.TestStackTrace\n" + + "\t.+/github.com/pkg/errors/stack_test.go:159", // this is the stack of Wrap, not New + }, + }, { + Cause(Wrap(New("ooh"), "ahh")), []string{ + "github.com/pkg/errors.TestStackTrace\n" + + "\t.+/github.com/pkg/errors/stack_test.go:164", // this is the stack of New + }, + }, { + func() error { return New("ooh") }(), []string{ + `github.com/pkg/errors.(func·009|TestStackTrace.func1)` + + "\n\t.+/github.com/pkg/errors/stack_test.go:169", // this is the stack of New + "github.com/pkg/errors.TestStackTrace\n" + + "\t.+/github.com/pkg/errors/stack_test.go:169", // this is the stack of New's caller + }, + }, { + Cause(func() error { + return func() error { + return Errorf("hello %s", fmt.Sprintf("world")) + }() + }()), []string{ + `github.com/pkg/errors.(func·010|TestStackTrace.func2.1)` + + "\n\t.+/github.com/pkg/errors/stack_test.go:178", // this is the stack of Errorf + `github.com/pkg/errors.(func·011|TestStackTrace.func2)` + + "\n\t.+/github.com/pkg/errors/stack_test.go:179", // this is the stack of Errorf's caller + "github.com/pkg/errors.TestStackTrace\n" + + "\t.+/github.com/pkg/errors/stack_test.go:180", // this is the stack of Errorf's caller's caller + }, + }} + for i, tt := range tests { + x, ok := tt.err.(interface { + StackTrace() StackTrace + }) + if !ok { + t.Errorf("expected %#v to implement StackTrace() StackTrace", tt.err) + continue + } + st := x.StackTrace() + for j, want := range tt.want { + testFormatRegexp(t, i, st[j], "%+v", want) + } + } +} + +func stackTrace() StackTrace { + const depth = 8 + var pcs [depth]uintptr + n := runtime.Callers(1, pcs[:]) + var st stack = pcs[0:n] + return st.StackTrace() +} + +func TestStackTraceFormat(t *testing.T) { + tests := []struct { + StackTrace + format string + want string + }{{ + nil, + "%s", + `\[\]`, + }, { + nil, + "%v", + `\[\]`, + }, { + nil, + "%+v", + "", + }, { + nil, + "%#v", + `\[\]errors.Frame\(nil\)`, + }, { + make(StackTrace, 0), + "%s", + `\[\]`, + }, { + make(StackTrace, 0), + "%v", + `\[\]`, + }, { + make(StackTrace, 0), + "%+v", + "", + }, { + make(StackTrace, 0), + "%#v", + `\[\]errors.Frame{}`, + }, { + stackTrace()[:2], + "%s", + `\[stack_test.go stack_test.go\]`, + }, { + stackTrace()[:2], + "%v", + `\[stack_test.go:207 stack_test.go:254\]`, + }, { + stackTrace()[:2], + "%+v", + "\n" + + "github.com/pkg/errors.stackTrace\n" + + "\t.+/github.com/pkg/errors/stack_test.go:207\n" + + "github.com/pkg/errors.TestStackTraceFormat\n" + + "\t.+/github.com/pkg/errors/stack_test.go:258", + }, { + stackTrace()[:2], + "%#v", + `\[\]errors.Frame{stack_test.go:207, stack_test.go:266}`, + }} + + for i, tt := range tests { + testFormatRegexp(t, i, tt.StackTrace, tt.format, tt.want) + } +} diff --git a/template/faaschain/vendor/github.com/s8sg/faaschain/sdk/chain.go b/template/faaschain/vendor/github.com/s8sg/faaschain/sdk/chain.go index 8e64c813..4fa512b6 100644 --- a/template/faaschain/vendor/github.com/s8sg/faaschain/sdk/chain.go +++ b/template/faaschain/vendor/github.com/s8sg/faaschain/sdk/chain.go @@ -5,7 +5,7 @@ import ( ) type Chain struct { - Phases []*Phase `json:"-"` // Phases that will be executed in async + Phases []*Phase `json:"phases"` // Phases that will be executed in async ExecutionPosition int `json:"position"` // Position of Executor } diff --git a/template/faaschain/vendor/github.com/s8sg/faaschain/sdk/function.go b/template/faaschain/vendor/github.com/s8sg/faaschain/sdk/function.go index d6b55f80..37726738 100644 --- a/template/faaschain/vendor/github.com/s8sg/faaschain/sdk/function.go +++ b/template/faaschain/vendor/github.com/s8sg/faaschain/sdk/function.go @@ -7,11 +7,11 @@ import ( type Modifier func([]byte) ([]byte, error) type Function struct { - Function string `json:"-"` // The name of the function - CallbackUrl string `json:"-"` // Callback Url - Mod Modifier `json:"-"` // Modifier - Header map[string]string `json:"-"` // The HTTP call header - Param map[string][]string `json:"-"` // The Parameter in Query string + Function string `json:"function"` // The name of the function + CallbackUrl string `json:"callback"` // Callback Url + Mod Modifier `json:"-"` // Modifier + Header map[string]string `json:"header"` // The HTTP call header + Param map[string][]string `json:"param"` // The Parameter in Query string } // Create a function with execution name diff --git a/template/faaschain/vendor/github.com/s8sg/faaschain/sdk/phase.go b/template/faaschain/vendor/github.com/s8sg/faaschain/sdk/phase.go index 278f0c6c..ef72f461 100644 --- a/template/faaschain/vendor/github.com/s8sg/faaschain/sdk/phase.go +++ b/template/faaschain/vendor/github.com/s8sg/faaschain/sdk/phase.go @@ -2,7 +2,7 @@ package sdk type Phase struct { // The list of function in the Phase - Functions []*Function `json:"-"` + Functions []*Function `json:"functions"` } func CreateExecutionPhase() *Phase { diff --git a/template/faaschain/vendor/github.com/s8sg/faaschain/sdk/request.go b/template/faaschain/vendor/github.com/s8sg/faaschain/sdk/request.go index 119a682b..763a558d 100644 --- a/template/faaschain/vendor/github.com/s8sg/faaschain/sdk/request.go +++ b/template/faaschain/vendor/github.com/s8sg/faaschain/sdk/request.go @@ -8,8 +8,8 @@ import ( type Request struct { Sign string `json: "sign"` ID string `json: "id"` - Chaindef string `json: "-"` Data []byte `json: "data"` + Chaindef string `json: "-"` } const ( diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/.github/ISSUE_TEMPLATE.md b/template/faaschain/vendor/github.com/uber/jaeger-client-go/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..7a5851ba --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,28 @@ + + +## Requirement - what kind of business use case are you trying to solve? + + + +## Problem - what in Jaeger blocks you from solving the requirement? + + + + +## Proposal - what do you suggest to solve the problem or improve the existing situation? + + + +## Any open questions to address + + diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/.github/PULL_REQUEST_TEMPLATE.md b/template/faaschain/vendor/github.com/uber/jaeger-client-go/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..1a1087b0 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,18 @@ + + +## Which problem is this PR solving? +- + +## Short description of the changes +- diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/.gitignore b/template/faaschain/vendor/github.com/uber/jaeger-client-go/.gitignore new file mode 100644 index 00000000..27349079 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/.gitignore @@ -0,0 +1,15 @@ +*.out +*.test +*.xml +*.swp +.idea/ +.tmp/ +*.iml +*.cov +*.html +*.log +gen/thrift/js +gen/thrift/py +vendor/ +crossdock-main +crossdock/jaeger-docker-compose.yml diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/.gitmodules b/template/faaschain/vendor/github.com/uber/jaeger-client-go/.gitmodules new file mode 100644 index 00000000..295ebcf6 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/.gitmodules @@ -0,0 +1,3 @@ +[submodule "idl"] + path = idl + url = https://github.com/uber/jaeger-idl.git diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/.travis.yml b/template/faaschain/vendor/github.com/uber/jaeger-client-go/.travis.yml new file mode 100644 index 00000000..1678a120 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/.travis.yml @@ -0,0 +1,48 @@ +sudo: required + +language: go +go_import_path: github.com/uber/jaeger-client-go + +dist: trusty + +matrix: + include: + - go: 1.9 + env: + - TESTS=true + - COVERAGE=true + - go: 1.9 + env: + - CROSSDOCK=true + - go: 1.9 + env: + - TESTS=true + - USE_DEP=true + +services: + - docker + +env: + global: + - DOCKER_COMPOSE_VERSION=1.8.0 + - GO15VENDOREXPERIMENT=1 + - COMMIT=${TRAVIS_COMMIT::8} + # DOCKER_PASS + - secure: "CnjVyxNvMC/dhr/eR7C+FiWucZ4/O5LfAuz9YU0qlnV6XLR7XXRtzZlfFKIImJT6xHp+OptTqAIXqUbvwK2OXDP1ZsLiWRm+2elb9/isGusWXjs3g817lX8njSUcIFILbfi+vAE7UD2BKjHxpmvWmCZidisU1rcaZ9OQNPqMnNIDxVx0FOTwYx+2hfkdjnN5dikzafBDQ6ZZV/mGbcaTG45GGFU6DHyVLzf9qCPXyXnz2+VDhcoPQsYkzE56XHCmHxvEfXxgfqYefJNUlFPhniAQySVsCNVDJ8QcCV6uHaXoIzxJKx9FdUnWKI1/AtpQsTZPgEm4Ujnt+kGJsXopXy2Xx4MZxmcTCBwAMjZxPMF7KoojbtDeOZgEMtf1tGPN6DTNc3NpVmr0BKZ44lhqk+vnd8HAiC1tHDEoSb1Esl7dMUUf1qZAh3MtT+NYi3mTwyx/ilXUS7KPyy7x0ezB3kGuMoLhvR2hrprqRr5NOV2hrd1au+IXmb+4IanFOsBlceBfs8P0JFMO/aw15r+HimSZpQsJx//IT0LReCZYXLe0/WVsF/8+HDwHKlO99gGpk4iXlNKKvdPWabihMp3I3peMrvL+jnlwh47RqHs/0Q71xsKjVWTn+Svq3FpVP0Pgyxhg+oG4WEByBiLnBQcZwSBhWexkJrNI73GzaZiIldk=" + # DOCKER_USER + - secure: "bpBSmypHzI4PnteM4cwLiMC2163Sj/4mEl+1dj+6NWl2tr1hREeVXKhsWBpah25n6BDyr2A4yhBZcWLaNKrsCKT3U37csAQTOFVeQ9x5xhPq+ohANd/OsspFsxNZaKwx161LizH/uTDotMxxevZacsyYWGNv/cRFkwcQ8upLkReRR6puJ+jNQC0BFpKWBJY/zpm5J7xFb7FO20LvQVyRgsgzqWmg9oRNVw9uwOfSY3btacftYctDLUbAr8YRNHd2C6dZnMAi8KdDTLXKTqjKmp6WidOmi92Ml7tOjB+bV6TOaVAhrcI5Rdje4rRWG4MucAjPMP0ZBW36KTfcGqFUcDhX7UqISe2WxoI+8ZD6fJ+nNtD3bk4YAUJB4BSs2sQdiYyjpHyGJR6RW50+3uRz2YbXpzVr9wqv2lZSl/xy3wC5Hag55uqzVlSiDw2pK8lctT3dnQveE7PqAI577PjF2NrHlgrBbykOwwUCNbRTmykzqoDnkxclmiZ+rflEeWsSYglePK/d6Gj9+N7wJZM5heprdJJMFTrzMWZ21Ll9ZGY9updCBKmJA8pBYiLHbu0lWOp+9QUGC+621Zq0d1PHhN6L4eXk/f3RNoZTr//cX6WdNmmO7tBbaGpmp/UYiYTY1WO9vP7tCDsT75k285HCfnIrlGRdbCZZbfuYNGPKIQ0=" + +install: + - make install-ci + - if [ "$CROSSDOCK" == true ]; then bash ./travis/install-crossdock-deps.sh ; fi + +script: + - if [ "$TESTS" == true ]; then make test-ci ; else echo 'skipping tests'; fi + - if [ "$CROSSDOCK" == true ]; then bash ./travis/build-crossdock.sh ; else echo 'skipping crossdock'; fi + +after_success: + - if [ "$COVERAGE" == true ]; then mv cover.out coverage.txt ; else echo 'skipping coverage'; fi + - if [ "$COVERAGE" == true ]; then bash <(curl -s https://codecov.io/bash) ; else echo 'skipping coverage'; fi + +after_failure: + - if [ "$CROSSDOCK" == true ]; then timeout 5 docker-compose -f crossdock/docker-compose.yml logs; fi diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/CHANGELOG.md b/template/faaschain/vendor/github.com/uber/jaeger-client-go/CHANGELOG.md new file mode 100644 index 00000000..28e2c242 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/CHANGELOG.md @@ -0,0 +1,186 @@ +Changes by Version +================== + +2.15.0 (unreleased) +------------------- + +- nothing yet + + +2.14.0 (2018-04-30) +------------------- + +- Support throttling for debug traces (#274) +- Remove dependency on Apache Thrift (#303) +- Remove dependency on tchannel (#295) (#294) +- Test with Go 1.9 (#298) + + +2.13.0 (2018-04-15) +------------------- + +- Use value receiver for config.NewTracer() (#283) +- Lock span during jaeger thrift conversion (#273) +- Fix the RemotelyControlledSampler so that it terminates go-routine on Close() (#260) +- Added support for client configuration via env vars (#275) +- Allow overriding sampler in the Config (#270) + + +2.12.0 (2018-03-14) +------------------- + +- Use lock when retrieving span.Context() (#268) +- Add Configuration support for custom Injector and Extractor (#263) + + +2.11.2 (2018-01-12) +------------------- + +- Add Gopkg.toml to allow using the lib with `dep` + + +2.11.1 (2018-01-03) +------------------- + +- Do not enqueue spans after Reporter is closed (#235, #245) +- Change default flush interval to 1sec (#243) + + +2.11.0 (2017-11-27) +------------------- + +- Normalize metric names and tags to be compatible with Prometheus (#222) + + +2.10.0 (2017-11-14) +------------------- + +- Support custom tracing headers (#176) +- Add BaggageRestrictionManager (#178) and RemoteBaggageRestrictionManager (#182) +- Do not coerce baggage keys to lower case (#196) +- Log span name when span cannot be reported (#198) +- Add option to enable gen128Bit for tracer (#193) and allow custom generator for high bits of trace ID (#219) + + +2.9.0 (2017-07-29) +------------------ + +- Pin thrift <= 0.10 (#179) +- Introduce a parallel interface ContribObserver (#159) + + +2.8.0 (2017-07-05) +------------------ + +- Drop `jaeger.` prefix from `jaeger.hostname` process-level tag +- Add options to set tracer tags + + +2.7.0 (2017-06-21) +------------------ + +- Fix rate limiter balance [#135](https://github.com/uber/jaeger-client-go/pull/135) [#140](https://github.com/uber/jaeger-client-go/pull/140) +- Default client to send Jaeger.thrift [#147](https://github.com/uber/jaeger-client-go/pull/147) +- Save baggage in span [#153](https://github.com/uber/jaeger-client-go/pull/153) +- Move reporter.queueLength to the top of the struct to guarantee 64bit alignment [#158](https://github.com/uber/jaeger-client-go/pull/158) +- Support HTTP transport with jaeger.thrift [#161](https://github.com/uber/jaeger-client-go/pull/161) + + +2.6.0 (2017-03-28) +------------------ + +- Add config option to initialize RPC Metrics feature + + +2.5.0 (2017-03-23) +------------------ + +- Split request latency metric by success/failure [#123](https://github.com/uber/jaeger-client-go/pull/123) +- Add mutex to adaptive sampler and fix race condition [#124](https://github.com/uber/jaeger-client-go/pull/124) +- Fix rate limiter panic [#125](https://github.com/uber/jaeger-client-go/pull/125) + + +2.4.0 (2017-03-21) +------------------ + +- Remove `_ms` suffix from request latency metric name [#121](https://github.com/uber/jaeger-client-go/pull/121) +- Rename all metrics to "request" and "http_request" and use tags for other dimensions [#121](https://github.com/uber/jaeger-client-go/pull/121) + + +2.3.0 (2017-03-20) +------------------ + +- Make Span type public to allow access to non-std methods for testing [#117](https://github.com/uber/jaeger-client-go/pull/117) +- Add a structured way to extract traces for logging with zap [#118](https://github.com/uber/jaeger-client-go/pull/118) + + +2.2.1 (2017-03-14) +------------------ + +- Fix panic caused by updating the remote sampler from adaptive sampler to any other sampler type (https://github.com/uber/jaeger-client-go/pull/111) + + +2.2.0 (2017-03-10) +------------------ + +- Introduce Observer and SpanObserver (https://github.com/uber/jaeger-client-go/pull/94) +- Add RPC metrics emitter as Observer/SpanObserver (https://github.com/uber/jaeger-client-go/pull/103) + + +2.1.2 (2017-02-27) +------------------- + +- Fix leaky bucket bug (https://github.com/uber/jaeger-client-go/pull/99) +- Fix zap logger Infof (https://github.com/uber/jaeger-client-go/pull/100) +- Add tracer initialization godoc examples + + +2.1.1 (2017-02-21) +------------------- + +- Fix inefficient usage of zap.Logger + + +2.1.0 (2017-02-17) +------------------- + +- Add adapter for zap.Logger (https://github.com/uber-go/zap) +- Move logging API to ./log/ package + + +2.0.0 (2017-02-08) +------------------- + +- Support Adaptive Sampling +- Support 128bit Trace IDs +- Change trace/span IDs from uint64 to strong types TraceID and SpanID +- Add Zipkin HTTP B3 Propagation format support #72 +- Rip out existing metrics and use github.com/uber/jaeger-lib/metrics +- Change API for tracer, reporter, sampler initialization + + +1.6.0 (2016-10-14) +------------------- + +- Add Zipkin HTTP transport +- Support external baggage via jaeger-baggage header +- Unpin Thrift version, keep to master + + +1.5.1 (2016-09-27) +------------------- + +- Relax dependency on opentracing to ^1 + + +1.5.0 (2016-09-27) +------------------- + +- Upgrade to opentracing-go 1.0 +- Support KV logging for Spans + + +1.4.0 (2016-09-14) +------------------- + +- Support debug traces via HTTP header "jaeger-debug-id" diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/CONTRIBUTING.md b/template/faaschain/vendor/github.com/uber/jaeger-client-go/CONTRIBUTING.md new file mode 100644 index 00000000..7cf014a5 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/CONTRIBUTING.md @@ -0,0 +1,170 @@ +# How to Contribute to Jaeger + +We'd love your help! + +Jaeger is [Apache 2.0 licensed](LICENSE) and accepts contributions via GitHub +pull requests. This document outlines some of the conventions on development +workflow, commit message formatting, contact points and other resources to make +it easier to get your contribution accepted. + +We gratefully welcome improvements to documentation as well as to code. + +# Certificate of Origin + +By contributing to this project you agree to the [Developer Certificate of +Origin](https://developercertificate.org/) (DCO). This document was created +by the Linux Kernel community and is a simple statement that you, as a +contributor, have the legal right to make the contribution. See the [DCO](DCO) +file for details. + +## Getting Started + +This library uses [glide](https://github.com/Masterminds/glide) to manage dependencies. + +To get started, make sure you clone the Git repository into the correct location +`github.com/uber/jaeger-client-go` relative to `$GOPATH`: + +``` +mkdir -p $GOPATH/src/github.com/uber +cd $GOPATH/src/github.com/uber +git clone git@github.com:jaegertracing/jaeger-client-go.git jaeger-client-go +cd jaeger-client-go +``` + +Then install dependencies and run the tests: + +``` +git submodule update --init --recursive +glide install +make test +``` + +## Imports grouping + +This projects follows the following pattern for grouping imports in Go files: + * imports from standard library + * imports from other projects + * imports from `jaeger-client-go` project + +For example: + +```go +import ( + "fmt" + + "github.com/uber/jaeger-lib/metrics" + "go.uber.org/zap" + + "github.com/uber/jaeger-client-go/config" +) +``` + +## Making A Change + +*Before making any significant changes, please [open an +issue](https://github.com/jaegertracing/jaeger-client-go/issues).* Discussing your proposed +changes ahead of time will make the contribution process smooth for everyone. + +Once we've discussed your changes and you've got your code ready, make sure +that tests are passing (`make test` or `make cover`) and open your PR. Your +pull request is most likely to be accepted if it: + +* Includes tests for new functionality. +* Follows the guidelines in [Effective + Go](https://golang.org/doc/effective_go.html) and the [Go team's common code + review comments](https://github.com/golang/go/wiki/CodeReviewComments). +* Has a [good commit message](https://chris.beams.io/posts/git-commit/): + * Separate subject from body with a blank line + * Limit the subject line to 50 characters + * Capitalize the subject line + * Do not end the subject line with a period + * Use the imperative mood in the subject line + * Wrap the body at 72 characters + * Use the body to explain _what_ and _why_ instead of _how_ +* Each commit must be signed by the author ([see below](#sign-your-work)). + +## License + +By contributing your code, you agree to license your contribution under the terms +of the [Apache License](LICENSE). + +If you are adding a new file it should have a header like below. The easiest +way to add such header is to run `make fmt`. + +``` +// Copyright (c) 2017 The Jaeger Authors. +// +// 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. +``` + +## Sign your work + +The sign-off is a simple line at the end of the explanation for the +patch, which certifies that you wrote it or otherwise have the right to +pass it on as an open-source patch. The rules are pretty simple: if you +can certify the below (from +[developercertificate.org](http://developercertificate.org/)): + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +660 York Street, Suite 102, +San Francisco, CA 94110 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` + +then you just add a line to every git commit message: + + Signed-off-by: Joe Smith + +using your real name (sorry, no pseudonyms or anonymous contributions.) + +You can add the sign off when creating the git commit via `git commit -s`. + +If you want this to be automatic you can set up some aliases: + +``` +git config --add alias.amend "commit -s --amend" +git config --add alias.c "commit -s" +``` diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/DCO b/template/faaschain/vendor/github.com/uber/jaeger-client-go/DCO new file mode 100644 index 00000000..068953d4 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/DCO @@ -0,0 +1,37 @@ +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +660 York Street, Suite 102, +San Francisco, CA 94110 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. + diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/Gopkg.lock b/template/faaschain/vendor/github.com/uber/jaeger-client-go/Gopkg.lock new file mode 100644 index 00000000..ec054c6e --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/Gopkg.lock @@ -0,0 +1,164 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + branch = "master" + name = "github.com/beorn7/perks" + packages = ["quantile"] + revision = "3a771d992973f24aa725d07868b467d1ddfceafb" + +[[projects]] + branch = "master" + name = "github.com/codahale/hdrhistogram" + packages = ["."] + revision = "3a0bb77429bd3a61596f5e8a3172445844342120" + +[[projects]] + branch = "master" + name = "github.com/crossdock/crossdock-go" + packages = [ + ".", + "assert", + "require" + ] + revision = "049aabb0122b03bc9bd30cab8f3f91fb60166361" + +[[projects]] + name = "github.com/davecgh/go-spew" + packages = ["spew"] + revision = "346938d642f2ec3594ed81d874461961cd0faa76" + version = "v1.1.0" + +[[projects]] + name = "github.com/golang/protobuf" + packages = ["proto"] + revision = "925541529c1fa6821df4e44ce2723319eb2be768" + version = "v1.0.0" + +[[projects]] + name = "github.com/matttproud/golang_protobuf_extensions" + packages = ["pbutil"] + revision = "3247c84500bff8d9fb6d579d800f20b3e091582c" + version = "v1.0.0" + +[[projects]] + name = "github.com/opentracing/opentracing-go" + packages = [ + ".", + "ext", + "log" + ] + revision = "1949ddbfd147afd4d964a9f00b24eb291e0e7c38" + version = "v1.0.2" + +[[projects]] + name = "github.com/pkg/errors" + packages = ["."] + revision = "645ef00459ed84a119197bfb8d8205042c6df63d" + version = "v0.8.0" + +[[projects]] + name = "github.com/pmezard/go-difflib" + packages = ["difflib"] + revision = "792786c7400a136282c1664665ae0a8db921c6c2" + version = "v1.0.0" + +[[projects]] + name = "github.com/prometheus/client_golang" + packages = ["prometheus"] + revision = "c5b7fccd204277076155f10851dad72b76a49317" + version = "v0.8.0" + +[[projects]] + branch = "master" + name = "github.com/prometheus/client_model" + packages = ["go"] + revision = "99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c" + +[[projects]] + branch = "master" + name = "github.com/prometheus/common" + packages = [ + "expfmt", + "internal/bitbucket.org/ww/goautoneg", + "model" + ] + revision = "d811d2e9bf898806ecfb6ef6296774b13ffc314c" + +[[projects]] + branch = "master" + name = "github.com/prometheus/procfs" + packages = [ + ".", + "internal/util", + "nfs", + "xfs" + ] + revision = "8b1c2da0d56deffdbb9e48d4414b4e674bd8083e" + +[[projects]] + name = "github.com/stretchr/testify" + packages = [ + "assert", + "require", + "suite" + ] + revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71" + version = "v1.2.1" + +[[projects]] + name = "github.com/uber-go/atomic" + packages = ["."] + revision = "8474b86a5a6f79c443ce4b2992817ff32cf208b8" + version = "v1.3.1" + +[[projects]] + name = "github.com/uber/jaeger-lib" + packages = [ + "metrics", + "metrics/prometheus", + "metrics/testutils" + ] + revision = "4267858c0679cd4e47cefed8d7f70fd386cfb567" + version = "v1.4.0" + +[[projects]] + name = "go.uber.org/atomic" + packages = ["."] + revision = "54f72d32435d760d5604f17a82e2435b28dc4ba5" + version = "v1.3.0" + +[[projects]] + name = "go.uber.org/multierr" + packages = ["."] + revision = "3c4937480c32f4c13a875a1829af76c98ca3d40a" + version = "v1.1.0" + +[[projects]] + name = "go.uber.org/zap" + packages = [ + ".", + "buffer", + "internal/bufferpool", + "internal/color", + "internal/exit", + "zapcore" + ] + revision = "eeedf312bc6c57391d84767a4cd413f02a917974" + version = "v1.8.0" + +[[projects]] + branch = "master" + name = "golang.org/x/net" + packages = [ + "context", + "context/ctxhttp" + ] + revision = "5f9ae10d9af5b1c89ae6904293b14b064d4ada23" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "f9dcfaf37a785c5dac1e20c29605eda29a83ba9c6f8842e92960dc94c8c4ff80" + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/Gopkg.toml b/template/faaschain/vendor/github.com/uber/jaeger-client-go/Gopkg.toml new file mode 100644 index 00000000..baf7a6bd --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/Gopkg.toml @@ -0,0 +1,27 @@ +[[constraint]] + name = "github.com/crossdock/crossdock-go" + branch = "master" + +[[constraint]] + name = "github.com/opentracing/opentracing-go" + version = "^1" + +[[constraint]] + name = "github.com/prometheus/client_golang" + version = "0.8.0" + +[[constraint]] + name = "github.com/stretchr/testify" + version = "^1.1.3" + +[[constraint]] + name = "github.com/uber-go/atomic" + version = "^1" + +[[constraint]] + name = "github.com/uber/jaeger-lib" + version = "^1.3" + +[[constraint]] + name = "go.uber.org/zap" + version = "^1" diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/LICENSE b/template/faaschain/vendor/github.com/uber/jaeger-client-go/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor 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, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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/template/faaschain/vendor/github.com/uber/jaeger-client-go/Makefile b/template/faaschain/vendor/github.com/uber/jaeger-client-go/Makefile new file mode 100644 index 00000000..601cc651 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/Makefile @@ -0,0 +1,117 @@ +PROJECT_ROOT=github.com/uber/jaeger-client-go +PACKAGES := $(shell glide novendor | grep -v -e ./thrift-gen/... -e ./thrift/...) +# all .go files that don't exist in hidden directories +ALL_SRC := $(shell find . -name "*.go" | grep -v -e vendor -e thrift-gen -e ./thrift/ \ + -e ".*/\..*" \ + -e ".*/_.*" \ + -e ".*/mocks.*") + +-include crossdock/rules.mk + +export GO15VENDOREXPERIMENT=1 + +RACE=-race +GOTEST=go test -v $(RACE) +GOLINT=golint +GOVET=go vet +GOFMT=gofmt +FMT_LOG=fmt.log +LINT_LOG=lint.log + +THRIFT_VER=0.9.3 +THRIFT_IMG=thrift:$(THRIFT_VER) +THRIFT=docker run -v "${PWD}:/data" $(THRIFT_IMG) thrift +THRIFT_GO_ARGS=thrift_import="github.com/apache/thrift/lib/go/thrift" +THRIFT_GEN_DIR=thrift-gen + +PASS=$(shell printf "\033[32mPASS\033[0m") +FAIL=$(shell printf "\033[31mFAIL\033[0m") +COLORIZE=sed ''/PASS/s//$(PASS)/'' | sed ''/FAIL/s//$(FAIL)/'' + +.DEFAULT_GOAL := test-and-lint + +.PHONY: test-and-lint +test-and-lint: test fmt lint + +.PHONY: test +test: + bash -c "set -e; set -o pipefail; $(GOTEST) $(PACKAGES) | $(COLORIZE)" + +.PHONY: fmt +fmt: + $(GOFMT) -e -s -l -w $(ALL_SRC) + ./scripts/updateLicenses.sh + +.PHONY: lint +lint: + $(GOVET) $(PACKAGES) + @cat /dev/null > $(LINT_LOG) + @$(foreach pkg, $(PACKAGES), $(GOLINT) $(pkg) | grep -v crossdock/thrift >> $(LINT_LOG) || true;) + @[ ! -s "$(LINT_LOG)" ] || (echo "Lint Failures" | cat - $(LINT_LOG) && false) + @$(GOFMT) -e -s -l $(ALL_SRC) > $(FMT_LOG) + ./scripts/updateLicenses.sh >> $(FMT_LOG) + @[ ! -s "$(FMT_LOG)" ] || (echo "go fmt or license check failures, run 'make fmt'" | cat - $(FMT_LOG) && false) + + +.PHONY: install +install: + glide --version || go get github.com/Masterminds/glide +ifeq ($(USE_DEP),true) + dep ensure +else + glide install +endif + + +.PHONY: cover +cover: + ./scripts/cover.sh $(shell go list $(PACKAGES)) + go tool cover -html=cover.out -o cover.html + + +# This is not part of the regular test target because we don't want to slow it +# down. +.PHONY: test-examples +test-examples: + make -C examples + +# TODO at the moment we're not generating tchan_*.go files +thrift: idl-submodule thrift-image + $(THRIFT) -o /data --gen go:$(THRIFT_GO_ARGS) --out /data/$(THRIFT_GEN_DIR) /data/idl/thrift/agent.thrift + $(THRIFT) -o /data --gen go:$(THRIFT_GO_ARGS) --out /data/$(THRIFT_GEN_DIR) /data/idl/thrift/sampling.thrift + $(THRIFT) -o /data --gen go:$(THRIFT_GO_ARGS) --out /data/$(THRIFT_GEN_DIR) /data/idl/thrift/jaeger.thrift + $(THRIFT) -o /data --gen go:$(THRIFT_GO_ARGS) --out /data/$(THRIFT_GEN_DIR) /data/idl/thrift/zipkincore.thrift + $(THRIFT) -o /data --gen go:$(THRIFT_GO_ARGS) --out /data/$(THRIFT_GEN_DIR) /data/idl/thrift/baggage.thrift + $(THRIFT) -o /data --gen go:$(THRIFT_GO_ARGS) --out /data/crossdock/thrift/ /data/idl/thrift/crossdock/tracetest.thrift + sed -i '' 's|"zipkincore"|"$(PROJECT_ROOT)/thrift-gen/zipkincore"|g' $(THRIFT_GEN_DIR)/agent/*.go + sed -i '' 's|"jaeger"|"$(PROJECT_ROOT)/thrift-gen/jaeger"|g' $(THRIFT_GEN_DIR)/agent/*.go + sed -i '' 's|"github.com/apache/thrift/lib/go/thrift"|"github.com/uber/jaeger-client-go/thrift"|g' \ + $(THRIFT_GEN_DIR)/*/*.go crossdock/thrift/tracetest/*.go + rm -rf thrift-gen/*/*-remote + rm -rf crossdock/thrift/*/*-remote + rm -rf thrift-gen/jaeger/collector.go + +idl-submodule: + git submodule init + git submodule update + +thrift-image: + $(THRIFT) -version + +.PHONY: install-dep-ci +install-dep-ci: + - curl -L -s https://github.com/golang/dep/releases/download/v0.3.2/dep-linux-amd64 -o $$GOPATH/bin/dep + - chmod +x $$GOPATH/bin/dep + +.PHONY: install-ci +install-ci: install-dep-ci install + go get github.com/wadey/gocovmerge + go get github.com/mattn/goveralls + go get golang.org/x/tools/cmd/cover + go get github.com/golang/lint/golint + +.PHONY: test-ci +test-ci: + @./scripts/cover.sh $(shell go list $(PACKAGES)) + make lint + diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/README.md b/template/faaschain/vendor/github.com/uber/jaeger-client-go/README.md new file mode 100644 index 00000000..ba2f9eaa --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/README.md @@ -0,0 +1,269 @@ +[![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] [![OpenTracing 1.0 Enabled][ot-img]][ot-url] + +# Jaeger Bindings for Go OpenTracing API + +Instrumentation library that implements an +[OpenTracing](http://opentracing.io) Tracer for Jaeger (https://jaegertracing.io). + +**IMPORTANT**: The library's import path is based on its original location under `github.com/uber`. Do not try to import it as `github.com/jaegertracing`, it will not compile. We might revisit this in the next major release. + * :white_check_mark: `import "github.com/uber/jaeger-client-go"` + * :x: `import "github.com/jaegertracing/jaeger-client-go"` + +## How to Contribute + +Please see [CONTRIBUTING.md](CONTRIBUTING.md). + +## Installation + +We recommended using a dependency manager like [glide](https://github.com/Masterminds/glide) +and [semantic versioning](http://semver.org/) when including this library into an application. +For example, Jaeger backend imports this library like this: + +```yaml +- package: github.com/uber/jaeger-client-go + version: ^2.7.0 +``` + +If you instead want to use the latest version in `master`, you can pull it via `go get`. +Note that during `go get` you may see build errors due to incompatible dependencies, which is why +we recommend using semantic versions for dependencies. The error may be fixed by running +`make install` (it will install `glide` if you don't have it): + +```shell +go get -u github.com/uber/jaeger-client-go/ +cd $GOPATH/src/github.com/uber/jaeger-client-go/ +git submodule update --init --recursive +make install +``` + +## Initialization + +See tracer initialization examples in [godoc](https://godoc.org/github.com/uber/jaeger-client-go/config#pkg-examples) +and [config/example_test.go](./config/example_test.go). + +### Environment variables + +The tracer can be initialized with values coming from environment variables. None of the env vars are required +and all of them can be overriden via direct setting of the property on the configuration object. + +Property| Description +--- | --- +JAEGER_SERVICE_NAME | The service name +JAEGER_AGENT_HOST | The hostname for communicating with agent via UDP +JAEGER_AGENT_PORT | The port for communicating with agent via UDP +JAEGER_ENDPOINT | The HTTP endpoint for sending spans directly to a collector, i.e. http://jaeger-collector:14268/api/traces +JAEGER_USER | Username to send as part of "Basic" authentication to the collector endpoint +JAEGER_PASSWORD | Password to send as part of "Basic" authentication to the collector endpoint +JAEGER_REPORTER_LOG_SPANS | Whether the reporter should also log the spans +JAEGER_REPORTER_MAX_QUEUE_SIZE | The reporter's maximum queue size +JAEGER_REPORTER_FLUSH_INTERVAL | The reporter's flush interval (ms) +JAEGER_SAMPLER_TYPE | The sampler type +JAEGER_SAMPLER_PARAM | The sampler parameter (number) +JAEGER_SAMPLER_MANAGER_HOST_PORT | The host name and port when using the remote controlled sampler +JAEGER_SAMPLER_MAX_OPERATIONS | The maximum number of operations that the sampler will keep track of +JAEGER_SAMPLER_REFRESH_INTERVAL | How often the remotely controlled sampler will poll jaeger-agent for the appropriate sampling strategy +JAEGER_TAGS | A comma separated list of `name = value` tracer level tags, which get added to all reported spans. The value can also refer to an environment variable using the format `${envVarName:default}`, where the `:default` is optional, and identifies a value to be used if the environment variable cannot be found +JAEGER_DISABLED | Whether the tracer is disabled or not. If true, the default `opentracing.NoopTracer` is used. +JAEGER_RPC_METRICS | Whether to store RPC metrics + +By default, the client sends traces via UDP to the agent at `localhost:6831`. Use `JAEGER_AGENT_HOST` and +`JAEGER_AGENT_PORT` to send UDP traces to a different `host:port`. If `JAEGER_ENDPOINT` is set, the client sends traces +to the endpoint via `HTTP`, making the `JAEGER_AGENT_HOST` and `JAEGER_AGENT_PORT` unused. If `JAEGER_ENDPOINT` is +secured, HTTP basic authentication can be performed by setting the `JAEGER_USER` and `JAEGER_PASSWORD` environment +variables. + +### Closing the tracer via `io.Closer` + +The constructor function for Jaeger Tracer returns the tracer itself and an `io.Closer` instance. +It is recommended to structure your `main()` so that it calls the `Close()` function on the closer +before exiting, e.g. + +```go +tracer, closer, err := cfg.NewTracer(...) +defer closer.Close() +``` + +This is especially useful for command-line tools that enable tracing, as well as +for the long-running apps that support graceful shutdown. For example, if your deployment +system sends SIGTERM instead of killing the process and you trap that signal to do a graceful +exit, then having `defer closer.Closer()` ensures that all buffered spans are flushed. + +### Metrics & Monitoring + +The tracer emits a number of different metrics, defined in +[metrics.go](metrics.go). The monitoring backend is expected to support +tag-based metric names, e.g. instead of `statsd`-style string names +like `counters.my-service.jaeger.spans.started.sampled`, the metrics +are defined by a short name and a collection of key/value tags, for +example: `name:jaeger.traces, state:started, sampled:y`. See [metrics.go](./metrics.go) +file for the full list and descriptions of emitted metrics. + +The monitoring backend is represented by the `metrics.Factory` interface from package +[`"github.com/uber/jaeger-lib/metrics"`](https://github.com/jaegertracing/jaeger-lib/tree/master/metrics). An implementation +of that interface can be passed as an option to either the Configuration object or the Tracer +constructor, for example: + +```go +import ( + "github.com/uber/jaeger-client-go/config" + "github.com/uber/jaeger-lib/metrics/prometheus" +) + + metricsFactory := prometheus.New() + tracer, closer, err := config.Configuration{ + ServiceName: "your-service-name", + }.NewTracer( + config.Metrics(metricsFactory), + ) +``` + +By default, a no-op `metrics.NullFactory` is used. + +### Logging + +The tracer can be configured with an optional logger, which will be +used to log communication errors, or log spans if a logging reporter +option is specified in the configuration. The logging API is abstracted +by the [Logger](logger.go) interface. A logger instance implementing +this interface can be set on the `Config` object before calling the +`New` method. + +Besides the [zap](https://github.com/uber-go/zap) implementation +bundled with this package there is also a [go-kit](https://github.com/go-kit/kit) +one in the [jaeger-lib](https://github.com/jaegertracing/jaeger-lib) repository. + +## Instrumentation for Tracing + +Since this tracer is fully compliant with OpenTracing API 1.0, +all code instrumentation should only use the API itself, as described +in the [opentracing-go](https://github.com/opentracing/opentracing-go) documentation. + +## Features + +### Reporters + +A "reporter" is a component that receives the finished spans and reports +them to somewhere. Under normal circumstances, the Tracer +should use the default `RemoteReporter`, which sends the spans out of +process via configurable "transport". For testing purposes, one can +use an `InMemoryReporter` that accumulates spans in a buffer and +allows to retrieve them for later verification. Also available are +`NullReporter`, a no-op reporter that does nothing, a `LoggingReporter` +which logs all finished spans using their `String()` method, and a +`CompositeReporter` that can be used to combine more than one reporter +into one, e.g. to attach a logging reporter to the main remote reporter. + +### Span Reporting Transports + +The remote reporter uses "transports" to actually send the spans out +of process. Currently the supported transports include: + * [Jaeger Thrift](https://github.com/jaegertracing/jaeger-idl/blob/master/thrift/agent.thrift) over UDP or HTTP, + * [Zipkin Thrift](https://github.com/jaegertracing/jaeger-idl/blob/master/thrift/zipkincore.thrift) over HTTP. + +### Sampling + +The tracer does not record all spans, but only those that have the +sampling bit set in the `flags`. When a new trace is started and a new +unique ID is generated, a sampling decision is made whether this trace +should be sampled. The sampling decision is propagated to all downstream +calls via the `flags` field of the trace context. The following samplers +are available: + 1. `RemotelyControlledSampler` uses one of the other simpler samplers + and periodically updates it by polling an external server. This + allows dynamic control of the sampling strategies. + 1. `ConstSampler` always makes the same sampling decision for all + trace IDs. it can be configured to either sample all traces, or + to sample none. + 1. `ProbabilisticSampler` uses a fixed sampling rate as a probability + for a given trace to be sampled. The actual decision is made by + comparing the trace ID with a random number multiplied by the + sampling rate. + 1. `RateLimitingSampler` can be used to allow only a certain fixed + number of traces to be sampled per second. + +### Baggage Injection + +The OpenTracing spec allows for [baggage][baggage], which are key value pairs that are added +to the span context and propagated throughout the trace. An external process can inject baggage +by setting the special HTTP Header `jaeger-baggage` on a request: + +```sh +curl -H "jaeger-baggage: key1=value1, key2=value2" http://myhost.com +``` + +Baggage can also be programatically set inside your service: + +```go +if span := opentracing.SpanFromContext(ctx); span != nil { + span.SetBaggageItem("key", "value") +} +``` + +Another service downstream of that can retrieve the baggage in a similar way: + +```go +if span := opentracing.SpanFromContext(ctx); span != nil { + val := span.BaggageItem("key") + println(val) +} +``` + +### Debug Traces (Forced Sampling) + +#### Programmatically + +The OpenTracing API defines a `sampling.priority` standard tag that +can be used to affect the sampling of a span and its children: + +```go +import ( + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" +) + +span := opentracing.SpanFromContext(ctx) +ext.SamplingPriority.Set(span, 1) +``` + +#### Via HTTP Headers + +Jaeger Tracer also understands a special HTTP Header `jaeger-debug-id`, +which can be set in the incoming request, e.g. + +```sh +curl -H "jaeger-debug-id: some-correlation-id" http://myhost.com +``` + +When Jaeger sees this header in the request that otherwise has no +tracing context, it ensures that the new trace started for this +request will be sampled in the "debug" mode (meaning it should survive +all downsampling that might happen in the collection pipeline), and the +root span will have a tag as if this statement was executed: + +```go +span.SetTag("jaeger-debug-id", "some-correlation-id") +``` + +This allows using Jaeger UI to find the trace by this tag. + +### Zipkin HTTP B3 compatible header propagation + +Jaeger Tracer supports Zipkin B3 Propagation HTTP headers, which are used +by a lot of Zipkin tracers. This means that you can use Jaeger in conjunction with e.g. [these OpenZipkin tracers](https://github.com/openzipkin). + +However it is not the default propagation format, see [here](zipkin/README.md#NewZipkinB3HTTPHeaderPropagator) how to set it up. + +## License + +[Apache 2.0 License](LICENSE). + + +[doc-img]: https://godoc.org/github.com/uber/jaeger-client-go?status.svg +[doc]: https://godoc.org/github.com/uber/jaeger-client-go +[ci-img]: https://travis-ci.org/jaegertracing/jaeger-client-go.svg?branch=master +[ci]: https://travis-ci.org/jaegertracing/jaeger-client-go +[cov-img]: https://codecov.io/gh/jaegertracing/jaeger-client-go/branch/master/graph/badge.svg +[cov]: https://codecov.io/gh/jaegertracing/jaeger-client-go +[ot-img]: https://img.shields.io/badge/OpenTracing--1.0-enabled-blue.svg +[ot-url]: http://opentracing.io +[baggage]: https://github.com/opentracing/specification/blob/master/specification.md#set-a-baggage-item diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/RELEASE.md b/template/faaschain/vendor/github.com/uber/jaeger-client-go/RELEASE.md new file mode 100644 index 00000000..115e49ab --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/RELEASE.md @@ -0,0 +1,11 @@ +# Release Process + +1. Create a PR "Preparing for release X.Y.Z" against master branch + * Alter CHANGELOG.md from ` (unreleased)` to ` (YYYY-MM-DD)` + * Update `JaegerClientVersion` in constants.go to `Go-X.Y.Z` +2. Create a release "Release X.Y.Z" on Github + * Create Tag `vX.Y.Z` + * Copy CHANGELOG.md into the release notes +3. Create a PR "Back to development" against master branch + * Add ` (unreleased)` to CHANGELOG.md + * Update `JaegerClientVersion` in constants.go to `Go-dev` diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/baggage_setter.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/baggage_setter.go new file mode 100644 index 00000000..1037ca0e --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/baggage_setter.go @@ -0,0 +1,77 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "github.com/opentracing/opentracing-go/log" + + "github.com/uber/jaeger-client-go/internal/baggage" +) + +// baggageSetter is an actor that can set a baggage value on a Span given certain +// restrictions (eg. maxValueLength). +type baggageSetter struct { + restrictionManager baggage.RestrictionManager + metrics *Metrics +} + +func newBaggageSetter(restrictionManager baggage.RestrictionManager, metrics *Metrics) *baggageSetter { + return &baggageSetter{ + restrictionManager: restrictionManager, + metrics: metrics, + } +} + +// (NB) span should hold the lock before making this call +func (s *baggageSetter) setBaggage(span *Span, key, value string) { + var truncated bool + var prevItem string + restriction := s.restrictionManager.GetRestriction(span.serviceName(), key) + if !restriction.KeyAllowed() { + s.logFields(span, key, value, prevItem, truncated, restriction.KeyAllowed()) + s.metrics.BaggageUpdateFailure.Inc(1) + return + } + if len(value) > restriction.MaxValueLength() { + truncated = true + value = value[:restriction.MaxValueLength()] + s.metrics.BaggageTruncate.Inc(1) + } + prevItem = span.context.baggage[key] + s.logFields(span, key, value, prevItem, truncated, restriction.KeyAllowed()) + span.context = span.context.WithBaggageItem(key, value) + s.metrics.BaggageUpdateSuccess.Inc(1) +} + +func (s *baggageSetter) logFields(span *Span, key, value, prevItem string, truncated, valid bool) { + if !span.context.IsSampled() { + return + } + fields := []log.Field{ + log.String("event", "baggage"), + log.String("key", key), + log.String("value", value), + } + if prevItem != "" { + fields = append(fields, log.String("override", "true")) + } + if truncated { + fields = append(fields, log.String("truncated", "true")) + } + if !valid { + fields = append(fields, log.String("invalid", "true")) + } + span.logFieldsNoLocking(fields...) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/baggage_setter_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/baggage_setter_test.go new file mode 100644 index 00000000..c0454edf --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/baggage_setter_test.go @@ -0,0 +1,126 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "testing" + + "github.com/opentracing/opentracing-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber/jaeger-lib/metrics" + "github.com/uber/jaeger-lib/metrics/testutils" + + "github.com/uber/jaeger-client-go/internal/baggage" +) + +func withTracerAndMetrics(f func(tracer *Tracer, metrics *Metrics, factory *metrics.LocalFactory)) { + factory := metrics.NewLocalFactory(0) + m := NewMetrics(factory, nil) + + service := "DOOP" + tracer, closer := NewTracer(service, NewConstSampler(true), NewNullReporter()) + defer closer.Close() + f(tracer.(*Tracer), m, factory) +} + +func TestTruncateBaggage(t *testing.T) { + withTracerAndMetrics(func(tracer *Tracer, metrics *Metrics, factory *metrics.LocalFactory) { + setter := newBaggageSetter(baggage.NewDefaultRestrictionManager(5), metrics) + key := "key" + value := "01234567890" + expected := "01234" + + parent := tracer.StartSpan("parent").(*Span) + parent.context = parent.context.WithBaggageItem(key, value) + span := tracer.StartSpan("child", opentracing.ChildOf(parent.Context())).(*Span) + + setter.setBaggage(span, key, value) + assertBaggageFields(t, span, key, expected, true, true, false) + assert.Equal(t, expected, span.context.baggage[key]) + + testutils.AssertCounterMetrics(t, factory, + testutils.ExpectedMetric{ + Name: "jaeger.baggage_truncations", + Value: 1, + }, + testutils.ExpectedMetric{ + Name: "jaeger.baggage_updates", + Tags: map[string]string{"result": "ok"}, + Value: 1, + }, + ) + }) +} + +type keyNotAllowedBaggageRestrictionManager struct{} + +func (m *keyNotAllowedBaggageRestrictionManager) GetRestriction(service, key string) *baggage.Restriction { + return baggage.NewRestriction(false, 0) +} + +func TestInvalidBaggage(t *testing.T) { + withTracerAndMetrics(func(tracer *Tracer, metrics *Metrics, factory *metrics.LocalFactory) { + setter := newBaggageSetter(&keyNotAllowedBaggageRestrictionManager{}, metrics) + key := "key" + value := "value" + + span := tracer.StartSpan("span").(*Span) + + setter.setBaggage(span, key, value) + assertBaggageFields(t, span, key, value, false, false, true) + assert.Empty(t, span.context.baggage[key]) + + testutils.AssertCounterMetrics(t, factory, + testutils.ExpectedMetric{ + Name: "jaeger.baggage_updates", + Tags: map[string]string{"result": "err"}, + Value: 1, + }, + ) + }) +} + +func TestNotSampled(t *testing.T) { + withTracerAndMetrics(func(_ *Tracer, metrics *Metrics, factory *metrics.LocalFactory) { + tracer, closer := NewTracer("svc", NewConstSampler(false), NewNullReporter()) + defer closer.Close() + + setter := newBaggageSetter(baggage.NewDefaultRestrictionManager(10), metrics) + span := tracer.StartSpan("span").(*Span) + setter.setBaggage(span, "key", "value") + assert.Empty(t, span.logs, "No baggage fields should be created if span is not sampled") + }) +} + +func assertBaggageFields(t *testing.T, sp *Span, key, value string, override, truncated, invalid bool) { + require.Len(t, sp.logs, 1) + keys := map[string]struct{}{} + for _, field := range sp.logs[0].Fields { + keys[field.String()] = struct{}{} + } + assert.Contains(t, keys, "event:baggage") + assert.Contains(t, keys, "key:"+key) + assert.Contains(t, keys, "value:"+value) + if invalid { + assert.Contains(t, keys, "invalid:true") + } + if override { + assert.Contains(t, keys, "override:true") + } + if truncated { + assert.Contains(t, keys, "truncated:true") + } +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/config/config.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/config/config.go new file mode 100644 index 00000000..6cb54600 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/config/config.go @@ -0,0 +1,394 @@ +// Copyright (c) 2017-2018 Uber Technologies, Inc. +// +// 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. + +package config + +import ( + "errors" + "fmt" + "io" + "strings" + "time" + + "github.com/opentracing/opentracing-go" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/internal/baggage/remote" + throttler "github.com/uber/jaeger-client-go/internal/throttler/remote" + "github.com/uber/jaeger-client-go/rpcmetrics" + "github.com/uber/jaeger-client-go/transport" +) + +const defaultSamplingProbability = 0.001 + +// Configuration configures and creates Jaeger Tracer +type Configuration struct { + // ServiceName specifies the service name to use on the tracer. + // Can be provided via environment variable named JAEGER_SERVICE_NAME + ServiceName string `yaml:"serviceName"` + + // Disabled can be provided via environment variable named JAEGER_DISABLED + Disabled bool `yaml:"disabled"` + + // RPCMetrics can be provided via environment variable named JAEGER_RPC_METRICS + RPCMetrics bool `yaml:"rpc_metrics"` + + // Tags can be provided via environment variable named JAEGER_TAGS + Tags []opentracing.Tag `yaml:"tags"` + + Sampler *SamplerConfig `yaml:"sampler"` + Reporter *ReporterConfig `yaml:"reporter"` + Headers *jaeger.HeadersConfig `yaml:"headers"` + BaggageRestrictions *BaggageRestrictionsConfig `yaml:"baggage_restrictions"` + Throttler *ThrottlerConfig `yaml:"throttler"` +} + +// SamplerConfig allows initializing a non-default sampler. All fields are optional. +type SamplerConfig struct { + // Type specifies the type of the sampler: const, probabilistic, rateLimiting, or remote + // Can be set by exporting an environment variable named JAEGER_SAMPLER_TYPE + Type string `yaml:"type"` + + // Param is a value passed to the sampler. + // Valid values for Param field are: + // - for "const" sampler, 0 or 1 for always false/true respectively + // - for "probabilistic" sampler, a probability between 0 and 1 + // - for "rateLimiting" sampler, the number of spans per second + // - for "remote" sampler, param is the same as for "probabilistic" + // and indicates the initial sampling rate before the actual one + // is received from the mothership. + // Can be set by exporting an environment variable named JAEGER_SAMPLER_PARAM + Param float64 `yaml:"param"` + + // SamplingServerURL is the address of jaeger-agent's HTTP sampling server + // Can be set by exporting an environment variable named JAEGER_SAMPLER_MANAGER_HOST_PORT + SamplingServerURL string `yaml:"samplingServerURL"` + + // MaxOperations is the maximum number of operations that the sampler + // will keep track of. If an operation is not tracked, a default probabilistic + // sampler will be used rather than the per operation specific sampler. + // Can be set by exporting an environment variable named JAEGER_SAMPLER_MAX_OPERATIONS + MaxOperations int `yaml:"maxOperations"` + + // SamplingRefreshInterval controls how often the remotely controlled sampler will poll + // jaeger-agent for the appropriate sampling strategy. + // Can be set by exporting an environment variable named JAEGER_SAMPLER_REFRESH_INTERVAL + SamplingRefreshInterval time.Duration `yaml:"samplingRefreshInterval"` +} + +// ReporterConfig configures the reporter. All fields are optional. +type ReporterConfig struct { + // QueueSize controls how many spans the reporter can keep in memory before it starts dropping + // new spans. The queue is continuously drained by a background go-routine, as fast as spans + // can be sent out of process. + // Can be set by exporting an environment variable named JAEGER_REPORTER_MAX_QUEUE_SIZE + QueueSize int `yaml:"queueSize"` + + // BufferFlushInterval controls how often the buffer is force-flushed, even if it's not full. + // It is generally not useful, as it only matters for very low traffic services. + // Can be set by exporting an environment variable named JAEGER_REPORTER_FLUSH_INTERVAL + BufferFlushInterval time.Duration + + // LogSpans, when true, enables LoggingReporter that runs in parallel with the main reporter + // and logs all submitted spans. Main Configuration.Logger must be initialized in the code + // for this option to have any effect. + // Can be set by exporting an environment variable named JAEGER_REPORTER_LOG_SPANS + LogSpans bool `yaml:"logSpans"` + + // LocalAgentHostPort instructs reporter to send spans to jaeger-agent at this address + // Can be set by exporting an environment variable named JAEGER_AGENT_HOST / JAEGER_AGENT_PORT + LocalAgentHostPort string `yaml:"localAgentHostPort"` + + // CollectorEndpoint instructs reporter to send spans to jaeger-collector at this URL + // Can be set by exporting an environment variable named JAEGER_ENDPOINT + CollectorEndpoint string `yaml:"collectorEndpoint"` + + // User instructs reporter to include a user for basic http authentication when sending spans to jaeger-collector. + // Can be set by exporting an environment variable named JAEGER_USER + User string `yaml:"user"` + + // Password instructs reporter to include a password for basic http authentication when sending spans to + // jaeger-collector. Can be set by exporting an environment variable named JAEGER_PASSWORD + Password string `yaml:"password"` +} + +// BaggageRestrictionsConfig configures the baggage restrictions manager which can be used to whitelist +// certain baggage keys. All fields are optional. +type BaggageRestrictionsConfig struct { + // DenyBaggageOnInitializationFailure controls the startup failure mode of the baggage restriction + // manager. If true, the manager will not allow any baggage to be written until baggage restrictions have + // been retrieved from jaeger-agent. If false, the manager wil allow any baggage to be written until baggage + // restrictions have been retrieved from jaeger-agent. + DenyBaggageOnInitializationFailure bool `yaml:"denyBaggageOnInitializationFailure"` + + // HostPort is the hostPort of jaeger-agent's baggage restrictions server + HostPort string `yaml:"hostPort"` + + // RefreshInterval controls how often the baggage restriction manager will poll + // jaeger-agent for the most recent baggage restrictions. + RefreshInterval time.Duration `yaml:"refreshInterval"` +} + +// ThrottlerConfig configures the throttler which can be used to throttle the +// rate at which the client may send debug requests. +type ThrottlerConfig struct { + // HostPort of jaeger-agent's credit server. + HostPort string `yaml:"hostPort"` + + // RefreshInterval controls how often the throttler will poll jaeger-agent + // for more throttling credits. + RefreshInterval time.Duration `yaml:"refreshInterval"` + + // SynchronousInitialization determines whether or not the throttler should + // synchronously fetch credits from the agent when an operation is seen for + // the first time. This should be set to true if the client will be used by + // a short lived service that needs to ensure that credits are fetched + // upfront such that sampling or throttling occurs. + SynchronousInitialization bool `yaml:"synchronousInitialization"` +} + +type nullCloser struct{} + +func (*nullCloser) Close() error { return nil } + +// New creates a new Jaeger Tracer, and a closer func that can be used to flush buffers +// before shutdown. +// +// Deprecated: use NewTracer() function +func (c Configuration) New( + serviceName string, + options ...Option, +) (opentracing.Tracer, io.Closer, error) { + if serviceName != "" { + c.ServiceName = serviceName + } + + return c.NewTracer(options...) +} + +// NewTracer returns a new tracer based on the current configuration, using the given options, +// and a closer func that can be used to flush buffers before shutdown. +func (c Configuration) NewTracer(options ...Option) (opentracing.Tracer, io.Closer, error) { + if c.ServiceName == "" { + return nil, nil, errors.New("no service name provided") + } + + if c.Disabled { + return &opentracing.NoopTracer{}, &nullCloser{}, nil + } + opts := applyOptions(options...) + tracerMetrics := jaeger.NewMetrics(opts.metrics, nil) + if c.RPCMetrics { + Observer( + rpcmetrics.NewObserver( + opts.metrics.Namespace("jaeger-rpc", map[string]string{"component": "jaeger"}), + rpcmetrics.DefaultNameNormalizer, + ), + )(&opts) // adds to c.observers + } + if c.Sampler == nil { + c.Sampler = &SamplerConfig{ + Type: jaeger.SamplerTypeRemote, + Param: defaultSamplingProbability, + } + } + if c.Reporter == nil { + c.Reporter = &ReporterConfig{} + } + + sampler := opts.sampler + if sampler == nil { + s, err := c.Sampler.NewSampler(c.ServiceName, tracerMetrics) + if err != nil { + return nil, nil, err + } + sampler = s + } + + reporter := opts.reporter + if reporter == nil { + r, err := c.Reporter.NewReporter(c.ServiceName, tracerMetrics, opts.logger) + if err != nil { + return nil, nil, err + } + reporter = r + } + + tracerOptions := []jaeger.TracerOption{ + jaeger.TracerOptions.Metrics(tracerMetrics), + jaeger.TracerOptions.Logger(opts.logger), + jaeger.TracerOptions.CustomHeaderKeys(c.Headers), + jaeger.TracerOptions.Gen128Bit(opts.gen128Bit), + jaeger.TracerOptions.ZipkinSharedRPCSpan(opts.zipkinSharedRPCSpan), + jaeger.TracerOptions.MaxTagValueLength(opts.maxTagValueLength), + } + + for _, tag := range opts.tags { + tracerOptions = append(tracerOptions, jaeger.TracerOptions.Tag(tag.Key, tag.Value)) + } + + for _, tag := range c.Tags { + tracerOptions = append(tracerOptions, jaeger.TracerOptions.Tag(tag.Key, tag.Value)) + } + + for _, obs := range opts.observers { + tracerOptions = append(tracerOptions, jaeger.TracerOptions.Observer(obs)) + } + + for _, cobs := range opts.contribObservers { + tracerOptions = append(tracerOptions, jaeger.TracerOptions.ContribObserver(cobs)) + } + + for format, injector := range opts.injectors { + tracerOptions = append(tracerOptions, jaeger.TracerOptions.Injector(format, injector)) + } + + for format, extractor := range opts.extractors { + tracerOptions = append(tracerOptions, jaeger.TracerOptions.Extractor(format, extractor)) + } + + if c.BaggageRestrictions != nil { + mgr := remote.NewRestrictionManager( + c.ServiceName, + remote.Options.Metrics(tracerMetrics), + remote.Options.Logger(opts.logger), + remote.Options.HostPort(c.BaggageRestrictions.HostPort), + remote.Options.RefreshInterval(c.BaggageRestrictions.RefreshInterval), + remote.Options.DenyBaggageOnInitializationFailure( + c.BaggageRestrictions.DenyBaggageOnInitializationFailure, + ), + ) + tracerOptions = append(tracerOptions, jaeger.TracerOptions.BaggageRestrictionManager(mgr)) + } + + if c.Throttler != nil { + debugThrottler := throttler.NewThrottler( + c.ServiceName, + throttler.Options.Metrics(tracerMetrics), + throttler.Options.Logger(opts.logger), + throttler.Options.HostPort(c.Throttler.HostPort), + throttler.Options.RefreshInterval(c.Throttler.RefreshInterval), + throttler.Options.SynchronousInitialization( + c.Throttler.SynchronousInitialization, + ), + ) + + tracerOptions = append(tracerOptions, jaeger.TracerOptions.DebugThrottler(debugThrottler)) + } + + tracer, closer := jaeger.NewTracer( + c.ServiceName, + sampler, + reporter, + tracerOptions..., + ) + + return tracer, closer, nil +} + +// InitGlobalTracer creates a new Jaeger Tracer, and sets it as global OpenTracing Tracer. +// It returns a closer func that can be used to flush buffers before shutdown. +func (c Configuration) InitGlobalTracer( + serviceName string, + options ...Option, +) (io.Closer, error) { + if c.Disabled { + return &nullCloser{}, nil + } + tracer, closer, err := c.New(serviceName, options...) + if err != nil { + return nil, err + } + opentracing.SetGlobalTracer(tracer) + return closer, nil +} + +// NewSampler creates a new sampler based on the configuration +func (sc *SamplerConfig) NewSampler( + serviceName string, + metrics *jaeger.Metrics, +) (jaeger.Sampler, error) { + samplerType := strings.ToLower(sc.Type) + if samplerType == jaeger.SamplerTypeConst { + return jaeger.NewConstSampler(sc.Param != 0), nil + } + if samplerType == jaeger.SamplerTypeProbabilistic { + if sc.Param >= 0 && sc.Param <= 1.0 { + return jaeger.NewProbabilisticSampler(sc.Param) + } + return nil, fmt.Errorf( + "Invalid Param for probabilistic sampler: %v. Expecting value between 0 and 1", + sc.Param, + ) + } + if samplerType == jaeger.SamplerTypeRateLimiting { + return jaeger.NewRateLimitingSampler(sc.Param), nil + } + if samplerType == jaeger.SamplerTypeRemote || sc.Type == "" { + sc2 := *sc + sc2.Type = jaeger.SamplerTypeProbabilistic + initSampler, err := sc2.NewSampler(serviceName, nil) + if err != nil { + return nil, err + } + options := []jaeger.SamplerOption{ + jaeger.SamplerOptions.Metrics(metrics), + jaeger.SamplerOptions.InitialSampler(initSampler), + jaeger.SamplerOptions.SamplingServerURL(sc.SamplingServerURL), + } + if sc.MaxOperations != 0 { + options = append(options, jaeger.SamplerOptions.MaxOperations(sc.MaxOperations)) + } + if sc.SamplingRefreshInterval != 0 { + options = append(options, jaeger.SamplerOptions.SamplingRefreshInterval(sc.SamplingRefreshInterval)) + } + return jaeger.NewRemotelyControlledSampler(serviceName, options...), nil + } + return nil, fmt.Errorf("Unknown sampler type %v", sc.Type) +} + +// NewReporter instantiates a new reporter that submits spans to the collector +func (rc *ReporterConfig) NewReporter( + serviceName string, + metrics *jaeger.Metrics, + logger jaeger.Logger, +) (jaeger.Reporter, error) { + sender, err := rc.newTransport() + if err != nil { + return nil, err + } + reporter := jaeger.NewRemoteReporter( + sender, + jaeger.ReporterOptions.QueueSize(rc.QueueSize), + jaeger.ReporterOptions.BufferFlushInterval(rc.BufferFlushInterval), + jaeger.ReporterOptions.Logger(logger), + jaeger.ReporterOptions.Metrics(metrics)) + if rc.LogSpans && logger != nil { + logger.Infof("Initializing logging reporter\n") + reporter = jaeger.NewCompositeReporter(jaeger.NewLoggingReporter(logger), reporter) + } + return reporter, err +} + +func (rc *ReporterConfig) newTransport() (jaeger.Transport, error) { + switch { + case rc.CollectorEndpoint != "" && rc.User != "" && rc.Password != "": + return transport.NewHTTPTransport(rc.CollectorEndpoint, transport.HTTPBatchSize(1), + transport.HTTPBasicAuth(rc.User, rc.Password)), nil + case rc.CollectorEndpoint != "": + return transport.NewHTTPTransport(rc.CollectorEndpoint, transport.HTTPBatchSize(1)), nil + default: + return jaeger.NewUDPTransport(rc.LocalAgentHostPort, 0) + } +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/config/config_env.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/config/config_env.go new file mode 100644 index 00000000..66fd53c1 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/config/config_env.go @@ -0,0 +1,232 @@ +// Copyright (c) 2018 The Jaeger Authors. +// +// 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. + +package config + +import ( + "fmt" + "net/url" + "os" + "strconv" + "strings" + "time" + + opentracing "github.com/opentracing/opentracing-go" + "github.com/pkg/errors" + + "github.com/uber/jaeger-client-go" +) + +const ( + // environment variable names + envServiceName = "JAEGER_SERVICE_NAME" + envDisabled = "JAEGER_DISABLED" + envRPCMetrics = "JAEGER_RPC_METRICS" + envTags = "JAEGER_TAGS" + envSamplerType = "JAEGER_SAMPLER_TYPE" + envSamplerParam = "JAEGER_SAMPLER_PARAM" + envSamplerManagerHostPort = "JAEGER_SAMPLER_MANAGER_HOST_PORT" + envSamplerMaxOperations = "JAEGER_SAMPLER_MAX_OPERATIONS" + envSamplerRefreshInterval = "JAEGER_SAMPLER_REFRESH_INTERVAL" + envReporterMaxQueueSize = "JAEGER_REPORTER_MAX_QUEUE_SIZE" + envReporterFlushInterval = "JAEGER_REPORTER_FLUSH_INTERVAL" + envReporterLogSpans = "JAEGER_REPORTER_LOG_SPANS" + envEndpoint = "JAEGER_ENDPOINT" + envUser = "JAEGER_USER" + envPassword = "JAEGER_PASSWORD" + envAgentHost = "JAEGER_AGENT_HOST" + envAgentPort = "JAEGER_AGENT_PORT" +) + +// FromEnv uses environment variables to set the tracer's Configuration +func FromEnv() (*Configuration, error) { + c := &Configuration{} + + if e := os.Getenv(envServiceName); e != "" { + c.ServiceName = e + } + + if e := os.Getenv(envRPCMetrics); e != "" { + if value, err := strconv.ParseBool(e); err == nil { + c.RPCMetrics = value + } else { + return nil, errors.Wrapf(err, "cannot parse env var %s=%s", envRPCMetrics, e) + } + } + + if e := os.Getenv(envDisabled); e != "" { + if value, err := strconv.ParseBool(e); err == nil { + c.Disabled = value + } else { + return nil, errors.Wrapf(err, "cannot parse env var %s=%s", envDisabled, e) + } + } + + if e := os.Getenv(envTags); e != "" { + c.Tags = parseTags(e) + } + + if s, err := samplerConfigFromEnv(); err == nil { + c.Sampler = s + } else { + return nil, errors.Wrap(err, "cannot obtain sampler config from env") + } + + if r, err := reporterConfigFromEnv(); err == nil { + c.Reporter = r + } else { + return nil, errors.Wrap(err, "cannot obtain reporter config from env") + } + + return c, nil +} + +// samplerConfigFromEnv creates a new SamplerConfig based on the environment variables +func samplerConfigFromEnv() (*SamplerConfig, error) { + sc := &SamplerConfig{} + + if e := os.Getenv(envSamplerType); e != "" { + sc.Type = e + } + + if e := os.Getenv(envSamplerParam); e != "" { + if value, err := strconv.ParseFloat(e, 64); err == nil { + sc.Param = value + } else { + return nil, errors.Wrapf(err, "cannot parse env var %s=%s", envSamplerParam, e) + } + } + + if e := os.Getenv(envSamplerManagerHostPort); e != "" { + sc.SamplingServerURL = e + } + + if e := os.Getenv(envSamplerMaxOperations); e != "" { + if value, err := strconv.ParseInt(e, 10, 0); err == nil { + sc.MaxOperations = int(value) + } else { + return nil, errors.Wrapf(err, "cannot parse env var %s=%s", envSamplerMaxOperations, e) + } + } + + if e := os.Getenv(envSamplerRefreshInterval); e != "" { + if value, err := time.ParseDuration(e); err == nil { + sc.SamplingRefreshInterval = value + } else { + return nil, errors.Wrapf(err, "cannot parse env var %s=%s", envSamplerRefreshInterval, e) + } + } + + return sc, nil +} + +// reporterConfigFromEnv creates a new ReporterConfig based on the environment variables +func reporterConfigFromEnv() (*ReporterConfig, error) { + rc := &ReporterConfig{} + + if e := os.Getenv(envReporterMaxQueueSize); e != "" { + if value, err := strconv.ParseInt(e, 10, 0); err == nil { + rc.QueueSize = int(value) + } else { + return nil, errors.Wrapf(err, "cannot parse env var %s=%s", envReporterMaxQueueSize, e) + } + } + + if e := os.Getenv(envReporterFlushInterval); e != "" { + if value, err := time.ParseDuration(e); err == nil { + rc.BufferFlushInterval = value + } else { + return nil, errors.Wrapf(err, "cannot parse env var %s=%s", envReporterFlushInterval, e) + } + } + + if e := os.Getenv(envReporterLogSpans); e != "" { + if value, err := strconv.ParseBool(e); err == nil { + rc.LogSpans = value + } else { + return nil, errors.Wrapf(err, "cannot parse env var %s=%s", envReporterLogSpans, e) + } + } + + host := jaeger.DefaultUDPSpanServerHost + ep := os.Getenv(envEndpoint) + if e := os.Getenv(envAgentHost); e != "" { + if ep != "" { + return nil, errors.Errorf("cannot set env vars %s and %s together", envAgentHost, envEndpoint) + } + host = e + } + + port := jaeger.DefaultUDPSpanServerPort + if e := os.Getenv(envAgentPort); e != "" { + if ep != "" { + return nil, errors.Errorf("cannot set env vars %s and %s together", envAgentPort, envEndpoint) + } + if value, err := strconv.ParseInt(e, 10, 0); err == nil { + port = int(value) + } else { + return nil, errors.Wrapf(err, "cannot parse env var %s=%s", envAgentPort, e) + } + } + + // the side effect of this is that we are building the default value, even if none of the env vars + // were not explicitly passed + rc.LocalAgentHostPort = fmt.Sprintf("%s:%d", host, port) + + if ep != "" { + u, err := url.ParseRequestURI(ep) + if err != nil { + return nil, errors.Wrapf(err, "cannot parse env var %s=%s", envEndpoint, ep) + } + rc.CollectorEndpoint = fmt.Sprintf("%s", u) + } + + user := os.Getenv(envUser) + pswd := os.Getenv(envPassword) + if user != "" && pswd == "" || user == "" && pswd != "" { + return nil, errors.Errorf("you must set %s and %s env vars together", envUser, envPassword) + } + rc.User = user + rc.Password = pswd + + return rc, nil +} + +// parseTags parses the given string into a collection of Tags. +// Spec for this value: +// - comma separated list of key=value +// - value can be specified using the notation ${envVar:defaultValue}, where `envVar` +// is an environment variable and `defaultValue` is the value to use in case the env var is not set +func parseTags(sTags string) []opentracing.Tag { + pairs := strings.Split(sTags, ",") + tags := make([]opentracing.Tag, 0) + for _, p := range pairs { + kv := strings.SplitN(p, "=", 2) + k, v := strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1]) + + if strings.HasPrefix(v, "${") && strings.HasSuffix(v, "}") { + ed := strings.SplitN(v[2:len(v)-1], ":", 2) + e, d := ed[0], ed[1] + v = os.Getenv(e) + if v == "" && d != "" { + v = d + } + } + + tag := opentracing.Tag{Key: k, Value: v} + tags = append(tags, tag) + } + + return tags +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/config/config_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/config/config_test.go new file mode 100644 index 00000000..03b4db0b --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/config/config_test.go @@ -0,0 +1,607 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package config + +import ( + "fmt" + "os" + "testing" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber/jaeger-lib/metrics" + "github.com/uber/jaeger-lib/metrics/testutils" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/log" + "github.com/uber/jaeger-client-go/transport" +) + +func TestNewSamplerConst(t *testing.T) { + constTests := []struct { + param float64 + decision bool + }{{1, true}, {0, false}} + for _, tst := range constTests { + cfg := &SamplerConfig{Type: jaeger.SamplerTypeConst, Param: tst.param} + s, err := cfg.NewSampler("x", nil) + require.NoError(t, err) + s1, ok := s.(*jaeger.ConstSampler) + require.True(t, ok, "converted to constSampler") + require.Equal(t, tst.decision, s1.Decision, "decision") + } +} + +func TestNewSamplerProbabilistic(t *testing.T) { + constTests := []struct { + param float64 + error bool + }{{1.5, true}, {0.5, false}} + for _, tst := range constTests { + cfg := &SamplerConfig{Type: jaeger.SamplerTypeProbabilistic, Param: tst.param} + s, err := cfg.NewSampler("x", nil) + if tst.error { + require.Error(t, err) + } else { + require.NoError(t, err) + _, ok := s.(*jaeger.ProbabilisticSampler) + require.True(t, ok, "converted to ProbabilisticSampler") + } + } +} + +func TestDefaultSampler(t *testing.T) { + cfg := Configuration{ + Sampler: &SamplerConfig{Type: "InvalidType"}, + } + _, _, err := cfg.New("testService") + require.Error(t, err) +} + +func TestServiceNameFromEnv(t *testing.T) { + os.Setenv(envServiceName, "my-service") + + cfg, err := FromEnv() + assert.NoError(t, err) + + _, c, err := cfg.New("") + defer c.Close() + assert.NoError(t, err) + os.Unsetenv(envServiceName) +} + +func TestFromEnv(t *testing.T) { + os.Setenv(envServiceName, "my-service") + os.Setenv(envDisabled, "false") + os.Setenv(envRPCMetrics, "true") + os.Setenv(envTags, "KEY=VALUE") + + cfg, err := FromEnv() + assert.NoError(t, err) + assert.Equal(t, "my-service", cfg.ServiceName) + assert.Equal(t, false, cfg.Disabled) + assert.Equal(t, true, cfg.RPCMetrics) + assert.Equal(t, "KEY", cfg.Tags[0].Key) + assert.Equal(t, "VALUE", cfg.Tags[0].Value) + + os.Unsetenv(envServiceName) + os.Unsetenv(envDisabled) + os.Unsetenv(envRPCMetrics) +} + +func TestNoServiceNameFromEnv(t *testing.T) { + os.Unsetenv(envServiceName) + + cfg, err := FromEnv() + assert.NoError(t, err) + + _, _, err = cfg.New("") + assert.Error(t, err) +} + +func TestSamplerConfigFromEnv(t *testing.T) { + // prepare + os.Setenv(envSamplerType, "const") + os.Setenv(envSamplerParam, "1") + os.Setenv(envSamplerManagerHostPort, "http://themaster") + os.Setenv(envSamplerMaxOperations, "10") + os.Setenv(envSamplerRefreshInterval, "1m1s") // 61 seconds + + // test + cfg, err := FromEnv() + assert.NoError(t, err) + + // verify + assert.Equal(t, "const", cfg.Sampler.Type) + assert.Equal(t, float64(1), cfg.Sampler.Param) + assert.Equal(t, "http://themaster", cfg.Sampler.SamplingServerURL) + assert.Equal(t, int(10), cfg.Sampler.MaxOperations) + assert.Equal(t, 61000000000, int(cfg.Sampler.SamplingRefreshInterval)) + + // cleanup + os.Unsetenv(envSamplerType) + os.Unsetenv(envSamplerParam) + os.Unsetenv(envSamplerManagerHostPort) + os.Unsetenv(envSamplerMaxOperations) + os.Unsetenv(envSamplerRefreshInterval) +} + +func TestReporterConfigFromEnv(t *testing.T) { + // prepare + os.Setenv(envReporterMaxQueueSize, "10") + os.Setenv(envReporterFlushInterval, "1m1s") // 61 seconds + os.Setenv(envReporterLogSpans, "true") + os.Setenv(envAgentHost, "nonlocalhost") + os.Setenv(envAgentPort, "6832") + os.Setenv(envUser, "user") + os.Setenv(envPassword, "password") + + // test + cfg, err := FromEnv() + assert.NoError(t, err) + + // verify + assert.Equal(t, int(10), cfg.Reporter.QueueSize) + assert.Equal(t, 61000000000, int(cfg.Reporter.BufferFlushInterval)) + assert.Equal(t, true, cfg.Reporter.LogSpans) + assert.Equal(t, "nonlocalhost:6832", cfg.Reporter.LocalAgentHostPort) + + // Test HTTP transport + os.Unsetenv(envAgentHost) + os.Unsetenv(envAgentPort) + os.Setenv(envEndpoint, "http://1.2.3.4:5678/api/traces") + + // test + cfg, err = FromEnv() + assert.NoError(t, err) + + // verify + assert.Equal(t, "http://1.2.3.4:5678/api/traces", cfg.Reporter.CollectorEndpoint) + + // cleanup + os.Unsetenv(envReporterMaxQueueSize) + os.Unsetenv(envReporterFlushInterval) + os.Unsetenv(envReporterLogSpans) + os.Unsetenv(envEndpoint) + os.Unsetenv(envUser) + os.Unsetenv(envPassword) +} + +func TestParsingErrorsFromEnv(t *testing.T) { + os.Setenv(envAgentHost, "localhost") // we require this in order to test the parsing of the port + + tests := []struct { + envVar string + value string + }{ + { + envVar: envRPCMetrics, + value: "NOT_A_BOOLEAN", + }, + { + envVar: envDisabled, + value: "NOT_A_BOOLEAN", + }, + { + envVar: envSamplerParam, + value: "NOT_A_FLOAT", + }, + { + envVar: envSamplerMaxOperations, + value: "NOT_AN_INT", + }, + { + envVar: envSamplerRefreshInterval, + value: "NOT_A_DURATION", + }, + { + envVar: envReporterMaxQueueSize, + value: "NOT_AN_INT", + }, + { + envVar: envReporterFlushInterval, + value: "NOT_A_DURATION", + }, + { + envVar: envReporterLogSpans, + value: "NOT_A_BOOLEAN", + }, + { + envVar: envAgentPort, + value: "NOT_AN_INT", + }, + { + envVar: envEndpoint, + value: "NOT_A_URL", + }, + } + + for _, test := range tests { + os.Setenv(test.envVar, test.value) + if test.envVar == envEndpoint { + os.Unsetenv(envAgentHost) + os.Unsetenv(envAgentPort) + } + _, err := FromEnv() + require.Error(t, err) + assert.Contains(t, err.Error(), fmt.Sprintf("cannot parse env var %s=%s", test.envVar, test.value)) + os.Unsetenv(test.envVar) + } + +} + +func TestParsingUserPasswordErrorEnv(t *testing.T) { + tests := []struct { + envVar string + value string + }{ + { + envVar: envUser, + value: "user", + }, + { + envVar: envPassword, + value: "password", + }, + } + + for _, test := range tests { + os.Setenv(test.envVar, test.value) + _, err := FromEnv() + require.Error(t, err) + assert.Contains(t, err.Error(), fmt.Sprintf("you must set %s and %s env vars together", envUser, + envPassword)) + os.Unsetenv(test.envVar) + } +} + +func TestHostPortEndpointEnvError(t *testing.T) { + tests := []struct { + envVar string + value string + err string + }{ + { + envVar: envAgentHost, + value: "user", + err: fmt.Sprintf("cannot set env vars %s and %s together", envAgentHost, envEndpoint), + }, + { + envVar: envAgentPort, + value: "password", + err: fmt.Sprintf("cannot set env vars %s and %s together", envAgentPort, envEndpoint), + }, + } + + os.Setenv(envEndpoint, "http://1.2.3.4:5678/api/traces") + for _, test := range tests { + os.Setenv(test.envVar, test.value) + _, err := FromEnv() + require.Error(t, err) + assert.Contains(t, err.Error(), test.err) + os.Unsetenv(test.envVar) + } + os.Unsetenv(envEndpoint) +} + +func TestInvalidSamplerType(t *testing.T) { + cfg := &SamplerConfig{MaxOperations: 10} + s, err := cfg.NewSampler("x", jaeger.NewNullMetrics()) + require.NoError(t, err) + rcs, ok := s.(*jaeger.RemotelyControlledSampler) + require.True(t, ok, "converted to RemotelyControlledSampler") + rcs.Close() +} + +func TestUDPTransportType(t *testing.T) { + rc := &ReporterConfig{LocalAgentHostPort: "localhost:1234"} + expect, _ := jaeger.NewUDPTransport(rc.LocalAgentHostPort, 0) + sender, err := rc.newTransport() + require.NoError(t, err) + require.IsType(t, expect, sender) +} + +func TestHTTPTransportType(t *testing.T) { + rc := &ReporterConfig{CollectorEndpoint: "http://1.2.3.4:5678/api/traces"} + expect := transport.NewHTTPTransport(rc.CollectorEndpoint) + sender, err := rc.newTransport() + require.NoError(t, err) + require.IsType(t, expect, sender) +} + +func TestDefaultConfig(t *testing.T) { + cfg := Configuration{} + _, _, err := cfg.New("", Metrics(metrics.NullFactory), Logger(log.NullLogger)) + require.EqualError(t, err, "no service name provided") + + _, closer, err := cfg.New("testService") + defer closer.Close() + require.NoError(t, err) +} + +func TestDisabledFlag(t *testing.T) { + cfg := Configuration{Disabled: true} + _, closer, err := cfg.New("testService") + defer closer.Close() + require.NoError(t, err) +} + +func TestNewReporterError(t *testing.T) { + cfg := Configuration{ + Reporter: &ReporterConfig{LocalAgentHostPort: "bad_local_agent"}, + } + _, _, err := cfg.New("testService") + require.Error(t, err) +} + +func TestInitGlobalTracer(t *testing.T) { + // Save the existing GlobalTracer and replace after finishing function + prevTracer := opentracing.GlobalTracer() + defer opentracing.SetGlobalTracer(prevTracer) + noopTracer := opentracing.NoopTracer{} + + tests := []struct { + cfg Configuration + shouldErr bool + tracerChanged bool + }{ + { + cfg: Configuration{Disabled: true}, + shouldErr: false, + tracerChanged: false, + }, + { + cfg: Configuration{Sampler: &SamplerConfig{Type: "InvalidType"}}, + shouldErr: true, + tracerChanged: false, + }, + { + cfg: Configuration{ + Sampler: &SamplerConfig{ + Type: "remote", + SamplingRefreshInterval: 1, + }, + }, + shouldErr: false, + tracerChanged: true, + }, + { + cfg: Configuration{}, + shouldErr: false, + tracerChanged: true, + }, + } + for _, test := range tests { + opentracing.SetGlobalTracer(noopTracer) + _, err := test.cfg.InitGlobalTracer("testService") + if test.shouldErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + if test.tracerChanged { + require.NotEqual(t, noopTracer, opentracing.GlobalTracer()) + } else { + require.Equal(t, noopTracer, opentracing.GlobalTracer()) + } + } +} + +func TestConfigWithReporter(t *testing.T) { + c := Configuration{ + Sampler: &SamplerConfig{ + Type: "const", + Param: 1, + }, + } + r := jaeger.NewInMemoryReporter() + tracer, closer, err := c.New("test", Reporter(r)) + require.NoError(t, err) + defer closer.Close() + + tracer.StartSpan("test").Finish() + assert.Len(t, r.GetSpans(), 1) +} + +func TestConfigWithRPCMetrics(t *testing.T) { + metrics := metrics.NewLocalFactory(0) + c := Configuration{ + Sampler: &SamplerConfig{ + Type: "const", + Param: 1, + }, + RPCMetrics: true, + } + r := jaeger.NewInMemoryReporter() + tracer, closer, err := c.New( + "test", + Reporter(r), + Metrics(metrics), + ContribObserver(fakeContribObserver{}), + ) + require.NoError(t, err) + defer closer.Close() + + tracer.StartSpan("test", ext.SpanKindRPCServer).Finish() + + testutils.AssertCounterMetrics(t, metrics, + testutils.ExpectedMetric{ + Name: "jaeger-rpc.requests", + Tags: map[string]string{"component": "jaeger", "endpoint": "test", "error": "false"}, + Value: 1, + }, + ) +} + +func TestBaggageRestrictionsConfig(t *testing.T) { + m := metrics.NewLocalFactory(0) + c := Configuration{ + BaggageRestrictions: &BaggageRestrictionsConfig{ + HostPort: "not:1929213", + RefreshInterval: time.Minute, + }, + } + _, closer, err := c.New( + "test", + Metrics(m), + ) + require.NoError(t, err) + defer closer.Close() + + metricName := "jaeger.baggage_restrictions_updates" + metricTags := map[string]string{"result": "err"} + key := metrics.GetKey(metricName, metricTags, "|", "=") + for i := 0; i < 100; i++ { + // wait until the async initialization call is complete + counters, _ := m.Snapshot() + if _, ok := counters[key]; ok { + break + } + time.Sleep(time.Millisecond) + } + + testutils.AssertCounterMetrics(t, m, + testutils.ExpectedMetric{ + Name: metricName, + Tags: metricTags, + Value: 1, + }, + ) +} + +func TestConfigWithGen128Bit(t *testing.T) { + c := Configuration{ + Sampler: &SamplerConfig{ + Type: "const", + Param: 1, + }, + RPCMetrics: true, + } + tracer, closer, err := c.New("test", Gen128Bit(true)) + require.NoError(t, err) + defer closer.Close() + + span := tracer.StartSpan("test") + defer span.Finish() + traceID := span.Context().(jaeger.SpanContext).TraceID() + require.True(t, traceID.High != 0) + require.True(t, traceID.Low != 0) +} + +func TestConfigWithInjector(t *testing.T) { + c := Configuration{} + tracer, closer, err := c.New("test", Injector("custom.format", fakeInjector{})) + require.NoError(t, err) + defer closer.Close() + + span := tracer.StartSpan("test") + defer span.Finish() + + err = tracer.Inject(span.Context(), "unknown.format", nil) + require.Error(t, err) + + err = tracer.Inject(span.Context(), "custom.format", nil) + require.NoError(t, err) +} + +func TestConfigWithExtractor(t *testing.T) { + c := Configuration{} + tracer, closer, err := c.New("test", Extractor("custom.format", fakeExtractor{})) + require.NoError(t, err) + defer closer.Close() + + _, err = tracer.Extract("unknown.format", nil) + require.Error(t, err) + + _, err = tracer.Extract("custom.format", nil) + require.NoError(t, err) +} + +func TestConfigWithSampler(t *testing.T) { + c := Configuration{} + sampler := &fakeSampler{} + + tracer, closer, err := c.New("test", Sampler(sampler)) + require.NoError(t, err) + defer closer.Close() + + span := tracer.StartSpan("test") + defer span.Finish() + + traceID := span.Context().(jaeger.SpanContext).TraceID() + require.Equal(t, traceID, sampler.lastTraceID) + require.Equal(t, "test", sampler.lastOperation) +} + +func TestNewTracer(t *testing.T) { + cfg := &Configuration{ServiceName: "my-service"} + _, closer, err := cfg.NewTracer(Metrics(metrics.NullFactory), Logger(log.NullLogger)) + defer closer.Close() + + assert.NoError(t, err) +} + +func TestNewTracerWithoutServiceName(t *testing.T) { + cfg := &Configuration{} + _, _, err := cfg.NewTracer(Metrics(metrics.NullFactory), Logger(log.NullLogger)) + assert.Contains(t, err.Error(), "no service name provided") +} + +func TestParseTags(t *testing.T) { + os.Setenv("existing", "not-default") + tags := "key=value,k1=${nonExisting:default}, k2=${withSpace:default},k3=${existing:default}" + ts := parseTags(tags) + assert.Equal(t, 4, len(ts)) + + assert.Equal(t, "key", ts[0].Key) + assert.Equal(t, "value", ts[0].Value) + + assert.Equal(t, "k1", ts[1].Key) + assert.Equal(t, "default", ts[1].Value) + + assert.Equal(t, "k2", ts[2].Key) + assert.Equal(t, "default", ts[2].Value) + + assert.Equal(t, "k3", ts[3].Key) + assert.Equal(t, "not-default", ts[3].Value) + + os.Unsetenv("existing") +} + +func TestServiceNameViaConfiguration(t *testing.T) { + cfg := &Configuration{ServiceName: "my-service"} + _, closer, err := cfg.New("") + assert.NoError(t, err) + defer closer.Close() +} + +func TestTracerTags(t *testing.T) { + cfg := &Configuration{Tags: []opentracing.Tag{{Key: "test", Value: 123}}} + _, closer, err := cfg.New("test-service") + assert.NoError(t, err) + defer closer.Close() +} + +func TestThrottlerDefaultConfig(t *testing.T) { + cfg := &Configuration{ + Throttler: &ThrottlerConfig{}, + } + _, closer, err := cfg.New("test-service") + assert.NoError(t, err) + defer closer.Close() +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/config/example_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/config/example_test.go new file mode 100644 index 00000000..a81853ad --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/config/example_test.go @@ -0,0 +1,128 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package config_test + +import ( + "log" + "os" + + opentracing "github.com/opentracing/opentracing-go" + "github.com/uber/jaeger-lib/metrics" + + "github.com/uber/jaeger-client-go" + jaegercfg "github.com/uber/jaeger-client-go/config" + jaegerlog "github.com/uber/jaeger-client-go/log" +) + +func ExampleConfiguration_InitGlobalTracer_testing() { + // Sample configuration for testing. Use constant sampling to sample every trace + // and enable LogSpan to log every span via configured Logger. + cfg := jaegercfg.Configuration{ + Sampler: &jaegercfg.SamplerConfig{ + Type: jaeger.SamplerTypeConst, + Param: 1, + }, + Reporter: &jaegercfg.ReporterConfig{ + LogSpans: true, + }, + } + + // Example logger and metrics factory. Use github.com/uber/jaeger-client-go/log + // and github.com/uber/jaeger-lib/metrics respectively to bind to real logging and metrics + // frameworks. + jLogger := jaegerlog.StdLogger + jMetricsFactory := metrics.NullFactory + + // Initialize tracer with a logger and a metrics factory + closer, err := cfg.InitGlobalTracer( + "serviceName", + jaegercfg.Logger(jLogger), + jaegercfg.Metrics(jMetricsFactory), + ) + if err != nil { + log.Printf("Could not initialize jaeger tracer: %s", err.Error()) + return + } + defer closer.Close() + + // continue main() +} + +func ExampleConfiguration_InitGlobalTracer_production() { + // Recommended configuration for production. + cfg := jaegercfg.Configuration{} + + // Example logger and metrics factory. Use github.com/uber/jaeger-client-go/log + // and github.com/uber/jaeger-lib/metrics respectively to bind to real logging and metrics + // frameworks. + jLogger := jaegerlog.StdLogger + jMetricsFactory := metrics.NullFactory + + // Initialize tracer with a logger and a metrics factory + closer, err := cfg.InitGlobalTracer( + "serviceName", + jaegercfg.Logger(jLogger), + jaegercfg.Metrics(jMetricsFactory), + ) + if err != nil { + log.Printf("Could not initialize jaeger tracer: %s", err.Error()) + return + } + defer closer.Close() + + // continue main() +} + +func ExampleFromEnv() { + cfg, err := jaegercfg.FromEnv() + if err != nil { + // parsing errors might happen here, such as when we get a string where we expect a number + log.Printf("Could not parse Jaeger env vars: %s", err.Error()) + return + } + + tracer, closer, err := cfg.NewTracer() + if err != nil { + log.Printf("Could not initialize jaeger tracer: %s", err.Error()) + return + } + defer closer.Close() + + opentracing.SetGlobalTracer(tracer) + // continue main() +} + +func ExampleFromEnv_override() { + os.Setenv("JAEGER_SERVICE_NAME", "not-effective") + + cfg, err := jaegercfg.FromEnv() + if err != nil { + // parsing errors might happen here, such as when we get a string where we expect a number + log.Printf("Could not parse Jaeger env vars: %s", err.Error()) + return + } + + cfg.ServiceName = "this-will-be-the-service-name" + + tracer, closer, err := cfg.NewTracer() + if err != nil { + log.Printf("Could not initialize jaeger tracer: %s", err.Error()) + return + } + defer closer.Close() + + opentracing.SetGlobalTracer(tracer) + // continue main() +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/config/options.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/config/options.go new file mode 100644 index 00000000..d14f1f8a --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/config/options.go @@ -0,0 +1,148 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package config + +import ( + opentracing "github.com/opentracing/opentracing-go" + "github.com/uber/jaeger-lib/metrics" + + "github.com/uber/jaeger-client-go" +) + +// Option is a function that sets some option on the client. +type Option func(c *Options) + +// Options control behavior of the client. +type Options struct { + metrics metrics.Factory + logger jaeger.Logger + reporter jaeger.Reporter + sampler jaeger.Sampler + contribObservers []jaeger.ContribObserver + observers []jaeger.Observer + gen128Bit bool + zipkinSharedRPCSpan bool + maxTagValueLength int + tags []opentracing.Tag + injectors map[interface{}]jaeger.Injector + extractors map[interface{}]jaeger.Extractor +} + +// Metrics creates an Option that initializes Metrics in the tracer, +// which is used to emit statistics about spans. +func Metrics(factory metrics.Factory) Option { + return func(c *Options) { + c.metrics = factory + } +} + +// Logger can be provided to log Reporter errors, as well as to log spans +// if Reporter.LogSpans is set to true. +func Logger(logger jaeger.Logger) Option { + return func(c *Options) { + c.logger = logger + } +} + +// Reporter can be provided explicitly to override the configuration. +// Useful for testing, e.g. by passing InMemoryReporter. +func Reporter(reporter jaeger.Reporter) Option { + return func(c *Options) { + c.reporter = reporter + } +} + +// Sampler can be provided explicitly to override the configuration. +func Sampler(sampler jaeger.Sampler) Option { + return func(c *Options) { + c.sampler = sampler + } +} + +// Observer can be registered with the Tracer to receive notifications about new Spans. +func Observer(observer jaeger.Observer) Option { + return func(c *Options) { + c.observers = append(c.observers, observer) + } +} + +// ContribObserver can be registered with the Tracer to recieve notifications +// about new spans. +func ContribObserver(observer jaeger.ContribObserver) Option { + return func(c *Options) { + c.contribObservers = append(c.contribObservers, observer) + } +} + +// Gen128Bit specifies whether to generate 128bit trace IDs. +func Gen128Bit(gen128Bit bool) Option { + return func(c *Options) { + c.gen128Bit = gen128Bit + } +} + +// ZipkinSharedRPCSpan creates an option that enables sharing span ID between client +// and server spans a la zipkin. If false, client and server spans will be assigned +// different IDs. +func ZipkinSharedRPCSpan(zipkinSharedRPCSpan bool) Option { + return func(c *Options) { + c.zipkinSharedRPCSpan = zipkinSharedRPCSpan + } +} + +// MaxTagValueLength can be provided to override the default max tag value length. +func MaxTagValueLength(maxTagValueLength int) Option { + return func(c *Options) { + c.maxTagValueLength = maxTagValueLength + } +} + +// Tag creates an option that adds a tracer-level tag. +func Tag(key string, value interface{}) Option { + return func(c *Options) { + c.tags = append(c.tags, opentracing.Tag{Key: key, Value: value}) + } +} + +// Injector registers an Injector with the given format. +func Injector(format interface{}, injector jaeger.Injector) Option { + return func(c *Options) { + c.injectors[format] = injector + } +} + +// Extractor registers an Extractor with the given format. +func Extractor(format interface{}, extractor jaeger.Extractor) Option { + return func(c *Options) { + c.extractors[format] = extractor + } +} + +func applyOptions(options ...Option) Options { + opts := Options{ + injectors: make(map[interface{}]jaeger.Injector), + extractors: make(map[interface{}]jaeger.Extractor), + } + for _, option := range options { + option(&opts) + } + if opts.metrics == nil { + opts.metrics = metrics.NullFactory + } + if opts.logger == nil { + opts.logger = jaeger.NullLogger + } + return opts +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/config/options_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/config/options_test.go new file mode 100644 index 00000000..f4d79e8d --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/config/options_test.go @@ -0,0 +1,107 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package config + +import ( + "testing" + + opentracing "github.com/opentracing/opentracing-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber/jaeger-lib/metrics" + + "github.com/uber/jaeger-client-go" +) + +func TestApplyOptions(t *testing.T) { + metricsFactory := metrics.NewLocalFactory(0) + observer := fakeObserver{} + sampler := &fakeSampler{} + contribObserver := fakeContribObserver{} + opts := applyOptions( + Metrics(metricsFactory), + Logger(jaeger.StdLogger), + Observer(observer), + Sampler(sampler), + ContribObserver(contribObserver), + Gen128Bit(true), + ZipkinSharedRPCSpan(true), + MaxTagValueLength(1024), + ) + assert.Equal(t, jaeger.StdLogger, opts.logger) + assert.Equal(t, sampler, opts.sampler) + assert.Equal(t, metricsFactory, opts.metrics) + assert.Equal(t, []jaeger.Observer{observer}, opts.observers) + assert.Equal(t, []jaeger.ContribObserver{contribObserver}, opts.contribObservers) + assert.True(t, opts.gen128Bit) + assert.True(t, opts.zipkinSharedRPCSpan) + assert.Equal(t, 1024, opts.maxTagValueLength) +} + +func TestTraceTagOption(t *testing.T) { + c := Configuration{} + tracer, closer, err := c.New("test-service", Tag("tag-key", "tag-value")) + require.NoError(t, err) + defer closer.Close() + assert.Equal(t, opentracing.Tag{Key: "tag-key", Value: "tag-value"}, tracer.(*jaeger.Tracer).Tags()[0]) +} + +func TestApplyOptionsDefaults(t *testing.T) { + opts := applyOptions() + assert.Equal(t, jaeger.NullLogger, opts.logger) + assert.Equal(t, metrics.NullFactory, opts.metrics) +} + +type fakeSampler struct { + lastTraceID jaeger.TraceID + lastOperation string +} + +func (s *fakeSampler) IsSampled(id jaeger.TraceID, operation string) (sampled bool, tags []jaeger.Tag) { + s.lastTraceID = id + s.lastOperation = operation + + return true, []jaeger.Tag{} +} + +func (s *fakeSampler) Close() {} + +func (s *fakeSampler) Equal(other jaeger.Sampler) bool { + return false +} + +type fakeObserver struct{} + +func (fakeObserver) OnStartSpan(operationName string, options opentracing.StartSpanOptions) jaeger.SpanObserver { + return nil +} + +type fakeContribObserver struct{} + +func (fakeContribObserver) OnStartSpan(span opentracing.Span, operationName string, options opentracing.StartSpanOptions) (jaeger.ContribSpanObserver, bool) { + return nil, false +} + +type fakeInjector struct{} + +func (fakeInjector) Inject(ctx jaeger.SpanContext, carrier interface{}) error { + return nil +} + +type fakeExtractor struct{} + +func (fakeExtractor) Extract(carrier interface{}) (jaeger.SpanContext, error) { + return jaeger.SpanContext{}, nil +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/constants.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/constants.go new file mode 100644 index 00000000..b5368ff3 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/constants.go @@ -0,0 +1,88 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +const ( + // JaegerClientVersion is the version of the client library reported as Span tag. + JaegerClientVersion = "Go-2.15.0-dev" + + // JaegerClientVersionTagKey is the name of the tag used to report client version. + JaegerClientVersionTagKey = "jaeger.version" + + // JaegerDebugHeader is the name of HTTP header or a TextMap carrier key which, + // if found in the carrier, forces the trace to be sampled as "debug" trace. + // The value of the header is recorded as the tag on the root span, so that the + // trace can be found in the UI using this value as a correlation ID. + JaegerDebugHeader = "jaeger-debug-id" + + // JaegerBaggageHeader is the name of the HTTP header that is used to submit baggage. + // It differs from TraceBaggageHeaderPrefix in that it can be used only in cases where + // a root span does not exist. + JaegerBaggageHeader = "jaeger-baggage" + + // TracerHostnameTagKey used to report host name of the process. + TracerHostnameTagKey = "hostname" + + // TracerIPTagKey used to report ip of the process. + TracerIPTagKey = "ip" + + // TracerUUIDTagKey used to report UUID of the client process. + TracerUUIDTagKey = "client-uuid" + + // SamplerTypeTagKey reports which sampler was used on the root span. + SamplerTypeTagKey = "sampler.type" + + // SamplerParamTagKey reports the parameter of the sampler, like sampling probability. + SamplerParamTagKey = "sampler.param" + + // TraceContextHeaderName is the http header name used to propagate tracing context. + // This must be in lower-case to avoid mismatches when decoding incoming headers. + TraceContextHeaderName = "uber-trace-id" + + // TracerStateHeaderName is deprecated. + // Deprecated: use TraceContextHeaderName + TracerStateHeaderName = TraceContextHeaderName + + // TraceBaggageHeaderPrefix is the prefix for http headers used to propagate baggage. + // This must be in lower-case to avoid mismatches when decoding incoming headers. + TraceBaggageHeaderPrefix = "uberctx-" + + // SamplerTypeConst is the type of sampler that always makes the same decision. + SamplerTypeConst = "const" + + // SamplerTypeRemote is the type of sampler that polls Jaeger agent for sampling strategy. + SamplerTypeRemote = "remote" + + // SamplerTypeProbabilistic is the type of sampler that samples traces + // with a certain fixed probability. + SamplerTypeProbabilistic = "probabilistic" + + // SamplerTypeRateLimiting is the type of sampler that samples + // only up to a fixed number of traces per second. + SamplerTypeRateLimiting = "ratelimiting" + + // SamplerTypeLowerBound is the type of sampler that samples + // at least a fixed number of traces per second. + SamplerTypeLowerBound = "lowerbound" + + // DefaultUDPSpanServerHost is the default host to send the spans to, via UDP + DefaultUDPSpanServerHost = "localhost" + + // DefaultUDPSpanServerPort is the default port to send the spans to, via UDP + DefaultUDPSpanServerPort = 6831 + + // DefaultMaxTagValueLength is the default max length of byte array or string allowed in the tag value. + DefaultMaxTagValueLength = 256 +) diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/constants_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/constants_test.go new file mode 100644 index 00000000..0beae191 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/constants_test.go @@ -0,0 +1,29 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "strings" + "testing" +) + +func TestHeaderConstants(t *testing.T) { + if TraceContextHeaderName != strings.ToLower(TraceContextHeaderName) { + t.Errorf("TraceContextHeaderName is not lower-case: %+v", TraceContextHeaderName) + } + if TraceBaggageHeaderPrefix != strings.ToLower(TraceBaggageHeaderPrefix) { + t.Errorf("TraceBaggageHeaderPrefix is not lower-case: %+v", TraceBaggageHeaderPrefix) + } +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/context.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/context.go new file mode 100644 index 00000000..8b06173d --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/context.go @@ -0,0 +1,258 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "errors" + "fmt" + "strconv" + "strings" +) + +const ( + flagSampled = byte(1) + flagDebug = byte(2) +) + +var ( + errEmptyTracerStateString = errors.New("Cannot convert empty string to tracer state") + errMalformedTracerStateString = errors.New("String does not match tracer state format") + + emptyContext = SpanContext{} +) + +// TraceID represents unique 128bit identifier of a trace +type TraceID struct { + High, Low uint64 +} + +// SpanID represents unique 64bit identifier of a span +type SpanID uint64 + +// SpanContext represents propagated span identity and state +type SpanContext struct { + // traceID represents globally unique ID of the trace. + // Usually generated as a random number. + traceID TraceID + + // spanID represents span ID that must be unique within its trace, + // but does not have to be globally unique. + spanID SpanID + + // parentID refers to the ID of the parent span. + // Should be 0 if the current span is a root span. + parentID SpanID + + // flags is a bitmap containing such bits as 'sampled' and 'debug'. + flags byte + + // Distributed Context baggage. The is a snapshot in time. + baggage map[string]string + + // debugID can be set to some correlation ID when the context is being + // extracted from a TextMap carrier. + // + // See JaegerDebugHeader in constants.go + debugID string +} + +// ForeachBaggageItem implements ForeachBaggageItem() of opentracing.SpanContext +func (c SpanContext) ForeachBaggageItem(handler func(k, v string) bool) { + for k, v := range c.baggage { + if !handler(k, v) { + break + } + } +} + +// IsSampled returns whether this trace was chosen for permanent storage +// by the sampling mechanism of the tracer. +func (c SpanContext) IsSampled() bool { + return (c.flags & flagSampled) == flagSampled +} + +// IsDebug indicates whether sampling was explicitly requested by the service. +func (c SpanContext) IsDebug() bool { + return (c.flags & flagDebug) == flagDebug +} + +// IsValid indicates whether this context actually represents a valid trace. +func (c SpanContext) IsValid() bool { + return c.traceID.IsValid() && c.spanID != 0 +} + +func (c SpanContext) String() string { + if c.traceID.High == 0 { + return fmt.Sprintf("%x:%x:%x:%x", c.traceID.Low, uint64(c.spanID), uint64(c.parentID), c.flags) + } + return fmt.Sprintf("%x%016x:%x:%x:%x", c.traceID.High, c.traceID.Low, uint64(c.spanID), uint64(c.parentID), c.flags) +} + +// ContextFromString reconstructs the Context encoded in a string +func ContextFromString(value string) (SpanContext, error) { + var context SpanContext + if value == "" { + return emptyContext, errEmptyTracerStateString + } + parts := strings.Split(value, ":") + if len(parts) != 4 { + return emptyContext, errMalformedTracerStateString + } + var err error + if context.traceID, err = TraceIDFromString(parts[0]); err != nil { + return emptyContext, err + } + if context.spanID, err = SpanIDFromString(parts[1]); err != nil { + return emptyContext, err + } + if context.parentID, err = SpanIDFromString(parts[2]); err != nil { + return emptyContext, err + } + flags, err := strconv.ParseUint(parts[3], 10, 8) + if err != nil { + return emptyContext, err + } + context.flags = byte(flags) + return context, nil +} + +// TraceID returns the trace ID of this span context +func (c SpanContext) TraceID() TraceID { + return c.traceID +} + +// SpanID returns the span ID of this span context +func (c SpanContext) SpanID() SpanID { + return c.spanID +} + +// ParentID returns the parent span ID of this span context +func (c SpanContext) ParentID() SpanID { + return c.parentID +} + +// NewSpanContext creates a new instance of SpanContext +func NewSpanContext(traceID TraceID, spanID, parentID SpanID, sampled bool, baggage map[string]string) SpanContext { + flags := byte(0) + if sampled { + flags = flagSampled + } + return SpanContext{ + traceID: traceID, + spanID: spanID, + parentID: parentID, + flags: flags, + baggage: baggage} +} + +// CopyFrom copies data from ctx into this context, including span identity and baggage. +// TODO This is only used by interop.go. Remove once TChannel Go supports OpenTracing. +func (c *SpanContext) CopyFrom(ctx *SpanContext) { + c.traceID = ctx.traceID + c.spanID = ctx.spanID + c.parentID = ctx.parentID + c.flags = ctx.flags + if l := len(ctx.baggage); l > 0 { + c.baggage = make(map[string]string, l) + for k, v := range ctx.baggage { + c.baggage[k] = v + } + } else { + c.baggage = nil + } +} + +// WithBaggageItem creates a new context with an extra baggage item. +func (c SpanContext) WithBaggageItem(key, value string) SpanContext { + var newBaggage map[string]string + if c.baggage == nil { + newBaggage = map[string]string{key: value} + } else { + newBaggage = make(map[string]string, len(c.baggage)+1) + for k, v := range c.baggage { + newBaggage[k] = v + } + newBaggage[key] = value + } + // Use positional parameters so the compiler will help catch new fields. + return SpanContext{c.traceID, c.spanID, c.parentID, c.flags, newBaggage, ""} +} + +// isDebugIDContainerOnly returns true when the instance of the context is only +// used to return the debug/correlation ID from extract() method. This happens +// in the situation when "jaeger-debug-id" header is passed in the carrier to +// the extract() method, but the request otherwise has no span context in it. +// Previously this would've returned opentracing.ErrSpanContextNotFound from the +// extract method, but now it returns a dummy context with only debugID filled in. +// +// See JaegerDebugHeader in constants.go +// See textMapPropagator#Extract +func (c *SpanContext) isDebugIDContainerOnly() bool { + return !c.traceID.IsValid() && c.debugID != "" +} + +// ------- TraceID ------- + +func (t TraceID) String() string { + if t.High == 0 { + return fmt.Sprintf("%x", t.Low) + } + return fmt.Sprintf("%x%016x", t.High, t.Low) +} + +// TraceIDFromString creates a TraceID from a hexadecimal string +func TraceIDFromString(s string) (TraceID, error) { + var hi, lo uint64 + var err error + if len(s) > 32 { + return TraceID{}, fmt.Errorf("TraceID cannot be longer than 32 hex characters: %s", s) + } else if len(s) > 16 { + hiLen := len(s) - 16 + if hi, err = strconv.ParseUint(s[0:hiLen], 16, 64); err != nil { + return TraceID{}, err + } + if lo, err = strconv.ParseUint(s[hiLen:], 16, 64); err != nil { + return TraceID{}, err + } + } else { + if lo, err = strconv.ParseUint(s, 16, 64); err != nil { + return TraceID{}, err + } + } + return TraceID{High: hi, Low: lo}, nil +} + +// IsValid checks if the trace ID is valid, i.e. not zero. +func (t TraceID) IsValid() bool { + return t.High != 0 || t.Low != 0 +} + +// ------- SpanID ------- + +func (s SpanID) String() string { + return fmt.Sprintf("%x", uint64(s)) +} + +// SpanIDFromString creates a SpanID from a hexadecimal string +func SpanIDFromString(s string) (SpanID, error) { + if len(s) > 16 { + return SpanID(0), fmt.Errorf("SpanID cannot be longer than 16 hex characters: %s", s) + } + id, err := strconv.ParseUint(s, 16, 64) + if err != nil { + return SpanID(0), err + } + return SpanID(id), nil +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/context_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/context_test.go new file mode 100644 index 00000000..34dfc745 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/context_test.go @@ -0,0 +1,110 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestContextFromString(t *testing.T) { + var err error + _, err = ContextFromString("") + assert.Error(t, err) + _, err = ContextFromString("abcd") + assert.Error(t, err) + _, err = ContextFromString("x:1:1:1") + assert.Error(t, err) + _, err = ContextFromString("1:x:1:1") + assert.Error(t, err) + _, err = ContextFromString("1:1:x:1") + assert.Error(t, err) + _, err = ContextFromString("1:1:1:x") + assert.Error(t, err) + _, err = ContextFromString("1:1:1:x") + assert.Error(t, err) + _, err = ContextFromString("01234567890123456789012345678901234:1:1:1") + assert.Error(t, err) + _, err = ContextFromString("01234567890123456789012345678901:1:1:1") + assert.NoError(t, err) + _, err = ContextFromString("01234_67890123456789012345678901:1:1:1") + assert.Error(t, err) + _, err = ContextFromString("0123456789012345678901_345678901:1:1:1") + assert.Error(t, err) + _, err = ContextFromString("1:0123456789012345:1:1") + assert.NoError(t, err) + _, err = ContextFromString("1:01234567890123456:1:1") + assert.Error(t, err) + ctx, err := ContextFromString("10000000000000001:1:1:1") + assert.NoError(t, err) + assert.EqualValues(t, TraceID{High: 1, Low: 1}, ctx.traceID) + ctx, err = ContextFromString("1:1:1:1") + assert.NoError(t, err) + assert.EqualValues(t, TraceID{Low: 1}, ctx.traceID) + assert.EqualValues(t, 1, ctx.spanID) + assert.EqualValues(t, 1, ctx.parentID) + assert.EqualValues(t, 1, ctx.flags) + ctx = NewSpanContext(TraceID{Low: 1}, 1, 1, true, nil) + assert.EqualValues(t, TraceID{Low: 1}, ctx.traceID) + assert.EqualValues(t, 1, ctx.spanID) + assert.EqualValues(t, 1, ctx.parentID) + assert.EqualValues(t, 1, ctx.flags) + assert.Equal(t, "ff", SpanID(255).String()) + assert.Equal(t, "ff", TraceID{Low: 255}.String()) + assert.Equal(t, "ff00000000000000ff", TraceID{High: 255, Low: 255}.String()) + ctx = NewSpanContext(TraceID{High: 255, Low: 255}, SpanID(1), SpanID(1), false, nil) + assert.Equal(t, "ff00000000000000ff:1:1:0", ctx.String()) +} + +func TestSpanContext_WithBaggageItem(t *testing.T) { + var ctx SpanContext + ctx = ctx.WithBaggageItem("some-KEY", "Some-Value") + assert.Equal(t, map[string]string{"some-KEY": "Some-Value"}, ctx.baggage) + ctx = ctx.WithBaggageItem("some-KEY", "Some-Other-Value") + assert.Equal(t, map[string]string{"some-KEY": "Some-Other-Value"}, ctx.baggage) +} + +func TestSpanContext_SampledDebug(t *testing.T) { + ctx, err := ContextFromString("1:1:1:1") + require.NoError(t, err) + assert.True(t, ctx.IsSampled()) + assert.False(t, ctx.IsDebug()) + + ctx, err = ContextFromString("1:1:1:3") + require.NoError(t, err) + assert.True(t, ctx.IsSampled()) + assert.True(t, ctx.IsDebug()) + + ctx, err = ContextFromString("1:1:1:0") + require.NoError(t, err) + assert.False(t, ctx.IsSampled()) + assert.False(t, ctx.IsDebug()) +} + +func TestSpanContext_CopyFrom(t *testing.T) { + ctx, err := ContextFromString("1:1:1:1") + require.NoError(t, err) + ctx2 := SpanContext{} + ctx2.CopyFrom(&ctx) + assert.Equal(t, ctx, ctx2) + // with baggage + ctx = ctx.WithBaggageItem("x", "y") + ctx2 = SpanContext{} + ctx2.CopyFrom(&ctx) + assert.Equal(t, ctx, ctx2) + assert.Equal(t, "y", ctx2.baggage["x"]) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/contrib_observer.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/contrib_observer.go new file mode 100644 index 00000000..4ce1881f --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/contrib_observer.go @@ -0,0 +1,56 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + opentracing "github.com/opentracing/opentracing-go" +) + +// ContribObserver can be registered with the Tracer to receive notifications +// about new Spans. Modelled after github.com/opentracing-contrib/go-observer. +type ContribObserver interface { + // Create and return a span observer. Called when a span starts. + // If the Observer is not interested in the given span, it must return (nil, false). + // E.g : + // func StartSpan(opName string, opts ...opentracing.StartSpanOption) { + // var sp opentracing.Span + // sso := opentracing.StartSpanOptions{} + // if spanObserver, ok := Observer.OnStartSpan(span, opName, sso); ok { + // // we have a valid SpanObserver + // } + // ... + // } + OnStartSpan(sp opentracing.Span, operationName string, options opentracing.StartSpanOptions) (ContribSpanObserver, bool) +} + +// ContribSpanObserver is created by the Observer and receives notifications +// about other Span events. This interface is meant to match +// github.com/opentracing-contrib/go-observer, via duck typing, without +// directly importing the go-observer package. +type ContribSpanObserver interface { + OnSetOperationName(operationName string) + OnSetTag(key string, value interface{}) + OnFinish(options opentracing.FinishOptions) +} + +// wrapper observer for the old observers (see observer.go) +type oldObserver struct { + obs Observer +} + +func (o *oldObserver) OnStartSpan(sp opentracing.Span, operationName string, options opentracing.StartSpanOptions) (ContribSpanObserver, bool) { + spanObserver := o.obs.OnStartSpan(operationName, options) + return spanObserver, spanObserver != nil +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/.gitignore b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/.gitignore new file mode 100644 index 00000000..b2cfbae2 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/.gitignore @@ -0,0 +1 @@ +/crossdock diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/Dockerfile b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/Dockerfile new file mode 100644 index 00000000..e11e977f --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/Dockerfile @@ -0,0 +1,10 @@ +FROM scratch + +ADD crossdock / + +ENV AGENT_HOST_PORT=jaeger-agent:5775 +ENV SAMPLING_SERVER_URL=http://test_driver:5778/sampling + +EXPOSE 8080-8082 + +CMD ["/crossdock"] diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/README.md b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/README.md new file mode 100644 index 00000000..ead16859 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/README.md @@ -0,0 +1,78 @@ +# Crossdock-based Integration Test Suite + +This package implements integration test suite for testing +interoperability between different Jaeger client libraries. + +## Actors + +There are five actors participating in any given test case, +the crossdock driver itself, a Client, and three Servers, S1-S3. + +### Driver + +The crossdock driver reads axis and test definitions from the YAML file, +generates permutations based on values listed for each axis, and +makes an HTTP request to the Client, passing it the selected value +for each axis. + +### Client + +Client runs as part of the `jaeger-client/go` image and orchestrates +the actual test case with the servers S1-S3. The incoming request +from the driver is expected to have parameters defined in +[client/constants.go](client/constants.go), which specify + + 1. The type of test to execute (only `trace` is currently supported) + 1. Whether the trace should be sampled or not + 1. For each of the servers S1-S3: + * the name of the server (same as docker image name, same as host name) + * the transport to send request to that server (only http supported) + * the type of client to use (e.g. in Python, `urllib2` vs. `requests`) + +The Client translates the parameters into a "call tree" instruction set, +and calls S1, which in turn calls S2 with the sub-set of instructions, +and so on. Upon receiving the response from S1, the Client validates the +response against the conditions of the test, and returns a summary result +to the crossdock driver, indicating a success of a failure of the test. +For the `trace` test type, the success conditions are that at all levels +the observed tracing spans have the same trace ID, the same sampling flag +equal to the input `sampled` parameter, and the same value of a baggage +item. The baggage item value is randomly selected by the client at the +start of each test. + +### Servers + +Servers represent examples of business services with Jaeger tracing enabled. +Servers must be implemented for each supported language, and potentially +multiple times for a given language depending on the framework used to build +the service, such as Flask vs. Tornado in Python. Each implementation of the +server may act as any of the S1-S3 servers in the test. Each server must +implement the `TracedService` interface from +[thrift/tracetest.thrift](thrift/tracetest.thrift): + + service TracedService { + TraceResponse startTrace(1: StartTraceRequest request) + TraceResponse joinTrace(1: JoinTraceRequest request) + } + + * In `startTrace` the server is supposed to ignore any trace it may have + received via inbound request and start a brand new trace, with the + sampling flag set appropriately, using `sampling.priority` tag, + see [Go server implementation](server/trace.go) for example. + * In `joinTrace` the server is supposed to respect the trace in the + inbound request and propagate it to the outbound downstream request. + +The response from the server contains the information about the current +tracing span it has observed (or started), including trace ID, sampling +flag, and the value of a baggage item. For S1 and S2 the response also +includes the response of the downstream server. + +## Running the tests + +The intended setup is that every commit to master branch of each of the client +libraries results in a build of a new docker image (or images, e.g. in Python). +When a new pull request is tested against one of the libraries, it will build +a new image from the modified version of the library, and use the existing +images for the other languages. The `docker-compose.yaml` file refers to those +images by name. + diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/client/client.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/client/client.go new file mode 100644 index 00000000..e2dcbfb9 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/client/client.go @@ -0,0 +1,107 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package client + +import ( + "fmt" + "net" + "net/http" + "sync" + + "github.com/crossdock/crossdock-go" + + "github.com/uber/jaeger-client-go/crossdock/common" +) + +// Client is a controller for the tests +type Client struct { + ClientHostPort string + ServerPortHTTP string + listener net.Listener + hostMapper func(service string) string +} + +// Start begins a blocking Crossdock client +func (c *Client) Start() error { + if err := c.Listen(); err != nil { + return err + } + return c.Serve() +} + +// AsyncStart begins a Crossdock client in the background, +// but does not return until it started serving. +func (c *Client) AsyncStart() error { + if err := c.Listen(); err != nil { + return err + } + var started sync.WaitGroup + started.Add(1) + go func() { + started.Done() + c.Serve() + }() + started.Wait() + return nil +} + +// Listen initializes the server +func (c *Client) Listen() error { + c.setDefaultPort(&c.ClientHostPort, ":"+common.DefaultClientPortHTTP) + c.setDefaultPort(&c.ServerPortHTTP, common.DefaultServerPortHTTP) + + behaviors := crossdock.Behaviors{ + behaviorTrace: c.trace, + } + + http.Handle("/", crossdock.Handler(behaviors, true)) + + listener, err := net.Listen("tcp", c.ClientHostPort) + if err != nil { + return err + } + c.listener = listener + c.ClientHostPort = listener.Addr().String() + return nil +} + +// Serve starts service crossdock traffic. This is a blocking call. +func (c *Client) Serve() error { + return http.Serve(c.listener, nil) +} + +// Close stops the client +func (c *Client) Close() error { + return c.listener.Close() +} + +// URL returns a URL that the client can be accessed on +func (c *Client) URL() string { + return fmt.Sprintf("http://%s/", c.ClientHostPort) +} + +func (c *Client) setDefaultPort(port *string, defaultPort string) { + if *port == "" { + *port = defaultPort + } +} + +func (c *Client) mapServiceToHost(service string) string { + mapper := c.hostMapper + if mapper == nil { + return service + } + return mapper(service) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/client/client_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/client/client_test.go new file mode 100644 index 00000000..be168337 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/client/client_test.go @@ -0,0 +1,106 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package client + +import ( + "net/url" + "testing" + + "github.com/crossdock/crossdock-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/crossdock/common" + "github.com/uber/jaeger-client-go/crossdock/log" + "github.com/uber/jaeger-client-go/crossdock/server" + jlog "github.com/uber/jaeger-client-go/log" +) + +func TestCrossdock(t *testing.T) { + log.Enabled = false // enable when debugging tests + log.Printf("Starting crossdock test") + + var reporter jaeger.Reporter + if log.Enabled { + reporter = jaeger.NewLoggingReporter(jlog.StdLogger) + } else { + reporter = jaeger.NewNullReporter() + } + + tracer, tCloser := jaeger.NewTracer( + "crossdock", + jaeger.NewConstSampler(false), + reporter) + defer tCloser.Close() + + s := &server.Server{ + HostPortHTTP: "127.0.0.1:0", + Tracer: tracer, + } + err := s.Start() + require.NoError(t, err) + defer s.Close() + + c := &Client{ + ClientHostPort: "127.0.0.1:0", + ServerPortHTTP: s.GetPortHTTP(), + hostMapper: func(server string) string { return "localhost" }, + } + err = c.AsyncStart() + require.NoError(t, err) + defer c.Close() + + crossdock.Wait(t, s.URL(), 10) + crossdock.Wait(t, c.URL(), 10) + + behaviors := []struct { + name string + axes map[string][]string + }{ + { + name: behaviorTrace, + axes: map[string][]string{ + server1NameParam: {common.DefaultTracerServiceName}, + sampledParam: {"true", "false"}, + server2NameParam: {common.DefaultTracerServiceName}, + server2TransportParam: {transportHTTP, transportDummy}, + server3NameParam: {common.DefaultTracerServiceName}, + server3TransportParam: {transportHTTP}, + }, + }, + } + + for _, bb := range behaviors { + for _, entry := range crossdock.Combinations(bb.axes) { + entryArgs := url.Values{} + for k, v := range entry { + entryArgs.Set(k, v) + } + // test via real HTTP call + crossdock.Call(t, c.URL(), bb.name, entryArgs) + } + } +} + +func TestHostMapper(t *testing.T) { + c := &Client{ + ClientHostPort: "127.0.0.1:0", + ServerPortHTTP: "8080", + } + assert.Equal(t, "go", c.mapServiceToHost("go")) + c.hostMapper = func(server string) string { return "localhost" } + assert.Equal(t, "localhost", c.mapServiceToHost("go")) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/client/constants.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/client/constants.go new file mode 100644 index 00000000..927efaa2 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/client/constants.go @@ -0,0 +1,42 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package client + +// Different parameter keys and values used by the system +const ( + // S1 instructions + sampledParam = "sampled" + server1NameParam = "s1name" + // S1->S2 instructions + server2NameParam = "s2name" + server2TransportParam = "s2transport" + // S2->S3 instructions + server3NameParam = "s3name" + server3TransportParam = "s3transport" + + transportHTTP = "http" + transportDummy = "dummy" + + behaviorTrace = "trace" + + // RoleS1 is the name of the role for server S1 + RoleS1 = "S1" + + // RoleS2 is the name of the role for server S2 + RoleS2 = "S2" + + // RoleS3 is the name of the role for server S3 + RoleS3 = "S3" +) diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/client/trace.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/client/trace.go new file mode 100644 index 00000000..f1ebb63e --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/client/trace.go @@ -0,0 +1,163 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package client + +import ( + "context" + "fmt" + "time" + + "github.com/crossdock/crossdock-go" + + "github.com/uber/jaeger-client-go/crossdock/common" + "github.com/uber/jaeger-client-go/crossdock/log" + "github.com/uber/jaeger-client-go/crossdock/thrift/tracetest" + "github.com/uber/jaeger-client-go/utils" +) + +func (c *Client) trace(t crossdock.T) { + sampled := str2bool(t.Param(sampledParam)) + baggage := randomBaggage() + + level1 := tracetest.NewStartTraceRequest() + level1.ServerRole = RoleS1 + level1.Sampled = sampled + level1.Baggage = baggage + server1 := t.Param(server1NameParam) + + level2 := tracetest.NewDownstream() + level2.ServiceName = t.Param(server2NameParam) + level2.ServerRole = RoleS2 + level2.Host = c.mapServiceToHost(level2.ServiceName) + level2.Port = c.transport2port(t.Param(server2TransportParam)) + level2.Transport = transport2transport(t.Param(server2TransportParam)) + level1.Downstream = level2 + + level3 := tracetest.NewDownstream() + level3.ServiceName = t.Param(server3NameParam) + level3.ServerRole = RoleS3 + level3.Host = c.mapServiceToHost(level3.ServiceName) + level3.Port = c.transport2port(t.Param(server3TransportParam)) + level3.Transport = transport2transport(t.Param(server3TransportParam)) + level2.Downstream = level3 + + server1host := c.mapServiceToHost(server1) + url := fmt.Sprintf("http://%s:%s/start_trace", server1host, c.ServerPortHTTP) + resp, err := common.PostJSON(context.Background(), url, level1) + if err != nil { + t.Errorf(err.Error()) + return + } + + for r := resp; r != nil; r = r.Downstream { + if r.NotImplementedError != "" { + t.Skipf(r.NotImplementedError) + log.Printf("SKIP: %s", r.NotImplementedError) + return + } + } + + traceID := resp.Span.TraceId + if traceID == "" { + t.Errorf("Trace ID is empty in S1(%s)", server1) + return + } + + success := validateTrace(t, level1.Downstream, resp, server1, 1, traceID, sampled, baggage) + if success { + t.Successf("trace checks out") + log.Printf("PASS") + } +} + +func validateTrace( + t crossdock.T, + target *tracetest.Downstream, + resp *tracetest.TraceResponse, + service string, + level int, + traceID string, + sampled bool, + baggage string) bool { + + success := true + if traceID != resp.Span.TraceId { + t.Errorf("Trace ID mismatch in S%d(%s): expected %s, received %s", + level, service, traceID, resp.Span.TraceId) + success = false + } + if baggage != resp.Span.Baggage { + t.Errorf("Baggage mismatch in S%d(%s): expected %s, received %s", + level, service, baggage, resp.Span.Baggage) + success = false + } + if sampled != resp.Span.Sampled { + t.Errorf("Sampled mismatch in S%d(%s): expected %t, received %t", + level, service, sampled, resp.Span.Sampled) + success = false + } + if target != nil { + if resp.Downstream == nil { + t.Errorf("Missing downstream in S%d(%s)", level, service) + success = false + } else { + success = validateTrace(t, target.Downstream, resp.Downstream, + target.Host, level+1, traceID, sampled, baggage) && success + } + } else if resp.Downstream != nil { + t.Errorf("Unexpected downstream in S%d(%s)", level, service) + success = false + } + return success +} + +func randomBaggage() string { + r := utils.NewRand(time.Now().UnixNano()) + n := uint64(r.Int63()) + return fmt.Sprintf("%x", n) +} + +func str2bool(v string) bool { + switch v { + case "true": + return true + case "false": + return false + default: + panic(v + " is not a Boolean") + } +} + +func (c *Client) transport2port(v string) string { + switch v { + case transportHTTP: + return c.ServerPortHTTP + case transportDummy: + return "9999" + default: + panic("Unknown protocol " + v) + } +} + +func transport2transport(v string) tracetest.Transport { + switch v { + case transportHTTP: + return tracetest.Transport_HTTP + case transportDummy: + return tracetest.Transport_DUMMY + default: + panic("Unknown protocol " + v) + } +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/common/constants.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/common/constants.go new file mode 100644 index 00000000..20322f7e --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/common/constants.go @@ -0,0 +1,26 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package common + +const ( + // DefaultClientPortHTTP is the port where the client (controller) runs + DefaultClientPortHTTP = "8080" + + // DefaultServerPortHTTP is the port where HTTP server runs + DefaultServerPortHTTP = "8081" + + // DefaultTracerServiceName is the service name used by the tracer + DefaultTracerServiceName = "crossdock-go" +) diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/common/json.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/common/json.go new file mode 100644 index 00000000..2d7713b4 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/common/json.go @@ -0,0 +1,73 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package common + +import ( + "bytes" + "context" + "encoding/json" + "net/http" + + "github.com/uber/jaeger-client-go/crossdock/thrift/tracetest" + "github.com/uber/jaeger-client-go/utils" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" +) + +// PostJSON sends a POST request to `url` with body containing JSON-serialized `req`. +// It injects tracing span into the headers (if found in the context). +// It returns parsed TraceResponse, or error. +func PostJSON(ctx context.Context, url string, req interface{}) (*tracetest.TraceResponse, error) { + data, err := json.Marshal(req) + if err != nil { + return nil, err + } + + httpReq, err := http.NewRequest("POST", url, bytes.NewBuffer(data)) + if err != nil { + return nil, err + } + httpReq.Header.Set("Content-Type", "application/json") + + span, err := injectSpan(ctx, httpReq) + if span != nil { + defer span.Finish() + } + if err != nil { + return nil, err + } + + resp, err := http.DefaultClient.Do(httpReq) + if err != nil { + return nil, err + } + + var result tracetest.TraceResponse + err = utils.ReadJSON(resp, &result) + return &result, err +} + +func injectSpan(ctx context.Context, req *http.Request) (opentracing.Span, error) { + span := opentracing.SpanFromContext(ctx) + if span == nil { + return nil, nil + } + span = span.Tracer().StartSpan("post", opentracing.ChildOf(span.Context())) + ext.SpanKindRPCClient.Set(span) + c := opentracing.HTTPHeadersCarrier(req.Header) + err := span.Tracer().Inject(span.Context(), opentracing.HTTPHeaders, c) + return span, err +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/docker-compose.yml b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/docker-compose.yml new file mode 100644 index 00000000..ae11c07a --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/docker-compose.yml @@ -0,0 +1,59 @@ +version: '2' + +services: + crossdock: + image: crossdock/crossdock + links: + - test_driver + - go + - java + - python + + environment: + - WAIT_FOR=test_driver,go,java,python + - WAIT_FOR_TIMEOUT=60s + + - CALL_TIMEOUT=60s + + - AXIS_CLIENT=go + - AXIS_S1NAME=go,java,python + - AXIS_SAMPLED=true,false + - AXIS_S2NAME=go,java,python + - AXIS_S2TRANSPORT=http + - AXIS_S3NAME=go,java,python + - AXIS_S3TRANSPORT=http + + - BEHAVIOR_TRACE=client,s1name,sampled,s2name,s2transport,s3name,s3transport + + - AXIS_TESTDRIVER=test_driver + - AXIS_SERVICES=go + + - BEHAVIOR_ENDTOEND=testdriver,services + + - REPORT=compact + go: + build: . + ports: + - "8080-8082" + + java: + image: jaegertracing/xdock-java + ports: + - "8080-8082" +# Udp sender needs to know agent's address + depends_on: + - jaeger-agent + + python: + image: jaegertracing/xdock-py + ports: + - "8080:8082" + + test_driver: + image: jaegertracing/test-driver + depends_on: + - jaeger-query + - jaeger-collector + - jaeger-agent + ports: + - "8080" diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/endtoend/handler.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/endtoend/handler.go new file mode 100644 index 00000000..ca89ef4d --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/endtoend/handler.go @@ -0,0 +1,156 @@ +// Copyright (c) 2017-2018 Uber Technologies, Inc. +// +// 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. + +package endtoend + +import ( + "encoding/json" + "fmt" + "net/http" + "sync" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/config" + "github.com/uber/jaeger-client-go/crossdock/common" + "github.com/uber/jaeger-client-go/crossdock/log" +) + +const ( + defaultSamplerType = jaeger.SamplerTypeRemote +) + +var ( + endToEndConfig = config.Configuration{ + Disabled: false, + Sampler: &config.SamplerConfig{ + Type: defaultSamplerType, + Param: 1.0, + SamplingRefreshInterval: 5 * time.Second, + }, + Reporter: &config.ReporterConfig{ + BufferFlushInterval: time.Second, + }, + Throttler: &config.ThrottlerConfig{ + SynchronousInitialization: true, + HostPort: "agent:5778", + }, + } +) + +/*Handler handles creating traces from a http request. + * + * json: { + * "type": "remote", + * "operation": "operationName", + * "count": 2, + * "tags": { + * "key": "value" + * } + * } + * + * Given the above json payload, the handler will use a tracer with the RemotelyControlledSampler + * to create 2 traces for "operationName" operation with the tags: {"key":"value"}. These traces + * are reported to the agent with the hostname "test_driver". + */ +type Handler struct { + sync.RWMutex + + tracers map[string]opentracing.Tracer + agentHostPort string + samplingServerURL string +} + +type traceRequest struct { + Type string `json:"type"` + Operation string `json:"operation"` + Tags map[string]string `json:"tags"` + Count int `json:"count"` +} + +// NewHandler returns a Handler. +func NewHandler(agentHostPort string, samplingServerURL string) *Handler { + return &Handler{ + agentHostPort: agentHostPort, + samplingServerURL: samplingServerURL, + tracers: make(map[string]opentracing.Tracer), + } +} + +// init initializes the handler with a tracer +func (h *Handler) init(cfg config.Configuration) error { + if cfg.Sampler != nil && cfg.Sampler.SamplingServerURL == "" { + cfg.Sampler.SamplingServerURL = h.samplingServerURL + } + if cfg.Reporter != nil && cfg.Reporter.LocalAgentHostPort == "" { + cfg.Reporter.LocalAgentHostPort = h.agentHostPort + } + tracer, _, err := cfg.New(common.DefaultTracerServiceName) + if err != nil { + return err + } + h.tracers[cfg.Sampler.Type] = tracer + return nil +} + +func (h *Handler) getTracer(samplerType string) opentracing.Tracer { + if samplerType == "" { + samplerType = defaultSamplerType + } + h.Lock() + defer h.Unlock() + tracer, ok := h.tracers[samplerType] + if !ok { + endToEndConfig.Sampler.Type = samplerType + if err := h.init(endToEndConfig); err != nil { + log.Printf("Failed to create tracer: %s", err.Error()) + return nil + } + tracer, _ = h.tracers[samplerType] + } + return tracer +} + +// GenerateTraces creates traces given the parameters in the request. +func (h *Handler) GenerateTraces(w http.ResponseWriter, r *http.Request) { + decoder := json.NewDecoder(r.Body) + var req traceRequest + if err := decoder.Decode(&req); err != nil { + http.Error(w, fmt.Sprintf("JSON payload is invalid: %s", err.Error()), http.StatusBadRequest) + return + } + tracer := h.getTracer(req.Type) + if tracer == nil { + http.Error(w, "Tracer is not initialized", http.StatusInternalServerError) + return + } + generateTraces(tracer, &req) +} + +func generateTraces(tracer opentracing.Tracer, r *traceRequest) { + for i := 0; i < r.Count; i++ { + span := tracer.StartSpan(r.Operation) + for k, v := range r.Tags { + if k == string(ext.SamplingPriority) && v == "1" { + span.SetTag(k, uint16(1)) + } else { + span.SetTag(k, v) + } + } + span.Finish() + } +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/endtoend/handler_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/endtoend/handler_test.go new file mode 100644 index 00000000..08e15ed8 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/endtoend/handler_test.go @@ -0,0 +1,154 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package endtoend + +import ( + "bytes" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/stretchr/testify/assert" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/config" + "github.com/uber/jaeger-client-go/log" +) + +var ( + testOperation = "testOperation" + testService = "testService" + + testConfig = config.Configuration{ + Disabled: false, + Sampler: &config.SamplerConfig{ + Type: jaeger.SamplerTypeRemote, + Param: 1.0, + SamplingServerURL: "http://localhost:5778/sampling", + }, + Reporter: &config.ReporterConfig{ + BufferFlushInterval: time.Second, + LocalAgentHostPort: "localhost:5775", + }, + } + + badConfig = config.Configuration{ + Disabled: false, + Sampler: &config.SamplerConfig{ + Type: "INVALID_TYPE", + }, + } + + testTraceRequest = traceRequest{ + Type: jaeger.SamplerTypeConst, + Operation: testOperation, + Tags: map[string]string{ + "key": "value", + }, + Count: 2, + } + + testInvalidJSON = `bad_json` + + testTraceJSONRequest = ` + { + "type": "const", + "operation": "testOperation", + "tags": { + "key": "value" + }, + "count": 2 + } + ` + + testInvalidTypeJSONRequest = ` + { + "type": "INVALID_TYPE", + "operation": "testOperation", + "tags": { + "key": "value" + }, + "count": 2 + } + ` +) + +func newInMemoryTracer() (opentracing.Tracer, *jaeger.InMemoryReporter) { + inMemoryReporter := jaeger.NewInMemoryReporter() + tracer, _ := jaeger.NewTracer( + testService, + jaeger.NewConstSampler(true), + inMemoryReporter, + jaeger.TracerOptions.Metrics(jaeger.NewNullMetrics()), + jaeger.TracerOptions.Logger(log.NullLogger)) + return tracer, inMemoryReporter +} + +func TestInit(t *testing.T) { + handler := NewHandler("", "") + err := handler.init(testConfig) + assert.NoError(t, err) +} + +func TestInitBadConfig(t *testing.T) { + handler := NewHandler("", "") + err := handler.init(badConfig) + assert.Error(t, err) +} + +func TestGetTracer(t *testing.T) { + iTracer, _ := newInMemoryTracer() + handler := &Handler{tracers: map[string]opentracing.Tracer{jaeger.SamplerTypeConst: iTracer}} + tracer := handler.getTracer(jaeger.SamplerTypeConst) + assert.Equal(t, iTracer, tracer) +} + +func TestGetTracerError(t *testing.T) { + handler := NewHandler("", "") + tracer := handler.getTracer("INVALID_TYPE") + assert.Nil(t, tracer) +} + +func TestGenerateTraces(t *testing.T) { + tracer, _ := newInMemoryTracer() + + tests := []struct { + expectedCode int + json string + handler *Handler + }{ + {http.StatusOK, testTraceJSONRequest, &Handler{tracers: map[string]opentracing.Tracer{jaeger.SamplerTypeConst: tracer}}}, + {http.StatusBadRequest, testInvalidJSON, NewHandler("", "")}, + {http.StatusInternalServerError, testInvalidTypeJSONRequest, NewHandler("", "")}, // Tracer failed to initialize + } + + for _, test := range tests { + req, err := http.NewRequest("POST", "/create_spans", bytes.NewBuffer([]byte(test.json))) + assert.NoError(t, err, "Failed to initialize request") + recorder := httptest.NewRecorder() + handlerFunc := http.HandlerFunc(test.handler.GenerateTraces) + handlerFunc.ServeHTTP(recorder, req) + + assert.Equal(t, test.expectedCode, recorder.Code) + } +} + +func TestGenerateTracesInternal(t *testing.T) { + tracer, reporter := newInMemoryTracer() + generateTraces(tracer, &testTraceRequest) + assert.Equal(t, 2, reporter.SpansSubmitted()) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/log/logger.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/log/logger.go new file mode 100644 index 00000000..c8258580 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/log/logger.go @@ -0,0 +1,29 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package log + +import ( + real_log "log" +) + +// Enabled controls logging from crossdock tests. It is enabled in main.go, but off in unit tests. +var Enabled bool + +// Printf delegates to log.Printf if Enabled == true +func Printf(msg string, args ...interface{}) { + if Enabled { + real_log.Printf(msg, args...) + } +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/main.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/main.go new file mode 100644 index 00000000..4bd05b0e --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/main.go @@ -0,0 +1,64 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package main + +import ( + "io" + "os" + + "github.com/opentracing/opentracing-go" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/crossdock/client" + "github.com/uber/jaeger-client-go/crossdock/common" + "github.com/uber/jaeger-client-go/crossdock/log" + "github.com/uber/jaeger-client-go/crossdock/server" + jlog "github.com/uber/jaeger-client-go/log" +) + +func main() { + log.Enabled = true + + agentHostPort, ok := os.LookupEnv("AGENT_HOST_PORT") + if !ok { + jlog.StdLogger.Error("env AGENT_HOST_PORT is not specified!") + } + sServerURL, ok := os.LookupEnv("SAMPLING_SERVER_URL") + if !ok { + jlog.StdLogger.Error("env SAMPLING_SERVER_URL is not specified!") + } + + tracer, tCloser := initTracer() + defer tCloser.Close() + + s := &server.Server{Tracer: tracer, SamplingServerURL: sServerURL, AgentHostPort: agentHostPort} + if err := s.Start(); err != nil { + panic(err.Error()) + } else { + defer s.Close() + } + client := &client.Client{} + if err := client.Start(); err != nil { + panic(err.Error()) + } +} + +func initTracer() (opentracing.Tracer, io.Closer) { + t, c := jaeger.NewTracer( + common.DefaultTracerServiceName, + jaeger.NewConstSampler(false), + jaeger.NewLoggingReporter(jlog.StdLogger)) + return t, c +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/rules.mk b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/rules.mk new file mode 100644 index 00000000..02822c45 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/rules.mk @@ -0,0 +1,32 @@ +XDOCK_YAML=crossdock/docker-compose.yml + +JAEGER_COMPOSE_URL=https://raw.githubusercontent.com/jaegertracing/jaeger/master/docker-compose/jaeger-docker-compose.yml +XDOCK_JAEGER_YAML=crossdock/jaeger-docker-compose.yml + +.PHONY: crossdock-linux-bin +crossdock-linux-bin: + CGO_ENABLED=0 GOOS=linux time go build -a -installsuffix cgo -o crossdock/crossdock ./crossdock + +.PHONY: crossdock +crossdock: crossdock-linux-bin crossdock-download-jaeger + docker-compose -f $(XDOCK_YAML) -f $(XDOCK_JAEGER_YAML) kill go + docker-compose -f $(XDOCK_YAML) -f $(XDOCK_JAEGER_YAML) rm -f go + docker-compose -f $(XDOCK_YAML) -f $(XDOCK_JAEGER_YAML) build go + docker-compose -f $(XDOCK_YAML) -f $(XDOCK_JAEGER_YAML) run crossdock 2>&1 | tee run-crossdock.log + grep 'Tests passed!' run-crossdock.log + +.PHONY: crossdock-fresh +crossdock-fresh: crossdock-linux-bin crossdock-download-jaeger + docker-compose -f $(XDOCK_JAEGER_YAML) -f $(XDOCK_YAML) kill + docker-compose -f $(XDOCK_JAEGER_YAML) -f $(XDOCK_YAML) rm --force + docker-compose -f $(XDOCK_JAEGER_YAML) -f $(XDOCK_YAML) pull + docker-compose -f $(XDOCK_JAEGER_YAML) -f $(XDOCK_YAML) build + docker-compose -f $(XDOCK_JAEGER_YAML) -f $(XDOCK_YAML) run crossdock + +.PHONE: crossdock-logs +crossdock-logs: crossdock-download-jaeger + docker-compose -f $(XDOCK_JAEGER_YAML) -f $(XDOCK_YAML) logs + +.PHONY: crossdock-download-jaeger +crossdock-download-jaeger: + curl -o $(XDOCK_JAEGER_YAML) $(JAEGER_COMPOSE_URL) diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/server/constants.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/server/constants.go new file mode 100644 index 00000000..57b0c19e --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/server/constants.go @@ -0,0 +1,25 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package server + +import "errors" + +// BaggageKey is the key used to pass baggage item +const BaggageKey = "crossdock-baggage-key" + +var ( + errNoSpanObserved = errors.New("no span found in Context") + errUnrecognizedProtocol = errors.New("unrecognized protocol for downstream call") +) diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/server/server.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/server/server.go new file mode 100644 index 00000000..95d185b6 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/server/server.go @@ -0,0 +1,150 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package server + +import ( + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net" + "net/http" + "strings" + "sync" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + + "github.com/uber/jaeger-client-go/crossdock/common" + "github.com/uber/jaeger-client-go/crossdock/endtoend" + "github.com/uber/jaeger-client-go/crossdock/log" + "github.com/uber/jaeger-client-go/crossdock/thrift/tracetest" +) + +// Server implements S1-S3 servers +type Server struct { + HostPortHTTP string + AgentHostPort string + SamplingServerURL string + Tracer opentracing.Tracer + listener net.Listener + eHandler *endtoend.Handler +} + +// Start starts the test server called by the Client and other upstream servers. +func (s *Server) Start() error { + if s.HostPortHTTP == "" { + s.HostPortHTTP = ":" + common.DefaultServerPortHTTP + } + + s.eHandler = endtoend.NewHandler(s.AgentHostPort, s.SamplingServerURL) + + mux := http.NewServeMux() + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { return }) // health check + mux.HandleFunc("/start_trace", func(w http.ResponseWriter, r *http.Request) { + s.handleJSON(w, r, func() interface{} { + return tracetest.NewStartTraceRequest() + }, func(ctx context.Context, req interface{}) (interface{}, error) { + return s.doStartTrace(req.(*tracetest.StartTraceRequest)) + }) + }) + mux.HandleFunc("/join_trace", func(w http.ResponseWriter, r *http.Request) { + s.handleJSON(w, r, func() interface{} { + return tracetest.NewJoinTraceRequest() + }, func(ctx context.Context, req interface{}) (interface{}, error) { + return s.doJoinTrace(ctx, req.(*tracetest.JoinTraceRequest)) + }) + }) + mux.HandleFunc("/create_traces", s.eHandler.GenerateTraces) + + listener, err := net.Listen("tcp", s.HostPortHTTP) + if err != nil { + return err + } + s.listener = listener + s.HostPortHTTP = listener.Addr().String() + + var started sync.WaitGroup + started.Add(1) + go func() { + started.Done() + http.Serve(listener, mux) + }() + started.Wait() + log.Printf("Started http server at %s\n", s.HostPortHTTP) + return nil +} + +// URL returns URL of the HTTP server +func (s *Server) URL() string { + return fmt.Sprintf("http://%s/", s.HostPortHTTP) +} + +// Close stops the server +func (s *Server) Close() error { + return s.listener.Close() +} + +// GetPortHTTP returns the network port the server listens to. +func (s *Server) GetPortHTTP() string { + hostPort := s.HostPortHTTP + hostPortSplit := strings.Split(hostPort, ":") + port := hostPortSplit[len(hostPortSplit)-1] + return port +} + +func (s *Server) handleJSON( + w http.ResponseWriter, + r *http.Request, + newReq func() interface{}, + handle func(ctx context.Context, req interface{}) (interface{}, error), +) { + spanCtx, err := s.Tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header)) + if err != nil && err != opentracing.ErrSpanContextNotFound { + http.Error(w, fmt.Sprintf("Cannot read request body: %+v", err), http.StatusBadRequest) + return + } + span := s.Tracer.StartSpan("post", ext.RPCServerOption(spanCtx)) + ctx := opentracing.ContextWithSpan(context.Background(), span) + defer span.Finish() + + body, err := ioutil.ReadAll(r.Body) + if err != nil { + http.Error(w, fmt.Sprintf("Cannot read request body: %+v", err), http.StatusInternalServerError) + return + } + log.Printf("Server request: %s", string(body)) + req := newReq() + if err := json.Unmarshal(body, req); err != nil { + http.Error(w, fmt.Sprintf("Cannot parse request JSON: %+v. body=[%s]", err, string(body)), http.StatusBadRequest) + return + } + resp, err := handle(ctx, req) + if err != nil { + log.Printf("Handle error: %s", err.Error()) + http.Error(w, fmt.Sprintf("Execution error: %+v", err), http.StatusInternalServerError) + return + } + json, err := json.Marshal(resp) + if err != nil { + http.Error(w, fmt.Sprintf("Cannot marshall response to JSON: %+v", err), http.StatusInternalServerError) + return + } + log.Printf("Server response: %s", string(json)) + w.Header().Add("Content-Type", "application/json") + if _, err := w.Write(json); err != nil { + return + } +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/server/server_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/server/server_test.go new file mode 100644 index 00000000..20d59e47 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/server/server_test.go @@ -0,0 +1,87 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package server + +import ( + "context" + "fmt" + "testing" + + "github.com/opentracing/opentracing-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/crossdock/common" + "github.com/uber/jaeger-client-go/crossdock/log" + "github.com/uber/jaeger-client-go/crossdock/thrift/tracetest" +) + +func TestServerJSON(t *testing.T) { + tracer, tCloser := jaeger.NewTracer( + "crossdock", + jaeger.NewConstSampler(false), + jaeger.NewNullReporter()) + defer tCloser.Close() + + s := &Server{HostPortHTTP: "127.0.0.1:0", Tracer: tracer} + err := s.Start() + require.NoError(t, err) + defer s.Close() + + req := tracetest.NewStartTraceRequest() + req.Sampled = true + req.Baggage = "Zoidberg" + req.Downstream = &tracetest.Downstream{ + ServiceName: "go", + Host: "localhost", + Port: s.GetPortHTTP(), + Transport: tracetest.Transport_HTTP, + Downstream: &tracetest.Downstream{ + ServiceName: "go", + Host: "localhost", + Port: s.GetPortHTTP(), + Transport: tracetest.Transport_HTTP, + }, + } + + url := fmt.Sprintf("http://%s/start_trace", s.HostPortHTTP) + result, err := common.PostJSON(context.Background(), url, req) + + require.NoError(t, err) + log.Printf("response=%+v", &result) +} + +func TestObserveSpan(t *testing.T) { + tracer, tCloser := jaeger.NewTracer( + "crossdock", + jaeger.NewConstSampler(true), + jaeger.NewNullReporter()) + defer tCloser.Close() + + _, err := observeSpan(context.Background(), tracer) + assert.Error(t, err) + + span := tracer.StartSpan("hi") + span.SetBaggageItem(BaggageKey, "xyz") + ctx := opentracing.ContextWithSpan(context.Background(), span) + + s, err := observeSpan(ctx, tracer) + assert.NoError(t, err) + assert.True(t, s.Sampled) + traceID := span.Context().(jaeger.SpanContext).TraceID().String() + assert.Equal(t, traceID, s.TraceId) + assert.Equal(t, "xyz", s.Baggage) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/server/trace.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/server/trace.go new file mode 100644 index 00000000..6a170185 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/server/trace.go @@ -0,0 +1,99 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package server + +import ( + "context" + "fmt" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/crossdock/common" + "github.com/uber/jaeger-client-go/crossdock/log" + "github.com/uber/jaeger-client-go/crossdock/thrift/tracetest" +) + +func (s *Server) doStartTrace(req *tracetest.StartTraceRequest) (*tracetest.TraceResponse, error) { + span := s.Tracer.StartSpan(req.ServerRole) + if req.Sampled { + ext.SamplingPriority.Set(span, 1) + } + span.SetBaggageItem(BaggageKey, req.Baggage) + defer span.Finish() + + ctx := opentracing.ContextWithSpan(context.Background(), span) + + return s.prepareResponse(ctx, req.ServerRole, req.Downstream) +} + +func (s *Server) doJoinTrace(ctx context.Context, req *tracetest.JoinTraceRequest) (*tracetest.TraceResponse, error) { + return s.prepareResponse(ctx, req.ServerRole, req.Downstream) +} + +func (s *Server) prepareResponse(ctx context.Context, role string, reqDwn *tracetest.Downstream) (*tracetest.TraceResponse, error) { + observedSpan, err := observeSpan(ctx, s.Tracer) + if err != nil { + return nil, err + } + + resp := tracetest.NewTraceResponse() + resp.Span = observedSpan + + if reqDwn != nil { + downstreamResp, err := s.callDownstream(ctx, role, reqDwn) + if err != nil { + return nil, err + } + resp.Downstream = downstreamResp + } + + return resp, nil +} + +func (s *Server) callDownstream(ctx context.Context, role string, downstream *tracetest.Downstream) (*tracetest.TraceResponse, error) { + switch downstream.Transport { + case tracetest.Transport_HTTP: + return s.callDownstreamHTTP(ctx, downstream) + case tracetest.Transport_DUMMY: + return &tracetest.TraceResponse{NotImplementedError: "DUMMY transport not implemented"}, nil + default: + return nil, errUnrecognizedProtocol + } +} + +func (s *Server) callDownstreamHTTP(ctx context.Context, target *tracetest.Downstream) (*tracetest.TraceResponse, error) { + req := &tracetest.JoinTraceRequest{ + ServerRole: target.ServerRole, + Downstream: target.Downstream, + } + url := fmt.Sprintf("http://%s:%s/join_trace", target.Host, target.Port) + log.Printf("Calling downstream service '%s' at %s", target.ServiceName, url) + return common.PostJSON(ctx, url, req) +} + +func observeSpan(ctx context.Context, tracer opentracing.Tracer) (*tracetest.ObservedSpan, error) { + span := opentracing.SpanFromContext(ctx) + if span == nil { + return nil, errNoSpanObserved + } + sc := span.Context().(jaeger.SpanContext) + observedSpan := tracetest.NewObservedSpan() + observedSpan.TraceId = sc.TraceID().String() + observedSpan.Sampled = sc.IsSampled() + observedSpan.Baggage = span.BaggageItem(BaggageKey) + return observedSpan, nil +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/thrift/.nocover b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/thrift/.nocover new file mode 100644 index 00000000..e69de29b diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/thrift/tracetest/constants.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/thrift/tracetest/constants.go new file mode 100644 index 00000000..eea45c1f --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/thrift/tracetest/constants.go @@ -0,0 +1,18 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package tracetest + +import ( + "bytes" + "fmt" + "github.com/uber/jaeger-client-go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +func init() { +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/thrift/tracetest/tracedservice.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/thrift/tracetest/tracedservice.go new file mode 100644 index 00000000..52d5b63f --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/thrift/tracetest/tracedservice.go @@ -0,0 +1,747 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package tracetest + +import ( + "bytes" + "fmt" + "github.com/uber/jaeger-client-go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +type TracedService interface { + // Parameters: + // - Request + StartTrace(request *StartTraceRequest) (r *TraceResponse, err error) + // Parameters: + // - Request + JoinTrace(request *JoinTraceRequest) (r *TraceResponse, err error) +} + +type TracedServiceClient struct { + Transport thrift.TTransport + ProtocolFactory thrift.TProtocolFactory + InputProtocol thrift.TProtocol + OutputProtocol thrift.TProtocol + SeqId int32 +} + +func NewTracedServiceClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *TracedServiceClient { + return &TracedServiceClient{Transport: t, + ProtocolFactory: f, + InputProtocol: f.GetProtocol(t), + OutputProtocol: f.GetProtocol(t), + SeqId: 0, + } +} + +func NewTracedServiceClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *TracedServiceClient { + return &TracedServiceClient{Transport: t, + ProtocolFactory: nil, + InputProtocol: iprot, + OutputProtocol: oprot, + SeqId: 0, + } +} + +// Parameters: +// - Request +func (p *TracedServiceClient) StartTrace(request *StartTraceRequest) (r *TraceResponse, err error) { + if err = p.sendStartTrace(request); err != nil { + return + } + return p.recvStartTrace() +} + +func (p *TracedServiceClient) sendStartTrace(request *StartTraceRequest) (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("startTrace", thrift.CALL, p.SeqId); err != nil { + return + } + args := TracedServiceStartTraceArgs{ + Request: request, + } + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *TracedServiceClient) recvStartTrace() (value *TraceResponse, err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "startTrace" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "startTrace failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "startTrace failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error0 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error1 error + error1, err = error0.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error1 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "startTrace failed: invalid message type") + return + } + result := TracedServiceStartTraceResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + value = result.GetSuccess() + return +} + +// Parameters: +// - Request +func (p *TracedServiceClient) JoinTrace(request *JoinTraceRequest) (r *TraceResponse, err error) { + if err = p.sendJoinTrace(request); err != nil { + return + } + return p.recvJoinTrace() +} + +func (p *TracedServiceClient) sendJoinTrace(request *JoinTraceRequest) (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("joinTrace", thrift.CALL, p.SeqId); err != nil { + return + } + args := TracedServiceJoinTraceArgs{ + Request: request, + } + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *TracedServiceClient) recvJoinTrace() (value *TraceResponse, err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "joinTrace" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "joinTrace failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "joinTrace failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error2 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error3 error + error3, err = error2.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error3 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "joinTrace failed: invalid message type") + return + } + result := TracedServiceJoinTraceResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + value = result.GetSuccess() + return +} + +type TracedServiceProcessor struct { + processorMap map[string]thrift.TProcessorFunction + handler TracedService +} + +func (p *TracedServiceProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { + p.processorMap[key] = processor +} + +func (p *TracedServiceProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { + processor, ok = p.processorMap[key] + return processor, ok +} + +func (p *TracedServiceProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { + return p.processorMap +} + +func NewTracedServiceProcessor(handler TracedService) *TracedServiceProcessor { + + self4 := &TracedServiceProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} + self4.processorMap["startTrace"] = &tracedServiceProcessorStartTrace{handler: handler} + self4.processorMap["joinTrace"] = &tracedServiceProcessorJoinTrace{handler: handler} + return self4 +} + +func (p *TracedServiceProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + name, _, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return false, err + } + if processor, ok := p.GetProcessorFunction(name); ok { + return processor.Process(seqId, iprot, oprot) + } + iprot.Skip(thrift.STRUCT) + iprot.ReadMessageEnd() + x5 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) + oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) + x5.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, x5 + +} + +type tracedServiceProcessorStartTrace struct { + handler TracedService +} + +func (p *tracedServiceProcessorStartTrace) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := TracedServiceStartTraceArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("startTrace", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := TracedServiceStartTraceResult{} + var retval *TraceResponse + var err2 error + if retval, err2 = p.handler.StartTrace(args.Request); err2 != nil { + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing startTrace: "+err2.Error()) + oprot.WriteMessageBegin("startTrace", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } else { + result.Success = retval + } + if err2 = oprot.WriteMessageBegin("startTrace", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +type tracedServiceProcessorJoinTrace struct { + handler TracedService +} + +func (p *tracedServiceProcessorJoinTrace) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := TracedServiceJoinTraceArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("joinTrace", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := TracedServiceJoinTraceResult{} + var retval *TraceResponse + var err2 error + if retval, err2 = p.handler.JoinTrace(args.Request); err2 != nil { + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing joinTrace: "+err2.Error()) + oprot.WriteMessageBegin("joinTrace", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } else { + result.Success = retval + } + if err2 = oprot.WriteMessageBegin("joinTrace", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +// HELPER FUNCTIONS AND STRUCTURES + +// Attributes: +// - Request +type TracedServiceStartTraceArgs struct { + Request *StartTraceRequest `thrift:"request,1" json:"request"` +} + +func NewTracedServiceStartTraceArgs() *TracedServiceStartTraceArgs { + return &TracedServiceStartTraceArgs{} +} + +var TracedServiceStartTraceArgs_Request_DEFAULT *StartTraceRequest + +func (p *TracedServiceStartTraceArgs) GetRequest() *StartTraceRequest { + if !p.IsSetRequest() { + return TracedServiceStartTraceArgs_Request_DEFAULT + } + return p.Request +} +func (p *TracedServiceStartTraceArgs) IsSetRequest() bool { + return p.Request != nil +} + +func (p *TracedServiceStartTraceArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *TracedServiceStartTraceArgs) readField1(iprot thrift.TProtocol) error { + p.Request = &StartTraceRequest{} + if err := p.Request.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Request), err) + } + return nil +} + +func (p *TracedServiceStartTraceArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("startTrace_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *TracedServiceStartTraceArgs) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("request", thrift.STRUCT, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:request: ", p), err) + } + if err := p.Request.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Request), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:request: ", p), err) + } + return err +} + +func (p *TracedServiceStartTraceArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("TracedServiceStartTraceArgs(%+v)", *p) +} + +// Attributes: +// - Success +type TracedServiceStartTraceResult struct { + Success *TraceResponse `thrift:"success,0" json:"success,omitempty"` +} + +func NewTracedServiceStartTraceResult() *TracedServiceStartTraceResult { + return &TracedServiceStartTraceResult{} +} + +var TracedServiceStartTraceResult_Success_DEFAULT *TraceResponse + +func (p *TracedServiceStartTraceResult) GetSuccess() *TraceResponse { + if !p.IsSetSuccess() { + return TracedServiceStartTraceResult_Success_DEFAULT + } + return p.Success +} +func (p *TracedServiceStartTraceResult) IsSetSuccess() bool { + return p.Success != nil +} + +func (p *TracedServiceStartTraceResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 0: + if err := p.readField0(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *TracedServiceStartTraceResult) readField0(iprot thrift.TProtocol) error { + p.Success = &TraceResponse{} + if err := p.Success.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Success), err) + } + return nil +} + +func (p *TracedServiceStartTraceResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("startTrace_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField0(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *TracedServiceStartTraceResult) writeField0(oprot thrift.TProtocol) (err error) { + if p.IsSetSuccess() { + if err := oprot.WriteFieldBegin("success", thrift.STRUCT, 0); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) + } + if err := p.Success.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Success), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) + } + } + return err +} + +func (p *TracedServiceStartTraceResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("TracedServiceStartTraceResult(%+v)", *p) +} + +// Attributes: +// - Request +type TracedServiceJoinTraceArgs struct { + Request *JoinTraceRequest `thrift:"request,1" json:"request"` +} + +func NewTracedServiceJoinTraceArgs() *TracedServiceJoinTraceArgs { + return &TracedServiceJoinTraceArgs{} +} + +var TracedServiceJoinTraceArgs_Request_DEFAULT *JoinTraceRequest + +func (p *TracedServiceJoinTraceArgs) GetRequest() *JoinTraceRequest { + if !p.IsSetRequest() { + return TracedServiceJoinTraceArgs_Request_DEFAULT + } + return p.Request +} +func (p *TracedServiceJoinTraceArgs) IsSetRequest() bool { + return p.Request != nil +} + +func (p *TracedServiceJoinTraceArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *TracedServiceJoinTraceArgs) readField1(iprot thrift.TProtocol) error { + p.Request = &JoinTraceRequest{} + if err := p.Request.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Request), err) + } + return nil +} + +func (p *TracedServiceJoinTraceArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("joinTrace_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *TracedServiceJoinTraceArgs) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("request", thrift.STRUCT, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:request: ", p), err) + } + if err := p.Request.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Request), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:request: ", p), err) + } + return err +} + +func (p *TracedServiceJoinTraceArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("TracedServiceJoinTraceArgs(%+v)", *p) +} + +// Attributes: +// - Success +type TracedServiceJoinTraceResult struct { + Success *TraceResponse `thrift:"success,0" json:"success,omitempty"` +} + +func NewTracedServiceJoinTraceResult() *TracedServiceJoinTraceResult { + return &TracedServiceJoinTraceResult{} +} + +var TracedServiceJoinTraceResult_Success_DEFAULT *TraceResponse + +func (p *TracedServiceJoinTraceResult) GetSuccess() *TraceResponse { + if !p.IsSetSuccess() { + return TracedServiceJoinTraceResult_Success_DEFAULT + } + return p.Success +} +func (p *TracedServiceJoinTraceResult) IsSetSuccess() bool { + return p.Success != nil +} + +func (p *TracedServiceJoinTraceResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 0: + if err := p.readField0(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *TracedServiceJoinTraceResult) readField0(iprot thrift.TProtocol) error { + p.Success = &TraceResponse{} + if err := p.Success.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Success), err) + } + return nil +} + +func (p *TracedServiceJoinTraceResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("joinTrace_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField0(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *TracedServiceJoinTraceResult) writeField0(oprot thrift.TProtocol) (err error) { + if p.IsSetSuccess() { + if err := oprot.WriteFieldBegin("success", thrift.STRUCT, 0); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) + } + if err := p.Success.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Success), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) + } + } + return err +} + +func (p *TracedServiceJoinTraceResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("TracedServiceJoinTraceResult(%+v)", *p) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/thrift/tracetest/ttypes.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/thrift/tracetest/ttypes.go new file mode 100644 index 00000000..26db5901 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/crossdock/thrift/tracetest/ttypes.go @@ -0,0 +1,1103 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package tracetest + +import ( + "bytes" + "fmt" + "github.com/uber/jaeger-client-go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +var GoUnusedProtection__ int + +type Transport int64 + +const ( + Transport_HTTP Transport = 0 + Transport_TCHANNEL Transport = 1 + Transport_DUMMY Transport = 2 +) + +func (p Transport) String() string { + switch p { + case Transport_HTTP: + return "HTTP" + case Transport_TCHANNEL: + return "TCHANNEL" + case Transport_DUMMY: + return "DUMMY" + } + return "" +} + +func TransportFromString(s string) (Transport, error) { + switch s { + case "HTTP": + return Transport_HTTP, nil + case "TCHANNEL": + return Transport_TCHANNEL, nil + case "DUMMY": + return Transport_DUMMY, nil + } + return Transport(0), fmt.Errorf("not a valid Transport string") +} + +func TransportPtr(v Transport) *Transport { return &v } + +func (p Transport) MarshalText() ([]byte, error) { + return []byte(p.String()), nil +} + +func (p *Transport) UnmarshalText(text []byte) error { + q, err := TransportFromString(string(text)) + if err != nil { + return err + } + *p = q + return nil +} + +// Attributes: +// - ServiceName +// - ServerRole +// - Host +// - Port +// - Transport +// - Downstream +type Downstream struct { + ServiceName string `thrift:"serviceName,1,required" json:"serviceName"` + ServerRole string `thrift:"serverRole,2,required" json:"serverRole"` + Host string `thrift:"host,3,required" json:"host"` + Port string `thrift:"port,4,required" json:"port"` + Transport Transport `thrift:"transport,5,required" json:"transport"` + Downstream *Downstream `thrift:"downstream,6" json:"downstream,omitempty"` +} + +func NewDownstream() *Downstream { + return &Downstream{} +} + +func (p *Downstream) GetServiceName() string { + return p.ServiceName +} + +func (p *Downstream) GetServerRole() string { + return p.ServerRole +} + +func (p *Downstream) GetHost() string { + return p.Host +} + +func (p *Downstream) GetPort() string { + return p.Port +} + +func (p *Downstream) GetTransport() Transport { + return p.Transport +} + +var Downstream_Downstream_DEFAULT Downstream + +func (p *Downstream) GetDownstream() Downstream { + if !p.IsSetDownstream() { + return Downstream_Downstream_DEFAULT + } + return *p.Downstream +} +func (p *Downstream) IsSetDownstream() bool { + return p.Downstream != nil +} + +func (p *Downstream) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetServiceName bool = false + var issetServerRole bool = false + var issetHost bool = false + var issetPort bool = false + var issetTransport bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetServiceName = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetServerRole = true + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + issetHost = true + case 4: + if err := p.readField4(iprot); err != nil { + return err + } + issetPort = true + case 5: + if err := p.readField5(iprot); err != nil { + return err + } + issetTransport = true + case 6: + if err := p.readField6(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetServiceName { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ServiceName is not set")) + } + if !issetServerRole { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ServerRole is not set")) + } + if !issetHost { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Host is not set")) + } + if !issetPort { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Port is not set")) + } + if !issetTransport { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Transport is not set")) + } + return nil +} + +func (p *Downstream) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.ServiceName = v + } + return nil +} + +func (p *Downstream) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.ServerRole = v + } + return nil +} + +func (p *Downstream) readField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + p.Host = v + } + return nil +} + +func (p *Downstream) readField4(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 4: ", err) + } else { + p.Port = v + } + return nil +} + +func (p *Downstream) readField5(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(); err != nil { + return thrift.PrependError("error reading field 5: ", err) + } else { + temp := Transport(v) + p.Transport = temp + } + return nil +} + +func (p *Downstream) readField6(iprot thrift.TProtocol) error { + p.Downstream = &Downstream{} + if err := p.Downstream.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Downstream), err) + } + return nil +} + +func (p *Downstream) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Downstream"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := p.writeField4(oprot); err != nil { + return err + } + if err := p.writeField5(oprot); err != nil { + return err + } + if err := p.writeField6(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *Downstream) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("serviceName", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:serviceName: ", p), err) + } + if err := oprot.WriteString(string(p.ServiceName)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.serviceName (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:serviceName: ", p), err) + } + return err +} + +func (p *Downstream) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("serverRole", thrift.STRING, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:serverRole: ", p), err) + } + if err := oprot.WriteString(string(p.ServerRole)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.serverRole (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:serverRole: ", p), err) + } + return err +} + +func (p *Downstream) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("host", thrift.STRING, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:host: ", p), err) + } + if err := oprot.WriteString(string(p.Host)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.host (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:host: ", p), err) + } + return err +} + +func (p *Downstream) writeField4(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("port", thrift.STRING, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:port: ", p), err) + } + if err := oprot.WriteString(string(p.Port)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.port (4) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:port: ", p), err) + } + return err +} + +func (p *Downstream) writeField5(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("transport", thrift.I32, 5); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 5:transport: ", p), err) + } + if err := oprot.WriteI32(int32(p.Transport)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.transport (5) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 5:transport: ", p), err) + } + return err +} + +func (p *Downstream) writeField6(oprot thrift.TProtocol) (err error) { + if p.IsSetDownstream() { + if err := oprot.WriteFieldBegin("downstream", thrift.STRUCT, 6); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 6:downstream: ", p), err) + } + if err := p.Downstream.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Downstream), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 6:downstream: ", p), err) + } + } + return err +} + +func (p *Downstream) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Downstream(%+v)", *p) +} + +// Attributes: +// - ServerRole +// - Sampled +// - Baggage +// - Downstream +type StartTraceRequest struct { + ServerRole string `thrift:"serverRole,1,required" json:"serverRole"` + Sampled bool `thrift:"sampled,2,required" json:"sampled"` + Baggage string `thrift:"baggage,3,required" json:"baggage"` + Downstream *Downstream `thrift:"downstream,4,required" json:"downstream"` +} + +func NewStartTraceRequest() *StartTraceRequest { + return &StartTraceRequest{} +} + +func (p *StartTraceRequest) GetServerRole() string { + return p.ServerRole +} + +func (p *StartTraceRequest) GetSampled() bool { + return p.Sampled +} + +func (p *StartTraceRequest) GetBaggage() string { + return p.Baggage +} + +var StartTraceRequest_Downstream_DEFAULT *Downstream + +func (p *StartTraceRequest) GetDownstream() *Downstream { + if !p.IsSetDownstream() { + return StartTraceRequest_Downstream_DEFAULT + } + return p.Downstream +} +func (p *StartTraceRequest) IsSetDownstream() bool { + return p.Downstream != nil +} + +func (p *StartTraceRequest) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetServerRole bool = false + var issetSampled bool = false + var issetBaggage bool = false + var issetDownstream bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetServerRole = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetSampled = true + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + issetBaggage = true + case 4: + if err := p.readField4(iprot); err != nil { + return err + } + issetDownstream = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetServerRole { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ServerRole is not set")) + } + if !issetSampled { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Sampled is not set")) + } + if !issetBaggage { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Baggage is not set")) + } + if !issetDownstream { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Downstream is not set")) + } + return nil +} + +func (p *StartTraceRequest) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.ServerRole = v + } + return nil +} + +func (p *StartTraceRequest) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadBool(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.Sampled = v + } + return nil +} + +func (p *StartTraceRequest) readField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + p.Baggage = v + } + return nil +} + +func (p *StartTraceRequest) readField4(iprot thrift.TProtocol) error { + p.Downstream = &Downstream{} + if err := p.Downstream.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Downstream), err) + } + return nil +} + +func (p *StartTraceRequest) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("StartTraceRequest"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := p.writeField4(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *StartTraceRequest) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("serverRole", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:serverRole: ", p), err) + } + if err := oprot.WriteString(string(p.ServerRole)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.serverRole (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:serverRole: ", p), err) + } + return err +} + +func (p *StartTraceRequest) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("sampled", thrift.BOOL, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:sampled: ", p), err) + } + if err := oprot.WriteBool(bool(p.Sampled)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.sampled (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:sampled: ", p), err) + } + return err +} + +func (p *StartTraceRequest) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("baggage", thrift.STRING, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:baggage: ", p), err) + } + if err := oprot.WriteString(string(p.Baggage)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.baggage (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:baggage: ", p), err) + } + return err +} + +func (p *StartTraceRequest) writeField4(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("downstream", thrift.STRUCT, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:downstream: ", p), err) + } + if err := p.Downstream.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Downstream), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:downstream: ", p), err) + } + return err +} + +func (p *StartTraceRequest) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("StartTraceRequest(%+v)", *p) +} + +// Attributes: +// - ServerRole +// - Downstream +type JoinTraceRequest struct { + ServerRole string `thrift:"serverRole,1,required" json:"serverRole"` + Downstream *Downstream `thrift:"downstream,2" json:"downstream,omitempty"` +} + +func NewJoinTraceRequest() *JoinTraceRequest { + return &JoinTraceRequest{} +} + +func (p *JoinTraceRequest) GetServerRole() string { + return p.ServerRole +} + +var JoinTraceRequest_Downstream_DEFAULT *Downstream + +func (p *JoinTraceRequest) GetDownstream() *Downstream { + if !p.IsSetDownstream() { + return JoinTraceRequest_Downstream_DEFAULT + } + return p.Downstream +} +func (p *JoinTraceRequest) IsSetDownstream() bool { + return p.Downstream != nil +} + +func (p *JoinTraceRequest) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetServerRole bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetServerRole = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetServerRole { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ServerRole is not set")) + } + return nil +} + +func (p *JoinTraceRequest) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.ServerRole = v + } + return nil +} + +func (p *JoinTraceRequest) readField2(iprot thrift.TProtocol) error { + p.Downstream = &Downstream{} + if err := p.Downstream.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Downstream), err) + } + return nil +} + +func (p *JoinTraceRequest) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("JoinTraceRequest"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *JoinTraceRequest) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("serverRole", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:serverRole: ", p), err) + } + if err := oprot.WriteString(string(p.ServerRole)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.serverRole (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:serverRole: ", p), err) + } + return err +} + +func (p *JoinTraceRequest) writeField2(oprot thrift.TProtocol) (err error) { + if p.IsSetDownstream() { + if err := oprot.WriteFieldBegin("downstream", thrift.STRUCT, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:downstream: ", p), err) + } + if err := p.Downstream.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Downstream), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:downstream: ", p), err) + } + } + return err +} + +func (p *JoinTraceRequest) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("JoinTraceRequest(%+v)", *p) +} + +// Attributes: +// - TraceId +// - Sampled +// - Baggage +type ObservedSpan struct { + TraceId string `thrift:"traceId,1,required" json:"traceId"` + Sampled bool `thrift:"sampled,2,required" json:"sampled"` + Baggage string `thrift:"baggage,3,required" json:"baggage"` +} + +func NewObservedSpan() *ObservedSpan { + return &ObservedSpan{} +} + +func (p *ObservedSpan) GetTraceId() string { + return p.TraceId +} + +func (p *ObservedSpan) GetSampled() bool { + return p.Sampled +} + +func (p *ObservedSpan) GetBaggage() string { + return p.Baggage +} +func (p *ObservedSpan) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetTraceId bool = false + var issetSampled bool = false + var issetBaggage bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetTraceId = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetSampled = true + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + issetBaggage = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetTraceId { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field TraceId is not set")) + } + if !issetSampled { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Sampled is not set")) + } + if !issetBaggage { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Baggage is not set")) + } + return nil +} + +func (p *ObservedSpan) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.TraceId = v + } + return nil +} + +func (p *ObservedSpan) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadBool(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.Sampled = v + } + return nil +} + +func (p *ObservedSpan) readField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + p.Baggage = v + } + return nil +} + +func (p *ObservedSpan) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("ObservedSpan"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *ObservedSpan) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("traceId", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:traceId: ", p), err) + } + if err := oprot.WriteString(string(p.TraceId)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.traceId (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:traceId: ", p), err) + } + return err +} + +func (p *ObservedSpan) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("sampled", thrift.BOOL, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:sampled: ", p), err) + } + if err := oprot.WriteBool(bool(p.Sampled)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.sampled (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:sampled: ", p), err) + } + return err +} + +func (p *ObservedSpan) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("baggage", thrift.STRING, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:baggage: ", p), err) + } + if err := oprot.WriteString(string(p.Baggage)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.baggage (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:baggage: ", p), err) + } + return err +} + +func (p *ObservedSpan) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("ObservedSpan(%+v)", *p) +} + +// Each server must include the information about the span it observed. +// It can only be omitted from the response if notImplementedError field is not empty. +// If the server was instructed to make a downstream call, it must embed the +// downstream response in its own response. +// +// Attributes: +// - Span +// - Downstream +// - NotImplementedError +type TraceResponse struct { + Span *ObservedSpan `thrift:"span,1" json:"span,omitempty"` + Downstream *TraceResponse `thrift:"downstream,2" json:"downstream,omitempty"` + NotImplementedError string `thrift:"notImplementedError,3,required" json:"notImplementedError"` +} + +func NewTraceResponse() *TraceResponse { + return &TraceResponse{} +} + +var TraceResponse_Span_DEFAULT *ObservedSpan + +func (p *TraceResponse) GetSpan() *ObservedSpan { + if !p.IsSetSpan() { + return TraceResponse_Span_DEFAULT + } + return p.Span +} + +var TraceResponse_Downstream_DEFAULT TraceResponse + +func (p *TraceResponse) GetDownstream() TraceResponse { + if !p.IsSetDownstream() { + return TraceResponse_Downstream_DEFAULT + } + return *p.Downstream +} + +func (p *TraceResponse) GetNotImplementedError() string { + return p.NotImplementedError +} +func (p *TraceResponse) IsSetSpan() bool { + return p.Span != nil +} + +func (p *TraceResponse) IsSetDownstream() bool { + return p.Downstream != nil +} + +func (p *TraceResponse) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetNotImplementedError bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + issetNotImplementedError = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetNotImplementedError { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field NotImplementedError is not set")) + } + return nil +} + +func (p *TraceResponse) readField1(iprot thrift.TProtocol) error { + p.Span = &ObservedSpan{} + if err := p.Span.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Span), err) + } + return nil +} + +func (p *TraceResponse) readField2(iprot thrift.TProtocol) error { + p.Downstream = &TraceResponse{} + if err := p.Downstream.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Downstream), err) + } + return nil +} + +func (p *TraceResponse) readField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + p.NotImplementedError = v + } + return nil +} + +func (p *TraceResponse) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("TraceResponse"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *TraceResponse) writeField1(oprot thrift.TProtocol) (err error) { + if p.IsSetSpan() { + if err := oprot.WriteFieldBegin("span", thrift.STRUCT, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:span: ", p), err) + } + if err := p.Span.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Span), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:span: ", p), err) + } + } + return err +} + +func (p *TraceResponse) writeField2(oprot thrift.TProtocol) (err error) { + if p.IsSetDownstream() { + if err := oprot.WriteFieldBegin("downstream", thrift.STRUCT, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:downstream: ", p), err) + } + if err := p.Downstream.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Downstream), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:downstream: ", p), err) + } + } + return err +} + +func (p *TraceResponse) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("notImplementedError", thrift.STRING, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:notImplementedError: ", p), err) + } + if err := oprot.WriteString(string(p.NotImplementedError)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.notImplementedError (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:notImplementedError: ", p), err) + } + return err +} + +func (p *TraceResponse) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("TraceResponse(%+v)", *p) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/doc.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/doc.go new file mode 100644 index 00000000..4f554903 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/doc.go @@ -0,0 +1,24 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +/* +Package jaeger implements an OpenTracing (http://opentracing.io) Tracer. +It is currently using Zipkin-compatible data model and can be directly +itegrated with Zipkin backend (http://zipkin.io). + +For integration instructions please refer to the README: + +https://github.com/uber/jaeger-client-go/blob/master/README.md +*/ +package jaeger diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/glide.lock b/template/faaschain/vendor/github.com/uber/jaeger-client-go/glide.lock new file mode 100644 index 00000000..d76b1536 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/glide.lock @@ -0,0 +1,89 @@ +hash: 3accf84f97bff4a91162736104c0e9b9790820712bd86db6fec5e665f7196a82 +updated: 2018-04-30T11:46:43.804556-04:00 +imports: +- name: github.com/beorn7/perks + version: 3a771d992973f24aa725d07868b467d1ddfceafb + subpackages: + - quantile +- name: github.com/codahale/hdrhistogram + version: 3a0bb77429bd3a61596f5e8a3172445844342120 +- name: github.com/crossdock/crossdock-go + version: 049aabb0122b03bc9bd30cab8f3f91fb60166361 + subpackages: + - assert + - require +- name: github.com/davecgh/go-spew + version: 8991bc29aa16c548c550c7ff78260e27b9ab7c73 + subpackages: + - spew +- name: github.com/golang/protobuf + version: bbd03ef6da3a115852eaf24c8a1c46aeb39aa175 + subpackages: + - proto +- name: github.com/matttproud/golang_protobuf_extensions + version: c12348ce28de40eed0136aa2b644d0ee0650e56c + subpackages: + - pbutil +- name: github.com/opentracing/opentracing-go + version: 1949ddbfd147afd4d964a9f00b24eb291e0e7c38 + subpackages: + - ext + - log +- name: github.com/pkg/errors + version: 645ef00459ed84a119197bfb8d8205042c6df63d +- name: github.com/pmezard/go-difflib + version: 792786c7400a136282c1664665ae0a8db921c6c2 + subpackages: + - difflib +- name: github.com/prometheus/client_golang + version: c5b7fccd204277076155f10851dad72b76a49317 + subpackages: + - prometheus +- name: github.com/prometheus/client_model + version: 99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c + subpackages: + - go +- name: github.com/prometheus/common + version: 38c53a9f4bfcd932d1b00bfc65e256a7fba6b37a + subpackages: + - expfmt + - internal/bitbucket.org/ww/goautoneg + - model +- name: github.com/prometheus/procfs + version: 780932d4fbbe0e69b84c34c20f5c8d0981e109ea + subpackages: + - internal/util + - nfs + - xfs +- name: github.com/stretchr/testify + version: 12b6f73e6084dad08a7c6e575284b177ecafbc71 + subpackages: + - assert + - require + - suite +- name: github.com/uber/jaeger-lib + version: 4267858c0679cd4e47cefed8d7f70fd386cfb567 + subpackages: + - metrics + - metrics/prometheus + - metrics/testutils +- name: go.uber.org/atomic + version: 8474b86a5a6f79c443ce4b2992817ff32cf208b8 +- name: go.uber.org/multierr + version: 3c4937480c32f4c13a875a1829af76c98ca3d40a +- name: go.uber.org/zap + version: eeedf312bc6c57391d84767a4cd413f02a917974 + subpackages: + - buffer + - internal/bufferpool + - internal/color + - internal/exit + - zapcore +- name: golang.org/x/net + version: 6078986fec03a1dcc236c34816c71b0e05018fda + subpackages: + - context + - context/ctxhttp +testImports: +- name: github.com/uber-go/atomic + version: 8474b86a5a6f79c443ce4b2992817ff32cf208b8 diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/glide.yaml b/template/faaschain/vendor/github.com/uber/jaeger-client-go/glide.yaml new file mode 100644 index 00000000..6637da21 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/glide.yaml @@ -0,0 +1,22 @@ +package: github.com/uber/jaeger-client-go +import: +- package: github.com/opentracing/opentracing-go + version: ^1 + subpackages: + - ext + - log +- package: github.com/crossdock/crossdock-go +- package: github.com/uber/jaeger-lib + version: ^1.2.1 + subpackages: + - metrics +- package: github.com/pkg/errors + version: ~0.8.0 +testImport: +- package: github.com/stretchr/testify + subpackages: + - assert + - require + - suite +- package: github.com/prometheus/client_golang + version: v0.8.0 diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/header.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/header.go new file mode 100644 index 00000000..19c2c055 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/header.go @@ -0,0 +1,64 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +// HeadersConfig contains the values for the header keys that Jaeger will use. +// These values may be either custom or default depending on whether custom +// values were provided via a configuration. +type HeadersConfig struct { + // JaegerDebugHeader is the name of HTTP header or a TextMap carrier key which, + // if found in the carrier, forces the trace to be sampled as "debug" trace. + // The value of the header is recorded as the tag on the root span, so that the + // trace can be found in the UI using this value as a correlation ID. + JaegerDebugHeader string `yaml:"jaegerDebugHeader"` + + // JaegerBaggageHeader is the name of the HTTP header that is used to submit baggage. + // It differs from TraceBaggageHeaderPrefix in that it can be used only in cases where + // a root span does not exist. + JaegerBaggageHeader string `yaml:"jaegerBaggageHeader"` + + // TraceContextHeaderName is the http header name used to propagate tracing context. + // This must be in lower-case to avoid mismatches when decoding incoming headers. + TraceContextHeaderName string `yaml:"TraceContextHeaderName"` + + // TraceBaggageHeaderPrefix is the prefix for http headers used to propagate baggage. + // This must be in lower-case to avoid mismatches when decoding incoming headers. + TraceBaggageHeaderPrefix string `yaml:"traceBaggageHeaderPrefix"` +} + +func (c *HeadersConfig) applyDefaults() *HeadersConfig { + if c.JaegerBaggageHeader == "" { + c.JaegerBaggageHeader = JaegerBaggageHeader + } + if c.JaegerDebugHeader == "" { + c.JaegerDebugHeader = JaegerDebugHeader + } + if c.TraceBaggageHeaderPrefix == "" { + c.TraceBaggageHeaderPrefix = TraceBaggageHeaderPrefix + } + if c.TraceContextHeaderName == "" { + c.TraceContextHeaderName = TraceContextHeaderName + } + return c +} + +func getDefaultHeadersConfig() *HeadersConfig { + return &HeadersConfig{ + JaegerDebugHeader: JaegerDebugHeader, + JaegerBaggageHeader: JaegerBaggageHeader, + TraceContextHeaderName: TraceContextHeaderName, + TraceBaggageHeaderPrefix: TraceBaggageHeaderPrefix, + } +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/header_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/header_test.go new file mode 100644 index 00000000..d6a481a8 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/header_test.go @@ -0,0 +1,50 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSetDefaultOrCustom(t *testing.T) { + assert.Equal(t, (&HeadersConfig{}).applyDefaults(), getDefaultHeadersConfig()) + assert.Equal(t, (&HeadersConfig{ + JaegerDebugHeader: "custom-jaeger-debug-header", + }).applyDefaults(), &HeadersConfig{ + JaegerDebugHeader: "custom-jaeger-debug-header", + JaegerBaggageHeader: JaegerBaggageHeader, + TraceContextHeaderName: TraceContextHeaderName, + TraceBaggageHeaderPrefix: TraceBaggageHeaderPrefix, + }) + + customHeaders := &HeadersConfig{ + JaegerDebugHeader: "custom-jaeger-debug-header", + JaegerBaggageHeader: "custom-jaeger-baggage-header", + TraceContextHeaderName: "custom-tracer-state-header-name", + TraceBaggageHeaderPrefix: "custom-tracer-baggage-header-prefix", + } + assert.Equal(t, customHeaders.applyDefaults(), customHeaders) +} + +func TestGetDefaultHeadersConfig(t *testing.T) { + assert.Equal(t, getDefaultHeadersConfig(), &HeadersConfig{ + JaegerDebugHeader: JaegerDebugHeader, + JaegerBaggageHeader: JaegerBaggageHeader, + TraceContextHeaderName: TraceContextHeaderName, + TraceBaggageHeaderPrefix: TraceBaggageHeaderPrefix, + }) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/.gitignore b/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/.gitignore new file mode 100644 index 00000000..5c91b9c8 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/.gitignore @@ -0,0 +1,5 @@ +.idea/ +gen-go/ +gen-java/ +gen-nodejs/ +gen-py.tornado/ diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/.travis.yml b/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/.travis.yml new file mode 100644 index 00000000..bfc118fa --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/.travis.yml @@ -0,0 +1,10 @@ +sudo: required + +services: + - docker + +before_install: + - docker version + +script: + - make test_ci diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/LICENSE b/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/LICENSE new file mode 100644 index 00000000..546ac3c9 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Uber Technologies, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/Makefile b/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/Makefile new file mode 100644 index 00000000..0db1ecf0 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/Makefile @@ -0,0 +1,32 @@ + +THRIFT_VER=0.9.2 +THRIFT_IMG=thrift:$(THRIFT_VER) +THRIFT=docker run -v "${PWD}:/data" $(THRIFT_IMG) thrift + +THRIFT_GO_ARGS=thrift_import="github.com/apache/thrift/lib/go/thrift" +THRIFT_PY_ARGS=new_style,tornado +THRIFT_JAVA_ARGS=private-members + +THRIFT_GEN=--gen go:$(THRIFT_GO_ARGS) --gen py:$(THRIFT_PY_ARGS) --gen java:$(THRIFT_JAVA_ARGS) --gen js:node +THRIFT_CMD=$(THRIFT) -o /data $(THRIFT_GEN) + +THRIFT_FILES=agent.thrift jaeger.thrift sampling.thrift zipkincore.thrift crossdock/tracetest.thrift \ + baggage.thrift dependency.thrift aggregation_validator.thrift + +test_ci: thrift + +clean: + rm -rf gen-* || true + +thrift: thrift-image clean $(THRIFT_FILES) + +$(THRIFT_FILES): + @echo Compiling $@ + $(THRIFT_CMD) /data/thrift/$@ + +thrift-image: + docker pull $(THRIFT_IMG) + $(THRIFT) -version + +.PHONY: test_ci clean thrift thrift-image $(THRIFT_FILES) + diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/README.md b/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/README.md new file mode 100644 index 00000000..259b613c --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/README.md @@ -0,0 +1,7 @@ +# jaeger-idl [![Build Status][ci-img]][ci] + +A set of shared data model definitions used by Jaeger components. + +[ci-img]: https://travis-ci.org/uber/jaeger-idl.svg?branch=master +[ci]: https://travis-ci.org/uber/jaeger-idl + diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/agent.thrift b/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/agent.thrift new file mode 100644 index 00000000..4169a2a8 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/agent.thrift @@ -0,0 +1,31 @@ +# The MIT License (MIT) +# +# Copyright (c) 2016 Uber Technologies, Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +include "jaeger.thrift" +include "zipkincore.thrift" + +namespace java com.uber.jaeger.agent.thrift + +service Agent { + oneway void emitZipkinBatch(1: list spans) + oneway void emitBatch(1: jaeger.Batch batch) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/aggregation_validator.thrift b/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/aggregation_validator.thrift new file mode 100644 index 00000000..4354a67b --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/aggregation_validator.thrift @@ -0,0 +1,31 @@ +# Copyright (c) 2017 Uber Technologies, Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +namespace java com.uber.jaeger.thriftjava + +# ValidateTraceResponse returns ok when a trace has been written to redis. +struct ValidateTraceResponse { + 1: required bool ok + 2: required i64 traceCount +} + +service AggregationValidator { + ValidateTraceResponse validateTrace(1: required string traceId) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/baggage.thrift b/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/baggage.thrift new file mode 100644 index 00000000..d45e39f1 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/baggage.thrift @@ -0,0 +1,36 @@ +# Copyright (c) 2017 Uber Technologies, Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +namespace java com.uber.jaeger.thriftjava + +# BaggageRestriction contains the baggage key and the maximum length of the baggage value. +struct BaggageRestriction { + 1: required string baggageKey + 2: required i32 maxValueLength +} + +service BaggageRestrictionManager { + /** + * getBaggageRestrictions retrieves the baggage restrictions for a specific service. + * Usually, baggageRestrictions apply to all services however there may be situations + * where a baggageKey might only be allowed to be set by a specific service. + */ + list getBaggageRestrictions(1: string serviceName) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/crossdock/tracetest.thrift b/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/crossdock/tracetest.thrift new file mode 100644 index 00000000..24559a82 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/crossdock/tracetest.thrift @@ -0,0 +1,47 @@ +namespace java com.uber.jaeger.crossdock.thrift + +enum Transport { HTTP, TCHANNEL, DUMMY } + +struct Downstream { + 1: required string serviceName + 2: required string serverRole + 3: required string host + 4: required string port + 5: required Transport transport + 6: optional Downstream downstream +} + +struct StartTraceRequest { + 1: required string serverRole // role of the server (always S1) + 2: required bool sampled + 3: required string baggage + 4: required Downstream downstream +} + +struct JoinTraceRequest { + 1: required string serverRole // role of the server, S2 or S3 + 2: optional Downstream downstream +} + +struct ObservedSpan { + 1: required string traceId + 2: required bool sampled + 3: required string baggage +} + +/** + * Each server must include the information about the span it observed. + * It can only be omitted from the response if notImplementedError field is not empty. + * If the server was instructed to make a downstream call, it must embed the + * downstream response in its own response. + */ +struct TraceResponse { + 1: optional ObservedSpan span + 2: optional TraceResponse downstream + 3: required string notImplementedError +} + +service TracedService { + TraceResponse startTrace(1: StartTraceRequest request) + TraceResponse joinTrace(1: JoinTraceRequest request) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/dependency.thrift b/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/dependency.thrift new file mode 100644 index 00000000..2b34c807 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/dependency.thrift @@ -0,0 +1,40 @@ +# Copyright (c) 2017 Uber Technologies, Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +namespace java com.uber.jaeger.thriftjava + +struct DependencyLink { + // parent service name (caller) + 1: required string parent + // child service name (callee) + 2: required string child + // calls made during the duration of this link + 4: required i64 callCount +} + +// An aggregate representation of services paired with every service they call. +struct Dependencies { + 1: required list links +} + +service Dependency { + Dependencies getDependenciesForTrace(1: required string traceId) + oneway void saveDependencies(1: Dependencies dependencies) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/jaeger.thrift b/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/jaeger.thrift new file mode 100644 index 00000000..6ed3e2da --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/jaeger.thrift @@ -0,0 +1,87 @@ +# Copyright (c) 2016 Uber Technologies, Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +namespace java com.uber.jaeger.thriftjava + +# TagType denotes the type of a Tag's value. +enum TagType { STRING, DOUBLE, BOOL, LONG, BINARY } + +# Tag is a basic strongly typed key/value pair. It has been flattened to reduce the use of pointers in golang +struct Tag { + 1: required string key + 2: required TagType vType + 3: optional string vStr + 4: optional double vDouble + 5: optional bool vBool + 6: optional i64 vLong + 7: optional binary vBinary +} + +# Log is a timed even with an arbitrary set of tags. +struct Log { + 1: required i64 timestamp + 2: required list fields +} + +enum SpanRefType { CHILD_OF, FOLLOWS_FROM } + +# SpanRef describes causal relationship of the current span to another span (e.g. 'child-of') +struct SpanRef { + 1: required SpanRefType refType + 2: required i64 traceIdLow + 3: required i64 traceIdHigh + 4: required i64 spanId +} + +# Span represents a named unit of work performed by a service. +struct Span { + 1: required i64 traceIdLow # the least significant 64 bits of a traceID + 2: required i64 traceIdHigh # the most significant 64 bits of a traceID; 0 when only 64bit IDs are used + 3: required i64 spanId # unique span id (only unique within a given trace) + 4: required i64 parentSpanId # since nearly all spans will have parents spans, CHILD_OF refs do not have to be explicit + 5: required string operationName + 6: optional list references # causal references to other spans + 7: required i32 flags # a bit field used to propagate sampling decisions. 1 signifies a SAMPLED span, 2 signifies a DEBUG span. + 8: required i64 startTime + 9: required i64 duration + 10: optional list tags + 11: optional list logs +} + +# Process describes the traced process/service that emits spans. +struct Process { + 1: required string serviceName + 2: optional list tags +} + +# Batch is a collection of spans reported out of process. +struct Batch { + 1: required Process process + 2: required list spans +} + +# BatchSubmitResponse is the response on submitting a batch. +struct BatchSubmitResponse { + 1: required bool ok # The Collector's client is expected to only log (or emit a counter) when not ok equals false +} + +service Collector { + list submitBatches(1: list batches) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/sampling.thrift b/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/sampling.thrift new file mode 100644 index 00000000..e3f0a3cd --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/sampling.thrift @@ -0,0 +1,63 @@ +# The MIT License (MIT) +# +# Copyright (c) 2016 Uber Technologies, Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +namespace java com.uber.jaeger.thrift.sampling_manager + +enum SamplingStrategyType { PROBABILISTIC, RATE_LIMITING } + +// ProbabilisticSamplingStrategy randomly samples a fixed percentage of all traces. +struct ProbabilisticSamplingStrategy { + 1: required double samplingRate // percentage expressed as rate (0..1] +} + +// RateLimitingStrategy samples traces with a rate that does not exceed specified number of traces per second. +// The recommended implementation approach is leaky bucket. +struct RateLimitingSamplingStrategy { + 1: required i16 maxTracesPerSecond +} + +// OperationSamplingStrategy defines a sampling strategy that randomly samples a fixed percentage of operation traces. +struct OperationSamplingStrategy { + 1: required string operation + 2: required ProbabilisticSamplingStrategy probabilisticSampling +} + +// PerOperationSamplingStrategies defines a sampling strategy per each operation name in the service +// with a guaranteed lower bound per second. Once the lower bound is met, operations are randomly sampled +// at a fixed percentage. +struct PerOperationSamplingStrategies { + 1: required double defaultSamplingProbability + 2: required double defaultLowerBoundTracesPerSecond + 3: required list perOperationStrategies + 4: optional double defaultUpperBoundTracesPerSecond +} + +struct SamplingStrategyResponse { + 1: required SamplingStrategyType strategyType + 2: optional ProbabilisticSamplingStrategy probabilisticSampling + 3: optional RateLimitingSamplingStrategy rateLimitingSampling + 4: optional PerOperationSamplingStrategies operationSampling +} + +service SamplingManager { + SamplingStrategyResponse getSamplingStrategy(1: string serviceName) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/zipkincore.thrift b/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/zipkincore.thrift new file mode 100644 index 00000000..f227b5c9 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/idl/thrift/zipkincore.thrift @@ -0,0 +1,300 @@ +# Copyright 2012 Twitter Inc. +# +# 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. +namespace java com.twitter.zipkin.thriftjava +#@namespace scala com.twitter.zipkin.thriftscala +namespace rb Zipkin + +#************** Annotation.value ************** +/** + * The client sent ("cs") a request to a server. There is only one send per + * span. For example, if there's a transport error, each attempt can be logged + * as a WIRE_SEND annotation. + * + * If chunking is involved, each chunk could be logged as a separate + * CLIENT_SEND_FRAGMENT in the same span. + * + * Annotation.host is not the server. It is the host which logged the send + * event, almost always the client. When logging CLIENT_SEND, instrumentation + * should also log the SERVER_ADDR. + */ +const string CLIENT_SEND = "cs" +/** + * The client received ("cr") a response from a server. There is only one + * receive per span. For example, if duplicate responses were received, each + * can be logged as a WIRE_RECV annotation. + * + * If chunking is involved, each chunk could be logged as a separate + * CLIENT_RECV_FRAGMENT in the same span. + * + * Annotation.host is not the server. It is the host which logged the receive + * event, almost always the client. The actual endpoint of the server is + * recorded separately as SERVER_ADDR when CLIENT_SEND is logged. + */ +const string CLIENT_RECV = "cr" +/** + * The server sent ("ss") a response to a client. There is only one response + * per span. If there's a transport error, each attempt can be logged as a + * WIRE_SEND annotation. + * + * Typically, a trace ends with a server send, so the last timestamp of a trace + * is often the timestamp of the root span's server send. + * + * If chunking is involved, each chunk could be logged as a separate + * SERVER_SEND_FRAGMENT in the same span. + * + * Annotation.host is not the client. It is the host which logged the send + * event, almost always the server. The actual endpoint of the client is + * recorded separately as CLIENT_ADDR when SERVER_RECV is logged. + */ +const string SERVER_SEND = "ss" +/** + * The server received ("sr") a request from a client. There is only one + * request per span. For example, if duplicate responses were received, each + * can be logged as a WIRE_RECV annotation. + * + * Typically, a trace starts with a server receive, so the first timestamp of a + * trace is often the timestamp of the root span's server receive. + * + * If chunking is involved, each chunk could be logged as a separate + * SERVER_RECV_FRAGMENT in the same span. + * + * Annotation.host is not the client. It is the host which logged the receive + * event, almost always the server. When logging SERVER_RECV, instrumentation + * should also log the CLIENT_ADDR. + */ +const string SERVER_RECV = "sr" +/** + * Optionally logs an attempt to send a message on the wire. Multiple wire send + * events could indicate network retries. A lag between client or server send + * and wire send might indicate queuing or processing delay. + */ +const string WIRE_SEND = "ws" +/** + * Optionally logs an attempt to receive a message from the wire. Multiple wire + * receive events could indicate network retries. A lag between wire receive + * and client or server receive might indicate queuing or processing delay. + */ +const string WIRE_RECV = "wr" +/** + * Optionally logs progress of a (CLIENT_SEND, WIRE_SEND). For example, this + * could be one chunk in a chunked request. + */ +const string CLIENT_SEND_FRAGMENT = "csf" +/** + * Optionally logs progress of a (CLIENT_RECV, WIRE_RECV). For example, this + * could be one chunk in a chunked response. + */ +const string CLIENT_RECV_FRAGMENT = "crf" +/** + * Optionally logs progress of a (SERVER_SEND, WIRE_SEND). For example, this + * could be one chunk in a chunked response. + */ +const string SERVER_SEND_FRAGMENT = "ssf" +/** + * Optionally logs progress of a (SERVER_RECV, WIRE_RECV). For example, this + * could be one chunk in a chunked request. + */ +const string SERVER_RECV_FRAGMENT = "srf" + +#***** BinaryAnnotation.key ****** +/** + * The value of "lc" is the component or namespace of a local span. + * + * BinaryAnnotation.host adds service context needed to support queries. + * + * Local Component("lc") supports three key features: flagging, query by + * service and filtering Span.name by namespace. + * + * While structurally the same, local spans are fundamentally different than + * RPC spans in how they should be interpreted. For example, zipkin v1 tools + * center on RPC latency and service graphs. Root local-spans are neither + * indicative of critical path RPC latency, nor have impact on the shape of a + * service graph. By flagging with "lc", tools can special-case local spans. + * + * Zipkin v1 Spans are unqueryable unless they can be indexed by service name. + * The only path to a service name is by (Binary)?Annotation.host.serviceName. + * By logging "lc", a local span can be queried even if no other annotations + * are logged. + * + * The value of "lc" is the namespace of Span.name. For example, it might be + * "finatra2", for a span named "bootstrap". "lc" allows you to resolves + * conflicts for the same Span.name, for example "finatra/bootstrap" vs + * "finch/bootstrap". Using local component, you'd search for spans named + * "bootstrap" where "lc=finch" + */ +const string LOCAL_COMPONENT = "lc" + +#***** BinaryAnnotation.key where value = [1] and annotation_type = BOOL ****** +/** + * Indicates a client address ("ca") in a span. Most likely, there's only one. + * Multiple addresses are possible when a client changes its ip or port within + * a span. + */ +const string CLIENT_ADDR = "ca" +/** + * Indicates a server address ("sa") in a span. Most likely, there's only one. + * Multiple addresses are possible when a client is redirected, or fails to a + * different server ip or port. + */ +const string SERVER_ADDR = "sa" + +/** + * Indicates the network context of a service recording an annotation with two + * exceptions. + * + * When a BinaryAnnotation, and key is CLIENT_ADDR or SERVER_ADDR, + * the endpoint indicates the source or destination of an RPC. This exception + * allows zipkin to display network context of uninstrumented services, or + * clients such as web browsers. + */ +struct Endpoint { + /** + * IPv4 host address packed into 4 bytes. + * + * Ex for the ip 1.2.3.4, it would be (1 << 24) | (2 << 16) | (3 << 8) | 4 + */ + 1: i32 ipv4 + /** + * IPv4 port + * + * Note: this is to be treated as an unsigned integer, so watch for negatives. + * + * Conventionally, when the port isn't known, port = 0. + */ + 2: i16 port + /** + * Service name in lowercase, such as "memcache" or "zipkin-web" + * + * Conventionally, when the service name isn't known, service_name = "unknown". + */ + 3: string service_name +} + +/** + * An annotation is similar to a log statement. It includes a host field which + * allows these events to be attributed properly, and also aggregatable. + */ +struct Annotation { + /** + * Microseconds from epoch. + * + * This value should use the most precise value possible. For example, + * gettimeofday or syncing nanoTime against a tick of currentTimeMillis. + */ + 1: i64 timestamp + 2: string value // what happened at the timestamp? + /** + * Always the host that recorded the event. By specifying the host you allow + * rollup of all events (such as client requests to a service) by IP address. + */ + 3: optional Endpoint host + // don't reuse 4: optional i32 OBSOLETE_duration // how long did the operation take? microseconds +} + +enum AnnotationType { BOOL, BYTES, I16, I32, I64, DOUBLE, STRING } + +/** + * Binary annotations are tags applied to a Span to give it context. For + * example, a binary annotation of "http.uri" could the path to a resource in a + * RPC call. + * + * Binary annotations of type STRING are always queryable, though more a + * historical implementation detail than a structural concern. + * + * Binary annotations can repeat, and vary on the host. Similar to Annotation, + * the host indicates who logged the event. This allows you to tell the + * difference between the client and server side of the same key. For example, + * the key "http.uri" might be different on the client and server side due to + * rewriting, like "/api/v1/myresource" vs "/myresource. Via the host field, + * you can see the different points of view, which often help in debugging. + */ +struct BinaryAnnotation { + 1: string key, + 2: binary value, + 3: AnnotationType annotation_type, + /** + * The host that recorded tag, which allows you to differentiate between + * multiple tags with the same key. There are two exceptions to this. + * + * When the key is CLIENT_ADDR or SERVER_ADDR, host indicates the source or + * destination of an RPC. This exception allows zipkin to display network + * context of uninstrumented services, or clients such as web browsers. + */ + 4: optional Endpoint host +} + +/** + * A trace is a series of spans (often RPC calls) which form a latency tree. + * + * The root span is where trace_id = id and parent_id = Nil. The root span is + * usually the longest interval in the trace, starting with a SERVER_RECV + * annotation and ending with a SERVER_SEND. + */ +struct Span { + 1: i64 trace_id # unique trace id, use for all spans in trace + /** + * Span name in lowercase, rpc method for example + * + * Conventionally, when the span name isn't known, name = "unknown". + */ + 3: string name, + 4: i64 id, # unique span id, only used for this span + 5: optional i64 parent_id, # parent span id + 6: list annotations, # all annotations/events that occured, sorted by timestamp + 8: list binary_annotations # any binary annotations + 9: optional bool debug = 0 # if true, we DEMAND that this span passes all samplers + /** + * Microseconds from epoch of the creation of this span. + * + * This value should be set directly by instrumentation, using the most + * precise value possible. For example, gettimeofday or syncing nanoTime + * against a tick of currentTimeMillis. + * + * For compatibilty with instrumentation that precede this field, collectors + * or span stores can derive this via Annotation.timestamp. + * For example, SERVER_RECV.timestamp or CLIENT_SEND.timestamp. + * + * This field is optional for compatibility with old data: first-party span + * stores are expected to support this at time of introduction. + */ + 10: optional i64 timestamp, + /** + * Measurement of duration in microseconds, used to support queries. + * + * This value should be set directly, where possible. Doing so encourages + * precise measurement decoupled from problems of clocks, such as skew or NTP + * updates causing time to move backwards. + * + * For compatibilty with instrumentation that precede this field, collectors + * or span stores can derive this by subtracting Annotation.timestamp. + * For example, SERVER_SEND.timestamp - SERVER_RECV.timestamp. + * + * If this field is persisted as unset, zipkin will continue to work, except + * duration query support will be implementation-specific. Similarly, setting + * this field non-atomically is implementation-specific. + * + * This field is i64 vs i32 to support spans longer than 35 minutes. + */ + 11: optional i64 duration +} + +# define TChannel service + +struct Response { + 1: required bool ok +} + +service ZipkinCollector { + list submitZipkinBatch(1: list spans) +} \ No newline at end of file diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/baggage/remote/options.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/baggage/remote/options.go new file mode 100644 index 00000000..74572931 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/baggage/remote/options.go @@ -0,0 +1,101 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package remote + +import ( + "time" + + "github.com/uber/jaeger-client-go" +) + +const ( + defaultMaxValueLength = 2048 + defaultRefreshInterval = time.Minute + defaultHostPort = "localhost:5778" +) + +// Option is a function that sets some option on the RestrictionManager +type Option func(options *options) + +// Options is a factory for all available options +var Options options + +type options struct { + denyBaggageOnInitializationFailure bool + metrics *jaeger.Metrics + logger jaeger.Logger + hostPort string + refreshInterval time.Duration +} + +// DenyBaggageOnInitializationFailure creates an Option that determines the startup failure mode of RestrictionManager. +// If DenyBaggageOnInitializationFailure is true, RestrictionManager will not allow any baggage to be written until baggage +// restrictions have been retrieved from agent. +// If DenyBaggageOnInitializationFailure is false, RestrictionManager will allow any baggage to be written until baggage +// restrictions have been retrieved from agent. +func (options) DenyBaggageOnInitializationFailure(b bool) Option { + return func(o *options) { + o.denyBaggageOnInitializationFailure = b + } +} + +// Metrics creates an Option that initializes Metrics on the RestrictionManager, which is used to emit statistics. +func (options) Metrics(m *jaeger.Metrics) Option { + return func(o *options) { + o.metrics = m + } +} + +// Logger creates an Option that sets the logger used by the RestrictionManager. +func (options) Logger(logger jaeger.Logger) Option { + return func(o *options) { + o.logger = logger + } +} + +// HostPort creates an Option that sets the hostPort of the local agent that contains the baggage restrictions. +func (options) HostPort(hostPort string) Option { + return func(o *options) { + o.hostPort = hostPort + } +} + +// RefreshInterval creates an Option that sets how often the RestrictionManager will poll local agent for +// the baggage restrictions. +func (options) RefreshInterval(refreshInterval time.Duration) Option { + return func(o *options) { + o.refreshInterval = refreshInterval + } +} + +func applyOptions(o ...Option) options { + opts := options{} + for _, option := range o { + option(&opts) + } + if opts.metrics == nil { + opts.metrics = jaeger.NewNullMetrics() + } + if opts.logger == nil { + opts.logger = jaeger.NullLogger + } + if opts.hostPort == "" { + opts.hostPort = defaultHostPort + } + if opts.refreshInterval == 0 { + opts.refreshInterval = defaultRefreshInterval + } + return opts +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/baggage/remote/restriction_manager.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/baggage/remote/restriction_manager.go new file mode 100644 index 00000000..a56515ac --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/baggage/remote/restriction_manager.go @@ -0,0 +1,157 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package remote + +import ( + "fmt" + "net/url" + "sync" + "time" + + "github.com/uber/jaeger-client-go/internal/baggage" + thrift "github.com/uber/jaeger-client-go/thrift-gen/baggage" + "github.com/uber/jaeger-client-go/utils" +) + +type httpBaggageRestrictionManagerProxy struct { + url string +} + +func newHTTPBaggageRestrictionManagerProxy(hostPort, serviceName string) *httpBaggageRestrictionManagerProxy { + v := url.Values{} + v.Set("service", serviceName) + return &httpBaggageRestrictionManagerProxy{ + url: fmt.Sprintf("http://%s/baggageRestrictions?%s", hostPort, v.Encode()), + } +} + +func (s *httpBaggageRestrictionManagerProxy) GetBaggageRestrictions(serviceName string) ([]*thrift.BaggageRestriction, error) { + var out []*thrift.BaggageRestriction + if err := utils.GetJSON(s.url, &out); err != nil { + return nil, err + } + return out, nil +} + +// RestrictionManager manages baggage restrictions by retrieving baggage restrictions from agent +type RestrictionManager struct { + options + + mux sync.RWMutex + serviceName string + restrictions map[string]*baggage.Restriction + thriftProxy thrift.BaggageRestrictionManager + pollStopped sync.WaitGroup + stopPoll chan struct{} + invalidRestriction *baggage.Restriction + validRestriction *baggage.Restriction + + // Determines if the manager has successfully retrieved baggage restrictions from agent + initialized bool +} + +// NewRestrictionManager returns a BaggageRestrictionManager that polls the agent for the latest +// baggage restrictions. +func NewRestrictionManager(serviceName string, options ...Option) *RestrictionManager { + // TODO there is a developing use case where a single tracer can generate traces on behalf of many services. + // restrictionsMap will need to exist per service + opts := applyOptions(options...) + m := &RestrictionManager{ + serviceName: serviceName, + options: opts, + restrictions: make(map[string]*baggage.Restriction), + thriftProxy: newHTTPBaggageRestrictionManagerProxy(opts.hostPort, serviceName), + stopPoll: make(chan struct{}), + invalidRestriction: baggage.NewRestriction(false, 0), + validRestriction: baggage.NewRestriction(true, defaultMaxValueLength), + } + m.pollStopped.Add(1) + go m.pollManager() + return m +} + +// isReady returns true if the manager has retrieved baggage restrictions from the remote source. +func (m *RestrictionManager) isReady() bool { + m.mux.RLock() + defer m.mux.RUnlock() + return m.initialized +} + +// GetRestriction implements RestrictionManager#GetRestriction. +func (m *RestrictionManager) GetRestriction(service, key string) *baggage.Restriction { + m.mux.RLock() + defer m.mux.RUnlock() + if !m.initialized { + if m.denyBaggageOnInitializationFailure { + return m.invalidRestriction + } + return m.validRestriction + } + if restriction, ok := m.restrictions[key]; ok { + return restriction + } + return m.invalidRestriction +} + +// Close stops remote polling and closes the RemoteRestrictionManager. +func (m *RestrictionManager) Close() error { + close(m.stopPoll) + m.pollStopped.Wait() + return nil +} + +func (m *RestrictionManager) pollManager() { + defer m.pollStopped.Done() + // attempt to initialize baggage restrictions + if err := m.updateRestrictions(); err != nil { + m.logger.Error(fmt.Sprintf("Failed to initialize baggage restrictions: %s", err.Error())) + } + ticker := time.NewTicker(m.refreshInterval) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + if err := m.updateRestrictions(); err != nil { + m.logger.Error(fmt.Sprintf("Failed to update baggage restrictions: %s", err.Error())) + } + case <-m.stopPoll: + return + } + } +} + +func (m *RestrictionManager) updateRestrictions() error { + restrictions, err := m.thriftProxy.GetBaggageRestrictions(m.serviceName) + if err != nil { + m.metrics.BaggageRestrictionsUpdateFailure.Inc(1) + return err + } + newRestrictions := m.parseRestrictions(restrictions) + m.metrics.BaggageRestrictionsUpdateSuccess.Inc(1) + m.mux.Lock() + defer m.mux.Unlock() + m.initialized = true + m.restrictions = newRestrictions + return nil +} + +func (m *RestrictionManager) parseRestrictions(restrictions []*thrift.BaggageRestriction) map[string]*baggage.Restriction { + setters := make(map[string]*baggage.Restriction, len(restrictions)) + for _, restriction := range restrictions { + setters[restriction.BaggageKey] = baggage.NewRestriction(true, int(restriction.MaxValueLength)) + } + return setters +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/baggage/remote/restriction_manager_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/baggage/remote/restriction_manager_test.go new file mode 100644 index 00000000..c0091eb4 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/baggage/remote/restriction_manager_test.go @@ -0,0 +1,220 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package remote + +import ( + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "net/url" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber-go/atomic" + "github.com/uber/jaeger-lib/metrics" + "github.com/uber/jaeger-lib/metrics/testutils" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/internal/baggage" + thrift "github.com/uber/jaeger-client-go/thrift-gen/baggage" +) + +const ( + service = "svc" + expectedKey = "key" + expectedSize = 10 +) + +var ( + testRestrictions = []*thrift.BaggageRestriction{ + {BaggageKey: expectedKey, MaxValueLength: int32(expectedSize)}, + } +) + +var _ io.Closer = new(RestrictionManager) // API check + +type baggageHandler struct { + returnError *atomic.Bool + restrictions []*thrift.BaggageRestriction +} + +func (h *baggageHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if h.returnError.Load() { + w.WriteHeader(http.StatusInternalServerError) + } else { + bytes, _ := json.Marshal(h.restrictions) + w.Header().Add("Content-Type", "application/json") + w.Write(bytes) + } +} + +func (h *baggageHandler) setReturnError(b bool) { + h.returnError.Store(b) +} + +func withHTTPServer( + restrictions []*thrift.BaggageRestriction, + f func( + metrics *jaeger.Metrics, + factory *metrics.LocalFactory, + handler *baggageHandler, + server *httptest.Server, + ), +) { + factory := metrics.NewLocalFactory(0) + m := jaeger.NewMetrics(factory, nil) + + handler := &baggageHandler{returnError: atomic.NewBool(true), restrictions: restrictions} + server := httptest.NewServer(handler) + defer server.Close() + + f(m, factory, handler, server) +} + +func TestNewRemoteRestrictionManager(t *testing.T) { + withHTTPServer( + testRestrictions, + func( + metrics *jaeger.Metrics, + factory *metrics.LocalFactory, + handler *baggageHandler, + server *httptest.Server, + ) { + handler.setReturnError(false) + mgr := NewRestrictionManager( + service, + Options.HostPort(getHostPort(t, server.URL)), + Options.Metrics(metrics), + Options.Logger(jaeger.NullLogger), + ) + defer mgr.Close() + + for i := 0; i < 100; i++ { + if mgr.isReady() { + break + } + time.Sleep(time.Millisecond) + } + require.True(t, mgr.isReady()) + + restriction := mgr.GetRestriction(service, expectedKey) + assert.EqualValues(t, baggage.NewRestriction(true, expectedSize), restriction) + + badKey := "bad-key" + restriction = mgr.GetRestriction(service, badKey) + assert.EqualValues(t, baggage.NewRestriction(false, 0), restriction) + + testutils.AssertCounterMetrics(t, factory, + testutils.ExpectedMetric{ + Name: "jaeger.baggage_restrictions_updates", + Tags: map[string]string{"result": "ok"}, + Value: 1, + }, + ) + }) +} + +func TestDenyBaggageOnInitializationFailure(t *testing.T) { + withHTTPServer( + testRestrictions, + func( + m *jaeger.Metrics, + factory *metrics.LocalFactory, + handler *baggageHandler, + server *httptest.Server, + ) { + mgr := NewRestrictionManager( + service, + Options.DenyBaggageOnInitializationFailure(true), + Options.HostPort(getHostPort(t, server.URL)), + Options.Metrics(m), + Options.Logger(jaeger.NullLogger), + ) + require.False(t, mgr.isReady()) + + metricName := "jaeger.baggage_restrictions_updates" + metricTags := map[string]string{"result": "err"} + key := metrics.GetKey(metricName, metricTags, "|", "=") + for i := 0; i < 100; i++ { + // wait until the async initialization call is complete + counters, _ := factory.Snapshot() + if _, ok := counters[key]; ok { + break + } + time.Sleep(time.Millisecond) + } + + testutils.AssertCounterMetrics(t, factory, + testutils.ExpectedMetric{ + Name: metricName, + Tags: metricTags, + Value: 1, + }, + ) + + // DenyBaggageOnInitializationFailure should not allow any key to be written + restriction := mgr.GetRestriction(service, expectedKey) + assert.EqualValues(t, baggage.NewRestriction(false, 0), restriction) + + // have the http server return restrictions + handler.setReturnError(false) + mgr.updateRestrictions() + + // Wait until manager retrieves baggage restrictions + for i := 0; i < 100; i++ { + if mgr.isReady() { + break + } + time.Sleep(time.Millisecond) + } + require.True(t, mgr.isReady()) + + restriction = mgr.GetRestriction(service, expectedKey) + assert.EqualValues(t, baggage.NewRestriction(true, expectedSize), restriction) + }) +} + +func TestAllowBaggageOnInitializationFailure(t *testing.T) { + withHTTPServer( + testRestrictions, + func( + metrics *jaeger.Metrics, + factory *metrics.LocalFactory, + handler *baggageHandler, + server *httptest.Server, + ) { + mgr := NewRestrictionManager( + service, + Options.RefreshInterval(time.Millisecond), + Options.HostPort(getHostPort(t, server.URL)), + Options.Metrics(metrics), + Options.Logger(jaeger.NullLogger), + ) + require.False(t, mgr.isReady()) + + // AllowBaggageOnInitializationFailure should allow any key to be written + restriction := mgr.GetRestriction(service, expectedKey) + assert.EqualValues(t, baggage.NewRestriction(true, 2048), restriction) + }) +} + +func getHostPort(t *testing.T, s string) string { + u, err := url.Parse(s) + require.NoError(t, err, "Failed to parse url") + return u.Host +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/baggage/restriction_manager.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/baggage/restriction_manager.go new file mode 100644 index 00000000..c16a5c56 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/baggage/restriction_manager.go @@ -0,0 +1,71 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package baggage + +const ( + defaultMaxValueLength = 2048 +) + +// Restriction determines whether a baggage key is allowed and contains any restrictions on the baggage value. +type Restriction struct { + keyAllowed bool + maxValueLength int +} + +// NewRestriction returns a new Restriction. +func NewRestriction(keyAllowed bool, maxValueLength int) *Restriction { + return &Restriction{ + keyAllowed: keyAllowed, + maxValueLength: maxValueLength, + } +} + +// KeyAllowed returns whether the baggage key for this restriction is allowed. +func (r *Restriction) KeyAllowed() bool { + return r.keyAllowed +} + +// MaxValueLength returns the max length for the baggage value. +func (r *Restriction) MaxValueLength() int { + return r.maxValueLength +} + +// RestrictionManager keeps track of valid baggage keys and their restrictions. The manager +// will return a Restriction for a specific baggage key which will determine whether the baggage +// key is allowed for the current service and any other applicable restrictions on the baggage +// value. +type RestrictionManager interface { + GetRestriction(service, key string) *Restriction +} + +// DefaultRestrictionManager allows any baggage key. +type DefaultRestrictionManager struct { + defaultRestriction *Restriction +} + +// NewDefaultRestrictionManager returns a DefaultRestrictionManager. +func NewDefaultRestrictionManager(maxValueLength int) *DefaultRestrictionManager { + if maxValueLength == 0 { + maxValueLength = defaultMaxValueLength + } + return &DefaultRestrictionManager{ + defaultRestriction: &Restriction{keyAllowed: true, maxValueLength: maxValueLength}, + } +} + +// GetRestriction implements RestrictionManager#GetRestriction. +func (m *DefaultRestrictionManager) GetRestriction(service, key string) *Restriction { + return m.defaultRestriction +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/baggage/restriction_manager_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/baggage/restriction_manager_test.go new file mode 100644 index 00000000..b91d866d --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/baggage/restriction_manager_test.go @@ -0,0 +1,29 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package baggage + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +var _ RestrictionManager = &DefaultRestrictionManager{} + +func TestDefaultRestrictionManager(t *testing.T) { + mgr := NewDefaultRestrictionManager(0) + restriction := mgr.GetRestriction("svc", "key") + assert.EqualValues(t, NewRestriction(true, 2048), restriction) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/spanlog/json.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/spanlog/json.go new file mode 100644 index 00000000..0e10b8a5 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/spanlog/json.go @@ -0,0 +1,81 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package spanlog + +import ( + "encoding/json" + "fmt" + + "github.com/opentracing/opentracing-go/log" +) + +type fieldsAsMap map[string]string + +// MaterializeWithJSON converts log Fields into JSON string +// TODO refactor into pluggable materializer +func MaterializeWithJSON(logFields []log.Field) ([]byte, error) { + fields := fieldsAsMap(make(map[string]string, len(logFields))) + for _, field := range logFields { + field.Marshal(fields) + } + if event, ok := fields["event"]; ok && len(fields) == 1 { + return []byte(event), nil + } + return json.Marshal(fields) +} + +func (ml fieldsAsMap) EmitString(key, value string) { + ml[key] = value +} + +func (ml fieldsAsMap) EmitBool(key string, value bool) { + ml[key] = fmt.Sprintf("%t", value) +} + +func (ml fieldsAsMap) EmitInt(key string, value int) { + ml[key] = fmt.Sprintf("%d", value) +} + +func (ml fieldsAsMap) EmitInt32(key string, value int32) { + ml[key] = fmt.Sprintf("%d", value) +} + +func (ml fieldsAsMap) EmitInt64(key string, value int64) { + ml[key] = fmt.Sprintf("%d", value) +} + +func (ml fieldsAsMap) EmitUint32(key string, value uint32) { + ml[key] = fmt.Sprintf("%d", value) +} + +func (ml fieldsAsMap) EmitUint64(key string, value uint64) { + ml[key] = fmt.Sprintf("%d", value) +} + +func (ml fieldsAsMap) EmitFloat32(key string, value float32) { + ml[key] = fmt.Sprintf("%f", value) +} + +func (ml fieldsAsMap) EmitFloat64(key string, value float64) { + ml[key] = fmt.Sprintf("%f", value) +} + +func (ml fieldsAsMap) EmitObject(key string, value interface{}) { + ml[key] = fmt.Sprintf("%+v", value) +} + +func (ml fieldsAsMap) EmitLazyLogger(value log.LazyLogger) { + value(ml) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/throttler/remote/options.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/throttler/remote/options.go new file mode 100644 index 00000000..f52c322f --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/throttler/remote/options.go @@ -0,0 +1,99 @@ +// Copyright (c) 2018 The Jaeger Authors. +// +// 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. + +package remote + +import ( + "time" + + "github.com/uber/jaeger-client-go" +) + +const ( + defaultHostPort = "localhost:5778" + defaultRefreshInterval = time.Second * 5 +) + +// Option is a function that sets some option on the Throttler +type Option func(options *options) + +// Options is a factory for all available options +var Options options + +type options struct { + metrics *jaeger.Metrics + logger jaeger.Logger + hostPort string + refreshInterval time.Duration + synchronousInitialization bool +} + +// Metrics creates an Option that initializes Metrics on the Throttler, which is used to emit statistics. +func (options) Metrics(m *jaeger.Metrics) Option { + return func(o *options) { + o.metrics = m + } +} + +// Logger creates an Option that sets the logger used by the Throttler. +func (options) Logger(logger jaeger.Logger) Option { + return func(o *options) { + o.logger = logger + } +} + +// HostPort creates an Option that sets the hostPort of the local agent that keeps track of credits. +func (options) HostPort(hostPort string) Option { + return func(o *options) { + o.hostPort = hostPort + } +} + +// RefreshInterval creates an Option that sets how often the Throttler will poll local agent for +// credits. +func (options) RefreshInterval(refreshInterval time.Duration) Option { + return func(o *options) { + o.refreshInterval = refreshInterval + } +} + +// SynchronousInitialization creates an Option that determines whether the throttler should synchronously +// fetch credits from the agent when an operation is seen for the first time. This should be set to true +// if the client will be used by a short lived service that needs to ensure that credits are fetched upfront +// such that sampling or throttling occurs. +func (options) SynchronousInitialization(b bool) Option { + return func(o *options) { + o.synchronousInitialization = b + } +} + +func applyOptions(o ...Option) options { + opts := options{} + for _, option := range o { + option(&opts) + } + if opts.metrics == nil { + opts.metrics = jaeger.NewNullMetrics() + } + if opts.logger == nil { + opts.logger = jaeger.NullLogger + } + if opts.hostPort == "" { + opts.hostPort = defaultHostPort + } + if opts.refreshInterval == 0 { + opts.refreshInterval = defaultRefreshInterval + } + return opts +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/throttler/remote/options_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/throttler/remote/options_test.go new file mode 100644 index 00000000..24e06a2a --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/throttler/remote/options_test.go @@ -0,0 +1,50 @@ +// Copyright (c) 2018 The Jaeger Authors. +// +// 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. + +package remote + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/uber/jaeger-client-go" +) + +func TestDefaults(t *testing.T) { + options := applyOptions() + assert.Equal(t, "localhost:5778", options.hostPort) + assert.Equal(t, time.Second*5, options.refreshInterval) + assert.NotNil(t, options.metrics) + assert.NotNil(t, options.logger) + assert.False(t, options.synchronousInitialization) +} + +func TestOptions(t *testing.T) { + metrics := jaeger.NewNullMetrics() + logger := jaeger.NullLogger + options := applyOptions( + Options.Metrics(metrics), + Options.Logger(logger), + Options.HostPort(":"), + Options.RefreshInterval(time.Second), + Options.SynchronousInitialization(true), + ) + assert.Equal(t, ":", options.hostPort) + assert.Equal(t, time.Second, options.refreshInterval) + assert.Equal(t, metrics, options.metrics) + assert.Equal(t, logger, options.logger) + assert.True(t, options.synchronousInitialization) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/throttler/remote/throttler.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/throttler/remote/throttler.go new file mode 100644 index 00000000..20f434fe --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/throttler/remote/throttler.go @@ -0,0 +1,216 @@ +// Copyright (c) 2018 The Jaeger Authors. +// +// 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. + +package remote + +import ( + "fmt" + "net/url" + "sync" + "sync/atomic" + "time" + + "github.com/pkg/errors" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/utils" +) + +const ( + // minimumCredits is the minimum amount of credits necessary to not be throttled. + // i.e. if currentCredits > minimumCredits, then the operation will not be throttled. + minimumCredits = 1.0 +) + +var ( + errorUUIDNotSet = errors.New("Throttler UUID must be set") +) + +type operationBalance struct { + Operation string `json:"operation"` + Balance float64 `json:"balance"` +} + +type creditResponse struct { + Balances []operationBalance `json:"balances"` +} + +type httpCreditManagerProxy struct { + hostPort string +} + +func newHTTPCreditManagerProxy(hostPort string) *httpCreditManagerProxy { + return &httpCreditManagerProxy{ + hostPort: hostPort, + } +} + +// N.B. Operations list must not be empty. +func (m *httpCreditManagerProxy) FetchCredits(uuid, serviceName string, operations []string) (*creditResponse, error) { + params := url.Values{} + params.Set("service", serviceName) + params.Set("uuid", uuid) + for _, op := range operations { + params.Add("operations", op) + } + var resp creditResponse + if err := utils.GetJSON(fmt.Sprintf("http://%s/credits?%s", m.hostPort, params.Encode()), &resp); err != nil { + return nil, errors.Wrap(err, "Failed to receive credits from agent") + } + return &resp, nil +} + +// Throttler retrieves credits from agent and uses it to throttle operations. +type Throttler struct { + options + + mux sync.RWMutex + service string + uuid atomic.Value + creditManager *httpCreditManagerProxy + credits map[string]float64 // map of operation->credits + close chan struct{} + stopped sync.WaitGroup +} + +// NewThrottler returns a Throttler that polls agent for credits and uses them to throttle +// the service. +func NewThrottler(service string, options ...Option) *Throttler { + opts := applyOptions(options...) + creditManager := newHTTPCreditManagerProxy(opts.hostPort) + t := &Throttler{ + options: opts, + creditManager: creditManager, + service: service, + credits: make(map[string]float64), + close: make(chan struct{}), + } + t.stopped.Add(1) + go t.pollManager() + return t +} + +// IsAllowed implements Throttler#IsAllowed. +func (t *Throttler) IsAllowed(operation string) bool { + t.mux.Lock() + defer t.mux.Unlock() + value, ok := t.credits[operation] + if !ok || value == 0 { + if !ok { + // NOTE: This appears to be a no-op at first glance, but it stores + // the operation key in the map. Necessary for functionality of + // Throttler#operations method. + t.credits[operation] = 0 + } + if !t.synchronousInitialization { + t.metrics.ThrottledDebugSpans.Inc(1) + return false + } + // If it is the first time this operation is being checked, synchronously fetch + // the credits. + credits, err := t.fetchCredits([]string{operation}) + if err != nil { + // Failed to receive credits from agent, try again next time + t.logger.Error("Failed to fetch credits: " + err.Error()) + return false + } + if len(credits.Balances) == 0 { + // This shouldn't happen but just in case + return false + } + for _, opBalance := range credits.Balances { + t.credits[opBalance.Operation] += opBalance.Balance + } + } + return t.isAllowed(operation) +} + +// Close stops the throttler from fetching credits from remote. +func (t *Throttler) Close() error { + close(t.close) + t.stopped.Wait() + return nil +} + +// SetProcess implements ProcessSetter#SetProcess. It's imperative that the UUID is set before any remote +// requests are made. +func (t *Throttler) SetProcess(process jaeger.Process) { + if process.UUID != "" { + t.uuid.Store(process.UUID) + } +} + +// N.B. This function must be called with the Write Lock +func (t *Throttler) isAllowed(operation string) bool { + credits := t.credits[operation] + if credits < minimumCredits { + t.metrics.ThrottledDebugSpans.Inc(1) + return false + } + t.credits[operation] = credits - minimumCredits + return true +} + +func (t *Throttler) pollManager() { + defer t.stopped.Done() + ticker := time.NewTicker(t.refreshInterval) + defer ticker.Stop() + for { + select { + case <-ticker.C: + t.refreshCredits() + case <-t.close: + return + } + } +} + +func (t *Throttler) operations() []string { + t.mux.RLock() + defer t.mux.RUnlock() + operations := make([]string, 0, len(t.credits)) + for op := range t.credits { + operations = append(operations, op) + } + return operations +} + +func (t *Throttler) refreshCredits() { + operations := t.operations() + if len(operations) == 0 { + return + } + newCredits, err := t.fetchCredits(operations) + if err != nil { + t.metrics.ThrottlerUpdateFailure.Inc(1) + t.logger.Error("Failed to fetch credits: " + err.Error()) + return + } + t.metrics.ThrottlerUpdateSuccess.Inc(1) + + t.mux.Lock() + defer t.mux.Unlock() + for _, opBalance := range newCredits.Balances { + t.credits[opBalance.Operation] += opBalance.Balance + } +} + +func (t *Throttler) fetchCredits(operations []string) (*creditResponse, error) { + uuid := t.uuid.Load() + uuidStr, _ := uuid.(string) + if uuid == nil || uuidStr == "" { + return nil, errorUUIDNotSet + } + return t.creditManager.FetchCredits(uuidStr, t.service, operations) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/throttler/remote/throttler_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/throttler/remote/throttler_test.go new file mode 100644 index 00000000..8a84ab95 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/throttler/remote/throttler_test.go @@ -0,0 +1,268 @@ +// Copyright (c) 2018 The Jaeger Authors. +// +// 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. + +package remote + +import ( + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "net/url" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/internal/throttler" + "github.com/uber/jaeger-client-go/log" + "github.com/uber/jaeger-lib/metrics" + "github.com/uber/jaeger-lib/metrics/testutils" +) + +var _ throttler.Throttler = &Throttler{} +var _ io.Closer = &Throttler{} +var _ jaeger.ProcessSetter = &Throttler{} + +var testOperation = "op" + +type creditHandler struct { + returnError bool + returnEmptyResp bool + credits float64 + lock sync.Mutex +} + +func (h *creditHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // this function can run in multiple go routines by HTTP server, so use a lock. + h.lock.Lock() + defer h.lock.Unlock() + + if h.returnError { + w.WriteHeader(http.StatusInternalServerError) + } else { + w.Header().Add("Content-Type", "application/json") + if h.returnEmptyResp { + bytes, _ := json.Marshal(map[string]float64{}) + w.Write(bytes) + return + } + operations := r.URL.Query()["operations"] + resp := creditResponse{Balances: []operationBalance{}} + for _, op := range operations { + resp.Balances = append(resp.Balances, operationBalance{ + Operation: op, + Balance: h.credits, + }) + } + h.credits = 0 + bytes, _ := json.Marshal(resp) + w.Write(bytes) + } +} + +func (h *creditHandler) setReturnError(b bool) { + h.lock.Lock() + defer h.lock.Unlock() + h.returnError = b +} + +func (h *creditHandler) setReturnEmptyResp(b bool) { + h.lock.Lock() + defer h.lock.Unlock() + h.returnEmptyResp = b +} + +func withHTTPServer( + credits float64, + f func( + m *jaeger.Metrics, + factory *metrics.LocalFactory, + handler *creditHandler, + server *httptest.Server, + ), +) { + factory := metrics.NewLocalFactory(0) + m := jaeger.NewMetrics(factory, nil) + + handler := &creditHandler{returnError: false, credits: credits} + server := httptest.NewServer(handler) + defer server.Close() + + f(m, factory, handler, server) +} + +func findOperation(resp creditResponse, operation string) *operationBalance { + for _, opBalance := range resp.Balances { + if opBalance.Operation == operation { + return &opBalance + } + } + return nil +} + +func TestCreditManager(t *testing.T) { + withHTTPServer( + 2, + func( + m *jaeger.Metrics, + factory *metrics.LocalFactory, + handler *creditHandler, + server *httptest.Server, + ) { + creditManager := newHTTPCreditManagerProxy(getHostPort(t, server.URL)) + credits, err := creditManager.FetchCredits("uuid", "svc", []string{"op1", "op2"}) + assert.NoError(t, err) + require.Len(t, credits.Balances, 2) + op1 := findOperation(*credits, "op1") + require.NotNil(t, op1) + assert.EqualValues(t, 2, op1.Balance) + + credits, err = creditManager.FetchCredits("uuid", "svc", []string{"op1"}) + assert.NoError(t, err) + require.Len(t, credits.Balances, 1) + op1 = findOperation(*credits, "op1") + assert.EqualValues(t, 0, op1.Balance) + + handler.setReturnError(true) + credits, err = creditManager.FetchCredits("uuid", "svc", []string{"op1"}) + assert.EqualError(t, err, "Failed to receive credits from agent: StatusCode: 500, Body: ") + }) +} + +func TestRemoteThrottler_fetchCreditsErrors(t *testing.T) { + withHTTPServer( + 2, + func( + m *jaeger.Metrics, + factory *metrics.LocalFactory, + handler *creditHandler, + server *httptest.Server, + ) { + logger := &log.BytesBufferLogger{} + creditManager := newHTTPCreditManagerProxy(getHostPort(t, server.URL)) + throttler := &Throttler{ + creditManager: creditManager, + service: "svc", + credits: make(map[string]float64), + options: options{logger: logger, synchronousInitialization: true, metrics: m}, + } + assert.False(t, throttler.IsAllowed(testOperation)) + assert.Equal(t, "ERROR: Failed to fetch credits: Throttler UUID must be set\n", logger.String()) + logger.Flush() + assert.False(t, throttler.IsAllowed(testOperation)) + assert.Equal(t, "ERROR: Failed to fetch credits: Throttler UUID must be set\n", logger.String()) + logger.Flush() + + throttler.SetProcess(jaeger.Process{UUID: "uuid"}) + handler.setReturnEmptyResp(true) + assert.False(t, throttler.IsAllowed(testOperation), "Received an empty response, should not be allowed") + handler.setReturnEmptyResp(false) + logger.Flush() + + throttler.SetProcess(jaeger.Process{UUID: "uuid"}) + assert.True(t, throttler.IsAllowed(testOperation)) + assert.True(t, throttler.IsAllowed(testOperation)) + assert.False(t, throttler.IsAllowed(testOperation)) + + handler.setReturnError(true) + logger.Flush() + throttler.refreshCredits() + assert.Equal(t, "ERROR: Failed to fetch credits: Failed to receive credits from agent: StatusCode: 500, Body: \n", logger.String()) + + testutils.AssertCounterMetrics(t, factory, + testutils.ExpectedMetric{ + Name: "jaeger.throttler_updates", + Tags: map[string]string{"result": "err"}, + Value: 1, + }) + }) +} + +func TestRemotelyControlledThrottler_pollManager(t *testing.T) { + withHTTPServer( + 2, + func( + m *jaeger.Metrics, + factory *metrics.LocalFactory, + handler *creditHandler, + server *httptest.Server, + ) { + throttler := NewThrottler( + "svc", + Options.RefreshInterval(time.Millisecond), + Options.HostPort(getHostPort(t, server.URL)), + Options.SynchronousInitialization(true), + Options.Metrics(m), + ) + defer throttler.Close() + throttler.refreshCredits() + throttler.SetProcess(jaeger.Process{UUID: "uuid"}) + assert.True(t, throttler.IsAllowed(testOperation)) + loopUntilCreditsReady(throttler) + assert.True(t, throttler.IsAllowed(testOperation)) + assert.False(t, throttler.IsAllowed(testOperation)) + + throttler.refreshCredits() + counters, _ := factory.Snapshot() + counter, ok := counters["jaeger.throttler_updates|result=ok"] + assert.True(t, ok) + assert.True(t, counter >= 1) + }) +} + +func TestRemotelyControlledThrottler_asynchronousInitialization(t *testing.T) { + withHTTPServer( + 2, + func( + m *jaeger.Metrics, + factory *metrics.LocalFactory, + handler *creditHandler, + server *httptest.Server, + ) { + throttler := NewThrottler( + "svc", + Options.RefreshInterval(time.Millisecond), + Options.HostPort(getHostPort(t, server.URL)), + ) + defer throttler.Close() + assert.False(t, throttler.IsAllowed(testOperation)) + throttler.SetProcess(jaeger.Process{UUID: "uuid"}) + loopUntilCreditsReady(throttler) + assert.True(t, throttler.IsAllowed(testOperation)) + assert.True(t, throttler.IsAllowed(testOperation)) + assert.False(t, throttler.IsAllowed(testOperation)) + }) +} + +func loopUntilCreditsReady(throttler *Throttler) { + for i := 0; i < 1000; i++ { + throttler.mux.RLock() + if throttler.credits[testOperation] > 0 { + throttler.mux.RUnlock() + break + } + throttler.mux.RUnlock() + time.Sleep(time.Millisecond) + } +} + +func getHostPort(t *testing.T, s string) string { + u, err := url.Parse(s) + require.NoError(t, err, "Failed to parse url") + return u.Host +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/throttler/throttler.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/throttler/throttler.go new file mode 100644 index 00000000..196ed69c --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/throttler/throttler.go @@ -0,0 +1,32 @@ +// Copyright (c) 2018 The Jaeger Authors. +// +// 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. + +package throttler + +// Throttler is used to rate limits operations. For example, given how debug spans +// are always sampled, a throttler can be enabled per client to rate limit the amount +// of debug spans a client can start. +type Throttler interface { + // IsAllowed determines whether the operation should be allowed and not be + // throttled. + IsAllowed(operation string) bool +} + +// DefaultThrottler doesn't throttle at all. +type DefaultThrottler struct{} + +// IsAllowed implements Throttler#IsAllowed. +func (t DefaultThrottler) IsAllowed(operation string) bool { + return true +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/throttler/throttler_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/throttler/throttler_test.go new file mode 100644 index 00000000..a41dcf2b --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/internal/throttler/throttler_test.go @@ -0,0 +1,28 @@ +// Copyright (c) 2018 The Jaeger Authors. +// +// 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. + +package throttler + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +var _ Throttler = DefaultThrottler{} + +func TestDefaultThrottler(t *testing.T) { + throttler := DefaultThrottler{} + assert.True(t, throttler.IsAllowed("")) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/interop.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/interop.go new file mode 100644 index 00000000..8402d087 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/interop.go @@ -0,0 +1,55 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "github.com/opentracing/opentracing-go" +) + +// TODO this file should not be needed after TChannel PR. + +type formatKey int + +// SpanContextFormat is a constant used as OpenTracing Format. +// Requires *SpanContext as carrier. +// This format is intended for interop with TChannel or other Zipkin-like tracers. +const SpanContextFormat formatKey = iota + +type jaegerTraceContextPropagator struct { + tracer *Tracer +} + +func (p *jaegerTraceContextPropagator) Inject( + ctx SpanContext, + abstractCarrier interface{}, +) error { + carrier, ok := abstractCarrier.(*SpanContext) + if !ok { + return opentracing.ErrInvalidCarrier + } + + carrier.CopyFrom(&ctx) + return nil +} + +func (p *jaegerTraceContextPropagator) Extract(abstractCarrier interface{}) (SpanContext, error) { + carrier, ok := abstractCarrier.(*SpanContext) + if !ok { + return emptyContext, opentracing.ErrInvalidCarrier + } + ctx := new(SpanContext) + ctx.CopyFrom(carrier) + return *ctx, nil +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/jaeger_tag.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/jaeger_tag.go new file mode 100644 index 00000000..868b2a5b --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/jaeger_tag.go @@ -0,0 +1,84 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "fmt" + + "github.com/opentracing/opentracing-go/log" + + j "github.com/uber/jaeger-client-go/thrift-gen/jaeger" +) + +type tags []*j.Tag + +// ConvertLogsToJaegerTags converts log Fields into jaeger tags. +func ConvertLogsToJaegerTags(logFields []log.Field) []*j.Tag { + fields := tags(make([]*j.Tag, 0, len(logFields))) + for _, field := range logFields { + field.Marshal(&fields) + } + return fields +} + +func (t *tags) EmitString(key, value string) { + *t = append(*t, &j.Tag{Key: key, VType: j.TagType_STRING, VStr: &value}) +} + +func (t *tags) EmitBool(key string, value bool) { + *t = append(*t, &j.Tag{Key: key, VType: j.TagType_BOOL, VBool: &value}) +} + +func (t *tags) EmitInt(key string, value int) { + vLong := int64(value) + *t = append(*t, &j.Tag{Key: key, VType: j.TagType_LONG, VLong: &vLong}) +} + +func (t *tags) EmitInt32(key string, value int32) { + vLong := int64(value) + *t = append(*t, &j.Tag{Key: key, VType: j.TagType_LONG, VLong: &vLong}) +} + +func (t *tags) EmitInt64(key string, value int64) { + *t = append(*t, &j.Tag{Key: key, VType: j.TagType_LONG, VLong: &value}) +} + +func (t *tags) EmitUint32(key string, value uint32) { + vLong := int64(value) + *t = append(*t, &j.Tag{Key: key, VType: j.TagType_LONG, VLong: &vLong}) +} + +func (t *tags) EmitUint64(key string, value uint64) { + vLong := int64(value) + *t = append(*t, &j.Tag{Key: key, VType: j.TagType_LONG, VLong: &vLong}) +} + +func (t *tags) EmitFloat32(key string, value float32) { + vDouble := float64(value) + *t = append(*t, &j.Tag{Key: key, VType: j.TagType_DOUBLE, VDouble: &vDouble}) +} + +func (t *tags) EmitFloat64(key string, value float64) { + *t = append(*t, &j.Tag{Key: key, VType: j.TagType_DOUBLE, VDouble: &value}) +} + +func (t *tags) EmitObject(key string, value interface{}) { + vStr := fmt.Sprintf("%+v", value) + *t = append(*t, &j.Tag{Key: key, VType: j.TagType_STRING, VStr: &vStr}) +} + +func (t *tags) EmitLazyLogger(value log.LazyLogger) { + value(t) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/jaeger_thrift_span.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/jaeger_thrift_span.go new file mode 100644 index 00000000..6ce1caf8 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/jaeger_thrift_span.go @@ -0,0 +1,179 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "time" + + "github.com/opentracing/opentracing-go" + + j "github.com/uber/jaeger-client-go/thrift-gen/jaeger" + "github.com/uber/jaeger-client-go/utils" +) + +// BuildJaegerThrift builds jaeger span based on internal span. +func BuildJaegerThrift(span *Span) *j.Span { + span.Lock() + defer span.Unlock() + startTime := utils.TimeToMicrosecondsSinceEpochInt64(span.startTime) + duration := span.duration.Nanoseconds() / int64(time.Microsecond) + jaegerSpan := &j.Span{ + TraceIdLow: int64(span.context.traceID.Low), + TraceIdHigh: int64(span.context.traceID.High), + SpanId: int64(span.context.spanID), + ParentSpanId: int64(span.context.parentID), + OperationName: span.operationName, + Flags: int32(span.context.flags), + StartTime: startTime, + Duration: duration, + Tags: buildTags(span.tags, span.tracer.options.maxTagValueLength), + Logs: buildLogs(span.logs), + References: buildReferences(span.references), + } + return jaegerSpan +} + +// BuildJaegerProcessThrift creates a thrift Process type. +func BuildJaegerProcessThrift(span *Span) *j.Process { + span.Lock() + defer span.Unlock() + return buildJaegerProcessThrift(span.tracer) +} + +func buildJaegerProcessThrift(tracer *Tracer) *j.Process { + process := &j.Process{ + ServiceName: tracer.serviceName, + Tags: buildTags(tracer.tags, tracer.options.maxTagValueLength), + } + if tracer.process.UUID != "" { + process.Tags = append(process.Tags, &j.Tag{Key: TracerUUIDTagKey, VStr: &tracer.process.UUID, VType: j.TagType_STRING}) + } + return process +} + +func buildTags(tags []Tag, maxTagValueLength int) []*j.Tag { + jTags := make([]*j.Tag, 0, len(tags)) + for _, tag := range tags { + jTag := buildTag(&tag, maxTagValueLength) + jTags = append(jTags, jTag) + } + return jTags +} + +func buildLogs(logs []opentracing.LogRecord) []*j.Log { + jLogs := make([]*j.Log, 0, len(logs)) + for _, log := range logs { + jLog := &j.Log{ + Timestamp: utils.TimeToMicrosecondsSinceEpochInt64(log.Timestamp), + Fields: ConvertLogsToJaegerTags(log.Fields), + } + jLogs = append(jLogs, jLog) + } + return jLogs +} + +func buildTag(tag *Tag, maxTagValueLength int) *j.Tag { + jTag := &j.Tag{Key: tag.key} + switch value := tag.value.(type) { + case string: + vStr := truncateString(value, maxTagValueLength) + jTag.VStr = &vStr + jTag.VType = j.TagType_STRING + case []byte: + if len(value) > maxTagValueLength { + value = value[:maxTagValueLength] + } + jTag.VBinary = value + jTag.VType = j.TagType_BINARY + case int: + vLong := int64(value) + jTag.VLong = &vLong + jTag.VType = j.TagType_LONG + case uint: + vLong := int64(value) + jTag.VLong = &vLong + jTag.VType = j.TagType_LONG + case int8: + vLong := int64(value) + jTag.VLong = &vLong + jTag.VType = j.TagType_LONG + case uint8: + vLong := int64(value) + jTag.VLong = &vLong + jTag.VType = j.TagType_LONG + case int16: + vLong := int64(value) + jTag.VLong = &vLong + jTag.VType = j.TagType_LONG + case uint16: + vLong := int64(value) + jTag.VLong = &vLong + jTag.VType = j.TagType_LONG + case int32: + vLong := int64(value) + jTag.VLong = &vLong + jTag.VType = j.TagType_LONG + case uint32: + vLong := int64(value) + jTag.VLong = &vLong + jTag.VType = j.TagType_LONG + case int64: + vLong := int64(value) + jTag.VLong = &vLong + jTag.VType = j.TagType_LONG + case uint64: + vLong := int64(value) + jTag.VLong = &vLong + jTag.VType = j.TagType_LONG + case float32: + vDouble := float64(value) + jTag.VDouble = &vDouble + jTag.VType = j.TagType_DOUBLE + case float64: + vDouble := float64(value) + jTag.VDouble = &vDouble + jTag.VType = j.TagType_DOUBLE + case bool: + vBool := value + jTag.VBool = &vBool + jTag.VType = j.TagType_BOOL + default: + vStr := truncateString(stringify(value), maxTagValueLength) + jTag.VStr = &vStr + jTag.VType = j.TagType_STRING + } + return jTag +} + +func buildReferences(references []Reference) []*j.SpanRef { + retMe := make([]*j.SpanRef, 0, len(references)) + for _, ref := range references { + if ref.Type == opentracing.ChildOfRef { + retMe = append(retMe, spanRef(ref.Context, j.SpanRefType_CHILD_OF)) + } else if ref.Type == opentracing.FollowsFromRef { + retMe = append(retMe, spanRef(ref.Context, j.SpanRefType_FOLLOWS_FROM)) + } + } + return retMe +} + +func spanRef(ctx SpanContext, refType j.SpanRefType) *j.SpanRef { + return &j.SpanRef{ + RefType: refType, + TraceIdLow: int64(ctx.traceID.Low), + TraceIdHigh: int64(ctx.traceID.High), + SpanId: int64(ctx.spanID), + } +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/jaeger_thrift_span_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/jaeger_thrift_span_test.go new file mode 100644 index 00000000..911114b4 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/jaeger_thrift_span_test.go @@ -0,0 +1,423 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "errors" + "fmt" + "strconv" + "testing" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/opentracing/opentracing-go/log" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + j "github.com/uber/jaeger-client-go/thrift-gen/jaeger" + "github.com/uber/jaeger-client-go/utils" +) + +var ( + someString = "str" + someBool = true + someLong = int64(123) + someDouble = float64(123) + someBinary = []byte("hello") + someSlice = []string{"a"} + someSliceString = "[a]" +) + +func TestBuildJaegerThrift(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter()) + defer closer.Close() + + sp1 := tracer.StartSpan("sp1").(*Span) + ext.SpanKindRPCServer.Set(sp1) + ext.PeerService.Set(sp1, "svc") + sp2 := tracer.StartSpan("sp2", opentracing.ChildOf(sp1.Context())).(*Span) + ext.SpanKindRPCClient.Set(sp2) + sp2.Finish() + sp1.Finish() + + jaegerSpan1 := BuildJaegerThrift(sp1) + jaegerSpan2 := BuildJaegerThrift(sp2) + assert.Equal(t, "sp1", jaegerSpan1.OperationName) + assert.Equal(t, "sp2", jaegerSpan2.OperationName) + assert.EqualValues(t, 0, jaegerSpan1.ParentSpanId) + assert.Equal(t, jaegerSpan1.SpanId, jaegerSpan2.ParentSpanId) + assert.Len(t, jaegerSpan1.Tags, 4) + tag := findTag(jaegerSpan1, SamplerTypeTagKey) + assert.Equal(t, SamplerTypeConst, *tag.VStr) + tag = findTag(jaegerSpan1, string(ext.SpanKind)) + assert.Equal(t, string(ext.SpanKindRPCServerEnum), *tag.VStr) + tag = findTag(jaegerSpan1, string(ext.PeerService)) + assert.Equal(t, "svc", *tag.VStr) + assert.Empty(t, jaegerSpan1.References) + + assert.Len(t, jaegerSpan2.References, 1) + assert.Equal(t, j.SpanRefType_CHILD_OF, jaegerSpan2.References[0].RefType) + assert.EqualValues(t, jaegerSpan1.TraceIdLow, jaegerSpan2.References[0].TraceIdLow) + assert.EqualValues(t, jaegerSpan1.TraceIdHigh, jaegerSpan2.References[0].TraceIdHigh) + assert.EqualValues(t, jaegerSpan1.SpanId, jaegerSpan2.References[0].SpanId) + tag = findTag(jaegerSpan2, string(ext.SpanKind)) + assert.Equal(t, string(ext.SpanKindRPCClientEnum), *tag.VStr) +} + +func TestBuildJaegerProcessThrift(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter()) + defer closer.Close() + + sp := tracer.StartSpan("sp1").(*Span) + sp.Finish() + + process := BuildJaegerProcessThrift(sp) + assert.Equal(t, process.ServiceName, "DOOP") + require.Len(t, process.Tags, 4) + assert.NotNil(t, findJaegerTag("jaeger.version", process.Tags)) + assert.NotNil(t, findJaegerTag("hostname", process.Tags)) + assert.NotNil(t, findJaegerTag("ip", process.Tags)) + assert.NotNil(t, findJaegerTag(TracerUUIDTagKey, process.Tags)) +} + +func TestBuildLogs(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter()) + defer closer.Close() + root := tracer.StartSpan("s1") + + someTime := time.Now().Add(-time.Minute) + someTimeInt64 := utils.TimeToMicrosecondsSinceEpochInt64(someTime) + + errString := "error" + + tests := []struct { + field log.Field + logFunc func(sp opentracing.Span) + expected []*j.Tag + expectedTimestamp int64 + disableSampling bool + }{ + {field: log.String("event", someString), expected: []*j.Tag{{Key: "event", VType: j.TagType_STRING, VStr: &someString}}}, + {field: log.String("k", someString), expected: []*j.Tag{{Key: "k", VType: j.TagType_STRING, VStr: &someString}}}, + {field: log.Bool("k", someBool), expected: []*j.Tag{{Key: "k", VType: j.TagType_BOOL, VBool: &someBool}}}, + {field: log.Int("k", 123), expected: []*j.Tag{{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}}, + {field: log.Int32("k", 123), expected: []*j.Tag{{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}}, + {field: log.Int64("k", 123), expected: []*j.Tag{{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}}, + {field: log.Uint32("k", 123), expected: []*j.Tag{{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}}, + {field: log.Uint64("k", 123), expected: []*j.Tag{{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}}, + {field: log.Float32("k", 123), expected: []*j.Tag{{Key: "k", VType: j.TagType_DOUBLE, VDouble: &someDouble}}}, + {field: log.Float64("k", 123), expected: []*j.Tag{{Key: "k", VType: j.TagType_DOUBLE, VDouble: &someDouble}}}, + {field: log.Error(errors.New(errString)), expected: []*j.Tag{{Key: "error", VType: j.TagType_STRING, VStr: &errString}}}, + {field: log.Object("k", someSlice), expected: []*j.Tag{{Key: "k", VType: j.TagType_STRING, VStr: &someSliceString}}}, + { + field: log.Lazy(func(fv log.Encoder) { + fv.EmitBool("k", someBool) + }), + expected: []*j.Tag{{Key: "k", VType: j.TagType_BOOL, VBool: &someBool}}, + }, + { + logFunc: func(sp opentracing.Span) { + sp.LogKV("event", someString) + }, + expected: []*j.Tag{{Key: "event", VType: j.TagType_STRING, VStr: &someString}}, + }, + { + logFunc: func(sp opentracing.Span) { + sp.LogKV("non-even number of arguments") + }, + // this is a bit fragile, but ¯\_(ツ)_/¯ + expected: []*j.Tag{ + {Key: "error", VType: j.TagType_STRING, VStr: getStringPtr("non-even keyValues len: 1")}, + {Key: "function", VType: j.TagType_STRING, VStr: getStringPtr("LogKV")}, + }, + }, + { + logFunc: func(sp opentracing.Span) { + sp.LogEvent(someString) + }, + expected: []*j.Tag{{Key: "event", VType: j.TagType_STRING, VStr: &someString}}, + }, + { + logFunc: func(sp opentracing.Span) { + sp.LogEventWithPayload(someString, "payload") + }, + expected: []*j.Tag{ + {Key: "event", VType: j.TagType_STRING, VStr: &someString}, + {Key: "payload", VType: j.TagType_STRING, VStr: getStringPtr("payload")}, + }, + }, + { + logFunc: func(sp opentracing.Span) { + sp.Log(opentracing.LogData{Event: someString}) + }, + expected: []*j.Tag{{Key: "event", VType: j.TagType_STRING, VStr: &someString}}, + }, + { + logFunc: func(sp opentracing.Span) { + sp.Log(opentracing.LogData{Event: someString, Payload: "payload"}) + }, + expected: []*j.Tag{ + {Key: "event", VType: j.TagType_STRING, VStr: &someString}, + {Key: "payload", VType: j.TagType_STRING, VStr: getStringPtr("payload")}, + }, + }, + { + logFunc: func(sp opentracing.Span) { + sp.FinishWithOptions(opentracing.FinishOptions{ + LogRecords: []opentracing.LogRecord{ + { + Timestamp: someTime, + Fields: []log.Field{log.String("event", someString)}, + }, + }, + }) + }, + expected: []*j.Tag{{Key: "event", VType: j.TagType_STRING, VStr: &someString}}, + expectedTimestamp: someTimeInt64, + }, + { + logFunc: func(sp opentracing.Span) { + sp.FinishWithOptions(opentracing.FinishOptions{ + BulkLogData: []opentracing.LogData{ + { + Timestamp: someTime, + Event: someString, + }, + }, + }) + }, + expected: []*j.Tag{{Key: "event", VType: j.TagType_STRING, VStr: &someString}}, + expectedTimestamp: someTimeInt64, + }, + { + logFunc: func(sp opentracing.Span) { + sp.FinishWithOptions(opentracing.FinishOptions{ + BulkLogData: []opentracing.LogData{ + { + Timestamp: someTime, + Event: someString, + Payload: "payload", + }, + }, + }) + }, + expected: []*j.Tag{ + {Key: "event", VType: j.TagType_STRING, VStr: &someString}, + {Key: "payload", VType: j.TagType_STRING, VStr: getStringPtr("payload")}, + }, + expectedTimestamp: someTimeInt64, + }, + { + disableSampling: true, + field: log.String("event", someString), + }, + { + disableSampling: true, + logFunc: func(sp opentracing.Span) { + sp.LogKV("event", someString) + }, + }, + } + for i, test := range tests { + testName := fmt.Sprintf("test-%02d", i) + sp := tracer.StartSpan(testName, opentracing.ChildOf(root.Context())) + if test.disableSampling { + ext.SamplingPriority.Set(sp, 0) + } + if test.logFunc != nil { + test.logFunc(sp) + } else if test.field != (log.Field{}) { + sp.LogFields(test.field) + } + jaegerSpan := BuildJaegerThrift(sp.(*Span)) + if test.disableSampling { + assert.Equal(t, 0, len(jaegerSpan.Logs), testName) + continue + } + assert.Equal(t, 1, len(jaegerSpan.Logs), testName) + compareTagSlices(t, test.expected, jaegerSpan.Logs[0].GetFields(), testName) + if test.expectedTimestamp != 0 { + assert.Equal(t, test.expectedTimestamp, jaegerSpan.Logs[0].Timestamp, testName) + } + } +} + +func TestBuildTags(t *testing.T) { + tests := []struct { + tag Tag + expected *j.Tag + }{ + {tag: Tag{key: "k", value: someString}, expected: &j.Tag{Key: "k", VType: j.TagType_STRING, VStr: &someString}}, + {tag: Tag{key: "k", value: int(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}, + {tag: Tag{key: "k", value: uint(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}, + {tag: Tag{key: "k", value: int8(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}, + {tag: Tag{key: "k", value: uint8(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}, + {tag: Tag{key: "k", value: int16(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}, + {tag: Tag{key: "k", value: uint16(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}, + {tag: Tag{key: "k", value: int32(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}, + {tag: Tag{key: "k", value: uint32(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}, + {tag: Tag{key: "k", value: int64(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}, + {tag: Tag{key: "k", value: uint64(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}, + {tag: Tag{key: "k", value: float32(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_DOUBLE, VDouble: &someDouble}}, + {tag: Tag{key: "k", value: float64(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_DOUBLE, VDouble: &someDouble}}, + {tag: Tag{key: "k", value: someBool}, expected: &j.Tag{Key: "k", VType: j.TagType_BOOL, VBool: &someBool}}, + {tag: Tag{key: "k", value: someBinary}, expected: &j.Tag{Key: "k", VType: j.TagType_BINARY, VBinary: someBinary}}, + {tag: Tag{key: "k", value: someSlice}, expected: &j.Tag{Key: "k", VType: j.TagType_STRING, VStr: &someSliceString}}, + } + for i, test := range tests { + testName := fmt.Sprintf("test-%02d", i) + actual := buildTags([]Tag{test.tag}, DefaultMaxTagValueLength) + assert.Len(t, actual, 1) + compareTags(t, test.expected, actual[0], testName) + } +} + +func TestBuildReferences(t *testing.T) { + references := []Reference{ + {Type: opentracing.ChildOfRef, Context: SpanContext{traceID: TraceID{High: 1, Low: 1}, spanID: SpanID(1)}}, + {Type: opentracing.FollowsFromRef, Context: SpanContext{traceID: TraceID{High: 2, Low: 2}, spanID: SpanID(2)}}, + } + spanRefs := buildReferences(references) + assert.Len(t, spanRefs, 2) + assert.Equal(t, j.SpanRefType_CHILD_OF, spanRefs[0].RefType) + assert.EqualValues(t, 1, spanRefs[0].SpanId) + assert.EqualValues(t, 1, spanRefs[0].TraceIdHigh) + assert.EqualValues(t, 1, spanRefs[0].TraceIdLow) + + assert.Equal(t, j.SpanRefType_FOLLOWS_FROM, spanRefs[1].RefType) + assert.EqualValues(t, 2, spanRefs[1].SpanId) + assert.EqualValues(t, 2, spanRefs[1].TraceIdHigh) + assert.EqualValues(t, 2, spanRefs[1].TraceIdLow) +} + +func TestJaegerSpanBaggageLogs(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter()) + defer closer.Close() + + sp := tracer.StartSpan("s1").(*Span) + sp.SetBaggageItem("auth.token", "token") + ext.SpanKindRPCServer.Set(sp) + sp.Finish() + + jaegerSpan := BuildJaegerThrift(sp) + require.Len(t, jaegerSpan.Logs, 1) + fields := jaegerSpan.Logs[0].Fields + require.Len(t, fields, 3) + assertJaegerTag(t, fields, "event", "baggage") + assertJaegerTag(t, fields, "key", "auth.token") + assertJaegerTag(t, fields, "value", "token") +} + +func TestJaegerMaxTagValueLength(t *testing.T) { + value := make([]byte, 512) + tests := []struct { + tagValueLength int + value []byte + expected []byte + }{ + {256, value, value[:256]}, + {512, value, value}, + } + + for _, test := range tests { + t.Run(strconv.Itoa(test.tagValueLength), func(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter(), + TracerOptions.MaxTagValueLength(test.tagValueLength)) + defer closer.Close() + sp := tracer.StartSpan("s1").(*Span) + sp.SetTag("tag.string", string(test.value)) + sp.SetTag("tag.bytes", test.value) + sp.Finish() + thriftSpan := BuildJaegerThrift(sp) + stringTag := findTag(thriftSpan, "tag.string") + assert.Equal(t, j.TagType_STRING, stringTag.VType) + assert.Equal(t, string(test.expected), *stringTag.VStr) + bytesTag := findTag(thriftSpan, "tag.bytes") + assert.Equal(t, j.TagType_BINARY, bytesTag.VType) + assert.Equal(t, test.expected, bytesTag.VBinary) + }) + } +} + +func assertJaegerTag(t *testing.T, tags []*j.Tag, key string, value string) { + tag := findJaegerTag(key, tags) + require.NotNil(t, tag) + assert.Equal(t, value, tag.GetVStr()) +} + +func getStringPtr(s string) *string { + return &s +} + +func findTag(span *j.Span, key string) *j.Tag { + for _, s := range span.Tags { + if s.Key == key { + return s + } + } + return nil +} + +func findJaegerTag(key string, tags []*j.Tag) *j.Tag { + for _, tag := range tags { + if tag.Key == key { + return tag + } + } + return nil +} + +func compareTagSlices(t *testing.T, expectedTags, actualTags []*j.Tag, testName string) { + assert.Equal(t, len(expectedTags), len(actualTags)) + for _, expectedTag := range expectedTags { + actualTag := findJaegerTag(expectedTag.Key, actualTags) + compareTags(t, expectedTag, actualTag, testName) + } +} + +func compareTags(t *testing.T, expected, actual *j.Tag, testName string) { + if expected == nil && actual == nil { + return + } + if expected == nil || actual == nil { + assert.Fail(t, "one of the tags is nil", testName) + return + } + assert.Equal(t, expected.Key, actual.Key, testName) + assert.Equal(t, expected.VType, actual.VType, testName) + switch expected.VType { + case j.TagType_STRING: + assert.Equal(t, *expected.VStr, *actual.VStr, testName) + case j.TagType_LONG: + assert.Equal(t, *expected.VLong, *actual.VLong, testName) + case j.TagType_DOUBLE: + assert.Equal(t, *expected.VDouble, *actual.VDouble, testName) + case j.TagType_BOOL: + assert.Equal(t, *expected.VBool, *actual.VBool, testName) + case j.TagType_BINARY: + assert.Equal(t, expected.VBinary, actual.VBinary, testName) + } +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/log/logger.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/log/logger.go new file mode 100644 index 00000000..894bb3db --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/log/logger.go @@ -0,0 +1,90 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package log + +import ( + "bytes" + "fmt" + "log" + "sync" +) + +// Logger provides an abstract interface for logging from Reporters. +// Applications can provide their own implementation of this interface to adapt +// reporters logging to whatever logging library they prefer (stdlib log, +// logrus, go-logging, etc). +type Logger interface { + // Error logs a message at error priority + Error(msg string) + + // Infof logs a message at info priority + Infof(msg string, args ...interface{}) +} + +// StdLogger is implementation of the Logger interface that delegates to default `log` package +var StdLogger = &stdLogger{} + +type stdLogger struct{} + +func (l *stdLogger) Error(msg string) { + log.Printf("ERROR: %s", msg) +} + +// Infof logs a message at info priority +func (l *stdLogger) Infof(msg string, args ...interface{}) { + log.Printf(msg, args...) +} + +// NullLogger is implementation of the Logger interface that is no-op +var NullLogger = &nullLogger{} + +type nullLogger struct{} + +func (l *nullLogger) Error(msg string) {} +func (l *nullLogger) Infof(msg string, args ...interface{}) {} + +// BytesBufferLogger implements Logger backed by a bytes.Buffer. +type BytesBufferLogger struct { + mux sync.Mutex + buf bytes.Buffer +} + +// Error implements Logger. +func (l *BytesBufferLogger) Error(msg string) { + l.mux.Lock() + l.buf.WriteString(fmt.Sprintf("ERROR: %s\n", msg)) + l.mux.Unlock() +} + +// Infof implements Logger. +func (l *BytesBufferLogger) Infof(msg string, args ...interface{}) { + l.mux.Lock() + l.buf.WriteString("INFO: " + fmt.Sprintf(msg, args...) + "\n") + l.mux.Unlock() +} + +// String returns string representation of the underlying buffer. +func (l *BytesBufferLogger) String() string { + l.mux.Lock() + defer l.mux.Unlock() + return l.buf.String() +} + +// Flush empties the underlying buffer. +func (l *BytesBufferLogger) Flush() { + l.mux.Lock() + defer l.mux.Unlock() + l.buf.Reset() +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/log/logger_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/log/logger_test.go new file mode 100644 index 00000000..129735d7 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/log/logger_test.go @@ -0,0 +1,32 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package log + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestLogger(t *testing.T) { + bbLogger := &BytesBufferLogger{} + for _, logger := range []Logger{StdLogger, NullLogger, bbLogger} { + logger.Infof("Hi %s", "there") + logger.Error("Bad wolf") + } + assert.Equal(t, "INFO: Hi there\nERROR: Bad wolf\n", bbLogger.String()) + bbLogger.Flush() + assert.Empty(t, bbLogger.String()) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/log/zap/field.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/log/zap/field.go new file mode 100644 index 00000000..a7f6683a --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/log/zap/field.go @@ -0,0 +1,60 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package zap + +import ( + "context" + "fmt" + + jaeger "github.com/uber/jaeger-client-go" + + opentracing "github.com/opentracing/opentracing-go" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +// Trace creates a field that extracts tracing information from a context and +// includes it under the "trace" key. +// +// Because the opentracing APIs don't expose this information, the returned +// zap.Field is a no-op for contexts that don't contain a span or contain a +// non-Jaeger span. +func Trace(ctx context.Context) zapcore.Field { + if ctx == nil { + return zap.Skip() + } + return zap.Object("trace", trace{ctx}) +} + +type trace struct { + ctx context.Context +} + +func (t trace) MarshalLogObject(enc zapcore.ObjectEncoder) error { + span := opentracing.SpanFromContext(t.ctx) + if span == nil { + return nil + } + j, ok := span.Context().(jaeger.SpanContext) + if !ok { + return nil + } + if !j.IsValid() { + return fmt.Errorf("invalid span: %v", j.SpanID()) + } + enc.AddString("span", j.SpanID().String()) + enc.AddString("trace", j.TraceID().String()) + return nil +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/log/zap/field_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/log/zap/field_test.go new file mode 100644 index 00000000..ee367b47 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/log/zap/field_test.go @@ -0,0 +1,64 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package zap + +import ( + "context" + "testing" + + jaeger "github.com/uber/jaeger-client-go" + + opentracing "github.com/opentracing/opentracing-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +func TestTraceField(t *testing.T) { + assert.Equal(t, zap.Skip(), Trace(nil), "Expected Trace of a nil context to be a no-op.") + + withTracedContext(func(ctx context.Context) { + enc := zapcore.NewMapObjectEncoder() + Trace(ctx).AddTo(enc) + + logged, ok := enc.Fields["trace"].(map[string]interface{}) + require.True(t, ok, "Expected trace to be a map.") + + // We could extract the span from the context and assert specific IDs, + // but that just copies the production code. Instead, just assert that + // the keys we expect are present. + keys := make(map[string]struct{}, len(logged)) + for k := range logged { + keys[k] = struct{}{} + } + assert.Equal( + t, + map[string]struct{}{"span": {}, "trace": {}}, + keys, + "Expected to log span and trace IDs.", + ) + }) +} + +func withTracedContext(f func(ctx context.Context)) { + tracer, closer := jaeger.NewTracer( + "serviceName", jaeger.NewConstSampler(true), jaeger.NewNullReporter(), + ) + defer closer.Close() + + ctx := opentracing.ContextWithSpan(context.Background(), tracer.StartSpan("test")) + f(ctx) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/log/zap/logger.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/log/zap/logger.go new file mode 100644 index 00000000..f05e568e --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/log/zap/logger.go @@ -0,0 +1,39 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package zap + +import ( + "go.uber.org/zap" +) + +// Logger is an adapter from zap Logger to jaeger-lib Logger. +type Logger struct { + logger *zap.SugaredLogger +} + +// NewLogger creates a new Logger. +func NewLogger(logger *zap.Logger) *Logger { + return &Logger{logger: logger.Sugar()} +} + +// Error logs a message at error priority +func (l *Logger) Error(msg string) { + l.logger.Error(msg) +} + +// Infof logs a message at info priority +func (l *Logger) Infof(msg string, args ...interface{}) { + l.logger.Infof(msg, args...) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/log/zap/logger_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/log/zap/logger_test.go new file mode 100644 index 00000000..325ea1ca --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/log/zap/logger_test.go @@ -0,0 +1,35 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package zap + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +func TestLogger(t *testing.T) { + buf := &bytes.Buffer{} + encoder := zapcore.NewConsoleEncoder(zapcore.EncoderConfig{MessageKey: "key"}) + logger := NewLogger(zap.New(zapcore.NewCore(encoder, zapcore.AddSync(buf), zapcore.InfoLevel))) + logger.Infof("Hi %s %d", "there", 5) + assert.Equal(t, buf.String(), "Hi there 5\n") + buf.Reset() + logger.Error("Bad wolf") + assert.Equal(t, buf.String(), "Bad wolf\n") +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/logger.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/logger.go new file mode 100644 index 00000000..d4f0b501 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/logger.go @@ -0,0 +1,53 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import "log" + +// NB This will be deprecated in 3.0.0, please use jaeger-client-go/log/logger instead. + +// Logger provides an abstract interface for logging from Reporters. +// Applications can provide their own implementation of this interface to adapt +// reporters logging to whatever logging library they prefer (stdlib log, +// logrus, go-logging, etc). +type Logger interface { + // Error logs a message at error priority + Error(msg string) + + // Infof logs a message at info priority + Infof(msg string, args ...interface{}) +} + +// StdLogger is implementation of the Logger interface that delegates to default `log` package +var StdLogger = &stdLogger{} + +type stdLogger struct{} + +func (l *stdLogger) Error(msg string) { + log.Printf("ERROR: %s", msg) +} + +// Infof logs a message at info priority +func (l *stdLogger) Infof(msg string, args ...interface{}) { + log.Printf(msg, args...) +} + +// NullLogger is implementation of the Logger interface that delegates to default `log` package +var NullLogger = &nullLogger{} + +type nullLogger struct{} + +func (l *nullLogger) Error(msg string) {} +func (l *nullLogger) Infof(msg string, args ...interface{}) {} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/logger_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/logger_test.go new file mode 100644 index 00000000..519c86a8 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/logger_test.go @@ -0,0 +1,40 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "testing" + + "github.com/uber/jaeger-client-go/log" +) + +func TestLogger(t *testing.T) { + for _, logger := range []Logger{StdLogger, NullLogger} { + logger.Infof("Hi %s", "there") + logger.Error("Bad wolf") + } +} + +func TestCompatibility(t *testing.T) { + for _, logger := range []log.Logger{StdLogger, NullLogger} { + logger.Infof("Hi %s", "there") + logger.Error("Bad wolf") + } + + for _, logger := range []Logger{log.StdLogger, log.NullLogger} { + logger.Infof("Hi %s", "there") + logger.Error("Bad wolf") + } +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/metrics.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/metrics.go new file mode 100644 index 00000000..cadb2b9c --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/metrics.go @@ -0,0 +1,107 @@ +// Copyright (c) 2017-2018 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "github.com/uber/jaeger-lib/metrics" +) + +// Metrics is a container of all stats emitted by Jaeger tracer. +type Metrics struct { + // Number of traces started by this tracer as sampled + TracesStartedSampled metrics.Counter `metric:"traces" tags:"state=started,sampled=y"` + + // Number of traces started by this tracer as not sampled + TracesStartedNotSampled metrics.Counter `metric:"traces" tags:"state=started,sampled=n"` + + // Number of externally started sampled traces this tracer joined + TracesJoinedSampled metrics.Counter `metric:"traces" tags:"state=joined,sampled=y"` + + // Number of externally started not-sampled traces this tracer joined + TracesJoinedNotSampled metrics.Counter `metric:"traces" tags:"state=joined,sampled=n"` + + // Number of sampled spans started by this tracer + SpansStartedSampled metrics.Counter `metric:"started_spans" tags:"sampled=y"` + + // Number of unsampled spans started by this tracer + SpansStartedNotSampled metrics.Counter `metric:"started_spans" tags:"sampled=n"` + + // Number of spans finished by this tracer + SpansFinished metrics.Counter `metric:"finished_spans"` + + // Number of errors decoding tracing context + DecodingErrors metrics.Counter `metric:"span_context_decoding_errors"` + + // Number of spans successfully reported + ReporterSuccess metrics.Counter `metric:"reporter_spans" tags:"result=ok"` + + // Number of spans not reported due to a Sender failure + ReporterFailure metrics.Counter `metric:"reporter_spans" tags:"result=err"` + + // Number of spans dropped due to internal queue overflow + ReporterDropped metrics.Counter `metric:"reporter_spans" tags:"result=dropped"` + + // Current number of spans in the reporter queue + ReporterQueueLength metrics.Gauge `metric:"reporter_queue_length"` + + // Number of times the Sampler succeeded to retrieve sampling strategy + SamplerRetrieved metrics.Counter `metric:"sampler_queries" tags:"result=ok"` + + // Number of times the Sampler failed to retrieve sampling strategy + SamplerQueryFailure metrics.Counter `metric:"sampler_queries" tags:"result=err"` + + // Number of times the Sampler succeeded to retrieve and update sampling strategy + SamplerUpdated metrics.Counter `metric:"sampler_updates" tags:"result=ok"` + + // Number of times the Sampler failed to update sampling strategy + SamplerUpdateFailure metrics.Counter `metric:"sampler_updates" tags:"result=err"` + + // Number of times baggage was successfully written or updated on spans. + BaggageUpdateSuccess metrics.Counter `metric:"baggage_updates" tags:"result=ok"` + + // Number of times baggage failed to write or update on spans. + BaggageUpdateFailure metrics.Counter `metric:"baggage_updates" tags:"result=err"` + + // Number of times baggage was truncated as per baggage restrictions. + BaggageTruncate metrics.Counter `metric:"baggage_truncations"` + + // Number of times baggage restrictions were successfully updated. + BaggageRestrictionsUpdateSuccess metrics.Counter `metric:"baggage_restrictions_updates" tags:"result=ok"` + + // Number of times baggage restrictions failed to update. + BaggageRestrictionsUpdateFailure metrics.Counter `metric:"baggage_restrictions_updates" tags:"result=err"` + + // Number of times debug spans were throttled. + ThrottledDebugSpans metrics.Counter `metric:"throttled_debug_spans"` + + // Number of times throttler successfully updated. + ThrottlerUpdateSuccess metrics.Counter `metric:"throttler_updates" tags:"result=ok"` + + // Number of times throttler failed to update. + ThrottlerUpdateFailure metrics.Counter `metric:"throttler_updates" tags:"result=err"` +} + +// NewMetrics creates a new Metrics struct and initializes it. +func NewMetrics(factory metrics.Factory, globalTags map[string]string) *Metrics { + m := &Metrics{} + // TODO the namespace "jaeger" should be configurable (e.g. in all-in-one "jaeger-client" would make more sense) + metrics.Init(m, factory.Namespace("jaeger", nil), globalTags) + return m +} + +// NewNullMetrics creates a new Metrics struct that won't report any metrics. +func NewNullMetrics() *Metrics { + return NewMetrics(metrics.NullFactory, nil) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/metrics/prometheus/metrics_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/metrics/prometheus/metrics_test.go new file mode 100644 index 00000000..3abd0904 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/metrics/prometheus/metrics_test.go @@ -0,0 +1,36 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package prometheus_test + +import ( + "testing" + + "github.com/prometheus/client_golang/prometheus" + "github.com/stretchr/testify/require" + jprom "github.com/uber/jaeger-lib/metrics/prometheus" + + "github.com/uber/jaeger-client-go" +) + +// TestNewPrometheusMetrics ensures that the metrics do not have conflicting dimensions and will work with Prometheus. +func TestNewPrometheusMetrics(t *testing.T) { + tags := map[string]string{"lib": "jaeger"} + + factory := jprom.New(jprom.WithRegisterer(prometheus.NewPedanticRegistry())) + m := jaeger.NewMetrics(factory, tags) + + require.NotNil(t, m.SpansStartedSampled, "counter not initialized") + require.NotNil(t, m.ReporterQueueLength, "gauge not initialized") +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/metrics_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/metrics_test.go new file mode 100644 index 00000000..09dbb372 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/metrics_test.go @@ -0,0 +1,48 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/uber/jaeger-lib/metrics" + "github.com/uber/jaeger-lib/metrics/testutils" +) + +func TestNewMetrics(t *testing.T) { + factory := metrics.NewLocalFactory(0) + m := NewMetrics(factory, map[string]string{"lib": "jaeger"}) + + require.NotNil(t, m.SpansStartedSampled, "counter not initialized") + require.NotNil(t, m.ReporterQueueLength, "gauge not initialized") + + m.SpansStartedSampled.Inc(1) + m.ReporterQueueLength.Update(11) + testutils.AssertCounterMetrics(t, factory, + testutils.ExpectedMetric{ + Name: "jaeger.started_spans", + Tags: map[string]string{"lib": "jaeger", "sampled": "y"}, + Value: 1, + }, + ) + testutils.AssertGaugeMetrics(t, factory, + testutils.ExpectedMetric{ + Name: "jaeger.reporter_queue_length", + Tags: map[string]string{"lib": "jaeger"}, + Value: 11, + }, + ) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/observer.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/observer.go new file mode 100644 index 00000000..7bbd0288 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/observer.go @@ -0,0 +1,88 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import opentracing "github.com/opentracing/opentracing-go" + +// Observer can be registered with the Tracer to receive notifications about +// new Spans. +// +// Deprecated: use jaeger.ContribObserver instead. +type Observer interface { + OnStartSpan(operationName string, options opentracing.StartSpanOptions) SpanObserver +} + +// SpanObserver is created by the Observer and receives notifications about +// other Span events. +// +// Deprecated: use jaeger.ContribSpanObserver instead. +type SpanObserver interface { + OnSetOperationName(operationName string) + OnSetTag(key string, value interface{}) + OnFinish(options opentracing.FinishOptions) +} + +// compositeObserver is a dispatcher to other observers +type compositeObserver struct { + observers []ContribObserver +} + +// compositeSpanObserver is a dispatcher to other span observers +type compositeSpanObserver struct { + observers []ContribSpanObserver +} + +// noopSpanObserver is used when there are no observers registered +// on the Tracer or none of them returns span observers from OnStartSpan. +var noopSpanObserver = &compositeSpanObserver{} + +func (o *compositeObserver) append(contribObserver ContribObserver) { + o.observers = append(o.observers, contribObserver) +} + +func (o *compositeObserver) OnStartSpan(sp opentracing.Span, operationName string, options opentracing.StartSpanOptions) ContribSpanObserver { + var spanObservers []ContribSpanObserver + for _, obs := range o.observers { + spanObs, ok := obs.OnStartSpan(sp, operationName, options) + if ok { + if spanObservers == nil { + spanObservers = make([]ContribSpanObserver, 0, len(o.observers)) + } + spanObservers = append(spanObservers, spanObs) + } + } + if len(spanObservers) == 0 { + return noopSpanObserver + } + return &compositeSpanObserver{observers: spanObservers} +} + +func (o *compositeSpanObserver) OnSetOperationName(operationName string) { + for _, obs := range o.observers { + obs.OnSetOperationName(operationName) + } +} + +func (o *compositeSpanObserver) OnSetTag(key string, value interface{}) { + for _, obs := range o.observers { + obs.OnSetTag(key, value) + } +} + +func (o *compositeSpanObserver) OnFinish(options opentracing.FinishOptions) { + for _, obs := range o.observers { + obs.OnFinish(options) + } +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/observer_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/observer_test.go new file mode 100644 index 00000000..84c8d0b9 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/observer_test.go @@ -0,0 +1,109 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "testing" + + opentracing "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/stretchr/testify/assert" +) + +func TestEmptyObserver(t *testing.T) { + tracer, closer := NewTracer("test", NewConstSampler(true), NewInMemoryReporter()) + defer closer.Close() + s := tracer.StartSpan("test", ext.RPCServerOption(nil)) + s.Finish() + assert.Equal(t, s.(*Span).observer, noopSpanObserver) +} + +func TestObservers(t *testing.T) { + tracer, closer := NewTracer( + "test", + NewConstSampler(true), + NewInMemoryReporter(), + TracerOptions.Observer(testObserver{}), + TracerOptions.Observer(testObserver{}), + ) + defer closer.Close() + + s := tracer.StartSpan("test", ext.RPCServerOption(nil)) + + forEachObs := func(f func(so *testSpanObserver)) { + observers := s.(*Span).observer.(*compositeSpanObserver).observers + assert.Len(t, observers, 2) + for _, so := range observers { + f(so.(*testSpanObserver)) + } + } + + forEachObs(func(so *testSpanObserver) { + assert.Equal(t, testSpanObserver{ + operationName: "test", + tags: map[string]interface{}{ + "span.kind": ext.SpanKindRPCServerEnum, + }, + }, *so) + }) + + s.SetOperationName("test2") + s.SetTag("bender", "rodriguez") + forEachObs(func(so *testSpanObserver) { + assert.Equal(t, testSpanObserver{ + operationName: "test2", + tags: map[string]interface{}{ + "span.kind": ext.SpanKindRPCServerEnum, + "bender": "rodriguez", + }, + }, *so) + }) + + s.Finish() + forEachObs(func(so *testSpanObserver) { + assert.True(t, so.finished) + }) +} + +type testObserver struct{} + +type testSpanObserver struct { + operationName string + tags map[string]interface{} + finished bool +} + +func (o testObserver) OnStartSpan(operationName string, options opentracing.StartSpanOptions) SpanObserver { + tags := make(map[string]interface{}) + for k, v := range options.Tags { + tags[k] = v + } + return &testSpanObserver{ + operationName: operationName, + tags: tags, + } +} + +func (o *testSpanObserver) OnSetOperationName(operationName string) { + o.operationName = operationName +} + +func (o *testSpanObserver) OnSetTag(key string, value interface{}) { + o.tags[key] = value +} + +func (o *testSpanObserver) OnFinish(options opentracing.FinishOptions) { + o.finished = true +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/process.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/process.go new file mode 100644 index 00000000..30cbf996 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/process.go @@ -0,0 +1,29 @@ +// Copyright (c) 2018 The Jaeger Authors. +// +// 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. + +package jaeger + +// Process holds process specific metadata that's relevant to this client. +type Process struct { + Service string + UUID string + Tags []Tag +} + +// ProcessSetter sets a process. This can be used by any class that requires +// the process to be set as part of initialization. +// See internal/throttler/remote/throttler.go for an example. +type ProcessSetter interface { + SetProcess(process Process) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/propagation.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/propagation.go new file mode 100644 index 00000000..abca67a3 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/propagation.go @@ -0,0 +1,300 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "log" + "net/url" + "strings" + "sync" + + opentracing "github.com/opentracing/opentracing-go" +) + +// Injector is responsible for injecting SpanContext instances in a manner suitable +// for propagation via a format-specific "carrier" object. Typically the +// injection will take place across an RPC boundary, but message queues and +// other IPC mechanisms are also reasonable places to use an Injector. +type Injector interface { + // Inject takes `SpanContext` and injects it into `carrier`. The actual type + // of `carrier` depends on the `format` passed to `Tracer.Inject()`. + // + // Implementations may return opentracing.ErrInvalidCarrier or any other + // implementation-specific error if injection fails. + Inject(ctx SpanContext, carrier interface{}) error +} + +// Extractor is responsible for extracting SpanContext instances from a +// format-specific "carrier" object. Typically the extraction will take place +// on the server side of an RPC boundary, but message queues and other IPC +// mechanisms are also reasonable places to use an Extractor. +type Extractor interface { + // Extract decodes a SpanContext instance from the given `carrier`, + // or (nil, opentracing.ErrSpanContextNotFound) if no context could + // be found in the `carrier`. + Extract(carrier interface{}) (SpanContext, error) +} + +type textMapPropagator struct { + headerKeys *HeadersConfig + metrics Metrics + encodeValue func(string) string + decodeValue func(string) string +} + +func newTextMapPropagator(headerKeys *HeadersConfig, metrics Metrics) *textMapPropagator { + return &textMapPropagator{ + headerKeys: headerKeys, + metrics: metrics, + encodeValue: func(val string) string { + return val + }, + decodeValue: func(val string) string { + return val + }, + } +} + +func newHTTPHeaderPropagator(headerKeys *HeadersConfig, metrics Metrics) *textMapPropagator { + return &textMapPropagator{ + headerKeys: headerKeys, + metrics: metrics, + encodeValue: func(val string) string { + return url.QueryEscape(val) + }, + decodeValue: func(val string) string { + // ignore decoding errors, cannot do anything about them + if v, err := url.QueryUnescape(val); err == nil { + return v + } + return val + }, + } +} + +type binaryPropagator struct { + tracer *Tracer + buffers sync.Pool +} + +func newBinaryPropagator(tracer *Tracer) *binaryPropagator { + return &binaryPropagator{ + tracer: tracer, + buffers: sync.Pool{New: func() interface{} { return &bytes.Buffer{} }}, + } +} + +func (p *textMapPropagator) Inject( + sc SpanContext, + abstractCarrier interface{}, +) error { + textMapWriter, ok := abstractCarrier.(opentracing.TextMapWriter) + if !ok { + return opentracing.ErrInvalidCarrier + } + + // Do not encode the string with trace context to avoid accidental double-encoding + // if people are using opentracing < 0.10.0. Our colon-separated representation + // of the trace context is already safe for HTTP headers. + textMapWriter.Set(p.headerKeys.TraceContextHeaderName, sc.String()) + for k, v := range sc.baggage { + safeKey := p.addBaggageKeyPrefix(k) + safeVal := p.encodeValue(v) + textMapWriter.Set(safeKey, safeVal) + } + return nil +} + +func (p *textMapPropagator) Extract(abstractCarrier interface{}) (SpanContext, error) { + textMapReader, ok := abstractCarrier.(opentracing.TextMapReader) + if !ok { + return emptyContext, opentracing.ErrInvalidCarrier + } + var ctx SpanContext + var baggage map[string]string + err := textMapReader.ForeachKey(func(rawKey, value string) error { + key := strings.ToLower(rawKey) // TODO not necessary for plain TextMap + if key == p.headerKeys.TraceContextHeaderName { + var err error + safeVal := p.decodeValue(value) + if ctx, err = ContextFromString(safeVal); err != nil { + return err + } + } else if key == p.headerKeys.JaegerDebugHeader { + ctx.debugID = p.decodeValue(value) + } else if key == p.headerKeys.JaegerBaggageHeader { + if baggage == nil { + baggage = make(map[string]string) + } + for k, v := range p.parseCommaSeparatedMap(value) { + baggage[k] = v + } + } else if strings.HasPrefix(key, p.headerKeys.TraceBaggageHeaderPrefix) { + if baggage == nil { + baggage = make(map[string]string) + } + safeKey := p.removeBaggageKeyPrefix(key) + safeVal := p.decodeValue(value) + baggage[safeKey] = safeVal + } + return nil + }) + if err != nil { + p.metrics.DecodingErrors.Inc(1) + return emptyContext, err + } + if !ctx.traceID.IsValid() && ctx.debugID == "" && len(baggage) == 0 { + return emptyContext, opentracing.ErrSpanContextNotFound + } + ctx.baggage = baggage + return ctx, nil +} + +func (p *binaryPropagator) Inject( + sc SpanContext, + abstractCarrier interface{}, +) error { + carrier, ok := abstractCarrier.(io.Writer) + if !ok { + return opentracing.ErrInvalidCarrier + } + + // Handle the tracer context + if err := binary.Write(carrier, binary.BigEndian, sc.traceID); err != nil { + return err + } + if err := binary.Write(carrier, binary.BigEndian, sc.spanID); err != nil { + return err + } + if err := binary.Write(carrier, binary.BigEndian, sc.parentID); err != nil { + return err + } + if err := binary.Write(carrier, binary.BigEndian, sc.flags); err != nil { + return err + } + + // Handle the baggage items + if err := binary.Write(carrier, binary.BigEndian, int32(len(sc.baggage))); err != nil { + return err + } + for k, v := range sc.baggage { + if err := binary.Write(carrier, binary.BigEndian, int32(len(k))); err != nil { + return err + } + io.WriteString(carrier, k) + if err := binary.Write(carrier, binary.BigEndian, int32(len(v))); err != nil { + return err + } + io.WriteString(carrier, v) + } + + return nil +} + +func (p *binaryPropagator) Extract(abstractCarrier interface{}) (SpanContext, error) { + carrier, ok := abstractCarrier.(io.Reader) + if !ok { + return emptyContext, opentracing.ErrInvalidCarrier + } + var ctx SpanContext + + if err := binary.Read(carrier, binary.BigEndian, &ctx.traceID); err != nil { + return emptyContext, opentracing.ErrSpanContextCorrupted + } + if err := binary.Read(carrier, binary.BigEndian, &ctx.spanID); err != nil { + return emptyContext, opentracing.ErrSpanContextCorrupted + } + if err := binary.Read(carrier, binary.BigEndian, &ctx.parentID); err != nil { + return emptyContext, opentracing.ErrSpanContextCorrupted + } + if err := binary.Read(carrier, binary.BigEndian, &ctx.flags); err != nil { + return emptyContext, opentracing.ErrSpanContextCorrupted + } + + // Handle the baggage items + var numBaggage int32 + if err := binary.Read(carrier, binary.BigEndian, &numBaggage); err != nil { + return emptyContext, opentracing.ErrSpanContextCorrupted + } + if iNumBaggage := int(numBaggage); iNumBaggage > 0 { + ctx.baggage = make(map[string]string, iNumBaggage) + buf := p.buffers.Get().(*bytes.Buffer) + defer p.buffers.Put(buf) + + var keyLen, valLen int32 + for i := 0; i < iNumBaggage; i++ { + if err := binary.Read(carrier, binary.BigEndian, &keyLen); err != nil { + return emptyContext, opentracing.ErrSpanContextCorrupted + } + buf.Reset() + buf.Grow(int(keyLen)) + if n, err := io.CopyN(buf, carrier, int64(keyLen)); err != nil || int32(n) != keyLen { + return emptyContext, opentracing.ErrSpanContextCorrupted + } + key := buf.String() + + if err := binary.Read(carrier, binary.BigEndian, &valLen); err != nil { + return emptyContext, opentracing.ErrSpanContextCorrupted + } + buf.Reset() + buf.Grow(int(valLen)) + if n, err := io.CopyN(buf, carrier, int64(valLen)); err != nil || int32(n) != valLen { + return emptyContext, opentracing.ErrSpanContextCorrupted + } + ctx.baggage[key] = buf.String() + } + } + + return ctx, nil +} + +// Converts a comma separated key value pair list into a map +// e.g. key1=value1, key2=value2, key3 = value3 +// is converted to map[string]string { "key1" : "value1", +// "key2" : "value2", +// "key3" : "value3" } +func (p *textMapPropagator) parseCommaSeparatedMap(value string) map[string]string { + baggage := make(map[string]string) + value, err := url.QueryUnescape(value) + if err != nil { + log.Printf("Unable to unescape %s, %v", value, err) + return baggage + } + for _, kvpair := range strings.Split(value, ",") { + kv := strings.Split(strings.TrimSpace(kvpair), "=") + if len(kv) == 2 { + baggage[kv[0]] = kv[1] + } else { + log.Printf("Malformed value passed in for %s", p.headerKeys.JaegerBaggageHeader) + } + } + return baggage +} + +// Converts a baggage item key into an http header format, +// by prepending TraceBaggageHeaderPrefix and encoding the key string +func (p *textMapPropagator) addBaggageKeyPrefix(key string) string { + // TODO encodeBaggageKeyAsHeader add caching and escaping + return fmt.Sprintf("%v%v", p.headerKeys.TraceBaggageHeaderPrefix, key) +} + +func (p *textMapPropagator) removeBaggageKeyPrefix(key string) string { + // TODO decodeBaggageHeaderKey add caching and escaping + return key[len(p.headerKeys.TraceBaggageHeaderPrefix):] +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/propagation_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/propagation_test.go new file mode 100644 index 00000000..fb419af2 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/propagation_test.go @@ -0,0 +1,295 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "bytes" + "net/http" + "testing" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber/jaeger-lib/metrics" + "github.com/uber/jaeger-lib/metrics/testutils" +) + +func initMetrics() (*metrics.LocalFactory, *Metrics) { + factory := metrics.NewLocalFactory(0) + return factory, NewMetrics(factory, nil) +} + +func TestSpanPropagator(t *testing.T) { + const op = "test" + reporter := NewInMemoryReporter() + metricsFactory, metrics := initMetrics() + tracer, closer := NewTracer("x", NewConstSampler(true), reporter, TracerOptions.Metrics(metrics), TracerOptions.ZipkinSharedRPCSpan(true)) + + mapc := opentracing.TextMapCarrier(make(map[string]string)) + httpc := opentracing.HTTPHeadersCarrier(http.Header{}) + tests := []struct { + format, carrier, formatName interface{} + }{ + {SpanContextFormat, new(SpanContext), "TraceContextFormat"}, + {opentracing.Binary, new(bytes.Buffer), "Binary"}, + {opentracing.TextMap, mapc, "TextMap"}, + {opentracing.HTTPHeaders, httpc, "HTTPHeaders"}, + } + + sp := tracer.StartSpan(op) + sp.SetTag("x", "y") // to avoid later comparing nil vs. [] + sp.SetBaggageItem("foo", "bar") + for _, test := range tests { + // starting normal child to extract its serialized context + child := tracer.StartSpan(op, opentracing.ChildOf(sp.Context())) + err := tracer.Inject(child.Context(), test.format, test.carrier) + assert.NoError(t, err) + // Note: we're not finishing the above span + childCtx, err := tracer.Extract(test.format, test.carrier) + assert.NoError(t, err) + child = tracer.StartSpan(test.formatName.(string), ext.RPCServerOption(childCtx)) + child.SetTag("x", "y") // to avoid later comparing nil vs. [] + child.Finish() + } + sp.Finish() + closer.Close() + + otSpans := reporter.GetSpans() + require.Equal(t, len(tests)+1, len(otSpans), "unexpected number of spans reporter") + + spans := make([]*Span, len(otSpans)) + for i, s := range otSpans { + spans[i] = s.(*Span) + } + + // The last span is the original one. + exp, spans := spans[len(spans)-1], spans[:len(spans)-1] + exp.duration = time.Duration(123) + exp.startTime = time.Time{}.Add(1) + require.Len(t, exp.logs, 1) // The parent span should have baggage logs + fields := exp.logs[0].Fields + require.Len(t, fields, 3) + require.Equal(t, "event", fields[0].Key()) + require.Equal(t, "baggage", fields[0].Value().(string)) + require.Equal(t, "key", fields[1].Key()) + require.Equal(t, "foo", fields[1].Value().(string)) + require.Equal(t, "value", fields[2].Key()) + require.Equal(t, "bar", fields[2].Value().(string)) + + if exp.context.ParentID() != 0 { + t.Fatalf("Root span's ParentID %d is not 0", exp.context.ParentID()) + } + + expTags := exp.tags[2:] // skip two sampler.xxx tags + for i, sp := range spans { + formatName := sp.operationName + if a, e := sp.context.ParentID(), exp.context.SpanID(); a != e { + t.Fatalf("%d: ParentID %d does not match expectation %d", i, a, e) + } else { + // Prepare for comparison. + sp.context.spanID, sp.context.parentID = exp.context.SpanID(), 0 + sp.duration, sp.startTime = exp.duration, exp.startTime + } + assert.Equal(t, exp.context, sp.context, formatName) + assert.Equal(t, "span.kind", sp.tags[0].key) + assert.Equal(t, expTags, sp.tags[1:] /*skip span.kind tag*/, formatName) + assert.Empty(t, sp.logs, formatName) + // Override collections to avoid tripping comparison on different pointers + sp.context = exp.context + sp.tags = exp.tags + sp.logs = exp.logs + sp.operationName = op + sp.references = exp.references + // Compare the rest of the fields + assert.Equal(t, exp, sp, formatName) + } + + testutils.AssertCounterMetrics(t, metricsFactory, []testutils.ExpectedMetric{ + {Name: "jaeger.started_spans", Tags: map[string]string{"sampled": "y"}, Value: 1 + 2*len(tests)}, + {Name: "jaeger.finished_spans", Value: 1 + len(tests)}, + {Name: "jaeger.traces", Tags: map[string]string{"state": "started", "sampled": "y"}, Value: 1}, + {Name: "jaeger.traces", Tags: map[string]string{"state": "joined", "sampled": "y"}, Value: len(tests)}, + }...) +} + +func TestSpanIntegrityAfterSerialize(t *testing.T) { + serializedString := "f6c385a2c57ed8d7:b04a90b7723bdc:76c385a2c57ed8d7:1" + + context, err := ContextFromString(serializedString) + require.NoError(t, err) + require.True(t, context.traceID.Low > (uint64(1)<<63)) + require.True(t, int64(context.traceID.Low) < 0) + + newSerializedString := context.String() + require.Equal(t, serializedString, newSerializedString) +} + +func TestDecodingError(t *testing.T) { + reporter := NewInMemoryReporter() + metricsFactory, metrics := initMetrics() + tracer, closer := NewTracer("x", NewConstSampler(true), reporter, TracerOptions.Metrics(metrics)) + defer closer.Close() + + badHeader := "x.x.x.x" + httpHeader := http.Header{} + httpHeader.Add(TraceContextHeaderName, badHeader) + tmc := opentracing.HTTPHeadersCarrier(httpHeader) + _, err := tracer.Extract(opentracing.HTTPHeaders, tmc) + assert.Error(t, err) + + testutils.AssertCounterMetrics(t, metricsFactory, testutils.ExpectedMetric{Name: "jaeger.span_context_decoding_errors", Value: 1}) +} + +func TestBaggagePropagationHTTP(t *testing.T) { + tracer, closer := NewTracer("DOOP", NewConstSampler(true), NewNullReporter()) + defer closer.Close() + + sp1 := tracer.StartSpan("s1").(*Span) + sp1.SetBaggageItem("Some_Key", "12345") + assert.Equal(t, "12345", sp1.BaggageItem("Some_Key"), "baggage: %+v", sp1.context.baggage) + assert.Empty(t, sp1.BaggageItem("some-KEY"), "baggage: %+v", sp1.context.baggage) + sp1.SetBaggageItem("Some_Key", "98:765") + assert.Equal(t, "98:765", sp1.BaggageItem("Some_Key"), "baggage: %+v", sp1.context.baggage) + assert.Empty(t, sp1.BaggageItem("some-KEY"), "baggage: %+v", sp1.context.baggage) + + h := http.Header{} + h.Add("header1", "value1") // make sure this does not get unmarshalled as baggage + err := tracer.Inject(sp1.Context(), opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(h)) + require.NoError(t, err) + // check that colon : was encoded as %3A + assert.Equal(t, "98%3A765", h.Get(TraceBaggageHeaderPrefix+"Some_Key"), "headers: %+v", h) + + sp2, err := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(h)) + require.NoError(t, err) + assert.Equal(t, map[string]string{"some_key": "98:765"}, sp2.(SpanContext).baggage) +} + +func TestJaegerBaggageHeader(t *testing.T) { + var testcases = []struct { + refFunc func(opentracing.SpanContext) opentracing.SpanReference + }{ + { + refFunc: opentracing.ChildOf, + }, + { + refFunc: opentracing.FollowsFrom, + }, + } + + for _, testcase := range testcases { + t.Run("", func(t *testing.T) { + metricsFactory, metrics := initMetrics() + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter(), + TracerOptions.Metrics(metrics), + ) + defer closer.Close() + + h := http.Header{} + h.Add(JaegerBaggageHeader, "key1=value1, key 2=value two") + + ctx, err := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(h)) + require.NoError(t, err) + + sp := tracer.StartSpan("root", testcase.refFunc(ctx)).(*Span) + + assert.Equal(t, "value1", sp.BaggageItem("key1")) + assert.Equal(t, "value two", sp.BaggageItem("key 2")) + + // ensure that traces.started counter is incremented, not traces.joined + testutils.AssertCounterMetrics(t, metricsFactory, + testutils.ExpectedMetric{ + Name: "jaeger.traces", Tags: map[string]string{"state": "started", "sampled": "y"}, Value: 1, + }, + ) + }) + } +} + +func TestParseCommaSeperatedMap(t *testing.T) { + var testcases = []struct { + in string + out map[string]string + }{ + {"hobbit=Bilbo Baggins", map[string]string{"hobbit": "Bilbo Baggins"}}, + {"hobbit=Bilbo Baggins, dwarf= Thrain", map[string]string{"hobbit": "Bilbo Baggins", "dwarf": " Thrain"}}, + {"kevin spacey=actor", map[string]string{"kevin spacey": "actor"}}, + {"kevin%20spacey=se7en%3Aactor", map[string]string{"kevin spacey": "se7en:actor"}}, + {"key1=, key2=", map[string]string{"key1": "", "key2": ""}}, + {"malformed", map[string]string{}}, + {"malformed, string", map[string]string{}}, + {"another malformed string", map[string]string{}}, + } + + for _, testcase := range testcases { + m := (&textMapPropagator{ + headerKeys: getDefaultHeadersConfig(), + }).parseCommaSeparatedMap(testcase.in) + assert.Equal(t, testcase.out, m) + } +} + +func TestDebugCorrelationID(t *testing.T) { + var testcases = []struct { + refType string + refFunc func(opentracing.SpanContext) opentracing.SpanReference + }{ + { + refFunc: opentracing.ChildOf, + }, + { + refFunc: opentracing.FollowsFrom, + }, + } + + for _, testcase := range testcases { + t.Run("", func(t *testing.T) { + metricsFactory, metrics := initMetrics() + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter(), + TracerOptions.Metrics(metrics), + ) + defer closer.Close() + + h := http.Header{} + val := "value1" + h.Add(JaegerDebugHeader, val) + ctx, err := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(h)) + require.NoError(t, err) + assert.EqualValues(t, 0, ctx.(SpanContext).parentID) + assert.EqualValues(t, val, ctx.(SpanContext).debugID) + sp := tracer.StartSpan("root", testcase.refFunc(ctx)).(*Span) + assert.EqualValues(t, 0, sp.context.parentID) + assert.True(t, sp.context.traceID.IsValid()) + assert.True(t, sp.context.IsSampled()) + assert.True(t, sp.context.IsDebug()) + + tag := findDomainTag(sp, JaegerDebugHeader) + assert.NotNil(t, tag) + assert.Equal(t, val, tag.value) + + // ensure that traces.started counter is incremented, not traces.joined + testutils.AssertCounterMetrics(t, metricsFactory, + testutils.ExpectedMetric{ + Name: "jaeger.traces", Tags: map[string]string{"state": "started", "sampled": "y"}, Value: 1, + }, + ) + }) + } +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/reference.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/reference.go new file mode 100644 index 00000000..5646e78b --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/reference.go @@ -0,0 +1,23 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import "github.com/opentracing/opentracing-go" + +// Reference represents a causal reference to other Spans (via their SpanContext). +type Reference struct { + Type opentracing.SpanReferenceType + Context SpanContext +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/reporter.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/reporter.go new file mode 100644 index 00000000..fe6288c4 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/reporter.go @@ -0,0 +1,289 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "fmt" + "sync" + "sync/atomic" + "time" + + "github.com/opentracing/opentracing-go" + + "github.com/uber/jaeger-client-go/log" +) + +// Reporter is called by the tracer when a span is completed to report the span to the tracing collector. +type Reporter interface { + // Report submits a new span to collectors, possibly asynchronously and/or with buffering. + Report(span *Span) + + // Close does a clean shutdown of the reporter, flushing any traces that may be buffered in memory. + Close() +} + +// ------------------------------ + +type nullReporter struct{} + +// NewNullReporter creates a no-op reporter that ignores all reported spans. +func NewNullReporter() Reporter { + return &nullReporter{} +} + +// Report implements Report() method of Reporter by doing nothing. +func (r *nullReporter) Report(span *Span) { + // no-op +} + +// Close implements Close() method of Reporter by doing nothing. +func (r *nullReporter) Close() { + // no-op +} + +// ------------------------------ + +type loggingReporter struct { + logger Logger +} + +// NewLoggingReporter creates a reporter that logs all reported spans to provided logger. +func NewLoggingReporter(logger Logger) Reporter { + return &loggingReporter{logger} +} + +// Report implements Report() method of Reporter by logging the span to the logger. +func (r *loggingReporter) Report(span *Span) { + r.logger.Infof("Reporting span %+v", span) +} + +// Close implements Close() method of Reporter by doing nothing. +func (r *loggingReporter) Close() { + // no-op +} + +// ------------------------------ + +// InMemoryReporter is used for testing, and simply collects spans in memory. +type InMemoryReporter struct { + spans []opentracing.Span + lock sync.Mutex +} + +// NewInMemoryReporter creates a reporter that stores spans in memory. +// NOTE: the Tracer should be created with options.PoolSpans = false. +func NewInMemoryReporter() *InMemoryReporter { + return &InMemoryReporter{ + spans: make([]opentracing.Span, 0, 10), + } +} + +// Report implements Report() method of Reporter by storing the span in the buffer. +func (r *InMemoryReporter) Report(span *Span) { + r.lock.Lock() + r.spans = append(r.spans, span) + r.lock.Unlock() +} + +// Close implements Close() method of Reporter by doing nothing. +func (r *InMemoryReporter) Close() { + // no-op +} + +// SpansSubmitted returns the number of spans accumulated in the buffer. +func (r *InMemoryReporter) SpansSubmitted() int { + r.lock.Lock() + defer r.lock.Unlock() + return len(r.spans) +} + +// GetSpans returns accumulated spans as a copy of the buffer. +func (r *InMemoryReporter) GetSpans() []opentracing.Span { + r.lock.Lock() + defer r.lock.Unlock() + copied := make([]opentracing.Span, len(r.spans)) + copy(copied, r.spans) + return copied +} + +// Reset clears all accumulated spans. +func (r *InMemoryReporter) Reset() { + r.lock.Lock() + defer r.lock.Unlock() + r.spans = nil +} + +// ------------------------------ + +type compositeReporter struct { + reporters []Reporter +} + +// NewCompositeReporter creates a reporter that ignores all reported spans. +func NewCompositeReporter(reporters ...Reporter) Reporter { + return &compositeReporter{reporters: reporters} +} + +// Report implements Report() method of Reporter by delegating to each underlying reporter. +func (r *compositeReporter) Report(span *Span) { + for _, reporter := range r.reporters { + reporter.Report(span) + } +} + +// Close implements Close() method of Reporter by closing each underlying reporter. +func (r *compositeReporter) Close() { + for _, reporter := range r.reporters { + reporter.Close() + } +} + +// ------------- REMOTE REPORTER ----------------- + +type reporterQueueItemType int + +const ( + defaultQueueSize = 100 + defaultBufferFlushInterval = 1 * time.Second + + reporterQueueItemSpan reporterQueueItemType = iota + reporterQueueItemClose +) + +type reporterQueueItem struct { + itemType reporterQueueItemType + span *Span + close *sync.WaitGroup +} + +type remoteReporter struct { + // These fields must be first in the struct because `sync/atomic` expects 64-bit alignment. + // Cf. https://github.com/uber/jaeger-client-go/issues/155, https://goo.gl/zW7dgq + queueLength int64 + closed int64 // 0 - not closed, 1 - closed + + reporterOptions + + sender Transport + queue chan reporterQueueItem +} + +// NewRemoteReporter creates a new reporter that sends spans out of process by means of Sender. +// Calls to Report(Span) return immediately (side effect: if internal buffer is full the span is dropped). +// Periodically the transport buffer is flushed even if it hasn't reached max packet size. +// Calls to Close() block until all spans reported prior to the call to Close are flushed. +func NewRemoteReporter(sender Transport, opts ...ReporterOption) Reporter { + options := reporterOptions{} + for _, option := range opts { + option(&options) + } + if options.bufferFlushInterval <= 0 { + options.bufferFlushInterval = defaultBufferFlushInterval + } + if options.logger == nil { + options.logger = log.NullLogger + } + if options.metrics == nil { + options.metrics = NewNullMetrics() + } + if options.queueSize <= 0 { + options.queueSize = defaultQueueSize + } + reporter := &remoteReporter{ + reporterOptions: options, + sender: sender, + queue: make(chan reporterQueueItem, options.queueSize), + } + go reporter.processQueue() + return reporter +} + +// Report implements Report() method of Reporter. +// It passes the span to a background go-routine for submission to Jaeger backend. +// If the internal queue is full, the span is dropped and metrics.ReporterDropped counter is incremented. +// If Report() is called after the reporter has been Close()-ed, the additional spans will not be +// sent to the backend, but the metrics.ReporterDropped counter may not reflect them correctly, +// because some of them may still be successfully added to the queue. +func (r *remoteReporter) Report(span *Span) { + select { + case r.queue <- reporterQueueItem{itemType: reporterQueueItemSpan, span: span}: + atomic.AddInt64(&r.queueLength, 1) + default: + r.metrics.ReporterDropped.Inc(1) + } +} + +// Close implements Close() method of Reporter by waiting for the queue to be drained. +func (r *remoteReporter) Close() { + if swapped := atomic.CompareAndSwapInt64(&r.closed, 0, 1); !swapped { + r.logger.Error("Repeated attempt to close the reporter is ignored") + return + } + r.sendCloseEvent() + r.sender.Close() +} + +func (r *remoteReporter) sendCloseEvent() { + wg := &sync.WaitGroup{} + wg.Add(1) + item := reporterQueueItem{itemType: reporterQueueItemClose, close: wg} + + r.queue <- item // if the queue is full we will block until there is space + atomic.AddInt64(&r.queueLength, 1) + wg.Wait() +} + +// processQueue reads spans from the queue, converts them to Thrift, and stores them in an internal buffer. +// When the buffer length reaches batchSize, it is flushed by submitting the accumulated spans to Jaeger. +// Buffer also gets flushed automatically every batchFlushInterval seconds, just in case the tracer stopped +// reporting new spans. +func (r *remoteReporter) processQueue() { + // flush causes the Sender to flush its accumulated spans and clear the buffer + flush := func() { + if flushed, err := r.sender.Flush(); err != nil { + r.metrics.ReporterFailure.Inc(int64(flushed)) + r.logger.Error(fmt.Sprintf("error when flushing the buffer: %s", err.Error())) + } else if flushed > 0 { + r.metrics.ReporterSuccess.Inc(int64(flushed)) + } + } + + timer := time.NewTicker(r.bufferFlushInterval) + for { + select { + case <-timer.C: + flush() + case item := <-r.queue: + atomic.AddInt64(&r.queueLength, -1) + switch item.itemType { + case reporterQueueItemSpan: + span := item.span + if flushed, err := r.sender.Append(span); err != nil { + r.metrics.ReporterFailure.Inc(int64(flushed)) + r.logger.Error(fmt.Sprintf("error reporting span %q: %s", span.OperationName(), err.Error())) + } else if flushed > 0 { + r.metrics.ReporterSuccess.Inc(int64(flushed)) + // to reduce the number of gauge stats, we only emit queue length on flush + r.metrics.ReporterQueueLength.Update(atomic.LoadInt64(&r.queueLength)) + } + case reporterQueueItemClose: + timer.Stop() + flush() + item.close.Done() + return + } + } + } +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/reporter_options.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/reporter_options.go new file mode 100644 index 00000000..65012d70 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/reporter_options.go @@ -0,0 +1,69 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "time" +) + +// ReporterOption is a function that sets some option on the reporter. +type ReporterOption func(c *reporterOptions) + +// ReporterOptions is a factory for all available ReporterOption's +var ReporterOptions reporterOptions + +// reporterOptions control behavior of the reporter. +type reporterOptions struct { + // queueSize is the size of internal queue where reported spans are stored before they are processed in the background + queueSize int + // bufferFlushInterval is how often the buffer is force-flushed, even if it's not full + bufferFlushInterval time.Duration + // logger is used to log errors of span submissions + logger Logger + // metrics is used to record runtime stats + metrics *Metrics +} + +// QueueSize creates a ReporterOption that sets the size of the internal queue where +// spans are stored before they are processed. +func (reporterOptions) QueueSize(queueSize int) ReporterOption { + return func(r *reporterOptions) { + r.queueSize = queueSize + } +} + +// Metrics creates a ReporterOption that initializes Metrics in the reporter, +// which is used to record runtime statistics. +func (reporterOptions) Metrics(metrics *Metrics) ReporterOption { + return func(r *reporterOptions) { + r.metrics = metrics + } +} + +// BufferFlushInterval creates a ReporterOption that sets how often the queue +// is force-flushed. +func (reporterOptions) BufferFlushInterval(bufferFlushInterval time.Duration) ReporterOption { + return func(r *reporterOptions) { + r.bufferFlushInterval = bufferFlushInterval + } +} + +// Logger creates a ReporterOption that initializes the logger used to log +// errors of span submissions. +func (reporterOptions) Logger(logger Logger) ReporterOption { + return func(r *reporterOptions) { + r.logger = logger + } +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/reporter_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/reporter_test.go new file mode 100644 index 00000000..ddc39fb5 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/reporter_test.go @@ -0,0 +1,378 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "errors" + "io" + "strings" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber/jaeger-lib/metrics" + mTestutils "github.com/uber/jaeger-lib/metrics/testutils" + + "github.com/uber/jaeger-client-go/log" + "github.com/uber/jaeger-client-go/testutils" + j "github.com/uber/jaeger-client-go/thrift-gen/jaeger" +) + +type reporterSuite struct { + tracer opentracing.Tracer + closer io.Closer + serviceName string + reporter *remoteReporter + sender *fakeSender + metricsFactory *metrics.LocalFactory + logger *log.BytesBufferLogger +} + +func makeReporterSuite(t *testing.T, opts ...ReporterOption) *reporterSuite { + return makeReporterSuiteWithSender(t, &fakeSender{bufferSize: 5}, opts...) +} + +func makeReporterSuiteWithSender(t *testing.T, sender *fakeSender, opts ...ReporterOption) *reporterSuite { + s := &reporterSuite{ + metricsFactory: metrics.NewLocalFactory(0), + serviceName: "DOOP", + sender: sender, + logger: &log.BytesBufferLogger{}, + } + metrics := NewMetrics(s.metricsFactory, nil) + opts = append([]ReporterOption{ + ReporterOptions.Metrics(metrics), + ReporterOptions.Logger(s.logger), + ReporterOptions.BufferFlushInterval(100 * time.Second), + }, opts...) + s.reporter = NewRemoteReporter(s.sender, opts...).(*remoteReporter) + s.tracer, s.closer = NewTracer( + "reporter-test-service", + NewConstSampler(true), + s.reporter, + // TracerOptions.Metrics(metrics), + ) + require.NotNil(t, s.tracer) + return s +} + +func (s *reporterSuite) close() { + s.closer.Close() +} + +func (s *reporterSuite) assertCounter(t *testing.T, name string, tags map[string]string, expectedValue int64) { + getValue := func() int64 { + counters, _ := s.metricsFactory.Snapshot() + key := metrics.GetKey(name, tags, "|", "=") + return counters[key] + } + for i := 0; i < 1000; i++ { + if getValue() == expectedValue { + break + } + time.Sleep(time.Millisecond) + } + assert.Equal(t, expectedValue, getValue(), "expected counter: name=%s, tags=%+v", name, tags) +} + +func (s *reporterSuite) assertLogs(t *testing.T, expectedLogs string) { + for i := 0; i < 1000; i++ { + if s.logger.String() == expectedLogs { + break + } + time.Sleep(time.Millisecond) + } + assert.Equal(t, expectedLogs, s.logger.String(), "expected logs: %s", expectedLogs) +} + +func TestRemoteReporterAppend(t *testing.T) { + s := makeReporterSuite(t) + defer s.close() + s.tracer.StartSpan("sp1").Finish() + s.sender.assertBufferedSpans(t, 1) +} + +func TestRemoteReporterAppendAndPeriodicFlush(t *testing.T) { + s := makeReporterSuite(t, ReporterOptions.BufferFlushInterval(50*time.Millisecond)) + defer s.close() + s.tracer.StartSpan("sp1").Finish() + s.sender.assertBufferedSpans(t, 1) + // here we wait for periodic flush to occur + s.sender.assertFlushedSpans(t, 1) + s.assertCounter(t, "jaeger.reporter_spans", map[string]string{"result": "ok"}, 1) +} + +func TestRemoteReporterFlushViaAppend(t *testing.T) { + s := makeReporterSuiteWithSender(t, &fakeSender{bufferSize: 2}) + defer s.close() + s.tracer.StartSpan("sp1").Finish() + s.tracer.StartSpan("sp2").Finish() + s.sender.assertFlushedSpans(t, 2) + s.tracer.StartSpan("sp3").Finish() + s.sender.assertBufferedSpans(t, 1) + s.assertCounter(t, "jaeger.reporter_spans", map[string]string{"result": "ok"}, 2) + s.assertCounter(t, "jaeger.reporter_spans", map[string]string{"result": "err"}, 0) +} + +func TestRemoteReporterFailedFlushViaAppend(t *testing.T) { + s := makeReporterSuiteWithSender(t, &fakeSender{bufferSize: 2, flushErr: errors.New("flush error")}, ReporterOptions.BufferFlushInterval(100*time.Second)) + s.tracer.StartSpan("sp1").Finish() + s.tracer.StartSpan("sp2").Finish() + s.sender.assertFlushedSpans(t, 2) + s.assertLogs(t, "ERROR: error reporting span \"sp2\": flush error\n") + s.assertCounter(t, "jaeger.reporter_spans", map[string]string{"result": "err"}, 2) + s.assertCounter(t, "jaeger.reporter_spans", map[string]string{"result": "ok"}, 0) + s.close() // causes explicit flush that also fails with the same error + s.assertLogs(t, "ERROR: error reporting span \"sp2\": flush error\nERROR: error when flushing the buffer: flush error\n") +} + +func TestRemoteReporterDroppedSpans(t *testing.T) { + s := makeReporterSuite(t, ReporterOptions.QueueSize(1)) + defer s.close() + + s.reporter.sendCloseEvent() // manually shut down the worker + s.tracer.StartSpan("s1").Finish() // this span should be added to the queue + s.tracer.StartSpan("s2").Finish() // this span should be dropped since the queue is full + + mTestutils.AssertCounterMetrics(t, s.metricsFactory, + mTestutils.ExpectedMetric{ + Name: "jaeger.reporter_spans", + Tags: map[string]string{"result": "ok"}, + Value: 0, + }, + mTestutils.ExpectedMetric{ + Name: "jaeger.reporter_spans", + Tags: map[string]string{"result": "dropped"}, + Value: 1, + }, + ) + + go s.reporter.processQueue() // restart the worker so that Close() doesn't deadlock +} + +func TestRemoteReporterDoubleClose(t *testing.T) { + logger := &log.BytesBufferLogger{} + reporter := NewRemoteReporter(&fakeSender{}, ReporterOptions.QueueSize(1), ReporterOptions.Logger(logger)) + reporter.Close() + reporter.Close() + assert.Equal(t, "ERROR: Repeated attempt to close the reporter is ignored\n", logger.String()) +} + +func TestRemoteReporterReportAfterClose(t *testing.T) { + s := makeReporterSuite(t) + span := s.tracer.StartSpan("leela") + + s.close() // Close the tracer, which also closes and flushes the reporter + + assert.EqualValues(t, 1, atomic.LoadInt64(&s.reporter.closed), "reporter state must be closed") + select { + case <-s.reporter.queue: + t.Fatal("Reporter queue must be empty") + default: + // expected to get here + } + + span.Finish() + item := <-s.reporter.queue + assert.Equal(t, span, item.span, "since the reporter is closed and its worker routing finished, the span should be in the queue") +} + +func TestUDPReporter(t *testing.T) { + agent, err := testutils.StartMockAgent() + require.NoError(t, err) + defer agent.Close() + + testRemoteReporterWithSender(t, + func(m *Metrics) (Transport, error) { + return NewUDPTransport(agent.SpanServerAddr(), 0) + }, + func() []*j.Batch { + return agent.GetJaegerBatches() + }) +} + +func testRemoteReporterWithSender( + t *testing.T, + senderFactory func(m *Metrics) (Transport, error), + getBatches func() []*j.Batch, +) { + metricsFactory := metrics.NewLocalFactory(0) + metrics := NewMetrics(metricsFactory, nil) + + sender, err := senderFactory(metrics) + require.NoError(t, err) + reporter := NewRemoteReporter(sender, ReporterOptions.Metrics(metrics)).(*remoteReporter) + + tracer, closer := NewTracer( + "reporter-test-service", + NewConstSampler(true), + reporter, + TracerOptions.Metrics(metrics)) + + span := tracer.StartSpan("leela") + ext.SpanKindRPCClient.Set(span) + ext.PeerService.Set(span, "downstream") + span.Finish() + closer.Close() // close the tracer, which also closes and flushes the reporter + + // UDP transport uses fire and forget, so we need to wait for spans to get to the agent + for i := 0; i < 1000; i++ { + time.Sleep(1 * time.Millisecond) + if batches := getBatches(); len(batches) > 0 { + break + } + } + + batches := getBatches() + require.Equal(t, 1, len(batches)) + require.Equal(t, 1, len(batches[0].Spans)) + assert.Equal(t, "leela", batches[0].Spans[0].OperationName) + assert.Equal(t, "reporter-test-service", batches[0].Process.ServiceName) + tag := findJaegerTag("peer.service", batches[0].Spans[0].Tags) + assert.NotNil(t, tag) + assert.Equal(t, "downstream", *tag.VStr) + + mTestutils.AssertCounterMetrics(t, metricsFactory, []mTestutils.ExpectedMetric{ + {Name: "jaeger.reporter_spans", Tags: map[string]string{"result": "ok"}, Value: 1}, + {Name: "jaeger.reporter_spans", Tags: map[string]string{"result": "err"}, Value: 0}, + }...) +} + +func TestMemoryReporterReport(t *testing.T) { + reporter := NewInMemoryReporter() + tracer, closer := NewTracer("DOOP", NewConstSampler(true), reporter) + defer closer.Close() + tracer.StartSpan("leela").Finish() + assert.Len(t, reporter.GetSpans(), 1, "expected number of spans submitted") + assert.Equal(t, 1, reporter.SpansSubmitted(), "expected number of spans submitted") + reporter.Reset() + assert.Len(t, reporter.GetSpans(), 0, "expected number of spans submitted") + assert.Equal(t, 0, reporter.SpansSubmitted(), "expected number of spans submitted") +} + +func TestCompositeReporterReport(t *testing.T) { + reporter1 := NewInMemoryReporter() + reporter2 := NewInMemoryReporter() + reporter3 := NewCompositeReporter(reporter1, reporter2) + tracer, closer := NewTracer("DOOP", NewConstSampler(true), reporter3) + defer closer.Close() + tracer.StartSpan("leela").Finish() + assert.Len(t, reporter1.GetSpans(), 1, "expected number of spans submitted") + assert.Len(t, reporter2.GetSpans(), 1, "expected number of spans submitted") +} + +func TestLoggingReporter(t *testing.T) { + logger := &log.BytesBufferLogger{} + reporter := NewLoggingReporter(logger) + tracer, closer := NewTracer("test", NewConstSampler(true), reporter) + defer closer.Close() // will call Close on the reporter + tracer.StartSpan("sp1").Finish() + assert.True(t, strings.HasPrefix(logger.String(), "INFO: Reporting span")) +} + +type fakeSender struct { + bufferSize int + appendErr error + flushErr error + + spans []*Span + flushed []*Span + mutex sync.Mutex +} + +func (s *fakeSender) Append(span *Span) (int, error) { + s.mutex.Lock() + defer s.mutex.Unlock() + + s.spans = append(s.spans, span) + if n := len(s.spans); n == s.bufferSize { + return s.flushNoLock() + } + return 0, s.appendErr +} + +func (s *fakeSender) Flush() (int, error) { + s.mutex.Lock() + defer s.mutex.Unlock() + return s.flushNoLock() +} + +func (s *fakeSender) flushNoLock() (int, error) { + n := len(s.spans) + s.flushed = append(s.flushed, s.spans...) + s.spans = nil + return n, s.flushErr +} + +func (s *fakeSender) Close() error { return nil } + +func (s *fakeSender) BufferedSpans() []*Span { + s.mutex.Lock() + defer s.mutex.Unlock() + res := make([]*Span, len(s.spans)) + copy(res, s.spans) + return res +} + +func (s *fakeSender) FlushedSpans() []*Span { + s.mutex.Lock() + defer s.mutex.Unlock() + res := make([]*Span, len(s.flushed)) + copy(res, s.flushed) + return res +} + +func (s *fakeSender) assertBufferedSpans(t *testing.T, count int) { + for i := 0; i < 1000; i++ { + if len(s.BufferedSpans()) == count { + break + } + time.Sleep(time.Millisecond) + } + assert.Len(t, s.BufferedSpans(), count) +} + +func (s *fakeSender) assertFlushedSpans(t *testing.T, count int) { + for i := 0; i < 1000; i++ { + if len(s.FlushedSpans()) == count { + break + } + time.Sleep(time.Millisecond) + } + assert.Len(t, s.FlushedSpans(), count) +} + +func findDomainLog(span *Span, key string) *opentracing.LogRecord { + for _, log := range span.logs { + if log.Fields[0].Value().(string) == key { + return &log + } + } + return nil +} + +func findDomainTag(span *Span, key string) *Tag { + for _, tag := range span.tags { + if tag.key == key { + return &tag + } + } + return nil +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/README.md b/template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/README.md new file mode 100644 index 00000000..879948e9 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/README.md @@ -0,0 +1,5 @@ +An Observer that can be used to emit RPC metrics +================================================ + +It can be attached to the tracer during tracer construction. +See `ExampleObserver` function in [observer_test.go](./observer_test.go). diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/doc.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/doc.go new file mode 100644 index 00000000..51aa11b3 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/doc.go @@ -0,0 +1,16 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +// Package rpcmetrics implements an Observer that can be used to emit RPC metrics. +package rpcmetrics diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/endpoints.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/endpoints.go new file mode 100644 index 00000000..30555243 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/endpoints.go @@ -0,0 +1,63 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package rpcmetrics + +import "sync" + +// normalizedEndpoints is a cache for endpointName -> safeName mappings. +type normalizedEndpoints struct { + names map[string]string + maxSize int + defaultName string + normalizer NameNormalizer + mux sync.RWMutex +} + +func newNormalizedEndpoints(maxSize int, normalizer NameNormalizer) *normalizedEndpoints { + return &normalizedEndpoints{ + maxSize: maxSize, + normalizer: normalizer, + names: make(map[string]string, maxSize), + } +} + +// normalize looks up the name in the cache, if not found it uses normalizer +// to convert the name to a safe name. If called with more than maxSize unique +// names it returns "" for all other names beyond those already cached. +func (n *normalizedEndpoints) normalize(name string) string { + n.mux.RLock() + norm, ok := n.names[name] + l := len(n.names) + n.mux.RUnlock() + if ok { + return norm + } + if l >= n.maxSize { + return "" + } + return n.normalizeWithLock(name) +} + +func (n *normalizedEndpoints) normalizeWithLock(name string) string { + norm := n.normalizer.Normalize(name) + n.mux.Lock() + defer n.mux.Unlock() + // cache may have grown while we were not holding the lock + if len(n.names) >= n.maxSize { + return "" + } + n.names[name] = norm + return norm +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/endpoints_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/endpoints_test.go new file mode 100644 index 00000000..8a5b4e53 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/endpoints_test.go @@ -0,0 +1,43 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package rpcmetrics + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNormalizedEndpoints(t *testing.T) { + n := newNormalizedEndpoints(1, DefaultNameNormalizer) + + assertLen := func(l int) { + n.mux.RLock() + defer n.mux.RUnlock() + assert.Len(t, n.names, l) + } + + assert.Equal(t, "ab-cd", n.normalize("ab^cd"), "one translation") + assert.Equal(t, "ab-cd", n.normalize("ab^cd"), "cache hit") + assertLen(1) + assert.Equal(t, "", n.normalize("xys"), "cache overflow") + assertLen(1) +} + +func TestNormalizedEndpointsDoubleLocking(t *testing.T) { + n := newNormalizedEndpoints(1, DefaultNameNormalizer) + assert.Equal(t, "ab-cd", n.normalize("ab^cd"), "fill out the cache") + assert.Equal(t, "", n.normalizeWithLock("xys"), "cache overflow") +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/metrics.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/metrics.go new file mode 100644 index 00000000..ab8d74c2 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/metrics.go @@ -0,0 +1,124 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package rpcmetrics + +import ( + "sync" + + "github.com/uber/jaeger-lib/metrics" +) + +const ( + otherEndpointsPlaceholder = "other" + endpointNameMetricTag = "endpoint" +) + +// Metrics is a collection of metrics for an endpoint describing +// throughput, success, errors, and performance. +type Metrics struct { + // RequestCountSuccess is a counter of the total number of successes. + RequestCountSuccess metrics.Counter `metric:"requests" tags:"error=false"` + + // RequestCountFailures is a counter of the number of times any failure has been observed. + RequestCountFailures metrics.Counter `metric:"requests" tags:"error=true"` + + // RequestLatencySuccess is a latency histogram of succesful requests. + RequestLatencySuccess metrics.Timer `metric:"request_latency" tags:"error=false"` + + // RequestLatencyFailures is a latency histogram of failed requests. + RequestLatencyFailures metrics.Timer `metric:"request_latency" tags:"error=true"` + + // HTTPStatusCode2xx is a counter of the total number of requests with HTTP status code 200-299 + HTTPStatusCode2xx metrics.Counter `metric:"http_requests" tags:"status_code=2xx"` + + // HTTPStatusCode3xx is a counter of the total number of requests with HTTP status code 300-399 + HTTPStatusCode3xx metrics.Counter `metric:"http_requests" tags:"status_code=3xx"` + + // HTTPStatusCode4xx is a counter of the total number of requests with HTTP status code 400-499 + HTTPStatusCode4xx metrics.Counter `metric:"http_requests" tags:"status_code=4xx"` + + // HTTPStatusCode5xx is a counter of the total number of requests with HTTP status code 500-599 + HTTPStatusCode5xx metrics.Counter `metric:"http_requests" tags:"status_code=5xx"` +} + +func (m *Metrics) recordHTTPStatusCode(statusCode uint16) { + if statusCode >= 200 && statusCode < 300 { + m.HTTPStatusCode2xx.Inc(1) + } else if statusCode >= 300 && statusCode < 400 { + m.HTTPStatusCode3xx.Inc(1) + } else if statusCode >= 400 && statusCode < 500 { + m.HTTPStatusCode4xx.Inc(1) + } else if statusCode >= 500 && statusCode < 600 { + m.HTTPStatusCode5xx.Inc(1) + } +} + +// MetricsByEndpoint is a registry/cache of metrics for each unique endpoint name. +// Only maxNumberOfEndpoints Metrics are stored, all other endpoint names are mapped +// to a generic endpoint name "other". +type MetricsByEndpoint struct { + metricsFactory metrics.Factory + endpoints *normalizedEndpoints + metricsByEndpoint map[string]*Metrics + mux sync.RWMutex +} + +func newMetricsByEndpoint( + metricsFactory metrics.Factory, + normalizer NameNormalizer, + maxNumberOfEndpoints int, +) *MetricsByEndpoint { + return &MetricsByEndpoint{ + metricsFactory: metricsFactory, + endpoints: newNormalizedEndpoints(maxNumberOfEndpoints, normalizer), + metricsByEndpoint: make(map[string]*Metrics, maxNumberOfEndpoints+1), // +1 for "other" + } +} + +func (m *MetricsByEndpoint) get(endpoint string) *Metrics { + safeName := m.endpoints.normalize(endpoint) + if safeName == "" { + safeName = otherEndpointsPlaceholder + } + m.mux.RLock() + met := m.metricsByEndpoint[safeName] + m.mux.RUnlock() + if met != nil { + return met + } + + return m.getWithWriteLock(safeName) +} + +// split to make easier to test +func (m *MetricsByEndpoint) getWithWriteLock(safeName string) *Metrics { + m.mux.Lock() + defer m.mux.Unlock() + + // it is possible that the name has been already registered after we released + // the read lock and before we grabbed the write lock, so check for that. + if met, ok := m.metricsByEndpoint[safeName]; ok { + return met + } + + // it would be nice to create the struct before locking, since Init() is somewhat + // expensive, however some metrics backends (e.g. expvar) may not like duplicate metrics. + met := &Metrics{} + tags := map[string]string{endpointNameMetricTag: safeName} + metrics.Init(met, m.metricsFactory, tags) + + m.metricsByEndpoint[safeName] = met + return met +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/metrics_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/metrics_test.go new file mode 100644 index 00000000..292ec943 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/metrics_test.go @@ -0,0 +1,61 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package rpcmetrics + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/uber/jaeger-lib/metrics" + "github.com/uber/jaeger-lib/metrics/testutils" +) + +// E.g. tags("key", "value", "key", "value") +func tags(kv ...string) map[string]string { + m := make(map[string]string) + for i := 0; i < len(kv)-1; i += 2 { + m[kv[i]] = kv[i+1] + } + return m +} + +func endpointTags(endpoint string, kv ...string) map[string]string { + return tags(append([]string{"endpoint", endpoint}, kv...)...) +} + +func TestMetricsByEndpoint(t *testing.T) { + met := metrics.NewLocalFactory(0) + mbe := newMetricsByEndpoint(met, DefaultNameNormalizer, 2) + + m1 := mbe.get("abc1") + m2 := mbe.get("abc1") // from cache + m2a := mbe.getWithWriteLock("abc1") // from cache in double-checked lock + assert.Equal(t, m1, m2) + assert.Equal(t, m1, m2a) + + m3 := mbe.get("abc3") + m4 := mbe.get("overflow") + m5 := mbe.get("overflow2") + + for _, m := range []*Metrics{m1, m2, m2a, m3, m4, m5} { + m.RequestCountSuccess.Inc(1) + } + + testutils.AssertCounterMetrics(t, met, + testutils.ExpectedMetric{Name: "requests", Tags: endpointTags("abc1", "error", "false"), Value: 3}, + testutils.ExpectedMetric{Name: "requests", Tags: endpointTags("abc3", "error", "false"), Value: 1}, + testutils.ExpectedMetric{Name: "requests", Tags: endpointTags("other", "error", "false"), Value: 2}, + ) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/normalizer.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/normalizer.go new file mode 100644 index 00000000..148d84b3 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/normalizer.go @@ -0,0 +1,101 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package rpcmetrics + +// NameNormalizer is used to convert the endpoint names to strings +// that can be safely used as tags in the metrics. +type NameNormalizer interface { + Normalize(name string) string +} + +// DefaultNameNormalizer converts endpoint names so that they contain only characters +// from the safe charset [a-zA-Z0-9-./_]. All other characters are replaced with '-'. +var DefaultNameNormalizer = &SimpleNameNormalizer{ + SafeSets: []SafeCharacterSet{ + &Range{From: 'a', To: 'z'}, + &Range{From: 'A', To: 'Z'}, + &Range{From: '0', To: '9'}, + &Char{'-'}, + &Char{'_'}, + &Char{'/'}, + &Char{'.'}, + }, + Replacement: '-', +} + +// SimpleNameNormalizer uses a set of safe character sets. +type SimpleNameNormalizer struct { + SafeSets []SafeCharacterSet + Replacement byte +} + +// SafeCharacterSet determines if the given character is "safe" +type SafeCharacterSet interface { + IsSafe(c byte) bool +} + +// Range implements SafeCharacterSet +type Range struct { + From, To byte +} + +// IsSafe implements SafeCharacterSet +func (r *Range) IsSafe(c byte) bool { + return c >= r.From && c <= r.To +} + +// Char implements SafeCharacterSet +type Char struct { + Val byte +} + +// IsSafe implements SafeCharacterSet +func (ch *Char) IsSafe(c byte) bool { + return c == ch.Val +} + +// Normalize checks each character in the string against SafeSets, +// and if it's not safe substitutes it with Replacement. +func (n *SimpleNameNormalizer) Normalize(name string) string { + var retMe []byte + nameBytes := []byte(name) + for i, b := range nameBytes { + if n.safeByte(b) { + if retMe != nil { + retMe[i] = b + } + } else { + if retMe == nil { + retMe = make([]byte, len(nameBytes)) + copy(retMe[0:i], nameBytes[0:i]) + } + retMe[i] = n.Replacement + } + } + if retMe == nil { + return name + } + return string(retMe) +} + +// safeByte checks if b against all safe charsets. +func (n *SimpleNameNormalizer) safeByte(b byte) bool { + for i := range n.SafeSets { + if n.SafeSets[i].IsSafe(b) { + return true + } + } + return false +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/normalizer_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/normalizer_test.go new file mode 100644 index 00000000..a93c1716 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/normalizer_test.go @@ -0,0 +1,34 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package rpcmetrics + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSimpleNameNormalizer(t *testing.T) { + n := &SimpleNameNormalizer{ + SafeSets: []SafeCharacterSet{ + &Range{From: 'a', To: 'z'}, + &Char{'-'}, + }, + Replacement: '-', + } + assert.Equal(t, "ab-cd", n.Normalize("ab-cd"), "all valid") + assert.Equal(t, "ab-cd", n.Normalize("ab.cd"), "single mismatch") + assert.Equal(t, "a--cd", n.Normalize("aB-cd"), "range letter mismatch") +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/observer.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/observer.go new file mode 100644 index 00000000..eca5ff6f --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/observer.go @@ -0,0 +1,171 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package rpcmetrics + +import ( + "strconv" + "sync" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/uber/jaeger-lib/metrics" + + jaeger "github.com/uber/jaeger-client-go" +) + +const defaultMaxNumberOfEndpoints = 200 + +// Observer is an observer that can emit RPC metrics. +type Observer struct { + metricsByEndpoint *MetricsByEndpoint +} + +// NewObserver creates a new observer that can emit RPC metrics. +func NewObserver(metricsFactory metrics.Factory, normalizer NameNormalizer) *Observer { + return &Observer{ + metricsByEndpoint: newMetricsByEndpoint( + metricsFactory, + normalizer, + defaultMaxNumberOfEndpoints, + ), + } +} + +// OnStartSpan creates a new Observer for the span. +func (o *Observer) OnStartSpan( + operationName string, + options opentracing.StartSpanOptions, +) jaeger.SpanObserver { + return NewSpanObserver(o.metricsByEndpoint, operationName, options) +} + +// SpanKind identifies the span as inboud, outbound, or internal +type SpanKind int + +const ( + // Local span kind + Local SpanKind = iota + // Inbound span kind + Inbound + // Outbound span kind + Outbound +) + +// SpanObserver collects RPC metrics +type SpanObserver struct { + metricsByEndpoint *MetricsByEndpoint + operationName string + startTime time.Time + mux sync.Mutex + kind SpanKind + httpStatusCode uint16 + err bool +} + +// NewSpanObserver creates a new SpanObserver that can emit RPC metrics. +func NewSpanObserver( + metricsByEndpoint *MetricsByEndpoint, + operationName string, + options opentracing.StartSpanOptions, +) *SpanObserver { + so := &SpanObserver{ + metricsByEndpoint: metricsByEndpoint, + operationName: operationName, + startTime: options.StartTime, + } + for k, v := range options.Tags { + so.handleTagInLock(k, v) + } + return so +} + +// handleTags watches for special tags +// - SpanKind +// - HttpStatusCode +// - Error +func (so *SpanObserver) handleTagInLock(key string, value interface{}) { + if key == string(ext.SpanKind) { + if v, ok := value.(ext.SpanKindEnum); ok { + value = string(v) + } + if v, ok := value.(string); ok { + if v == string(ext.SpanKindRPCClientEnum) { + so.kind = Outbound + } else if v == string(ext.SpanKindRPCServerEnum) { + so.kind = Inbound + } + } + return + } + if key == string(ext.HTTPStatusCode) { + if v, ok := value.(uint16); ok { + so.httpStatusCode = v + } else if v, ok := value.(int); ok { + so.httpStatusCode = uint16(v) + } else if v, ok := value.(string); ok { + if vv, err := strconv.Atoi(v); err == nil { + so.httpStatusCode = uint16(vv) + } + } + return + } + if key == string(ext.Error) { + if v, ok := value.(bool); ok { + so.err = v + } else if v, ok := value.(string); ok { + if vv, err := strconv.ParseBool(v); err == nil { + so.err = vv + } + } + return + } +} + +// OnFinish emits the RPC metrics. It only has an effect when operation name +// is not blank, and the span kind is an RPC server. +func (so *SpanObserver) OnFinish(options opentracing.FinishOptions) { + so.mux.Lock() + defer so.mux.Unlock() + + if so.operationName == "" || so.kind != Inbound { + return + } + + mets := so.metricsByEndpoint.get(so.operationName) + latency := options.FinishTime.Sub(so.startTime) + if so.err { + mets.RequestCountFailures.Inc(1) + mets.RequestLatencyFailures.Record(latency) + } else { + mets.RequestCountSuccess.Inc(1) + mets.RequestLatencySuccess.Record(latency) + } + mets.recordHTTPStatusCode(so.httpStatusCode) +} + +// OnSetOperationName records new operation name. +func (so *SpanObserver) OnSetOperationName(operationName string) { + so.mux.Lock() + so.operationName = operationName + so.mux.Unlock() +} + +// OnSetTag implements SpanObserver +func (so *SpanObserver) OnSetTag(key string, value interface{}) { + so.mux.Lock() + so.handleTagInLock(key, value) + so.mux.Unlock() +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/observer_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/observer_test.go new file mode 100644 index 00000000..c2c31ae0 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/rpcmetrics/observer_test.go @@ -0,0 +1,177 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package rpcmetrics + +import ( + "fmt" + "testing" + "time" + + opentracing "github.com/opentracing/opentracing-go" + "github.com/stretchr/testify/assert" + "github.com/uber/jaeger-lib/metrics" + u "github.com/uber/jaeger-lib/metrics/testutils" + + "github.com/opentracing/opentracing-go/ext" + jaeger "github.com/uber/jaeger-client-go" +) + +func ExampleObserver() { + metricsFactory := metrics.NewLocalFactory(0) + metricsObserver := NewObserver( + metricsFactory, + DefaultNameNormalizer, + ) + tracer, closer := jaeger.NewTracer( + "serviceName", + jaeger.NewConstSampler(true), + jaeger.NewInMemoryReporter(), + jaeger.TracerOptions.Observer(metricsObserver), + ) + defer closer.Close() + + span := tracer.StartSpan("test", ext.SpanKindRPCServer) + span.Finish() + + c, _ := metricsFactory.Snapshot() + fmt.Printf("requests (success): %d\n", c["requests|endpoint=test|error=false"]) + fmt.Printf("requests (failure): %d\n", c["requests|endpoint=test|error=true"]) + // Output: + // requests (success): 1 + // requests (failure): 0 +} + +type testTracer struct { + metrics *metrics.LocalFactory + tracer opentracing.Tracer +} + +func withTestTracer(runTest func(tt *testTracer)) { + sampler := jaeger.NewConstSampler(true) + reporter := jaeger.NewInMemoryReporter() + metrics := metrics.NewLocalFactory(time.Minute) + observer := NewObserver(metrics, DefaultNameNormalizer) + tracer, closer := jaeger.NewTracer( + "test", + sampler, + reporter, + jaeger.TracerOptions.Observer(observer)) + defer closer.Close() + runTest(&testTracer{ + metrics: metrics, + tracer: tracer, + }) +} + +func TestObserver(t *testing.T) { + withTestTracer(func(testTracer *testTracer) { + ts := time.Now() + finishOptions := opentracing.FinishOptions{ + FinishTime: ts.Add(50 * time.Millisecond), + } + + testCases := []struct { + name string + tag opentracing.Tag + opNameOverride string + err bool + }{ + {name: "local-span", tag: opentracing.Tag{Key: "x", Value: "y"}}, + {name: "get-user", tag: ext.SpanKindRPCServer}, + {name: "get-user", tag: ext.SpanKindRPCServer, opNameOverride: "get-user-override"}, + {name: "get-user", tag: ext.SpanKindRPCServer, err: true}, + {name: "get-user-client", tag: ext.SpanKindRPCClient}, + } + + for _, testCase := range testCases { + span := testTracer.tracer.StartSpan( + testCase.name, + testCase.tag, + opentracing.StartTime(ts), + ) + if testCase.opNameOverride != "" { + span.SetOperationName(testCase.opNameOverride) + } + if testCase.err { + ext.Error.Set(span, true) + } + span.FinishWithOptions(finishOptions) + } + + u.AssertCounterMetrics(t, + testTracer.metrics, + u.ExpectedMetric{Name: "requests", Tags: endpointTags("local-span", "error", "false"), Value: 0}, + u.ExpectedMetric{Name: "requests", Tags: endpointTags("get-user", "error", "false"), Value: 1}, + u.ExpectedMetric{Name: "requests", Tags: endpointTags("get-user", "error", "true"), Value: 1}, + u.ExpectedMetric{Name: "requests", Tags: endpointTags("get-user-override", "error", "false"), Value: 1}, + u.ExpectedMetric{Name: "requests", Tags: endpointTags("get-user-client", "error", "false"), Value: 0}, + ) + // TODO something wrong with string generation, .P99 should not be appended to the tag + // as a result we cannot use u.AssertGaugeMetrics + _, g := testTracer.metrics.Snapshot() + assert.EqualValues(t, 51, g["request_latency|endpoint=get-user|error=false.P99"]) + assert.EqualValues(t, 51, g["request_latency|endpoint=get-user|error=true.P99"]) + }) +} + +func TestTags(t *testing.T) { + type tagTestCase struct { + key string + value interface{} + metrics []u.ExpectedMetric + } + + testCases := []tagTestCase{ + {key: "something", value: 42, metrics: []u.ExpectedMetric{ + {Name: "requests", Value: 1, Tags: tags("error", "false")}, + }}, + {key: "error", value: true, metrics: []u.ExpectedMetric{ + {Name: "requests", Value: 1, Tags: tags("error", "true")}, + }}, + {key: "error", value: "true", metrics: []u.ExpectedMetric{ + {Name: "requests", Value: 1, Tags: tags("error", "true")}, + }}, + } + + for i := 2; i <= 5; i++ { + values := []interface{}{ + i * 100, + uint16(i * 100), + fmt.Sprintf("%d00", i), + } + for _, v := range values { + testCases = append(testCases, tagTestCase{ + key: "http.status_code", value: v, metrics: []u.ExpectedMetric{ + {Name: "http_requests", Value: 1, Tags: tags("status_code", fmt.Sprintf("%dxx", i))}, + }, + }) + } + } + + for _, tc := range testCases { + testCase := tc // capture loop var + for i := range testCase.metrics { + testCase.metrics[i].Tags["endpoint"] = "span" + } + t.Run(fmt.Sprintf("%s-%v", testCase.key, testCase.value), func(t *testing.T) { + withTestTracer(func(testTracer *testTracer) { + span := testTracer.tracer.StartSpan("span", ext.SpanKindRPCServer) + span.SetTag(testCase.key, testCase.value) + span.Finish() + u.AssertCounterMetrics(t, testTracer.metrics, testCase.metrics...) + }) + }) + } +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/sampler.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/sampler.go new file mode 100644 index 00000000..e6a32b38 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/sampler.go @@ -0,0 +1,556 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "fmt" + "math" + "net/url" + "sync" + "sync/atomic" + "time" + + "github.com/uber/jaeger-client-go/log" + "github.com/uber/jaeger-client-go/thrift-gen/sampling" + "github.com/uber/jaeger-client-go/utils" +) + +const ( + defaultSamplingServerURL = "http://localhost:5778/sampling" + defaultSamplingRefreshInterval = time.Minute + defaultMaxOperations = 2000 +) + +// Sampler decides whether a new trace should be sampled or not. +type Sampler interface { + // IsSampled decides whether a trace with given `id` and `operation` + // should be sampled. This function will also return the tags that + // can be used to identify the type of sampling that was applied to + // the root span. Most simple samplers would return two tags, + // sampler.type and sampler.param, similar to those used in the Configuration + IsSampled(id TraceID, operation string) (sampled bool, tags []Tag) + + // Close does a clean shutdown of the sampler, stopping any background + // go-routines it may have started. + Close() + + // Equal checks if the `other` sampler is functionally equivalent + // to this sampler. + // TODO remove this function. This function is used to determine if 2 samplers are equivalent + // which does not bode well with the adaptive sampler which has to create all the composite samplers + // for the comparison to occur. This is expensive to do if only one sampler has changed. + Equal(other Sampler) bool +} + +// ----------------------- + +// ConstSampler is a sampler that always makes the same decision. +type ConstSampler struct { + Decision bool + tags []Tag +} + +// NewConstSampler creates a ConstSampler. +func NewConstSampler(sample bool) Sampler { + tags := []Tag{ + {key: SamplerTypeTagKey, value: SamplerTypeConst}, + {key: SamplerParamTagKey, value: sample}, + } + return &ConstSampler{Decision: sample, tags: tags} +} + +// IsSampled implements IsSampled() of Sampler. +func (s *ConstSampler) IsSampled(id TraceID, operation string) (bool, []Tag) { + return s.Decision, s.tags +} + +// Close implements Close() of Sampler. +func (s *ConstSampler) Close() { + // nothing to do +} + +// Equal implements Equal() of Sampler. +func (s *ConstSampler) Equal(other Sampler) bool { + if o, ok := other.(*ConstSampler); ok { + return s.Decision == o.Decision + } + return false +} + +// ----------------------- + +// ProbabilisticSampler is a sampler that randomly samples a certain percentage +// of traces. +type ProbabilisticSampler struct { + samplingRate float64 + samplingBoundary uint64 + tags []Tag +} + +const maxRandomNumber = ^(uint64(1) << 63) // i.e. 0x7fffffffffffffff + +// NewProbabilisticSampler creates a sampler that randomly samples a certain percentage of traces specified by the +// samplingRate, in the range between 0.0 and 1.0. +// +// It relies on the fact that new trace IDs are 63bit random numbers themselves, thus making the sampling decision +// without generating a new random number, but simply calculating if traceID < (samplingRate * 2^63). +// TODO remove the error from this function for next major release +func NewProbabilisticSampler(samplingRate float64) (*ProbabilisticSampler, error) { + if samplingRate < 0.0 || samplingRate > 1.0 { + return nil, fmt.Errorf("Sampling Rate must be between 0.0 and 1.0, received %f", samplingRate) + } + return newProbabilisticSampler(samplingRate), nil +} + +func newProbabilisticSampler(samplingRate float64) *ProbabilisticSampler { + samplingRate = math.Max(0.0, math.Min(samplingRate, 1.0)) + tags := []Tag{ + {key: SamplerTypeTagKey, value: SamplerTypeProbabilistic}, + {key: SamplerParamTagKey, value: samplingRate}, + } + return &ProbabilisticSampler{ + samplingRate: samplingRate, + samplingBoundary: uint64(float64(maxRandomNumber) * samplingRate), + tags: tags, + } +} + +// SamplingRate returns the sampling probability this sampled was constructed with. +func (s *ProbabilisticSampler) SamplingRate() float64 { + return s.samplingRate +} + +// IsSampled implements IsSampled() of Sampler. +func (s *ProbabilisticSampler) IsSampled(id TraceID, operation string) (bool, []Tag) { + return s.samplingBoundary >= id.Low, s.tags +} + +// Close implements Close() of Sampler. +func (s *ProbabilisticSampler) Close() { + // nothing to do +} + +// Equal implements Equal() of Sampler. +func (s *ProbabilisticSampler) Equal(other Sampler) bool { + if o, ok := other.(*ProbabilisticSampler); ok { + return s.samplingBoundary == o.samplingBoundary + } + return false +} + +// ----------------------- + +type rateLimitingSampler struct { + maxTracesPerSecond float64 + rateLimiter utils.RateLimiter + tags []Tag +} + +// NewRateLimitingSampler creates a sampler that samples at most maxTracesPerSecond. The distribution of sampled +// traces follows burstiness of the service, i.e. a service with uniformly distributed requests will have those +// requests sampled uniformly as well, but if requests are bursty, especially sub-second, then a number of +// sequential requests can be sampled each second. +func NewRateLimitingSampler(maxTracesPerSecond float64) Sampler { + tags := []Tag{ + {key: SamplerTypeTagKey, value: SamplerTypeRateLimiting}, + {key: SamplerParamTagKey, value: maxTracesPerSecond}, + } + return &rateLimitingSampler{ + maxTracesPerSecond: maxTracesPerSecond, + rateLimiter: utils.NewRateLimiter(maxTracesPerSecond, math.Max(maxTracesPerSecond, 1.0)), + tags: tags, + } +} + +// IsSampled implements IsSampled() of Sampler. +func (s *rateLimitingSampler) IsSampled(id TraceID, operation string) (bool, []Tag) { + return s.rateLimiter.CheckCredit(1.0), s.tags +} + +func (s *rateLimitingSampler) Close() { + // nothing to do +} + +func (s *rateLimitingSampler) Equal(other Sampler) bool { + if o, ok := other.(*rateLimitingSampler); ok { + return s.maxTracesPerSecond == o.maxTracesPerSecond + } + return false +} + +// ----------------------- + +// GuaranteedThroughputProbabilisticSampler is a sampler that leverages both probabilisticSampler and +// rateLimitingSampler. The rateLimitingSampler is used as a guaranteed lower bound sampler such that +// every operation is sampled at least once in a time interval defined by the lowerBound. ie a lowerBound +// of 1.0 / (60 * 10) will sample an operation at least once every 10 minutes. +// +// The probabilisticSampler is given higher priority when tags are emitted, ie. if IsSampled() for both +// samplers return true, the tags for probabilisticSampler will be used. +type GuaranteedThroughputProbabilisticSampler struct { + probabilisticSampler *ProbabilisticSampler + lowerBoundSampler Sampler + tags []Tag + samplingRate float64 + lowerBound float64 +} + +// NewGuaranteedThroughputProbabilisticSampler returns a delegating sampler that applies both +// probabilisticSampler and rateLimitingSampler. +func NewGuaranteedThroughputProbabilisticSampler( + lowerBound, samplingRate float64, +) (*GuaranteedThroughputProbabilisticSampler, error) { + return newGuaranteedThroughputProbabilisticSampler(lowerBound, samplingRate), nil +} + +func newGuaranteedThroughputProbabilisticSampler(lowerBound, samplingRate float64) *GuaranteedThroughputProbabilisticSampler { + s := &GuaranteedThroughputProbabilisticSampler{ + lowerBoundSampler: NewRateLimitingSampler(lowerBound), + lowerBound: lowerBound, + } + s.setProbabilisticSampler(samplingRate) + return s +} + +func (s *GuaranteedThroughputProbabilisticSampler) setProbabilisticSampler(samplingRate float64) { + if s.probabilisticSampler == nil || s.samplingRate != samplingRate { + s.probabilisticSampler = newProbabilisticSampler(samplingRate) + s.samplingRate = s.probabilisticSampler.SamplingRate() + s.tags = []Tag{ + {key: SamplerTypeTagKey, value: SamplerTypeLowerBound}, + {key: SamplerParamTagKey, value: s.samplingRate}, + } + } +} + +// IsSampled implements IsSampled() of Sampler. +func (s *GuaranteedThroughputProbabilisticSampler) IsSampled(id TraceID, operation string) (bool, []Tag) { + if sampled, tags := s.probabilisticSampler.IsSampled(id, operation); sampled { + s.lowerBoundSampler.IsSampled(id, operation) + return true, tags + } + sampled, _ := s.lowerBoundSampler.IsSampled(id, operation) + return sampled, s.tags +} + +// Close implements Close() of Sampler. +func (s *GuaranteedThroughputProbabilisticSampler) Close() { + s.probabilisticSampler.Close() + s.lowerBoundSampler.Close() +} + +// Equal implements Equal() of Sampler. +func (s *GuaranteedThroughputProbabilisticSampler) Equal(other Sampler) bool { + // NB The Equal() function is expensive and will be removed. See adaptiveSampler.Equal() for + // more information. + return false +} + +// this function should only be called while holding a Write lock +func (s *GuaranteedThroughputProbabilisticSampler) update(lowerBound, samplingRate float64) { + s.setProbabilisticSampler(samplingRate) + if s.lowerBound != lowerBound { + s.lowerBoundSampler = NewRateLimitingSampler(lowerBound) + s.lowerBound = lowerBound + } +} + +// ----------------------- + +type adaptiveSampler struct { + sync.RWMutex + + samplers map[string]*GuaranteedThroughputProbabilisticSampler + defaultSampler *ProbabilisticSampler + lowerBound float64 + maxOperations int +} + +// NewAdaptiveSampler returns a delegating sampler that applies both probabilisticSampler and +// rateLimitingSampler via the guaranteedThroughputProbabilisticSampler. This sampler keeps track of all +// operations and delegates calls to the respective guaranteedThroughputProbabilisticSampler. +func NewAdaptiveSampler(strategies *sampling.PerOperationSamplingStrategies, maxOperations int) (Sampler, error) { + return newAdaptiveSampler(strategies, maxOperations), nil +} + +func newAdaptiveSampler(strategies *sampling.PerOperationSamplingStrategies, maxOperations int) Sampler { + samplers := make(map[string]*GuaranteedThroughputProbabilisticSampler) + for _, strategy := range strategies.PerOperationStrategies { + sampler := newGuaranteedThroughputProbabilisticSampler( + strategies.DefaultLowerBoundTracesPerSecond, + strategy.ProbabilisticSampling.SamplingRate, + ) + samplers[strategy.Operation] = sampler + } + return &adaptiveSampler{ + samplers: samplers, + defaultSampler: newProbabilisticSampler(strategies.DefaultSamplingProbability), + lowerBound: strategies.DefaultLowerBoundTracesPerSecond, + maxOperations: maxOperations, + } +} + +func (s *adaptiveSampler) IsSampled(id TraceID, operation string) (bool, []Tag) { + s.RLock() + sampler, ok := s.samplers[operation] + if ok { + defer s.RUnlock() + return sampler.IsSampled(id, operation) + } + s.RUnlock() + s.Lock() + defer s.Unlock() + + // Check if sampler has already been created + sampler, ok = s.samplers[operation] + if ok { + return sampler.IsSampled(id, operation) + } + // Store only up to maxOperations of unique ops. + if len(s.samplers) >= s.maxOperations { + return s.defaultSampler.IsSampled(id, operation) + } + newSampler := newGuaranteedThroughputProbabilisticSampler(s.lowerBound, s.defaultSampler.SamplingRate()) + s.samplers[operation] = newSampler + return newSampler.IsSampled(id, operation) +} + +func (s *adaptiveSampler) Close() { + s.Lock() + defer s.Unlock() + for _, sampler := range s.samplers { + sampler.Close() + } + s.defaultSampler.Close() +} + +func (s *adaptiveSampler) Equal(other Sampler) bool { + // NB The Equal() function is overly expensive for adaptiveSampler since it's composed of multiple + // samplers which all need to be initialized before this function can be called for a comparison. + // Therefore, adaptiveSampler uses the update() function to only alter the samplers that need + // changing. Hence this function always returns false so that the update function can be called. + // Once the Equal() function is removed from the Sampler API, this will no longer be needed. + return false +} + +func (s *adaptiveSampler) update(strategies *sampling.PerOperationSamplingStrategies) { + s.Lock() + defer s.Unlock() + for _, strategy := range strategies.PerOperationStrategies { + operation := strategy.Operation + samplingRate := strategy.ProbabilisticSampling.SamplingRate + lowerBound := strategies.DefaultLowerBoundTracesPerSecond + if sampler, ok := s.samplers[operation]; ok { + sampler.update(lowerBound, samplingRate) + } else { + sampler := newGuaranteedThroughputProbabilisticSampler( + lowerBound, + samplingRate, + ) + s.samplers[operation] = sampler + } + } + s.lowerBound = strategies.DefaultLowerBoundTracesPerSecond + if s.defaultSampler.SamplingRate() != strategies.DefaultSamplingProbability { + s.defaultSampler = newProbabilisticSampler(strategies.DefaultSamplingProbability) + } +} + +// ----------------------- + +// RemotelyControlledSampler is a delegating sampler that polls a remote server +// for the appropriate sampling strategy, constructs a corresponding sampler and +// delegates to it for sampling decisions. +type RemotelyControlledSampler struct { + // These fields must be first in the struct because `sync/atomic` expects 64-bit alignment. + // Cf. https://github.com/uber/jaeger-client-go/issues/155, https://goo.gl/zW7dgq + closed int64 // 0 - not closed, 1 - closed + + sync.RWMutex + samplerOptions + + serviceName string + manager sampling.SamplingManager + doneChan chan *sync.WaitGroup +} + +type httpSamplingManager struct { + serverURL string +} + +func (s *httpSamplingManager) GetSamplingStrategy(serviceName string) (*sampling.SamplingStrategyResponse, error) { + var out sampling.SamplingStrategyResponse + v := url.Values{} + v.Set("service", serviceName) + if err := utils.GetJSON(s.serverURL+"?"+v.Encode(), &out); err != nil { + return nil, err + } + return &out, nil +} + +// NewRemotelyControlledSampler creates a sampler that periodically pulls +// the sampling strategy from an HTTP sampling server (e.g. jaeger-agent). +func NewRemotelyControlledSampler( + serviceName string, + opts ...SamplerOption, +) *RemotelyControlledSampler { + options := applySamplerOptions(opts...) + sampler := &RemotelyControlledSampler{ + samplerOptions: options, + serviceName: serviceName, + manager: &httpSamplingManager{serverURL: options.samplingServerURL}, + doneChan: make(chan *sync.WaitGroup), + } + go sampler.pollController() + return sampler +} + +func applySamplerOptions(opts ...SamplerOption) samplerOptions { + options := samplerOptions{} + for _, option := range opts { + option(&options) + } + if options.sampler == nil { + options.sampler = newProbabilisticSampler(0.001) + } + if options.logger == nil { + options.logger = log.NullLogger + } + if options.maxOperations <= 0 { + options.maxOperations = defaultMaxOperations + } + if options.samplingServerURL == "" { + options.samplingServerURL = defaultSamplingServerURL + } + if options.metrics == nil { + options.metrics = NewNullMetrics() + } + if options.samplingRefreshInterval <= 0 { + options.samplingRefreshInterval = defaultSamplingRefreshInterval + } + return options +} + +// IsSampled implements IsSampled() of Sampler. +func (s *RemotelyControlledSampler) IsSampled(id TraceID, operation string) (bool, []Tag) { + s.RLock() + defer s.RUnlock() + return s.sampler.IsSampled(id, operation) +} + +// Close implements Close() of Sampler. +func (s *RemotelyControlledSampler) Close() { + if swapped := atomic.CompareAndSwapInt64(&s.closed, 0, 1); !swapped { + s.logger.Error("Repeated attempt to close the sampler is ignored") + return + } + + var wg sync.WaitGroup + wg.Add(1) + s.doneChan <- &wg + wg.Wait() +} + +// Equal implements Equal() of Sampler. +func (s *RemotelyControlledSampler) Equal(other Sampler) bool { + // NB The Equal() function is expensive and will be removed. See adaptiveSampler.Equal() for + // more information. + if o, ok := other.(*RemotelyControlledSampler); ok { + s.RLock() + o.RLock() + defer s.RUnlock() + defer o.RUnlock() + return s.sampler.Equal(o.sampler) + } + return false +} + +func (s *RemotelyControlledSampler) pollController() { + ticker := time.NewTicker(s.samplingRefreshInterval) + defer ticker.Stop() + s.pollControllerWithTicker(ticker) +} + +func (s *RemotelyControlledSampler) pollControllerWithTicker(ticker *time.Ticker) { + for { + select { + case <-ticker.C: + s.updateSampler() + case wg := <-s.doneChan: + wg.Done() + return + } + } +} + +func (s *RemotelyControlledSampler) getSampler() Sampler { + s.Lock() + defer s.Unlock() + return s.sampler +} + +func (s *RemotelyControlledSampler) setSampler(sampler Sampler) { + s.Lock() + defer s.Unlock() + s.sampler = sampler +} + +func (s *RemotelyControlledSampler) updateSampler() { + res, err := s.manager.GetSamplingStrategy(s.serviceName) + if err != nil { + s.metrics.SamplerQueryFailure.Inc(1) + return + } + s.Lock() + defer s.Unlock() + + s.metrics.SamplerRetrieved.Inc(1) + if strategies := res.GetOperationSampling(); strategies != nil { + s.updateAdaptiveSampler(strategies) + } else { + err = s.updateRateLimitingOrProbabilisticSampler(res) + } + if err != nil { + s.metrics.SamplerUpdateFailure.Inc(1) + s.logger.Infof("Unable to handle sampling strategy response %+v. Got error: %v", res, err) + return + } + s.metrics.SamplerUpdated.Inc(1) +} + +// NB: this function should only be called while holding a Write lock +func (s *RemotelyControlledSampler) updateAdaptiveSampler(strategies *sampling.PerOperationSamplingStrategies) { + if adaptiveSampler, ok := s.sampler.(*adaptiveSampler); ok { + adaptiveSampler.update(strategies) + } else { + s.sampler = newAdaptiveSampler(strategies, s.maxOperations) + } +} + +// NB: this function should only be called while holding a Write lock +func (s *RemotelyControlledSampler) updateRateLimitingOrProbabilisticSampler(res *sampling.SamplingStrategyResponse) error { + var newSampler Sampler + if probabilistic := res.GetProbabilisticSampling(); probabilistic != nil { + newSampler = newProbabilisticSampler(probabilistic.SamplingRate) + } else if rateLimiting := res.GetRateLimitingSampling(); rateLimiting != nil { + newSampler = NewRateLimitingSampler(float64(rateLimiting.MaxTracesPerSecond)) + } else { + return fmt.Errorf("Unsupported sampling strategy type %v", res.GetStrategyType()) + } + if !s.sampler.Equal(newSampler) { + s.sampler = newSampler + } + return nil +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/sampler_options.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/sampler_options.go new file mode 100644 index 00000000..75d28a56 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/sampler_options.go @@ -0,0 +1,81 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "time" +) + +// SamplerOption is a function that sets some option on the sampler +type SamplerOption func(options *samplerOptions) + +// SamplerOptions is a factory for all available SamplerOption's +var SamplerOptions samplerOptions + +type samplerOptions struct { + metrics *Metrics + maxOperations int + sampler Sampler + logger Logger + samplingServerURL string + samplingRefreshInterval time.Duration +} + +// Metrics creates a SamplerOption that initializes Metrics on the sampler, +// which is used to emit statistics. +func (samplerOptions) Metrics(m *Metrics) SamplerOption { + return func(o *samplerOptions) { + o.metrics = m + } +} + +// MaxOperations creates a SamplerOption that sets the maximum number of +// operations the sampler will keep track of. +func (samplerOptions) MaxOperations(maxOperations int) SamplerOption { + return func(o *samplerOptions) { + o.maxOperations = maxOperations + } +} + +// InitialSampler creates a SamplerOption that sets the initial sampler +// to use before a remote sampler is created and used. +func (samplerOptions) InitialSampler(sampler Sampler) SamplerOption { + return func(o *samplerOptions) { + o.sampler = sampler + } +} + +// Logger creates a SamplerOption that sets the logger used by the sampler. +func (samplerOptions) Logger(logger Logger) SamplerOption { + return func(o *samplerOptions) { + o.logger = logger + } +} + +// SamplingServerURL creates a SamplerOption that sets the sampling server url +// of the local agent that contains the sampling strategies. +func (samplerOptions) SamplingServerURL(samplingServerURL string) SamplerOption { + return func(o *samplerOptions) { + o.samplingServerURL = samplingServerURL + } +} + +// SamplingRefreshInterval creates a SamplerOption that sets how often the +// sampler will poll local agent for the appropriate sampling strategy. +func (samplerOptions) SamplingRefreshInterval(samplingRefreshInterval time.Duration) SamplerOption { + return func(o *samplerOptions) { + o.samplingRefreshInterval = samplingRefreshInterval + } +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/sampler_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/sampler_test.go new file mode 100644 index 00000000..3faea035 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/sampler_test.go @@ -0,0 +1,690 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "errors" + "fmt" + "runtime" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber/jaeger-lib/metrics" + mTestutils "github.com/uber/jaeger-lib/metrics/testutils" + + "github.com/uber/jaeger-client-go/log" + "github.com/uber/jaeger-client-go/testutils" + "github.com/uber/jaeger-client-go/thrift-gen/sampling" + "github.com/uber/jaeger-client-go/utils" +) + +const ( + testOperationName = "op" + testFirstTimeOperationName = "firstTimeOp" + + testDefaultSamplingProbability = 0.5 + testMaxID = uint64(1) << 62 + testDefaultMaxOperations = 10 +) + +var ( + testProbabilisticExpectedTags = []Tag{ + {"sampler.type", "probabilistic"}, + {"sampler.param", 0.5}, + } + testLowerBoundExpectedTags = []Tag{ + {"sampler.type", "lowerbound"}, + {"sampler.param", 0.5}, + } +) + +func TestSamplerTags(t *testing.T) { + prob, err := NewProbabilisticSampler(0.1) + require.NoError(t, err) + rate := NewRateLimitingSampler(0.1) + remote := &RemotelyControlledSampler{} + remote.sampler = NewConstSampler(true) + tests := []struct { + sampler Sampler + typeTag string + paramTag interface{} + }{ + {NewConstSampler(true), "const", true}, + {NewConstSampler(false), "const", false}, + {prob, "probabilistic", 0.1}, + {rate, "ratelimiting", 0.1}, + {remote, "const", true}, + } + for _, test := range tests { + _, tags := test.sampler.IsSampled(TraceID{}, testOperationName) + count := 0 + for _, tag := range tags { + if tag.key == SamplerTypeTagKey { + assert.Equal(t, test.typeTag, tag.value) + count++ + } + if tag.key == SamplerParamTagKey { + assert.Equal(t, test.paramTag, tag.value) + count++ + } + } + assert.Equal(t, 2, count) + } +} + +func TestApplySamplerOptions(t *testing.T) { + options := applySamplerOptions() + sampler, ok := options.sampler.(*ProbabilisticSampler) + assert.True(t, ok) + assert.Equal(t, 0.001, sampler.samplingRate) + + assert.NotNil(t, options.logger) + assert.NotZero(t, options.maxOperations) + assert.NotEmpty(t, options.samplingServerURL) + assert.NotNil(t, options.metrics) + assert.NotZero(t, options.samplingRefreshInterval) +} + +func TestProbabilisticSamplerErrors(t *testing.T) { + _, err := NewProbabilisticSampler(-0.1) + assert.Error(t, err) + _, err = NewProbabilisticSampler(1.1) + assert.Error(t, err) +} + +func TestProbabilisticSampler(t *testing.T) { + sampler, _ := NewProbabilisticSampler(0.5) + sampled, tags := sampler.IsSampled(TraceID{Low: testMaxID + 10}, testOperationName) + assert.False(t, sampled) + assert.Equal(t, testProbabilisticExpectedTags, tags) + sampled, tags = sampler.IsSampled(TraceID{Low: testMaxID - 20}, testOperationName) + assert.True(t, sampled) + assert.Equal(t, testProbabilisticExpectedTags, tags) + sampler2, _ := NewProbabilisticSampler(0.5) + assert.True(t, sampler.Equal(sampler2)) + assert.False(t, sampler.Equal(NewConstSampler(true))) +} + +func TestProbabilisticSamplerPerformance(t *testing.T) { + t.Skip("Skipped performance test") + sampler, _ := NewProbabilisticSampler(0.01) + rand := utils.NewRand(8736823764) + var count uint64 + for i := 0; i < 100000000; i++ { + id := TraceID{Low: uint64(rand.Int63())} + if sampled, _ := sampler.IsSampled(id, testOperationName); sampled { + count++ + } + } + // println("Sampled:", count, "rate=", float64(count)/float64(100000000)) + // Sampled: 999829 rate= 0.009998290 +} + +func TestRateLimitingSampler(t *testing.T) { + sampler := NewRateLimitingSampler(2) + sampler2 := NewRateLimitingSampler(2) + sampler3 := NewRateLimitingSampler(3) + assert.True(t, sampler.Equal(sampler2)) + assert.False(t, sampler.Equal(sampler3)) + assert.False(t, sampler.Equal(NewConstSampler(false))) + + sampler = NewRateLimitingSampler(2) + sampled, _ := sampler.IsSampled(TraceID{}, testOperationName) + assert.True(t, sampled) + sampled, _ = sampler.IsSampled(TraceID{}, testOperationName) + assert.True(t, sampled) + sampled, _ = sampler.IsSampled(TraceID{}, testOperationName) + assert.False(t, sampled) + + sampler = NewRateLimitingSampler(0.1) + sampled, _ = sampler.IsSampled(TraceID{}, testOperationName) + assert.True(t, sampled) + sampled, _ = sampler.IsSampled(TraceID{}, testOperationName) + assert.False(t, sampled) +} + +func TestGuaranteedThroughputProbabilisticSamplerUpdate(t *testing.T) { + samplingRate := 0.5 + lowerBound := 2.0 + sampler, err := NewGuaranteedThroughputProbabilisticSampler(lowerBound, samplingRate) + assert.NoError(t, err) + + assert.Equal(t, lowerBound, sampler.lowerBound) + assert.Equal(t, samplingRate, sampler.samplingRate) + + newSamplingRate := 0.6 + newLowerBound := 1.0 + sampler.update(newLowerBound, newSamplingRate) + assert.Equal(t, newLowerBound, sampler.lowerBound) + assert.Equal(t, newSamplingRate, sampler.samplingRate) + + newSamplingRate = 1.1 + sampler.update(newLowerBound, newSamplingRate) + assert.Equal(t, 1.0, sampler.samplingRate) +} + +func TestAdaptiveSampler(t *testing.T) { + samplingRates := []*sampling.OperationSamplingStrategy{ + { + Operation: testOperationName, + ProbabilisticSampling: &sampling.ProbabilisticSamplingStrategy{SamplingRate: testDefaultSamplingProbability}, + }, + } + strategies := &sampling.PerOperationSamplingStrategies{ + DefaultSamplingProbability: testDefaultSamplingProbability, + DefaultLowerBoundTracesPerSecond: 1.0, + PerOperationStrategies: samplingRates, + } + + sampler, err := NewAdaptiveSampler(strategies, testDefaultMaxOperations) + require.NoError(t, err) + defer sampler.Close() + + sampled, tags := sampler.IsSampled(TraceID{Low: testMaxID + 10}, testOperationName) + assert.True(t, sampled) + assert.Equal(t, testLowerBoundExpectedTags, tags) + + sampled, tags = sampler.IsSampled(TraceID{Low: testMaxID - 20}, testOperationName) + assert.True(t, sampled) + assert.Equal(t, testProbabilisticExpectedTags, tags) + + sampled, tags = sampler.IsSampled(TraceID{Low: testMaxID + 10}, testOperationName) + assert.False(t, sampled) + + // This operation is seen for the first time by the sampler + sampled, tags = sampler.IsSampled(TraceID{Low: testMaxID}, testFirstTimeOperationName) + assert.True(t, sampled) + assert.Equal(t, testProbabilisticExpectedTags, tags) +} + +func TestAdaptiveSamplerErrors(t *testing.T) { + strategies := &sampling.PerOperationSamplingStrategies{ + DefaultSamplingProbability: testDefaultSamplingProbability, + DefaultLowerBoundTracesPerSecond: 2.0, + PerOperationStrategies: []*sampling.OperationSamplingStrategy{ + { + Operation: testOperationName, + ProbabilisticSampling: &sampling.ProbabilisticSamplingStrategy{SamplingRate: -0.1}, + }, + }, + } + + sampler, err := NewAdaptiveSampler(strategies, testDefaultMaxOperations) + assert.NoError(t, err) + assert.Equal(t, 0.0, sampler.(*adaptiveSampler).samplers[testOperationName].samplingRate) + + strategies.PerOperationStrategies[0].ProbabilisticSampling.SamplingRate = 1.1 + sampler, err = NewAdaptiveSampler(strategies, testDefaultMaxOperations) + assert.NoError(t, err) + assert.Equal(t, 1.0, sampler.(*adaptiveSampler).samplers[testOperationName].samplingRate) +} + +func TestAdaptiveSamplerUpdate(t *testing.T) { + samplingRate := 0.1 + lowerBound := 2.0 + samplingRates := []*sampling.OperationSamplingStrategy{ + { + Operation: testOperationName, + ProbabilisticSampling: &sampling.ProbabilisticSamplingStrategy{SamplingRate: samplingRate}, + }, + } + strategies := &sampling.PerOperationSamplingStrategies{ + DefaultSamplingProbability: testDefaultSamplingProbability, + DefaultLowerBoundTracesPerSecond: lowerBound, + PerOperationStrategies: samplingRates, + } + + s, err := NewAdaptiveSampler(strategies, testDefaultMaxOperations) + assert.NoError(t, err) + + sampler, ok := s.(*adaptiveSampler) + assert.True(t, ok) + assert.Equal(t, lowerBound, sampler.lowerBound) + assert.Equal(t, testDefaultSamplingProbability, sampler.defaultSampler.SamplingRate()) + assert.Len(t, sampler.samplers, 1) + + // Update the sampler with new sampling rates + newSamplingRate := 0.2 + newLowerBound := 3.0 + newDefaultSamplingProbability := 0.1 + newSamplingRates := []*sampling.OperationSamplingStrategy{ + { + Operation: testOperationName, + ProbabilisticSampling: &sampling.ProbabilisticSamplingStrategy{SamplingRate: newSamplingRate}, + }, + { + Operation: testFirstTimeOperationName, + ProbabilisticSampling: &sampling.ProbabilisticSamplingStrategy{SamplingRate: newSamplingRate}, + }, + } + strategies = &sampling.PerOperationSamplingStrategies{ + DefaultSamplingProbability: newDefaultSamplingProbability, + DefaultLowerBoundTracesPerSecond: newLowerBound, + PerOperationStrategies: newSamplingRates, + } + + sampler.update(strategies) + assert.Equal(t, newLowerBound, sampler.lowerBound) + assert.Equal(t, newDefaultSamplingProbability, sampler.defaultSampler.SamplingRate()) + assert.Len(t, sampler.samplers, 2) +} + +func initAgent(t *testing.T) (*testutils.MockAgent, *RemotelyControlledSampler, *metrics.LocalFactory) { + agent, err := testutils.StartMockAgent() + require.NoError(t, err) + + metricsFactory := metrics.NewLocalFactory(0) + metrics := NewMetrics(metricsFactory, nil) + + initialSampler, _ := NewProbabilisticSampler(0.001) + sampler := NewRemotelyControlledSampler( + "client app", + SamplerOptions.Metrics(metrics), + SamplerOptions.SamplingServerURL("http://"+agent.SamplingServerAddr()), + SamplerOptions.MaxOperations(testDefaultMaxOperations), + SamplerOptions.InitialSampler(initialSampler), + SamplerOptions.Logger(log.NullLogger), + SamplerOptions.SamplingRefreshInterval(time.Minute), + ) + sampler.Close() // stop timer-based updates, we want to call them manually + + return agent, sampler, metricsFactory +} + +func TestRemotelyControlledSampler(t *testing.T) { + agent, remoteSampler, metricsFactory := initAgent(t) + defer agent.Close() + + initSampler, ok := remoteSampler.getSampler().(*ProbabilisticSampler) + assert.True(t, ok) + + agent.AddSamplingStrategy("client app", + getSamplingStrategyResponse(sampling.SamplingStrategyType_PROBABILISTIC, testDefaultSamplingProbability)) + remoteSampler.updateSampler() + mTestutils.AssertCounterMetrics(t, metricsFactory, []mTestutils.ExpectedMetric{ + {Name: "jaeger.sampler_queries", Tags: map[string]string{"result": "ok"}, Value: 1}, + {Name: "jaeger.sampler_updates", Tags: map[string]string{"result": "ok"}, Value: 1}, + }...) + s1, ok := remoteSampler.getSampler().(*ProbabilisticSampler) + assert.True(t, ok) + assert.NotEqual(t, initSampler, s1, "Sampler should have been updated") + + sampled, tags := remoteSampler.IsSampled(TraceID{Low: testMaxID + 10}, testOperationName) + assert.False(t, sampled) + assert.Equal(t, testProbabilisticExpectedTags, tags) + sampled, tags = remoteSampler.IsSampled(TraceID{Low: testMaxID - 10}, testOperationName) + assert.True(t, sampled) + assert.Equal(t, testProbabilisticExpectedTags, tags) + + remoteSampler.setSampler(initSampler) + + c := make(chan time.Time) + ticker := &time.Ticker{C: c} + go remoteSampler.pollControllerWithTicker(ticker) + + c <- time.Now() // force update based on timer + time.Sleep(10 * time.Millisecond) + remoteSampler.Close() + + s2, ok := remoteSampler.getSampler().(*ProbabilisticSampler) + assert.True(t, ok) + assert.NotEqual(t, initSampler, s2, "Sampler should have been updated from timer") + + assert.True(t, remoteSampler.Equal(remoteSampler)) +} + +func generateTags(key string, value float64) []Tag { + return []Tag{ + {"sampler.type", key}, + {"sampler.param", value}, + } +} + +func TestRemotelyControlledSampler_updateSampler(t *testing.T) { + tests := []struct { + probabilities map[string]float64 + defaultProbability float64 + expectedDefaultProbability float64 + expectedTags []Tag + }{ + { + probabilities: map[string]float64{testOperationName: 1.1}, + defaultProbability: testDefaultSamplingProbability, + expectedDefaultProbability: testDefaultSamplingProbability, + expectedTags: generateTags("probabilistic", 1.0), + }, + { + probabilities: map[string]float64{testOperationName: testDefaultSamplingProbability}, + defaultProbability: testDefaultSamplingProbability, + expectedDefaultProbability: testDefaultSamplingProbability, + expectedTags: testProbabilisticExpectedTags, + }, + { + probabilities: map[string]float64{ + testOperationName: testDefaultSamplingProbability, + testFirstTimeOperationName: testDefaultSamplingProbability, + }, + defaultProbability: testDefaultSamplingProbability, + expectedDefaultProbability: testDefaultSamplingProbability, + expectedTags: testProbabilisticExpectedTags, + }, + { + probabilities: map[string]float64{"new op": 1.1}, + defaultProbability: testDefaultSamplingProbability, + expectedDefaultProbability: testDefaultSamplingProbability, + expectedTags: testProbabilisticExpectedTags, + }, + { + probabilities: map[string]float64{"new op": 1.1}, + defaultProbability: 1.1, + expectedDefaultProbability: 1.0, + expectedTags: generateTags("probabilistic", 1.0), + }, + } + + for i, test := range tests { + t.Run(fmt.Sprintf("test_%d", i), func(t *testing.T) { + agent, sampler, metricsFactory := initAgent(t) + defer agent.Close() + + initSampler, ok := sampler.sampler.(*ProbabilisticSampler) + assert.True(t, ok) + + res := &sampling.SamplingStrategyResponse{ + StrategyType: sampling.SamplingStrategyType_PROBABILISTIC, + OperationSampling: &sampling.PerOperationSamplingStrategies{ + DefaultSamplingProbability: test.defaultProbability, + DefaultLowerBoundTracesPerSecond: 0.001, + }, + } + for opName, prob := range test.probabilities { + res.OperationSampling.PerOperationStrategies = append(res.OperationSampling.PerOperationStrategies, + &sampling.OperationSamplingStrategy{ + Operation: opName, + ProbabilisticSampling: &sampling.ProbabilisticSamplingStrategy{ + SamplingRate: prob, + }, + }, + ) + } + + agent.AddSamplingStrategy("client app", res) + sampler.updateSampler() + + mTestutils.AssertCounterMetrics(t, metricsFactory, + mTestutils.ExpectedMetric{ + Name: "jaeger.sampler_updates", Tags: map[string]string{"result": "ok"}, Value: 1, + }, + ) + + s, ok := sampler.sampler.(*adaptiveSampler) + assert.True(t, ok) + assert.NotEqual(t, initSampler, sampler.sampler, "Sampler should have been updated") + assert.Equal(t, test.expectedDefaultProbability, s.defaultSampler.SamplingRate()) + + // First call is always sampled + sampled, tags := sampler.IsSampled(TraceID{Low: testMaxID + 10}, testOperationName) + assert.True(t, sampled) + + sampled, tags = sampler.IsSampled(TraceID{Low: testMaxID - 10}, testOperationName) + assert.True(t, sampled) + assert.Equal(t, test.expectedTags, tags) + }) + } +} + +func TestMaxOperations(t *testing.T) { + samplingRates := []*sampling.OperationSamplingStrategy{ + { + Operation: testOperationName, + ProbabilisticSampling: &sampling.ProbabilisticSamplingStrategy{SamplingRate: 0.1}, + }, + } + strategies := &sampling.PerOperationSamplingStrategies{ + DefaultSamplingProbability: testDefaultSamplingProbability, + DefaultLowerBoundTracesPerSecond: 2.0, + PerOperationStrategies: samplingRates, + } + + sampler, err := NewAdaptiveSampler(strategies, 1) + assert.NoError(t, err) + + sampled, tags := sampler.IsSampled(TraceID{Low: testMaxID - 10}, testFirstTimeOperationName) + assert.True(t, sampled) + assert.Equal(t, testProbabilisticExpectedTags, tags) +} + +func TestSamplerQueryError(t *testing.T) { + agent, sampler, metricsFactory := initAgent(t) + defer agent.Close() + + // override the actual handler + sampler.manager = &fakeSamplingManager{} + + initSampler, ok := sampler.sampler.(*ProbabilisticSampler) + assert.True(t, ok) + + sampler.Close() // stop timer-based updates, we want to call them manually + + sampler.updateSampler() + assert.Equal(t, initSampler, sampler.sampler, "Sampler should not have been updated due to query error") + + mTestutils.AssertCounterMetrics(t, metricsFactory, + mTestutils.ExpectedMetric{Name: "jaeger.sampler_queries", Tags: map[string]string{"result": "err"}, Value: 1}, + ) +} + +type fakeSamplingManager struct{} + +func (c *fakeSamplingManager) GetSamplingStrategy(serviceName string) (*sampling.SamplingStrategyResponse, error) { + return nil, errors.New("query error") +} + +func TestRemotelyControlledSampler_updateSamplerFromAdaptiveSampler(t *testing.T) { + agent, remoteSampler, metricsFactory := initAgent(t) + defer agent.Close() + remoteSampler.Close() // close the second time (initAgent already called Close) + + strategies := &sampling.PerOperationSamplingStrategies{ + DefaultSamplingProbability: testDefaultSamplingProbability, + DefaultLowerBoundTracesPerSecond: 1.0, + } + + adaptiveSampler, err := NewAdaptiveSampler(strategies, testDefaultMaxOperations) + require.NoError(t, err) + + // Overwrite the sampler with an adaptive sampler + remoteSampler.sampler = adaptiveSampler + + agent.AddSamplingStrategy("client app", + getSamplingStrategyResponse(sampling.SamplingStrategyType_PROBABILISTIC, 0.5)) + remoteSampler.updateSampler() + + // Sampler should have been updated to probabilistic + _, ok := remoteSampler.sampler.(*ProbabilisticSampler) + require.True(t, ok) + + // Overwrite the sampler with an adaptive sampler + remoteSampler.sampler = adaptiveSampler + + agent.AddSamplingStrategy("client app", + getSamplingStrategyResponse(sampling.SamplingStrategyType_RATE_LIMITING, 1)) + remoteSampler.updateSampler() + + // Sampler should have been updated to ratelimiting + _, ok = remoteSampler.sampler.(*rateLimitingSampler) + require.True(t, ok) + + // Overwrite the sampler with an adaptive sampler + remoteSampler.sampler = adaptiveSampler + + // Update existing adaptive sampler + agent.AddSamplingStrategy("client app", &sampling.SamplingStrategyResponse{OperationSampling: strategies}) + remoteSampler.updateSampler() + + mTestutils.AssertCounterMetrics(t, metricsFactory, + mTestutils.ExpectedMetric{Name: "jaeger.sampler_queries", Tags: map[string]string{"result": "ok"}, Value: 3}, + mTestutils.ExpectedMetric{Name: "jaeger.sampler_updates", Tags: map[string]string{"result": "ok"}, Value: 3}, + ) +} + +func TestRemotelyControlledSampler_updateRateLimitingOrProbabilisticSampler(t *testing.T) { + probabilisticSampler, err := NewProbabilisticSampler(0.002) + require.NoError(t, err) + otherProbabilisticSampler, err := NewProbabilisticSampler(0.003) + require.NoError(t, err) + maxProbabilisticSampler, err := NewProbabilisticSampler(1.0) + require.NoError(t, err) + + rateLimitingSampler := NewRateLimitingSampler(2) + otherRateLimitingSampler := NewRateLimitingSampler(3) + + testCases := []struct { + res *sampling.SamplingStrategyResponse + initSampler Sampler + expectedSampler Sampler + shouldErr bool + referenceEquivalence bool + caption string + }{ + { + res: getSamplingStrategyResponse(sampling.SamplingStrategyType_PROBABILISTIC, 1.5), + initSampler: probabilisticSampler, + expectedSampler: maxProbabilisticSampler, + shouldErr: false, + referenceEquivalence: false, + caption: "invalid probabilistic strategy", + }, + { + res: getSamplingStrategyResponse(sampling.SamplingStrategyType_PROBABILISTIC, 0.002), + initSampler: probabilisticSampler, + expectedSampler: probabilisticSampler, + shouldErr: false, + referenceEquivalence: true, + caption: "unchanged probabilistic strategy", + }, + { + res: getSamplingStrategyResponse(sampling.SamplingStrategyType_PROBABILISTIC, 0.003), + initSampler: probabilisticSampler, + expectedSampler: otherProbabilisticSampler, + shouldErr: false, + referenceEquivalence: false, + caption: "valid probabilistic strategy", + }, + { + res: getSamplingStrategyResponse(sampling.SamplingStrategyType_RATE_LIMITING, 2), + initSampler: rateLimitingSampler, + expectedSampler: rateLimitingSampler, + shouldErr: false, + referenceEquivalence: true, + caption: "unchanged rate limiting strategy", + }, + { + res: getSamplingStrategyResponse(sampling.SamplingStrategyType_RATE_LIMITING, 3), + initSampler: rateLimitingSampler, + expectedSampler: otherRateLimitingSampler, + shouldErr: false, + referenceEquivalence: false, + caption: "valid rate limiting strategy", + }, + { + res: &sampling.SamplingStrategyResponse{}, + initSampler: rateLimitingSampler, + expectedSampler: rateLimitingSampler, + shouldErr: true, + referenceEquivalence: true, + caption: "invalid strategy", + }, + } + + for _, tc := range testCases { + testCase := tc // capture loop var + t.Run(testCase.caption, func(t *testing.T) { + remoteSampler := &RemotelyControlledSampler{samplerOptions: samplerOptions{sampler: testCase.initSampler}} + err := remoteSampler.updateRateLimitingOrProbabilisticSampler(testCase.res) + if testCase.shouldErr { + require.Error(t, err) + } + if testCase.referenceEquivalence { + assert.Equal(t, testCase.expectedSampler, remoteSampler.sampler) + } else { + assert.True(t, testCase.expectedSampler.Equal(remoteSampler.sampler)) + } + }) + } +} + +func getSamplingStrategyResponse(strategyType sampling.SamplingStrategyType, value float64) *sampling.SamplingStrategyResponse { + if strategyType == sampling.SamplingStrategyType_PROBABILISTIC { + return &sampling.SamplingStrategyResponse{ + StrategyType: sampling.SamplingStrategyType_PROBABILISTIC, + ProbabilisticSampling: &sampling.ProbabilisticSamplingStrategy{ + SamplingRate: value, + }, + } + } + if strategyType == sampling.SamplingStrategyType_RATE_LIMITING { + return &sampling.SamplingStrategyResponse{ + StrategyType: sampling.SamplingStrategyType_RATE_LIMITING, + RateLimitingSampling: &sampling.RateLimitingSamplingStrategy{ + MaxTracesPerSecond: int16(value), + }, + } + } + return nil +} + +func TestAdaptiveSampler_lockRaceCondition(t *testing.T) { + agent, remoteSampler, _ := initAgent(t) + defer agent.Close() + remoteSampler.Close() // stop timer-based updates, we want to call them manually + + numOperations := 1000 + adaptiveSampler, err := NewAdaptiveSampler( + &sampling.PerOperationSamplingStrategies{ + DefaultSamplingProbability: 1, + }, + 2000, + ) + require.NoError(t, err) + + // Overwrite the sampler with an adaptive sampler + remoteSampler.sampler = adaptiveSampler + + var wg sync.WaitGroup + defer wg.Wait() + wg.Add(2) + + // Start 2 go routines that will simulate simultaneous calls to IsSampled + go func() { + defer wg.Done() + isSampled(t, remoteSampler, numOperations, "a") + }() + go func() { + defer wg.Done() + isSampled(t, remoteSampler, numOperations, "b") + }() +} + +func isSampled(t *testing.T, remoteSampler *RemotelyControlledSampler, numOperations int, operationNamePrefix string) { + for i := 0; i < numOperations; i++ { + runtime.Gosched() + sampled, _ := remoteSampler.IsSampled(TraceID{}, fmt.Sprintf("%s%d", operationNamePrefix, i)) + assert.True(t, sampled) + } +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/scripts/cover.sh b/template/faaschain/vendor/github.com/uber/jaeger-client-go/scripts/cover.sh new file mode 100755 index 00000000..d0fafbdb --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/scripts/cover.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +set -e + +COVER=.cover +ROOT_PKG=github.com/uber/jaeger-client-go/ + +if [[ -d "$COVER" ]]; then + rm -rf "$COVER" +fi +mkdir -p "$COVER" + +# If a package directory has a .nocover file, don't count it when calculating +# coverage. +filter="" +for pkg in "$@"; do + if [[ -f "$GOPATH/src/$pkg/.nocover" ]]; then + if [[ -n "$filter" ]]; then + filter="$filter, " + fi + filter="\"$pkg\": true" + fi +done + + +i=0 +for pkg in "$@"; do + i=$((i + 1)) + + extracoverpkg="" + if [[ -f "$GOPATH/src/$pkg/.extra-coverpkg" ]]; then + extracoverpkg=$( \ + sed -e "s|^|$pkg/|g" < "$GOPATH/src/$pkg/.extra-coverpkg" \ + | tr '\n' ',') + fi + + coverpkg=$(go list -json "$pkg" | jq -r ' + .Deps + | . + ["'"$pkg"'"] + | map + ( select(startswith("'"$ROOT_PKG"'")) + | select(contains("/vendor/") | not) + | select(in({'"$filter"'}) | not) + ) + | join(",") + ') + if [[ -n "$extracoverpkg" ]]; then + coverpkg="$extracoverpkg$coverpkg" + fi + + args="" + if [[ -n "$coverpkg" ]]; then + args="-coverprofile $COVER/cover.${i}.out" # -coverpkg $coverpkg" + fi + + echo go test -v -race "$pkg" + go test $args -v -race "$pkg" +done + +gocovmerge "$COVER"/*.out > cover.out diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/scripts/updateLicense.py b/template/faaschain/vendor/github.com/uber/jaeger-client-go/scripts/updateLicense.py new file mode 100644 index 00000000..6503a6f5 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/scripts/updateLicense.py @@ -0,0 +1,93 @@ +from __future__ import ( + absolute_import, print_function, division, unicode_literals +) + +import logging +import re +import sys +from datetime import datetime + +logging.basicConfig(level=logging.DEBUG) +logger = logging.getLogger(__name__) + +CURRENT_YEAR = datetime.today().year + +LICENSE_BLOB = """Copyright (c) %d The Jaeger Authors. + +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.""" % CURRENT_YEAR + +LICENSE_BLOB_LINES_GO = [ + ('// ' + l).strip() + '\n' for l in LICENSE_BLOB.split('\n') +] + +COPYRIGHT_RE = re.compile(r'Copyright \(c\) (\d+)', re.I) + + +def update_go_license(name, force=False): + with open(name) as f: + orig_lines = list(f) + lines = list(orig_lines) + + found = False + changed = False + for i, line in enumerate(lines[:5]): + m = COPYRIGHT_RE.search(line) + if not m: + continue + + found = True + year = int(m.group(1)) + if year == CURRENT_YEAR: + break + + # Avoid updating the copyright year. + # + # new_line = COPYRIGHT_RE.sub('Copyright (c) %d' % CURRENT_YEAR, line) + # assert line != new_line, ('Could not change year in: %s' % line) + # lines[i] = new_line + # changed = True + break + + if not found: + if 'Code generated by' in lines[0]: + lines[1:1] = ['\n'] + LICENSE_BLOB_LINES_GO + else: + lines[0:0] = LICENSE_BLOB_LINES_GO + ['\n'] + changed = True + + if changed: + with open(name, 'w') as f: + for line in lines: + f.write(line) + print(name) + + +def main(): + if len(sys.argv) == 1: + print('USAGE: %s FILE ...' % sys.argv[0]) + sys.exit(1) + + for name in sys.argv[1:]: + if name.endswith('.go'): + try: + update_go_license(name) + except Exception as error: + logger.error('Failed to process file %s', name) + logger.exception(error) + raise error + else: + raise NotImplementedError('Unsupported file type: %s' % name) + + +if __name__ == "__main__": + main() diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/scripts/updateLicenses.sh b/template/faaschain/vendor/github.com/uber/jaeger-client-go/scripts/updateLicenses.sh new file mode 100755 index 00000000..177d6f08 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/scripts/updateLicenses.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +set -e + +python scripts/updateLicense.py $(git ls-files "*\.go" | grep -v -e thrift-gen -e tracetest -e thrift/) diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/span.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/span.go new file mode 100644 index 00000000..f0b497a9 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/span.go @@ -0,0 +1,249 @@ +// Copyright (c) 2017-2018 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "sync" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/opentracing/opentracing-go/log" +) + +// Span implements opentracing.Span +type Span struct { + sync.RWMutex + + tracer *Tracer + + context SpanContext + + // The name of the "operation" this span is an instance of. + // Known as a "span name" in some implementations. + operationName string + + // firstInProcess, if true, indicates that this span is the root of the (sub)tree + // of spans in the current process. In other words it's true for the root spans, + // and the ingress spans when the process joins another trace. + firstInProcess bool + + // startTime is the timestamp indicating when the span began, with microseconds precision. + startTime time.Time + + // duration returns duration of the span with microseconds precision. + // Zero value means duration is unknown. + duration time.Duration + + // tags attached to this span + tags []Tag + + // The span's "micro-log" + logs []opentracing.LogRecord + + // references for this span + references []Reference + + observer ContribSpanObserver +} + +// Tag is a simple key value wrapper. +// TODO deprecate in the next major release, use opentracing.Tag instead. +type Tag struct { + key string + value interface{} +} + +// SetOperationName sets or changes the operation name. +func (s *Span) SetOperationName(operationName string) opentracing.Span { + s.Lock() + defer s.Unlock() + if s.context.IsSampled() { + s.operationName = operationName + } + s.observer.OnSetOperationName(operationName) + return s +} + +// SetTag implements SetTag() of opentracing.Span +func (s *Span) SetTag(key string, value interface{}) opentracing.Span { + s.observer.OnSetTag(key, value) + if key == string(ext.SamplingPriority) && !setSamplingPriority(s, value) { + return s + } + s.Lock() + defer s.Unlock() + if s.context.IsSampled() { + s.setTagNoLocking(key, value) + } + return s +} + +func (s *Span) setTagNoLocking(key string, value interface{}) { + s.tags = append(s.tags, Tag{key: key, value: value}) +} + +// LogFields implements opentracing.Span API +func (s *Span) LogFields(fields ...log.Field) { + s.Lock() + defer s.Unlock() + if !s.context.IsSampled() { + return + } + s.logFieldsNoLocking(fields...) +} + +// this function should only be called while holding a Write lock +func (s *Span) logFieldsNoLocking(fields ...log.Field) { + lr := opentracing.LogRecord{ + Fields: fields, + Timestamp: time.Now(), + } + s.appendLog(lr) +} + +// LogKV implements opentracing.Span API +func (s *Span) LogKV(alternatingKeyValues ...interface{}) { + s.RLock() + sampled := s.context.IsSampled() + s.RUnlock() + if !sampled { + return + } + fields, err := log.InterleavedKVToFields(alternatingKeyValues...) + if err != nil { + s.LogFields(log.Error(err), log.String("function", "LogKV")) + return + } + s.LogFields(fields...) +} + +// LogEvent implements opentracing.Span API +func (s *Span) LogEvent(event string) { + s.Log(opentracing.LogData{Event: event}) +} + +// LogEventWithPayload implements opentracing.Span API +func (s *Span) LogEventWithPayload(event string, payload interface{}) { + s.Log(opentracing.LogData{Event: event, Payload: payload}) +} + +// Log implements opentracing.Span API +func (s *Span) Log(ld opentracing.LogData) { + s.Lock() + defer s.Unlock() + if s.context.IsSampled() { + if ld.Timestamp.IsZero() { + ld.Timestamp = s.tracer.timeNow() + } + s.appendLog(ld.ToLogRecord()) + } +} + +// this function should only be called while holding a Write lock +func (s *Span) appendLog(lr opentracing.LogRecord) { + // TODO add logic to limit number of logs per span (issue #46) + s.logs = append(s.logs, lr) +} + +// SetBaggageItem implements SetBaggageItem() of opentracing.SpanContext +func (s *Span) SetBaggageItem(key, value string) opentracing.Span { + s.Lock() + defer s.Unlock() + s.tracer.setBaggage(s, key, value) + return s +} + +// BaggageItem implements BaggageItem() of opentracing.SpanContext +func (s *Span) BaggageItem(key string) string { + s.RLock() + defer s.RUnlock() + return s.context.baggage[key] +} + +// Finish implements opentracing.Span API +func (s *Span) Finish() { + s.FinishWithOptions(opentracing.FinishOptions{}) +} + +// FinishWithOptions implements opentracing.Span API +func (s *Span) FinishWithOptions(options opentracing.FinishOptions) { + if options.FinishTime.IsZero() { + options.FinishTime = s.tracer.timeNow() + } + s.observer.OnFinish(options) + s.Lock() + if s.context.IsSampled() { + s.duration = options.FinishTime.Sub(s.startTime) + // Note: bulk logs are not subject to maxLogsPerSpan limit + if options.LogRecords != nil { + s.logs = append(s.logs, options.LogRecords...) + } + for _, ld := range options.BulkLogData { + s.logs = append(s.logs, ld.ToLogRecord()) + } + } + s.Unlock() + // call reportSpan even for non-sampled traces, to return span to the pool + s.tracer.reportSpan(s) +} + +// Context implements opentracing.Span API +func (s *Span) Context() opentracing.SpanContext { + s.Lock() + defer s.Unlock() + return s.context +} + +// Tracer implements opentracing.Span API +func (s *Span) Tracer() opentracing.Tracer { + return s.tracer +} + +func (s *Span) String() string { + s.RLock() + defer s.RUnlock() + return s.context.String() +} + +// OperationName allows retrieving current operation name. +func (s *Span) OperationName() string { + s.RLock() + defer s.RUnlock() + return s.operationName +} + +func (s *Span) serviceName() string { + return s.tracer.serviceName +} + +// setSamplingPriority returns true if the flag was updated successfully, false otherwise. +func setSamplingPriority(s *Span, value interface{}) bool { + s.Lock() + defer s.Unlock() + val, ok := value.(uint16) + if !ok { + return false + } + if val == 0 { + s.context.flags = s.context.flags & (^flagSampled) + return true + } + if s.tracer.isDebugAllowed(s.operationName) { + s.context.flags = s.context.flags | flagDebug | flagSampled + return true + } + return false +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/span_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/span_test.go new file mode 100644 index 00000000..d44cb1dc --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/span_test.go @@ -0,0 +1,148 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "sync" + "testing" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/uber/jaeger-client-go/internal/throttler" +) + +func TestBaggageIterator(t *testing.T) { + service := "DOOP" + tracer, closer := NewTracer(service, NewConstSampler(true), NewNullReporter()) + defer closer.Close() + + sp1 := tracer.StartSpan("s1").(*Span) + sp1.SetBaggageItem("Some_Key", "12345") + sp1.SetBaggageItem("Some-other-key", "42") + expectedBaggage := map[string]string{"Some_Key": "12345", "Some-other-key": "42"} + assertBaggage(t, sp1, expectedBaggage) + assertBaggageRecords(t, sp1, expectedBaggage) + + b := extractBaggage(sp1, false) // break out early + assert.Equal(t, 1, len(b), "only one baggage item should be extracted") + + sp2 := tracer.StartSpan("s2", opentracing.ChildOf(sp1.Context())).(*Span) + assertBaggage(t, sp2, expectedBaggage) // child inherits the same baggage + require.Len(t, sp2.logs, 0) // child doesn't inherit the baggage logs +} + +func assertBaggageRecords(t *testing.T, sp *Span, expected map[string]string) { + require.Len(t, sp.logs, len(expected)) + for _, logRecord := range sp.logs { + require.Len(t, logRecord.Fields, 3) + require.Equal(t, "event:baggage", logRecord.Fields[0].String()) + key := logRecord.Fields[1].Value().(string) + value := logRecord.Fields[2].Value().(string) + + require.Contains(t, expected, key) + assert.Equal(t, expected[key], value) + } +} + +func assertBaggage(t *testing.T, sp opentracing.Span, expected map[string]string) { + b := extractBaggage(sp, true) + assert.Equal(t, expected, b) +} + +func extractBaggage(sp opentracing.Span, allItems bool) map[string]string { + b := make(map[string]string) + sp.Context().ForeachBaggageItem(func(k, v string) bool { + b[k] = v + return allItems + }) + return b +} + +func TestSpanProperties(t *testing.T) { + tracer, closer := NewTracer("DOOP", NewConstSampler(true), NewNullReporter()) + defer closer.Close() + + sp1 := tracer.StartSpan("s1").(*Span) + assert.Equal(t, tracer, sp1.Tracer()) + assert.NotNil(t, sp1.Context()) +} + +func TestSpanOperationName(t *testing.T) { + tracer, closer := NewTracer("DOOP", NewConstSampler(true), NewNullReporter()) + defer closer.Close() + + sp1 := tracer.StartSpan("s1").(*Span) + sp1.SetOperationName("s2") + sp1.Finish() + + assert.Equal(t, "s2", sp1.OperationName()) +} + +func TestSetTag_SamplingPriority(t *testing.T) { + tracer, closer := NewTracer("DOOP", NewConstSampler(true), NewNullReporter(), + TracerOptions.DebugThrottler(throttler.DefaultThrottler{})) + + sp1 := tracer.StartSpan("s1").(*Span) + ext.SamplingPriority.Set(sp1, 0) + assert.False(t, sp1.context.IsDebug()) + + ext.SamplingPriority.Set(sp1, 1) + assert.True(t, sp1.context.IsDebug()) + assert.NotNil(t, findDomainTag(sp1, "sampling.priority"), "sampling.priority tag should be added") + closer.Close() + + tracer, closer = NewTracer("DOOP", NewConstSampler(true), NewNullReporter(), + TracerOptions.DebugThrottler(testThrottler{allowAll: false})) + defer closer.Close() + + sp1 = tracer.StartSpan("s1").(*Span) + ext.SamplingPriority.Set(sp1, 1) + assert.False(t, sp1.context.IsDebug(), "debug should not be allowed by the throttler") +} + +type testThrottler struct { + allowAll bool +} + +func (t testThrottler) IsAllowed(operation string) bool { + return t.allowAll +} + +func TestBaggageContextRace(t *testing.T) { + tracer, closer := NewTracer("DOOP", NewConstSampler(true), NewNullReporter()) + defer closer.Close() + + sp1 := tracer.StartSpan("s1").(*Span) + + var startWg, endWg sync.WaitGroup + startWg.Add(1) + endWg.Add(2) + + f := func() { + startWg.Wait() + sp1.SetBaggageItem("x", "y") + sp1.Context().ForeachBaggageItem(func(k, v string) bool { return false }) + endWg.Done() + } + + go f() + go f() + + startWg.Done() + endWg.Wait() +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/testutils/mock_agent.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/testutils/mock_agent.go new file mode 100644 index 00000000..9c1079f4 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/testutils/mock_agent.go @@ -0,0 +1,189 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package testutils + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "net/http/httptest" + "sync" + "sync/atomic" + + "github.com/uber/jaeger-client-go/thrift" + + "github.com/uber/jaeger-client-go/thrift-gen/agent" + "github.com/uber/jaeger-client-go/thrift-gen/jaeger" + "github.com/uber/jaeger-client-go/thrift-gen/sampling" + "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" + "github.com/uber/jaeger-client-go/utils" +) + +// StartMockAgent runs a mock representation of jaeger-agent. +// This function returns a started server. +func StartMockAgent() (*MockAgent, error) { + transport, err := NewTUDPServerTransport("127.0.0.1:0") + if err != nil { + return nil, err + } + + samplingManager := newSamplingManager() + samplingHandler := &samplingHandler{manager: samplingManager} + samplingServer := httptest.NewServer(samplingHandler) + + agent := &MockAgent{ + transport: transport, + samplingMgr: samplingManager, + samplingSrv: samplingServer, + } + + var started sync.WaitGroup + started.Add(1) + go agent.serve(&started) + started.Wait() + + return agent, nil +} + +// Close stops the serving of traffic +func (s *MockAgent) Close() { + atomic.StoreUint32(&s.serving, 0) + s.transport.Close() + s.samplingSrv.Close() +} + +// MockAgent is a mock representation of Jaeger Agent. +// It receives spans over UDP, and has an HTTP endpoint for sampling strategies. +type MockAgent struct { + transport *TUDPTransport + jaegerBatches []*jaeger.Batch + mutex sync.Mutex + serving uint32 + samplingMgr *samplingManager + samplingSrv *httptest.Server +} + +// SpanServerAddr returns the UDP host:port where MockAgent listens for spans +func (s *MockAgent) SpanServerAddr() string { + return s.transport.Addr().String() +} + +// SpanServerClient returns a UDP client that can be used to send spans to the MockAgent +func (s *MockAgent) SpanServerClient() (agent.Agent, error) { + return utils.NewAgentClientUDP(s.SpanServerAddr(), 0) +} + +// SamplingServerAddr returns the host:port of HTTP server exposing sampling strategy endpoint +func (s *MockAgent) SamplingServerAddr() string { + return s.samplingSrv.Listener.Addr().String() +} + +func (s *MockAgent) serve(started *sync.WaitGroup) { + handler := agent.NewAgentProcessor(s) + protocolFact := thrift.NewTCompactProtocolFactory() + buf := make([]byte, utils.UDPPacketMaxLength, utils.UDPPacketMaxLength) + trans := thrift.NewTMemoryBufferLen(utils.UDPPacketMaxLength) + + atomic.StoreUint32(&s.serving, 1) + started.Done() + for s.IsServing() { + n, err := s.transport.Read(buf) + if err == nil { + trans.Write(buf[:n]) + protocol := protocolFact.GetProtocol(trans) + handler.Process(protocol, protocol) + } + } +} + +// EmitZipkinBatch is deprecated, use EmitBatch +func (s *MockAgent) EmitZipkinBatch(spans []*zipkincore.Span) (err error) { + // TODO remove this for 3.0.0 + return errors.New("Not implemented") +} + +// GetZipkinSpans is deprecated use GetJaegerBatches +func (s *MockAgent) GetZipkinSpans() []*zipkincore.Span { + return nil +} + +// ResetZipkinSpans is deprecated use ResetJaegerBatches +func (s *MockAgent) ResetZipkinSpans() {} + +// EmitBatch implements EmitBatch() of TChanSamplingManagerServer +func (s *MockAgent) EmitBatch(batch *jaeger.Batch) (err error) { + s.mutex.Lock() + defer s.mutex.Unlock() + s.jaegerBatches = append(s.jaegerBatches, batch) + return err +} + +// IsServing indicates whether the server is currently serving traffic +func (s *MockAgent) IsServing() bool { + return atomic.LoadUint32(&s.serving) == 1 +} + +// AddSamplingStrategy registers a sampling strategy for a service +func (s *MockAgent) AddSamplingStrategy(service string, strategy *sampling.SamplingStrategyResponse) { + s.samplingMgr.AddSamplingStrategy(service, strategy) +} + +// GetJaegerBatches returns accumulated Jaeger batches +func (s *MockAgent) GetJaegerBatches() []*jaeger.Batch { + s.mutex.Lock() + defer s.mutex.Unlock() + n := len(s.jaegerBatches) + batches := make([]*jaeger.Batch, n, n) + copy(batches, s.jaegerBatches) + return batches +} + +// ResetJaegerBatches discards accumulated Jaeger batches +func (s *MockAgent) ResetJaegerBatches() { + s.mutex.Lock() + defer s.mutex.Unlock() + s.jaegerBatches = nil +} + +type samplingHandler struct { + manager *samplingManager +} + +func (h *samplingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + services := r.URL.Query()["service"] + if len(services) == 0 { + http.Error(w, "'service' parameter is empty", http.StatusBadRequest) + return + } + if len(services) > 1 { + http.Error(w, "'service' parameter must occur only once", http.StatusBadRequest) + return + } + resp, err := h.manager.GetSamplingStrategy(services[0]) + if err != nil { + http.Error(w, fmt.Sprintf("Error retrieving strategy: %+v", err), http.StatusInternalServerError) + return + } + json, err := json.Marshal(resp) + if err != nil { + http.Error(w, "Cannot marshall Thrift to JSON", http.StatusInternalServerError) + return + } + w.Header().Add("Content-Type", "application/json") + if _, err := w.Write(json); err != nil { + return + } +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/testutils/mock_agent_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/testutils/mock_agent_test.go new file mode 100644 index 00000000..4afa8387 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/testutils/mock_agent_test.go @@ -0,0 +1,93 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package testutils + +import ( + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/uber/jaeger-client-go/thrift-gen/jaeger" + "github.com/uber/jaeger-client-go/thrift-gen/sampling" + "github.com/uber/jaeger-client-go/utils" +) + +func TestMockAgentSpanServer(t *testing.T) { + mockAgent, err := StartMockAgent() + require.NoError(t, err) + defer mockAgent.Close() + + client, err := mockAgent.SpanServerClient() + require.NoError(t, err) + + for i := 1; i < 5; i++ { + batch := &jaeger.Batch{Process: &jaeger.Process{ServiceName: "svc"}} + spans := make([]*jaeger.Span, i, i) + for j := 0; j < i; j++ { + spans[j] = jaeger.NewSpan() + spans[j].OperationName = fmt.Sprintf("span-%d", j) + } + batch.Spans = spans + + err = client.EmitBatch(batch) + assert.NoError(t, err) + + for k := 0; k < 100; k++ { + time.Sleep(time.Millisecond) + batches := mockAgent.GetJaegerBatches() + if len(batches) > 0 && len(batches[0].Spans) == i { + break + } + } + batches := mockAgent.GetJaegerBatches() + require.NotEmpty(t, len(batches)) + require.Equal(t, i, len(batches[0].Spans)) + for j := 0; j < i; j++ { + assert.Equal(t, fmt.Sprintf("span-%d", j), batches[0].Spans[j].OperationName) + } + mockAgent.ResetJaegerBatches() + } +} + +func TestMockAgentSamplingManager(t *testing.T) { + mockAgent, err := StartMockAgent() + require.NoError(t, err) + defer mockAgent.Close() + + err = utils.GetJSON("http://"+mockAgent.SamplingServerAddr()+"/", nil) + require.Error(t, err, "no 'service' parameter") + err = utils.GetJSON("http://"+mockAgent.SamplingServerAddr()+"/?service=a&service=b", nil) + require.Error(t, err, "Too many 'service' parameters") + + var resp sampling.SamplingStrategyResponse + err = utils.GetJSON("http://"+mockAgent.SamplingServerAddr()+"/?service=something", &resp) + require.NoError(t, err) + assert.Equal(t, sampling.SamplingStrategyType_PROBABILISTIC, resp.StrategyType) + + mockAgent.AddSamplingStrategy("service123", &sampling.SamplingStrategyResponse{ + StrategyType: sampling.SamplingStrategyType_RATE_LIMITING, + RateLimitingSampling: &sampling.RateLimitingSamplingStrategy{ + MaxTracesPerSecond: 123, + }, + }) + err = utils.GetJSON("http://"+mockAgent.SamplingServerAddr()+"/?service=service123", &resp) + require.NoError(t, err) + assert.Equal(t, sampling.SamplingStrategyType_RATE_LIMITING, resp.StrategyType) + require.NotNil(t, resp.RateLimitingSampling) + assert.EqualValues(t, 123, resp.RateLimitingSampling.MaxTracesPerSecond) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/testutils/sampling_manager.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/testutils/sampling_manager.go new file mode 100644 index 00000000..6352c510 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/testutils/sampling_manager.go @@ -0,0 +1,53 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package testutils + +import ( + "sync" + + "github.com/uber/jaeger-client-go/thrift-gen/sampling" +) + +func newSamplingManager() *samplingManager { + return &samplingManager{ + sampling: make(map[string]*sampling.SamplingStrategyResponse), + } +} + +type samplingManager struct { + sampling map[string]*sampling.SamplingStrategyResponse + mutex sync.Mutex +} + +// GetSamplingStrategy implements handler method of sampling.SamplingManager +func (s *samplingManager) GetSamplingStrategy(serviceName string) (*sampling.SamplingStrategyResponse, error) { + s.mutex.Lock() + defer s.mutex.Unlock() + if strategy, ok := s.sampling[serviceName]; ok { + return strategy, nil + } + return &sampling.SamplingStrategyResponse{ + StrategyType: sampling.SamplingStrategyType_PROBABILISTIC, + ProbabilisticSampling: &sampling.ProbabilisticSamplingStrategy{ + SamplingRate: 0.01, + }}, nil +} + +// AddSamplingStrategy registers a sampling strategy for a service +func (s *samplingManager) AddSamplingStrategy(service string, strategy *sampling.SamplingStrategyResponse) { + s.mutex.Lock() + defer s.mutex.Unlock() + s.sampling[service] = strategy +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/testutils/udp_transport.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/testutils/udp_transport.go new file mode 100644 index 00000000..603504a0 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/testutils/udp_transport.go @@ -0,0 +1,106 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package testutils + +import ( + "bytes" + "net" + "sync/atomic" + + "github.com/uber/jaeger-client-go/thrift" +) + +const ( + // used in RemainingBytes() + maxRemainingBytes = ^uint64(0) +) + +// TUDPTransport does UDP as a thrift.TTransport (read-only, write functions not implemented). +type TUDPTransport struct { + conn *net.UDPConn + addr net.Addr + writeBuf bytes.Buffer + closed uint32 +} + +// NewTUDPServerTransport creates a net.UDPConn-backed TTransport for Thrift servers +// It will listen for incoming udp packets on the specified host/port +// Example: +// trans, err := utils.NewTUDPClientTransport("localhost:9001") +func NewTUDPServerTransport(hostPort string) (*TUDPTransport, error) { + addr, err := net.ResolveUDPAddr("udp", hostPort) + if err != nil { + return nil, thrift.NewTTransportException(thrift.NOT_OPEN, err.Error()) + } + conn, err := net.ListenUDP(addr.Network(), addr) + if err != nil { + return nil, thrift.NewTTransportException(thrift.NOT_OPEN, err.Error()) + } + return &TUDPTransport{addr: conn.LocalAddr(), conn: conn}, nil +} + +// Open does nothing as connection is opened on creation +// Required to maintain thrift.TTransport interface +func (p *TUDPTransport) Open() error { + return nil +} + +// Conn retrieves the underlying net.UDPConn +func (p *TUDPTransport) Conn() *net.UDPConn { + return p.conn +} + +// IsOpen returns true if the connection is open +func (p *TUDPTransport) IsOpen() bool { + return p.conn != nil && atomic.LoadUint32(&p.closed) == 0 +} + +// Close closes the connection +func (p *TUDPTransport) Close() error { + if p.conn != nil && atomic.CompareAndSwapUint32(&p.closed, 0, 1) { + return p.conn.Close() + } + return nil +} + +// Addr returns the address that the transport is listening on or writing to +func (p *TUDPTransport) Addr() net.Addr { + return p.addr +} + +// Read reads one UDP packet and puts it in the specified buf +func (p *TUDPTransport) Read(buf []byte) (int, error) { + if !p.IsOpen() { + return 0, thrift.NewTTransportException(thrift.NOT_OPEN, "Connection not open") + } + n, err := p.conn.Read(buf) + return n, thrift.NewTTransportExceptionFromError(err) +} + +// RemainingBytes returns the max number of bytes (same as Thrift's StreamTransport) as we +// do not know how many bytes we have left. +func (p *TUDPTransport) RemainingBytes() uint64 { + return maxRemainingBytes +} + +// Write writes specified buf to the write buffer +func (p *TUDPTransport) Write(buf []byte) (int, error) { + return 0, thrift.NewTTransportException(thrift.UNKNOWN_TRANSPORT_EXCEPTION, "Write not implemented") +} + +// Flush flushes the write buffer as one udp packet +func (p *TUDPTransport) Flush() error { + return thrift.NewTTransportException(thrift.UNKNOWN_TRANSPORT_EXCEPTION, "Flush not implemented") +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/testutils/udp_transport_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/testutils/udp_transport_test.go new file mode 100644 index 00000000..14708b54 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/testutils/udp_transport_test.go @@ -0,0 +1,67 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package testutils + +import ( + "net" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestUDPTransport(t *testing.T) { + server, err := NewTUDPServerTransport("127.0.0.1:0") + require.NoError(t, err) + defer server.Close() + + assert.NoError(t, server.Open()) + assert.True(t, server.IsOpen()) + assert.NotNil(t, server.Conn()) + + c := make(chan []byte) + defer close(c) + + go serveOnce(t, server, c) + + destAddr, err := net.ResolveUDPAddr("udp", server.Addr().String()) + require.NoError(t, err) + + connUDP, err := net.DialUDP(destAddr.Network(), nil, destAddr) + require.NoError(t, err) + defer connUDP.Close() + + n, err := connUDP.Write([]byte("test")) + assert.NoError(t, err) + assert.Equal(t, 4, n) + + select { + case data := <-c: + assert.Equal(t, "test", string(data)) + case <-time.After(time.Second * 1): + t.Error("Server did not respond in time") + } +} + +func serveOnce(t *testing.T, transport *TUDPTransport, c chan []byte) { + b := make([]byte, 65000, 65000) + n, err := transport.Read(b) + if err == nil { + c <- b[:n] + } else { + panic("Server failed to read: " + err.Error()) + } +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/agent.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/agent.go new file mode 100644 index 00000000..e48811c5 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/agent.go @@ -0,0 +1,411 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package agent + +import ( + "bytes" + "fmt" + "github.com/uber/jaeger-client-go/thrift" + "github.com/uber/jaeger-client-go/thrift-gen/jaeger" + "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +var _ = jaeger.GoUnusedProtection__ +var _ = zipkincore.GoUnusedProtection__ + +type Agent interface { + // Parameters: + // - Spans + EmitZipkinBatch(spans []*zipkincore.Span) (err error) + // Parameters: + // - Batch + EmitBatch(batch *jaeger.Batch) (err error) +} + +type AgentClient struct { + Transport thrift.TTransport + ProtocolFactory thrift.TProtocolFactory + InputProtocol thrift.TProtocol + OutputProtocol thrift.TProtocol + SeqId int32 +} + +func NewAgentClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *AgentClient { + return &AgentClient{Transport: t, + ProtocolFactory: f, + InputProtocol: f.GetProtocol(t), + OutputProtocol: f.GetProtocol(t), + SeqId: 0, + } +} + +func NewAgentClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *AgentClient { + return &AgentClient{Transport: t, + ProtocolFactory: nil, + InputProtocol: iprot, + OutputProtocol: oprot, + SeqId: 0, + } +} + +// Parameters: +// - Spans +func (p *AgentClient) EmitZipkinBatch(spans []*zipkincore.Span) (err error) { + if err = p.sendEmitZipkinBatch(spans); err != nil { + return + } + return +} + +func (p *AgentClient) sendEmitZipkinBatch(spans []*zipkincore.Span) (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("emitZipkinBatch", thrift.ONEWAY, p.SeqId); err != nil { + return + } + args := AgentEmitZipkinBatchArgs{ + Spans: spans, + } + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +// Parameters: +// - Batch +func (p *AgentClient) EmitBatch(batch *jaeger.Batch) (err error) { + if err = p.sendEmitBatch(batch); err != nil { + return + } + return +} + +func (p *AgentClient) sendEmitBatch(batch *jaeger.Batch) (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("emitBatch", thrift.ONEWAY, p.SeqId); err != nil { + return + } + args := AgentEmitBatchArgs{ + Batch: batch, + } + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +type AgentProcessor struct { + processorMap map[string]thrift.TProcessorFunction + handler Agent +} + +func (p *AgentProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { + p.processorMap[key] = processor +} + +func (p *AgentProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { + processor, ok = p.processorMap[key] + return processor, ok +} + +func (p *AgentProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { + return p.processorMap +} + +func NewAgentProcessor(handler Agent) *AgentProcessor { + + self0 := &AgentProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} + self0.processorMap["emitZipkinBatch"] = &agentProcessorEmitZipkinBatch{handler: handler} + self0.processorMap["emitBatch"] = &agentProcessorEmitBatch{handler: handler} + return self0 +} + +func (p *AgentProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + name, _, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return false, err + } + if processor, ok := p.GetProcessorFunction(name); ok { + return processor.Process(seqId, iprot, oprot) + } + iprot.Skip(thrift.STRUCT) + iprot.ReadMessageEnd() + x1 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) + oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) + x1.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, x1 + +} + +type agentProcessorEmitZipkinBatch struct { + handler Agent +} + +func (p *agentProcessorEmitZipkinBatch) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := AgentEmitZipkinBatchArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + return false, err + } + + iprot.ReadMessageEnd() + var err2 error + if err2 = p.handler.EmitZipkinBatch(args.Spans); err2 != nil { + return true, err2 + } + return true, nil +} + +type agentProcessorEmitBatch struct { + handler Agent +} + +func (p *agentProcessorEmitBatch) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := AgentEmitBatchArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + return false, err + } + + iprot.ReadMessageEnd() + var err2 error + if err2 = p.handler.EmitBatch(args.Batch); err2 != nil { + return true, err2 + } + return true, nil +} + +// HELPER FUNCTIONS AND STRUCTURES + +// Attributes: +// - Spans +type AgentEmitZipkinBatchArgs struct { + Spans []*zipkincore.Span `thrift:"spans,1" json:"spans"` +} + +func NewAgentEmitZipkinBatchArgs() *AgentEmitZipkinBatchArgs { + return &AgentEmitZipkinBatchArgs{} +} + +func (p *AgentEmitZipkinBatchArgs) GetSpans() []*zipkincore.Span { + return p.Spans +} +func (p *AgentEmitZipkinBatchArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *AgentEmitZipkinBatchArgs) readField1(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*zipkincore.Span, 0, size) + p.Spans = tSlice + for i := 0; i < size; i++ { + _elem2 := &zipkincore.Span{} + if err := _elem2.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem2), err) + } + p.Spans = append(p.Spans, _elem2) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *AgentEmitZipkinBatchArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("emitZipkinBatch_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *AgentEmitZipkinBatchArgs) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("spans", thrift.LIST, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:spans: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Spans)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Spans { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:spans: ", p), err) + } + return err +} + +func (p *AgentEmitZipkinBatchArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("AgentEmitZipkinBatchArgs(%+v)", *p) +} + +// Attributes: +// - Batch +type AgentEmitBatchArgs struct { + Batch *jaeger.Batch `thrift:"batch,1" json:"batch"` +} + +func NewAgentEmitBatchArgs() *AgentEmitBatchArgs { + return &AgentEmitBatchArgs{} +} + +var AgentEmitBatchArgs_Batch_DEFAULT *jaeger.Batch + +func (p *AgentEmitBatchArgs) GetBatch() *jaeger.Batch { + if !p.IsSetBatch() { + return AgentEmitBatchArgs_Batch_DEFAULT + } + return p.Batch +} +func (p *AgentEmitBatchArgs) IsSetBatch() bool { + return p.Batch != nil +} + +func (p *AgentEmitBatchArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *AgentEmitBatchArgs) readField1(iprot thrift.TProtocol) error { + p.Batch = &jaeger.Batch{} + if err := p.Batch.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Batch), err) + } + return nil +} + +func (p *AgentEmitBatchArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("emitBatch_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *AgentEmitBatchArgs) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("batch", thrift.STRUCT, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:batch: ", p), err) + } + if err := p.Batch.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Batch), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:batch: ", p), err) + } + return err +} + +func (p *AgentEmitBatchArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("AgentEmitBatchArgs(%+v)", *p) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/constants.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/constants.go new file mode 100644 index 00000000..aa9857bb --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/constants.go @@ -0,0 +1,23 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package agent + +import ( + "bytes" + "fmt" + "github.com/uber/jaeger-client-go/thrift" + "github.com/uber/jaeger-client-go/thrift-gen/jaeger" + "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +var _ = jaeger.GoUnusedProtection__ +var _ = zipkincore.GoUnusedProtection__ + +func init() { +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/ttypes.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/ttypes.go new file mode 100644 index 00000000..9c28f11c --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/ttypes.go @@ -0,0 +1,21 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package agent + +import ( + "bytes" + "fmt" + "github.com/uber/jaeger-client-go/thrift" + "github.com/uber/jaeger-client-go/thrift-gen/jaeger" + "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +var _ = jaeger.GoUnusedProtection__ +var _ = zipkincore.GoUnusedProtection__ +var GoUnusedProtection__ int diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/baggage/baggagerestrictionmanager.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/baggage/baggagerestrictionmanager.go new file mode 100644 index 00000000..1f79c125 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/baggage/baggagerestrictionmanager.go @@ -0,0 +1,435 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package baggage + +import ( + "bytes" + "fmt" + "github.com/uber/jaeger-client-go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +type BaggageRestrictionManager interface { + // getBaggageRestrictions retrieves the baggage restrictions for a specific service. + // Usually, baggageRestrictions apply to all services however there may be situations + // where a baggageKey might only be allowed to be set by a specific service. + // + // Parameters: + // - ServiceName + GetBaggageRestrictions(serviceName string) (r []*BaggageRestriction, err error) +} + +type BaggageRestrictionManagerClient struct { + Transport thrift.TTransport + ProtocolFactory thrift.TProtocolFactory + InputProtocol thrift.TProtocol + OutputProtocol thrift.TProtocol + SeqId int32 +} + +func NewBaggageRestrictionManagerClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *BaggageRestrictionManagerClient { + return &BaggageRestrictionManagerClient{Transport: t, + ProtocolFactory: f, + InputProtocol: f.GetProtocol(t), + OutputProtocol: f.GetProtocol(t), + SeqId: 0, + } +} + +func NewBaggageRestrictionManagerClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *BaggageRestrictionManagerClient { + return &BaggageRestrictionManagerClient{Transport: t, + ProtocolFactory: nil, + InputProtocol: iprot, + OutputProtocol: oprot, + SeqId: 0, + } +} + +// getBaggageRestrictions retrieves the baggage restrictions for a specific service. +// Usually, baggageRestrictions apply to all services however there may be situations +// where a baggageKey might only be allowed to be set by a specific service. +// +// Parameters: +// - ServiceName +func (p *BaggageRestrictionManagerClient) GetBaggageRestrictions(serviceName string) (r []*BaggageRestriction, err error) { + if err = p.sendGetBaggageRestrictions(serviceName); err != nil { + return + } + return p.recvGetBaggageRestrictions() +} + +func (p *BaggageRestrictionManagerClient) sendGetBaggageRestrictions(serviceName string) (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("getBaggageRestrictions", thrift.CALL, p.SeqId); err != nil { + return + } + args := BaggageRestrictionManagerGetBaggageRestrictionsArgs{ + ServiceName: serviceName, + } + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *BaggageRestrictionManagerClient) recvGetBaggageRestrictions() (value []*BaggageRestriction, err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "getBaggageRestrictions" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "getBaggageRestrictions failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "getBaggageRestrictions failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error0 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error1 error + error1, err = error0.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error1 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "getBaggageRestrictions failed: invalid message type") + return + } + result := BaggageRestrictionManagerGetBaggageRestrictionsResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + value = result.GetSuccess() + return +} + +type BaggageRestrictionManagerProcessor struct { + processorMap map[string]thrift.TProcessorFunction + handler BaggageRestrictionManager +} + +func (p *BaggageRestrictionManagerProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { + p.processorMap[key] = processor +} + +func (p *BaggageRestrictionManagerProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { + processor, ok = p.processorMap[key] + return processor, ok +} + +func (p *BaggageRestrictionManagerProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { + return p.processorMap +} + +func NewBaggageRestrictionManagerProcessor(handler BaggageRestrictionManager) *BaggageRestrictionManagerProcessor { + + self2 := &BaggageRestrictionManagerProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} + self2.processorMap["getBaggageRestrictions"] = &baggageRestrictionManagerProcessorGetBaggageRestrictions{handler: handler} + return self2 +} + +func (p *BaggageRestrictionManagerProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + name, _, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return false, err + } + if processor, ok := p.GetProcessorFunction(name); ok { + return processor.Process(seqId, iprot, oprot) + } + iprot.Skip(thrift.STRUCT) + iprot.ReadMessageEnd() + x3 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) + oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) + x3.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, x3 + +} + +type baggageRestrictionManagerProcessorGetBaggageRestrictions struct { + handler BaggageRestrictionManager +} + +func (p *baggageRestrictionManagerProcessorGetBaggageRestrictions) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := BaggageRestrictionManagerGetBaggageRestrictionsArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("getBaggageRestrictions", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := BaggageRestrictionManagerGetBaggageRestrictionsResult{} + var retval []*BaggageRestriction + var err2 error + if retval, err2 = p.handler.GetBaggageRestrictions(args.ServiceName); err2 != nil { + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing getBaggageRestrictions: "+err2.Error()) + oprot.WriteMessageBegin("getBaggageRestrictions", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } else { + result.Success = retval + } + if err2 = oprot.WriteMessageBegin("getBaggageRestrictions", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +// HELPER FUNCTIONS AND STRUCTURES + +// Attributes: +// - ServiceName +type BaggageRestrictionManagerGetBaggageRestrictionsArgs struct { + ServiceName string `thrift:"serviceName,1" json:"serviceName"` +} + +func NewBaggageRestrictionManagerGetBaggageRestrictionsArgs() *BaggageRestrictionManagerGetBaggageRestrictionsArgs { + return &BaggageRestrictionManagerGetBaggageRestrictionsArgs{} +} + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsArgs) GetServiceName() string { + return p.ServiceName +} +func (p *BaggageRestrictionManagerGetBaggageRestrictionsArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsArgs) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.ServiceName = v + } + return nil +} + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("getBaggageRestrictions_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsArgs) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("serviceName", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:serviceName: ", p), err) + } + if err := oprot.WriteString(string(p.ServiceName)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.serviceName (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:serviceName: ", p), err) + } + return err +} + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("BaggageRestrictionManagerGetBaggageRestrictionsArgs(%+v)", *p) +} + +// Attributes: +// - Success +type BaggageRestrictionManagerGetBaggageRestrictionsResult struct { + Success []*BaggageRestriction `thrift:"success,0" json:"success,omitempty"` +} + +func NewBaggageRestrictionManagerGetBaggageRestrictionsResult() *BaggageRestrictionManagerGetBaggageRestrictionsResult { + return &BaggageRestrictionManagerGetBaggageRestrictionsResult{} +} + +var BaggageRestrictionManagerGetBaggageRestrictionsResult_Success_DEFAULT []*BaggageRestriction + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsResult) GetSuccess() []*BaggageRestriction { + return p.Success +} +func (p *BaggageRestrictionManagerGetBaggageRestrictionsResult) IsSetSuccess() bool { + return p.Success != nil +} + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 0: + if err := p.readField0(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsResult) readField0(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*BaggageRestriction, 0, size) + p.Success = tSlice + for i := 0; i < size; i++ { + _elem4 := &BaggageRestriction{} + if err := _elem4.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem4), err) + } + p.Success = append(p.Success, _elem4) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("getBaggageRestrictions_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField0(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsResult) writeField0(oprot thrift.TProtocol) (err error) { + if p.IsSetSuccess() { + if err := oprot.WriteFieldBegin("success", thrift.LIST, 0); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Success)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Success { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) + } + } + return err +} + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("BaggageRestrictionManagerGetBaggageRestrictionsResult(%+v)", *p) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/baggage/constants.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/baggage/constants.go new file mode 100644 index 00000000..ed35ce9a --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/baggage/constants.go @@ -0,0 +1,18 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package baggage + +import ( + "bytes" + "fmt" + "github.com/uber/jaeger-client-go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +func init() { +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/baggage/ttypes.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/baggage/ttypes.go new file mode 100644 index 00000000..7888892f --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/baggage/ttypes.go @@ -0,0 +1,154 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package baggage + +import ( + "bytes" + "fmt" + "github.com/uber/jaeger-client-go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +var GoUnusedProtection__ int + +// Attributes: +// - BaggageKey +// - MaxValueLength +type BaggageRestriction struct { + BaggageKey string `thrift:"baggageKey,1,required" json:"baggageKey"` + MaxValueLength int32 `thrift:"maxValueLength,2,required" json:"maxValueLength"` +} + +func NewBaggageRestriction() *BaggageRestriction { + return &BaggageRestriction{} +} + +func (p *BaggageRestriction) GetBaggageKey() string { + return p.BaggageKey +} + +func (p *BaggageRestriction) GetMaxValueLength() int32 { + return p.MaxValueLength +} +func (p *BaggageRestriction) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetBaggageKey bool = false + var issetMaxValueLength bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetBaggageKey = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetMaxValueLength = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetBaggageKey { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field BaggageKey is not set")) + } + if !issetMaxValueLength { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field MaxValueLength is not set")) + } + return nil +} + +func (p *BaggageRestriction) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.BaggageKey = v + } + return nil +} + +func (p *BaggageRestriction) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.MaxValueLength = v + } + return nil +} + +func (p *BaggageRestriction) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("BaggageRestriction"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *BaggageRestriction) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("baggageKey", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:baggageKey: ", p), err) + } + if err := oprot.WriteString(string(p.BaggageKey)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.baggageKey (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:baggageKey: ", p), err) + } + return err +} + +func (p *BaggageRestriction) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("maxValueLength", thrift.I32, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:maxValueLength: ", p), err) + } + if err := oprot.WriteI32(int32(p.MaxValueLength)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.maxValueLength (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:maxValueLength: ", p), err) + } + return err +} + +func (p *BaggageRestriction) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("BaggageRestriction(%+v)", *p) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/agent.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/agent.go new file mode 100644 index 00000000..b32c37dd --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/agent.go @@ -0,0 +1,242 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package jaeger + +import ( + "bytes" + "fmt" + "github.com/uber/jaeger-client-go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +type Agent interface { + // Parameters: + // - Batch + EmitBatch(batch *Batch) (err error) +} + +type AgentClient struct { + Transport thrift.TTransport + ProtocolFactory thrift.TProtocolFactory + InputProtocol thrift.TProtocol + OutputProtocol thrift.TProtocol + SeqId int32 +} + +func NewAgentClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *AgentClient { + return &AgentClient{Transport: t, + ProtocolFactory: f, + InputProtocol: f.GetProtocol(t), + OutputProtocol: f.GetProtocol(t), + SeqId: 0, + } +} + +func NewAgentClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *AgentClient { + return &AgentClient{Transport: t, + ProtocolFactory: nil, + InputProtocol: iprot, + OutputProtocol: oprot, + SeqId: 0, + } +} + +// Parameters: +// - Batch +func (p *AgentClient) EmitBatch(batch *Batch) (err error) { + if err = p.sendEmitBatch(batch); err != nil { + return + } + return +} + +func (p *AgentClient) sendEmitBatch(batch *Batch) (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("emitBatch", thrift.ONEWAY, p.SeqId); err != nil { + return + } + args := AgentEmitBatchArgs{ + Batch: batch, + } + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +type AgentProcessor struct { + processorMap map[string]thrift.TProcessorFunction + handler Agent +} + +func (p *AgentProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { + p.processorMap[key] = processor +} + +func (p *AgentProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { + processor, ok = p.processorMap[key] + return processor, ok +} + +func (p *AgentProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { + return p.processorMap +} + +func NewAgentProcessor(handler Agent) *AgentProcessor { + + self6 := &AgentProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} + self6.processorMap["emitBatch"] = &agentProcessorEmitBatch{handler: handler} + return self6 +} + +func (p *AgentProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + name, _, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return false, err + } + if processor, ok := p.GetProcessorFunction(name); ok { + return processor.Process(seqId, iprot, oprot) + } + iprot.Skip(thrift.STRUCT) + iprot.ReadMessageEnd() + x7 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) + oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) + x7.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, x7 + +} + +type agentProcessorEmitBatch struct { + handler Agent +} + +func (p *agentProcessorEmitBatch) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := AgentEmitBatchArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + return false, err + } + + iprot.ReadMessageEnd() + var err2 error + if err2 = p.handler.EmitBatch(args.Batch); err2 != nil { + return true, err2 + } + return true, nil +} + +// HELPER FUNCTIONS AND STRUCTURES + +// Attributes: +// - Batch +type AgentEmitBatchArgs struct { + Batch *Batch `thrift:"batch,1" json:"batch"` +} + +func NewAgentEmitBatchArgs() *AgentEmitBatchArgs { + return &AgentEmitBatchArgs{} +} + +var AgentEmitBatchArgs_Batch_DEFAULT *Batch + +func (p *AgentEmitBatchArgs) GetBatch() *Batch { + if !p.IsSetBatch() { + return AgentEmitBatchArgs_Batch_DEFAULT + } + return p.Batch +} +func (p *AgentEmitBatchArgs) IsSetBatch() bool { + return p.Batch != nil +} + +func (p *AgentEmitBatchArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *AgentEmitBatchArgs) readField1(iprot thrift.TProtocol) error { + p.Batch = &Batch{} + if err := p.Batch.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Batch), err) + } + return nil +} + +func (p *AgentEmitBatchArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("emitBatch_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *AgentEmitBatchArgs) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("batch", thrift.STRUCT, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:batch: ", p), err) + } + if err := p.Batch.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Batch), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:batch: ", p), err) + } + return err +} + +func (p *AgentEmitBatchArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("AgentEmitBatchArgs(%+v)", *p) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/constants.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/constants.go new file mode 100644 index 00000000..621b8b1c --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/constants.go @@ -0,0 +1,18 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package jaeger + +import ( + "bytes" + "fmt" + "github.com/uber/jaeger-client-go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +func init() { +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/ttypes.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/ttypes.go new file mode 100644 index 00000000..d23ed2fc --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/ttypes.go @@ -0,0 +1,1838 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package jaeger + +import ( + "bytes" + "fmt" + "github.com/uber/jaeger-client-go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +var GoUnusedProtection__ int + +type TagType int64 + +const ( + TagType_STRING TagType = 0 + TagType_DOUBLE TagType = 1 + TagType_BOOL TagType = 2 + TagType_LONG TagType = 3 + TagType_BINARY TagType = 4 +) + +func (p TagType) String() string { + switch p { + case TagType_STRING: + return "STRING" + case TagType_DOUBLE: + return "DOUBLE" + case TagType_BOOL: + return "BOOL" + case TagType_LONG: + return "LONG" + case TagType_BINARY: + return "BINARY" + } + return "" +} + +func TagTypeFromString(s string) (TagType, error) { + switch s { + case "STRING": + return TagType_STRING, nil + case "DOUBLE": + return TagType_DOUBLE, nil + case "BOOL": + return TagType_BOOL, nil + case "LONG": + return TagType_LONG, nil + case "BINARY": + return TagType_BINARY, nil + } + return TagType(0), fmt.Errorf("not a valid TagType string") +} + +func TagTypePtr(v TagType) *TagType { return &v } + +func (p TagType) MarshalText() ([]byte, error) { + return []byte(p.String()), nil +} + +func (p *TagType) UnmarshalText(text []byte) error { + q, err := TagTypeFromString(string(text)) + if err != nil { + return err + } + *p = q + return nil +} + +type SpanRefType int64 + +const ( + SpanRefType_CHILD_OF SpanRefType = 0 + SpanRefType_FOLLOWS_FROM SpanRefType = 1 +) + +func (p SpanRefType) String() string { + switch p { + case SpanRefType_CHILD_OF: + return "CHILD_OF" + case SpanRefType_FOLLOWS_FROM: + return "FOLLOWS_FROM" + } + return "" +} + +func SpanRefTypeFromString(s string) (SpanRefType, error) { + switch s { + case "CHILD_OF": + return SpanRefType_CHILD_OF, nil + case "FOLLOWS_FROM": + return SpanRefType_FOLLOWS_FROM, nil + } + return SpanRefType(0), fmt.Errorf("not a valid SpanRefType string") +} + +func SpanRefTypePtr(v SpanRefType) *SpanRefType { return &v } + +func (p SpanRefType) MarshalText() ([]byte, error) { + return []byte(p.String()), nil +} + +func (p *SpanRefType) UnmarshalText(text []byte) error { + q, err := SpanRefTypeFromString(string(text)) + if err != nil { + return err + } + *p = q + return nil +} + +// Attributes: +// - Key +// - VType +// - VStr +// - VDouble +// - VBool +// - VLong +// - VBinary +type Tag struct { + Key string `thrift:"key,1,required" json:"key"` + VType TagType `thrift:"vType,2,required" json:"vType"` + VStr *string `thrift:"vStr,3" json:"vStr,omitempty"` + VDouble *float64 `thrift:"vDouble,4" json:"vDouble,omitempty"` + VBool *bool `thrift:"vBool,5" json:"vBool,omitempty"` + VLong *int64 `thrift:"vLong,6" json:"vLong,omitempty"` + VBinary []byte `thrift:"vBinary,7" json:"vBinary,omitempty"` +} + +func NewTag() *Tag { + return &Tag{} +} + +func (p *Tag) GetKey() string { + return p.Key +} + +func (p *Tag) GetVType() TagType { + return p.VType +} + +var Tag_VStr_DEFAULT string + +func (p *Tag) GetVStr() string { + if !p.IsSetVStr() { + return Tag_VStr_DEFAULT + } + return *p.VStr +} + +var Tag_VDouble_DEFAULT float64 + +func (p *Tag) GetVDouble() float64 { + if !p.IsSetVDouble() { + return Tag_VDouble_DEFAULT + } + return *p.VDouble +} + +var Tag_VBool_DEFAULT bool + +func (p *Tag) GetVBool() bool { + if !p.IsSetVBool() { + return Tag_VBool_DEFAULT + } + return *p.VBool +} + +var Tag_VLong_DEFAULT int64 + +func (p *Tag) GetVLong() int64 { + if !p.IsSetVLong() { + return Tag_VLong_DEFAULT + } + return *p.VLong +} + +var Tag_VBinary_DEFAULT []byte + +func (p *Tag) GetVBinary() []byte { + return p.VBinary +} +func (p *Tag) IsSetVStr() bool { + return p.VStr != nil +} + +func (p *Tag) IsSetVDouble() bool { + return p.VDouble != nil +} + +func (p *Tag) IsSetVBool() bool { + return p.VBool != nil +} + +func (p *Tag) IsSetVLong() bool { + return p.VLong != nil +} + +func (p *Tag) IsSetVBinary() bool { + return p.VBinary != nil +} + +func (p *Tag) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetKey bool = false + var issetVType bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetKey = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetVType = true + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + case 4: + if err := p.readField4(iprot); err != nil { + return err + } + case 5: + if err := p.readField5(iprot); err != nil { + return err + } + case 6: + if err := p.readField6(iprot); err != nil { + return err + } + case 7: + if err := p.readField7(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetKey { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Key is not set")) + } + if !issetVType { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field VType is not set")) + } + return nil +} + +func (p *Tag) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Key = v + } + return nil +} + +func (p *Tag) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + temp := TagType(v) + p.VType = temp + } + return nil +} + +func (p *Tag) readField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + p.VStr = &v + } + return nil +} + +func (p *Tag) readField4(iprot thrift.TProtocol) error { + if v, err := iprot.ReadDouble(); err != nil { + return thrift.PrependError("error reading field 4: ", err) + } else { + p.VDouble = &v + } + return nil +} + +func (p *Tag) readField5(iprot thrift.TProtocol) error { + if v, err := iprot.ReadBool(); err != nil { + return thrift.PrependError("error reading field 5: ", err) + } else { + p.VBool = &v + } + return nil +} + +func (p *Tag) readField6(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 6: ", err) + } else { + p.VLong = &v + } + return nil +} + +func (p *Tag) readField7(iprot thrift.TProtocol) error { + if v, err := iprot.ReadBinary(); err != nil { + return thrift.PrependError("error reading field 7: ", err) + } else { + p.VBinary = v + } + return nil +} + +func (p *Tag) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Tag"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := p.writeField4(oprot); err != nil { + return err + } + if err := p.writeField5(oprot); err != nil { + return err + } + if err := p.writeField6(oprot); err != nil { + return err + } + if err := p.writeField7(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *Tag) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("key", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:key: ", p), err) + } + if err := oprot.WriteString(string(p.Key)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.key (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:key: ", p), err) + } + return err +} + +func (p *Tag) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("vType", thrift.I32, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:vType: ", p), err) + } + if err := oprot.WriteI32(int32(p.VType)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.vType (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:vType: ", p), err) + } + return err +} + +func (p *Tag) writeField3(oprot thrift.TProtocol) (err error) { + if p.IsSetVStr() { + if err := oprot.WriteFieldBegin("vStr", thrift.STRING, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:vStr: ", p), err) + } + if err := oprot.WriteString(string(*p.VStr)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.vStr (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:vStr: ", p), err) + } + } + return err +} + +func (p *Tag) writeField4(oprot thrift.TProtocol) (err error) { + if p.IsSetVDouble() { + if err := oprot.WriteFieldBegin("vDouble", thrift.DOUBLE, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:vDouble: ", p), err) + } + if err := oprot.WriteDouble(float64(*p.VDouble)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.vDouble (4) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:vDouble: ", p), err) + } + } + return err +} + +func (p *Tag) writeField5(oprot thrift.TProtocol) (err error) { + if p.IsSetVBool() { + if err := oprot.WriteFieldBegin("vBool", thrift.BOOL, 5); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 5:vBool: ", p), err) + } + if err := oprot.WriteBool(bool(*p.VBool)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.vBool (5) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 5:vBool: ", p), err) + } + } + return err +} + +func (p *Tag) writeField6(oprot thrift.TProtocol) (err error) { + if p.IsSetVLong() { + if err := oprot.WriteFieldBegin("vLong", thrift.I64, 6); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 6:vLong: ", p), err) + } + if err := oprot.WriteI64(int64(*p.VLong)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.vLong (6) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 6:vLong: ", p), err) + } + } + return err +} + +func (p *Tag) writeField7(oprot thrift.TProtocol) (err error) { + if p.IsSetVBinary() { + if err := oprot.WriteFieldBegin("vBinary", thrift.STRING, 7); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 7:vBinary: ", p), err) + } + if err := oprot.WriteBinary(p.VBinary); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.vBinary (7) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 7:vBinary: ", p), err) + } + } + return err +} + +func (p *Tag) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Tag(%+v)", *p) +} + +// Attributes: +// - Timestamp +// - Fields +type Log struct { + Timestamp int64 `thrift:"timestamp,1,required" json:"timestamp"` + Fields []*Tag `thrift:"fields,2,required" json:"fields"` +} + +func NewLog() *Log { + return &Log{} +} + +func (p *Log) GetTimestamp() int64 { + return p.Timestamp +} + +func (p *Log) GetFields() []*Tag { + return p.Fields +} +func (p *Log) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetTimestamp bool = false + var issetFields bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetTimestamp = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetFields = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetTimestamp { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Timestamp is not set")) + } + if !issetFields { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Fields is not set")) + } + return nil +} + +func (p *Log) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Timestamp = v + } + return nil +} + +func (p *Log) readField2(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Tag, 0, size) + p.Fields = tSlice + for i := 0; i < size; i++ { + _elem0 := &Tag{} + if err := _elem0.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem0), err) + } + p.Fields = append(p.Fields, _elem0) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *Log) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Log"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *Log) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("timestamp", thrift.I64, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:timestamp: ", p), err) + } + if err := oprot.WriteI64(int64(p.Timestamp)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.timestamp (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:timestamp: ", p), err) + } + return err +} + +func (p *Log) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("fields", thrift.LIST, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:fields: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Fields)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Fields { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:fields: ", p), err) + } + return err +} + +func (p *Log) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Log(%+v)", *p) +} + +// Attributes: +// - RefType +// - TraceIdLow +// - TraceIdHigh +// - SpanId +type SpanRef struct { + RefType SpanRefType `thrift:"refType,1,required" json:"refType"` + TraceIdLow int64 `thrift:"traceIdLow,2,required" json:"traceIdLow"` + TraceIdHigh int64 `thrift:"traceIdHigh,3,required" json:"traceIdHigh"` + SpanId int64 `thrift:"spanId,4,required" json:"spanId"` +} + +func NewSpanRef() *SpanRef { + return &SpanRef{} +} + +func (p *SpanRef) GetRefType() SpanRefType { + return p.RefType +} + +func (p *SpanRef) GetTraceIdLow() int64 { + return p.TraceIdLow +} + +func (p *SpanRef) GetTraceIdHigh() int64 { + return p.TraceIdHigh +} + +func (p *SpanRef) GetSpanId() int64 { + return p.SpanId +} +func (p *SpanRef) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetRefType bool = false + var issetTraceIdLow bool = false + var issetTraceIdHigh bool = false + var issetSpanId bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetRefType = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetTraceIdLow = true + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + issetTraceIdHigh = true + case 4: + if err := p.readField4(iprot); err != nil { + return err + } + issetSpanId = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetRefType { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field RefType is not set")) + } + if !issetTraceIdLow { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field TraceIdLow is not set")) + } + if !issetTraceIdHigh { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field TraceIdHigh is not set")) + } + if !issetSpanId { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field SpanId is not set")) + } + return nil +} + +func (p *SpanRef) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + temp := SpanRefType(v) + p.RefType = temp + } + return nil +} + +func (p *SpanRef) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.TraceIdLow = v + } + return nil +} + +func (p *SpanRef) readField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + p.TraceIdHigh = v + } + return nil +} + +func (p *SpanRef) readField4(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 4: ", err) + } else { + p.SpanId = v + } + return nil +} + +func (p *SpanRef) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("SpanRef"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := p.writeField4(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *SpanRef) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("refType", thrift.I32, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:refType: ", p), err) + } + if err := oprot.WriteI32(int32(p.RefType)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.refType (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:refType: ", p), err) + } + return err +} + +func (p *SpanRef) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("traceIdLow", thrift.I64, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:traceIdLow: ", p), err) + } + if err := oprot.WriteI64(int64(p.TraceIdLow)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.traceIdLow (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:traceIdLow: ", p), err) + } + return err +} + +func (p *SpanRef) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("traceIdHigh", thrift.I64, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:traceIdHigh: ", p), err) + } + if err := oprot.WriteI64(int64(p.TraceIdHigh)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.traceIdHigh (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:traceIdHigh: ", p), err) + } + return err +} + +func (p *SpanRef) writeField4(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("spanId", thrift.I64, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:spanId: ", p), err) + } + if err := oprot.WriteI64(int64(p.SpanId)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.spanId (4) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:spanId: ", p), err) + } + return err +} + +func (p *SpanRef) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("SpanRef(%+v)", *p) +} + +// Attributes: +// - TraceIdLow +// - TraceIdHigh +// - SpanId +// - ParentSpanId +// - OperationName +// - References +// - Flags +// - StartTime +// - Duration +// - Tags +// - Logs +type Span struct { + TraceIdLow int64 `thrift:"traceIdLow,1,required" json:"traceIdLow"` + TraceIdHigh int64 `thrift:"traceIdHigh,2,required" json:"traceIdHigh"` + SpanId int64 `thrift:"spanId,3,required" json:"spanId"` + ParentSpanId int64 `thrift:"parentSpanId,4,required" json:"parentSpanId"` + OperationName string `thrift:"operationName,5,required" json:"operationName"` + References []*SpanRef `thrift:"references,6" json:"references,omitempty"` + Flags int32 `thrift:"flags,7,required" json:"flags"` + StartTime int64 `thrift:"startTime,8,required" json:"startTime"` + Duration int64 `thrift:"duration,9,required" json:"duration"` + Tags []*Tag `thrift:"tags,10" json:"tags,omitempty"` + Logs []*Log `thrift:"logs,11" json:"logs,omitempty"` +} + +func NewSpan() *Span { + return &Span{} +} + +func (p *Span) GetTraceIdLow() int64 { + return p.TraceIdLow +} + +func (p *Span) GetTraceIdHigh() int64 { + return p.TraceIdHigh +} + +func (p *Span) GetSpanId() int64 { + return p.SpanId +} + +func (p *Span) GetParentSpanId() int64 { + return p.ParentSpanId +} + +func (p *Span) GetOperationName() string { + return p.OperationName +} + +var Span_References_DEFAULT []*SpanRef + +func (p *Span) GetReferences() []*SpanRef { + return p.References +} + +func (p *Span) GetFlags() int32 { + return p.Flags +} + +func (p *Span) GetStartTime() int64 { + return p.StartTime +} + +func (p *Span) GetDuration() int64 { + return p.Duration +} + +var Span_Tags_DEFAULT []*Tag + +func (p *Span) GetTags() []*Tag { + return p.Tags +} + +var Span_Logs_DEFAULT []*Log + +func (p *Span) GetLogs() []*Log { + return p.Logs +} +func (p *Span) IsSetReferences() bool { + return p.References != nil +} + +func (p *Span) IsSetTags() bool { + return p.Tags != nil +} + +func (p *Span) IsSetLogs() bool { + return p.Logs != nil +} + +func (p *Span) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetTraceIdLow bool = false + var issetTraceIdHigh bool = false + var issetSpanId bool = false + var issetParentSpanId bool = false + var issetOperationName bool = false + var issetFlags bool = false + var issetStartTime bool = false + var issetDuration bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetTraceIdLow = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetTraceIdHigh = true + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + issetSpanId = true + case 4: + if err := p.readField4(iprot); err != nil { + return err + } + issetParentSpanId = true + case 5: + if err := p.readField5(iprot); err != nil { + return err + } + issetOperationName = true + case 6: + if err := p.readField6(iprot); err != nil { + return err + } + case 7: + if err := p.readField7(iprot); err != nil { + return err + } + issetFlags = true + case 8: + if err := p.readField8(iprot); err != nil { + return err + } + issetStartTime = true + case 9: + if err := p.readField9(iprot); err != nil { + return err + } + issetDuration = true + case 10: + if err := p.readField10(iprot); err != nil { + return err + } + case 11: + if err := p.readField11(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetTraceIdLow { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field TraceIdLow is not set")) + } + if !issetTraceIdHigh { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field TraceIdHigh is not set")) + } + if !issetSpanId { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field SpanId is not set")) + } + if !issetParentSpanId { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ParentSpanId is not set")) + } + if !issetOperationName { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field OperationName is not set")) + } + if !issetFlags { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Flags is not set")) + } + if !issetStartTime { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field StartTime is not set")) + } + if !issetDuration { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Duration is not set")) + } + return nil +} + +func (p *Span) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.TraceIdLow = v + } + return nil +} + +func (p *Span) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.TraceIdHigh = v + } + return nil +} + +func (p *Span) readField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + p.SpanId = v + } + return nil +} + +func (p *Span) readField4(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 4: ", err) + } else { + p.ParentSpanId = v + } + return nil +} + +func (p *Span) readField5(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 5: ", err) + } else { + p.OperationName = v + } + return nil +} + +func (p *Span) readField6(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*SpanRef, 0, size) + p.References = tSlice + for i := 0; i < size; i++ { + _elem1 := &SpanRef{} + if err := _elem1.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem1), err) + } + p.References = append(p.References, _elem1) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *Span) readField7(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(); err != nil { + return thrift.PrependError("error reading field 7: ", err) + } else { + p.Flags = v + } + return nil +} + +func (p *Span) readField8(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 8: ", err) + } else { + p.StartTime = v + } + return nil +} + +func (p *Span) readField9(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 9: ", err) + } else { + p.Duration = v + } + return nil +} + +func (p *Span) readField10(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Tag, 0, size) + p.Tags = tSlice + for i := 0; i < size; i++ { + _elem2 := &Tag{} + if err := _elem2.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem2), err) + } + p.Tags = append(p.Tags, _elem2) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *Span) readField11(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Log, 0, size) + p.Logs = tSlice + for i := 0; i < size; i++ { + _elem3 := &Log{} + if err := _elem3.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem3), err) + } + p.Logs = append(p.Logs, _elem3) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *Span) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Span"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := p.writeField4(oprot); err != nil { + return err + } + if err := p.writeField5(oprot); err != nil { + return err + } + if err := p.writeField6(oprot); err != nil { + return err + } + if err := p.writeField7(oprot); err != nil { + return err + } + if err := p.writeField8(oprot); err != nil { + return err + } + if err := p.writeField9(oprot); err != nil { + return err + } + if err := p.writeField10(oprot); err != nil { + return err + } + if err := p.writeField11(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *Span) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("traceIdLow", thrift.I64, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:traceIdLow: ", p), err) + } + if err := oprot.WriteI64(int64(p.TraceIdLow)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.traceIdLow (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:traceIdLow: ", p), err) + } + return err +} + +func (p *Span) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("traceIdHigh", thrift.I64, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:traceIdHigh: ", p), err) + } + if err := oprot.WriteI64(int64(p.TraceIdHigh)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.traceIdHigh (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:traceIdHigh: ", p), err) + } + return err +} + +func (p *Span) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("spanId", thrift.I64, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:spanId: ", p), err) + } + if err := oprot.WriteI64(int64(p.SpanId)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.spanId (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:spanId: ", p), err) + } + return err +} + +func (p *Span) writeField4(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("parentSpanId", thrift.I64, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:parentSpanId: ", p), err) + } + if err := oprot.WriteI64(int64(p.ParentSpanId)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.parentSpanId (4) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:parentSpanId: ", p), err) + } + return err +} + +func (p *Span) writeField5(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("operationName", thrift.STRING, 5); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 5:operationName: ", p), err) + } + if err := oprot.WriteString(string(p.OperationName)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.operationName (5) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 5:operationName: ", p), err) + } + return err +} + +func (p *Span) writeField6(oprot thrift.TProtocol) (err error) { + if p.IsSetReferences() { + if err := oprot.WriteFieldBegin("references", thrift.LIST, 6); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 6:references: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.References)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.References { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 6:references: ", p), err) + } + } + return err +} + +func (p *Span) writeField7(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("flags", thrift.I32, 7); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 7:flags: ", p), err) + } + if err := oprot.WriteI32(int32(p.Flags)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.flags (7) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 7:flags: ", p), err) + } + return err +} + +func (p *Span) writeField8(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("startTime", thrift.I64, 8); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 8:startTime: ", p), err) + } + if err := oprot.WriteI64(int64(p.StartTime)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.startTime (8) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 8:startTime: ", p), err) + } + return err +} + +func (p *Span) writeField9(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("duration", thrift.I64, 9); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 9:duration: ", p), err) + } + if err := oprot.WriteI64(int64(p.Duration)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.duration (9) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 9:duration: ", p), err) + } + return err +} + +func (p *Span) writeField10(oprot thrift.TProtocol) (err error) { + if p.IsSetTags() { + if err := oprot.WriteFieldBegin("tags", thrift.LIST, 10); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 10:tags: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Tags)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Tags { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 10:tags: ", p), err) + } + } + return err +} + +func (p *Span) writeField11(oprot thrift.TProtocol) (err error) { + if p.IsSetLogs() { + if err := oprot.WriteFieldBegin("logs", thrift.LIST, 11); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 11:logs: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Logs)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Logs { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 11:logs: ", p), err) + } + } + return err +} + +func (p *Span) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Span(%+v)", *p) +} + +// Attributes: +// - ServiceName +// - Tags +type Process struct { + ServiceName string `thrift:"serviceName,1,required" json:"serviceName"` + Tags []*Tag `thrift:"tags,2" json:"tags,omitempty"` +} + +func NewProcess() *Process { + return &Process{} +} + +func (p *Process) GetServiceName() string { + return p.ServiceName +} + +var Process_Tags_DEFAULT []*Tag + +func (p *Process) GetTags() []*Tag { + return p.Tags +} +func (p *Process) IsSetTags() bool { + return p.Tags != nil +} + +func (p *Process) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetServiceName bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetServiceName = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetServiceName { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ServiceName is not set")) + } + return nil +} + +func (p *Process) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.ServiceName = v + } + return nil +} + +func (p *Process) readField2(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Tag, 0, size) + p.Tags = tSlice + for i := 0; i < size; i++ { + _elem4 := &Tag{} + if err := _elem4.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem4), err) + } + p.Tags = append(p.Tags, _elem4) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *Process) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Process"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *Process) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("serviceName", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:serviceName: ", p), err) + } + if err := oprot.WriteString(string(p.ServiceName)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.serviceName (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:serviceName: ", p), err) + } + return err +} + +func (p *Process) writeField2(oprot thrift.TProtocol) (err error) { + if p.IsSetTags() { + if err := oprot.WriteFieldBegin("tags", thrift.LIST, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:tags: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Tags)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Tags { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:tags: ", p), err) + } + } + return err +} + +func (p *Process) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Process(%+v)", *p) +} + +// Attributes: +// - Process +// - Spans +type Batch struct { + Process *Process `thrift:"process,1,required" json:"process"` + Spans []*Span `thrift:"spans,2,required" json:"spans"` +} + +func NewBatch() *Batch { + return &Batch{} +} + +var Batch_Process_DEFAULT *Process + +func (p *Batch) GetProcess() *Process { + if !p.IsSetProcess() { + return Batch_Process_DEFAULT + } + return p.Process +} + +func (p *Batch) GetSpans() []*Span { + return p.Spans +} +func (p *Batch) IsSetProcess() bool { + return p.Process != nil +} + +func (p *Batch) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetProcess bool = false + var issetSpans bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetProcess = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetSpans = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetProcess { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Process is not set")) + } + if !issetSpans { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Spans is not set")) + } + return nil +} + +func (p *Batch) readField1(iprot thrift.TProtocol) error { + p.Process = &Process{} + if err := p.Process.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Process), err) + } + return nil +} + +func (p *Batch) readField2(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Span, 0, size) + p.Spans = tSlice + for i := 0; i < size; i++ { + _elem5 := &Span{} + if err := _elem5.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem5), err) + } + p.Spans = append(p.Spans, _elem5) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *Batch) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Batch"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *Batch) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("process", thrift.STRUCT, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:process: ", p), err) + } + if err := p.Process.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Process), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:process: ", p), err) + } + return err +} + +func (p *Batch) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("spans", thrift.LIST, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:spans: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Spans)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Spans { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:spans: ", p), err) + } + return err +} + +func (p *Batch) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Batch(%+v)", *p) +} + +// Attributes: +// - Ok +type BatchSubmitResponse struct { + Ok bool `thrift:"ok,1,required" json:"ok"` +} + +func NewBatchSubmitResponse() *BatchSubmitResponse { + return &BatchSubmitResponse{} +} + +func (p *BatchSubmitResponse) GetOk() bool { + return p.Ok +} +func (p *BatchSubmitResponse) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetOk bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetOk = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetOk { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Ok is not set")) + } + return nil +} + +func (p *BatchSubmitResponse) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadBool(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Ok = v + } + return nil +} + +func (p *BatchSubmitResponse) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("BatchSubmitResponse"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *BatchSubmitResponse) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("ok", thrift.BOOL, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:ok: ", p), err) + } + if err := oprot.WriteBool(bool(p.Ok)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.ok (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:ok: ", p), err) + } + return err +} + +func (p *BatchSubmitResponse) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("BatchSubmitResponse(%+v)", *p) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/sampling/constants.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/sampling/constants.go new file mode 100644 index 00000000..0f6e3a88 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/sampling/constants.go @@ -0,0 +1,18 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package sampling + +import ( + "bytes" + "fmt" + "github.com/uber/jaeger-client-go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +func init() { +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/sampling/samplingmanager.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/sampling/samplingmanager.go new file mode 100644 index 00000000..33179cfe --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/sampling/samplingmanager.go @@ -0,0 +1,410 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package sampling + +import ( + "bytes" + "fmt" + "github.com/uber/jaeger-client-go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +type SamplingManager interface { + // Parameters: + // - ServiceName + GetSamplingStrategy(serviceName string) (r *SamplingStrategyResponse, err error) +} + +type SamplingManagerClient struct { + Transport thrift.TTransport + ProtocolFactory thrift.TProtocolFactory + InputProtocol thrift.TProtocol + OutputProtocol thrift.TProtocol + SeqId int32 +} + +func NewSamplingManagerClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *SamplingManagerClient { + return &SamplingManagerClient{Transport: t, + ProtocolFactory: f, + InputProtocol: f.GetProtocol(t), + OutputProtocol: f.GetProtocol(t), + SeqId: 0, + } +} + +func NewSamplingManagerClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *SamplingManagerClient { + return &SamplingManagerClient{Transport: t, + ProtocolFactory: nil, + InputProtocol: iprot, + OutputProtocol: oprot, + SeqId: 0, + } +} + +// Parameters: +// - ServiceName +func (p *SamplingManagerClient) GetSamplingStrategy(serviceName string) (r *SamplingStrategyResponse, err error) { + if err = p.sendGetSamplingStrategy(serviceName); err != nil { + return + } + return p.recvGetSamplingStrategy() +} + +func (p *SamplingManagerClient) sendGetSamplingStrategy(serviceName string) (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("getSamplingStrategy", thrift.CALL, p.SeqId); err != nil { + return + } + args := SamplingManagerGetSamplingStrategyArgs{ + ServiceName: serviceName, + } + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *SamplingManagerClient) recvGetSamplingStrategy() (value *SamplingStrategyResponse, err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "getSamplingStrategy" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "getSamplingStrategy failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "getSamplingStrategy failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error1 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error2 error + error2, err = error1.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error2 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "getSamplingStrategy failed: invalid message type") + return + } + result := SamplingManagerGetSamplingStrategyResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + value = result.GetSuccess() + return +} + +type SamplingManagerProcessor struct { + processorMap map[string]thrift.TProcessorFunction + handler SamplingManager +} + +func (p *SamplingManagerProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { + p.processorMap[key] = processor +} + +func (p *SamplingManagerProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { + processor, ok = p.processorMap[key] + return processor, ok +} + +func (p *SamplingManagerProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { + return p.processorMap +} + +func NewSamplingManagerProcessor(handler SamplingManager) *SamplingManagerProcessor { + + self3 := &SamplingManagerProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} + self3.processorMap["getSamplingStrategy"] = &samplingManagerProcessorGetSamplingStrategy{handler: handler} + return self3 +} + +func (p *SamplingManagerProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + name, _, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return false, err + } + if processor, ok := p.GetProcessorFunction(name); ok { + return processor.Process(seqId, iprot, oprot) + } + iprot.Skip(thrift.STRUCT) + iprot.ReadMessageEnd() + x4 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) + oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) + x4.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, x4 + +} + +type samplingManagerProcessorGetSamplingStrategy struct { + handler SamplingManager +} + +func (p *samplingManagerProcessorGetSamplingStrategy) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := SamplingManagerGetSamplingStrategyArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("getSamplingStrategy", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := SamplingManagerGetSamplingStrategyResult{} + var retval *SamplingStrategyResponse + var err2 error + if retval, err2 = p.handler.GetSamplingStrategy(args.ServiceName); err2 != nil { + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing getSamplingStrategy: "+err2.Error()) + oprot.WriteMessageBegin("getSamplingStrategy", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } else { + result.Success = retval + } + if err2 = oprot.WriteMessageBegin("getSamplingStrategy", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +// HELPER FUNCTIONS AND STRUCTURES + +// Attributes: +// - ServiceName +type SamplingManagerGetSamplingStrategyArgs struct { + ServiceName string `thrift:"serviceName,1" json:"serviceName"` +} + +func NewSamplingManagerGetSamplingStrategyArgs() *SamplingManagerGetSamplingStrategyArgs { + return &SamplingManagerGetSamplingStrategyArgs{} +} + +func (p *SamplingManagerGetSamplingStrategyArgs) GetServiceName() string { + return p.ServiceName +} +func (p *SamplingManagerGetSamplingStrategyArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *SamplingManagerGetSamplingStrategyArgs) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.ServiceName = v + } + return nil +} + +func (p *SamplingManagerGetSamplingStrategyArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("getSamplingStrategy_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *SamplingManagerGetSamplingStrategyArgs) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("serviceName", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:serviceName: ", p), err) + } + if err := oprot.WriteString(string(p.ServiceName)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.serviceName (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:serviceName: ", p), err) + } + return err +} + +func (p *SamplingManagerGetSamplingStrategyArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("SamplingManagerGetSamplingStrategyArgs(%+v)", *p) +} + +// Attributes: +// - Success +type SamplingManagerGetSamplingStrategyResult struct { + Success *SamplingStrategyResponse `thrift:"success,0" json:"success,omitempty"` +} + +func NewSamplingManagerGetSamplingStrategyResult() *SamplingManagerGetSamplingStrategyResult { + return &SamplingManagerGetSamplingStrategyResult{} +} + +var SamplingManagerGetSamplingStrategyResult_Success_DEFAULT *SamplingStrategyResponse + +func (p *SamplingManagerGetSamplingStrategyResult) GetSuccess() *SamplingStrategyResponse { + if !p.IsSetSuccess() { + return SamplingManagerGetSamplingStrategyResult_Success_DEFAULT + } + return p.Success +} +func (p *SamplingManagerGetSamplingStrategyResult) IsSetSuccess() bool { + return p.Success != nil +} + +func (p *SamplingManagerGetSamplingStrategyResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 0: + if err := p.readField0(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *SamplingManagerGetSamplingStrategyResult) readField0(iprot thrift.TProtocol) error { + p.Success = &SamplingStrategyResponse{} + if err := p.Success.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Success), err) + } + return nil +} + +func (p *SamplingManagerGetSamplingStrategyResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("getSamplingStrategy_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField0(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *SamplingManagerGetSamplingStrategyResult) writeField0(oprot thrift.TProtocol) (err error) { + if p.IsSetSuccess() { + if err := oprot.WriteFieldBegin("success", thrift.STRUCT, 0); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) + } + if err := p.Success.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Success), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) + } + } + return err +} + +func (p *SamplingManagerGetSamplingStrategyResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("SamplingManagerGetSamplingStrategyResult(%+v)", *p) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/sampling/ttypes.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/sampling/ttypes.go new file mode 100644 index 00000000..9abaf054 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/sampling/ttypes.go @@ -0,0 +1,873 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package sampling + +import ( + "bytes" + "fmt" + "github.com/uber/jaeger-client-go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +var GoUnusedProtection__ int + +type SamplingStrategyType int64 + +const ( + SamplingStrategyType_PROBABILISTIC SamplingStrategyType = 0 + SamplingStrategyType_RATE_LIMITING SamplingStrategyType = 1 +) + +func (p SamplingStrategyType) String() string { + switch p { + case SamplingStrategyType_PROBABILISTIC: + return "PROBABILISTIC" + case SamplingStrategyType_RATE_LIMITING: + return "RATE_LIMITING" + } + return "" +} + +func SamplingStrategyTypeFromString(s string) (SamplingStrategyType, error) { + switch s { + case "PROBABILISTIC": + return SamplingStrategyType_PROBABILISTIC, nil + case "RATE_LIMITING": + return SamplingStrategyType_RATE_LIMITING, nil + } + return SamplingStrategyType(0), fmt.Errorf("not a valid SamplingStrategyType string") +} + +func SamplingStrategyTypePtr(v SamplingStrategyType) *SamplingStrategyType { return &v } + +func (p SamplingStrategyType) MarshalText() ([]byte, error) { + return []byte(p.String()), nil +} + +func (p *SamplingStrategyType) UnmarshalText(text []byte) error { + q, err := SamplingStrategyTypeFromString(string(text)) + if err != nil { + return err + } + *p = q + return nil +} + +// Attributes: +// - SamplingRate +type ProbabilisticSamplingStrategy struct { + SamplingRate float64 `thrift:"samplingRate,1,required" json:"samplingRate"` +} + +func NewProbabilisticSamplingStrategy() *ProbabilisticSamplingStrategy { + return &ProbabilisticSamplingStrategy{} +} + +func (p *ProbabilisticSamplingStrategy) GetSamplingRate() float64 { + return p.SamplingRate +} +func (p *ProbabilisticSamplingStrategy) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetSamplingRate bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetSamplingRate = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetSamplingRate { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field SamplingRate is not set")) + } + return nil +} + +func (p *ProbabilisticSamplingStrategy) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadDouble(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.SamplingRate = v + } + return nil +} + +func (p *ProbabilisticSamplingStrategy) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("ProbabilisticSamplingStrategy"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *ProbabilisticSamplingStrategy) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("samplingRate", thrift.DOUBLE, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:samplingRate: ", p), err) + } + if err := oprot.WriteDouble(float64(p.SamplingRate)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.samplingRate (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:samplingRate: ", p), err) + } + return err +} + +func (p *ProbabilisticSamplingStrategy) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("ProbabilisticSamplingStrategy(%+v)", *p) +} + +// Attributes: +// - MaxTracesPerSecond +type RateLimitingSamplingStrategy struct { + MaxTracesPerSecond int16 `thrift:"maxTracesPerSecond,1,required" json:"maxTracesPerSecond"` +} + +func NewRateLimitingSamplingStrategy() *RateLimitingSamplingStrategy { + return &RateLimitingSamplingStrategy{} +} + +func (p *RateLimitingSamplingStrategy) GetMaxTracesPerSecond() int16 { + return p.MaxTracesPerSecond +} +func (p *RateLimitingSamplingStrategy) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetMaxTracesPerSecond bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetMaxTracesPerSecond = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetMaxTracesPerSecond { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field MaxTracesPerSecond is not set")) + } + return nil +} + +func (p *RateLimitingSamplingStrategy) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI16(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.MaxTracesPerSecond = v + } + return nil +} + +func (p *RateLimitingSamplingStrategy) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("RateLimitingSamplingStrategy"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *RateLimitingSamplingStrategy) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("maxTracesPerSecond", thrift.I16, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:maxTracesPerSecond: ", p), err) + } + if err := oprot.WriteI16(int16(p.MaxTracesPerSecond)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.maxTracesPerSecond (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:maxTracesPerSecond: ", p), err) + } + return err +} + +func (p *RateLimitingSamplingStrategy) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("RateLimitingSamplingStrategy(%+v)", *p) +} + +// Attributes: +// - Operation +// - ProbabilisticSampling +type OperationSamplingStrategy struct { + Operation string `thrift:"operation,1,required" json:"operation"` + ProbabilisticSampling *ProbabilisticSamplingStrategy `thrift:"probabilisticSampling,2,required" json:"probabilisticSampling"` +} + +func NewOperationSamplingStrategy() *OperationSamplingStrategy { + return &OperationSamplingStrategy{} +} + +func (p *OperationSamplingStrategy) GetOperation() string { + return p.Operation +} + +var OperationSamplingStrategy_ProbabilisticSampling_DEFAULT *ProbabilisticSamplingStrategy + +func (p *OperationSamplingStrategy) GetProbabilisticSampling() *ProbabilisticSamplingStrategy { + if !p.IsSetProbabilisticSampling() { + return OperationSamplingStrategy_ProbabilisticSampling_DEFAULT + } + return p.ProbabilisticSampling +} +func (p *OperationSamplingStrategy) IsSetProbabilisticSampling() bool { + return p.ProbabilisticSampling != nil +} + +func (p *OperationSamplingStrategy) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetOperation bool = false + var issetProbabilisticSampling bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetOperation = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetProbabilisticSampling = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetOperation { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Operation is not set")) + } + if !issetProbabilisticSampling { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ProbabilisticSampling is not set")) + } + return nil +} + +func (p *OperationSamplingStrategy) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Operation = v + } + return nil +} + +func (p *OperationSamplingStrategy) readField2(iprot thrift.TProtocol) error { + p.ProbabilisticSampling = &ProbabilisticSamplingStrategy{} + if err := p.ProbabilisticSampling.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.ProbabilisticSampling), err) + } + return nil +} + +func (p *OperationSamplingStrategy) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("OperationSamplingStrategy"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *OperationSamplingStrategy) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("operation", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:operation: ", p), err) + } + if err := oprot.WriteString(string(p.Operation)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.operation (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:operation: ", p), err) + } + return err +} + +func (p *OperationSamplingStrategy) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("probabilisticSampling", thrift.STRUCT, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:probabilisticSampling: ", p), err) + } + if err := p.ProbabilisticSampling.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.ProbabilisticSampling), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:probabilisticSampling: ", p), err) + } + return err +} + +func (p *OperationSamplingStrategy) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("OperationSamplingStrategy(%+v)", *p) +} + +// Attributes: +// - DefaultSamplingProbability +// - DefaultLowerBoundTracesPerSecond +// - PerOperationStrategies +// - DefaultUpperBoundTracesPerSecond +type PerOperationSamplingStrategies struct { + DefaultSamplingProbability float64 `thrift:"defaultSamplingProbability,1,required" json:"defaultSamplingProbability"` + DefaultLowerBoundTracesPerSecond float64 `thrift:"defaultLowerBoundTracesPerSecond,2,required" json:"defaultLowerBoundTracesPerSecond"` + PerOperationStrategies []*OperationSamplingStrategy `thrift:"perOperationStrategies,3,required" json:"perOperationStrategies"` + DefaultUpperBoundTracesPerSecond *float64 `thrift:"defaultUpperBoundTracesPerSecond,4" json:"defaultUpperBoundTracesPerSecond,omitempty"` +} + +func NewPerOperationSamplingStrategies() *PerOperationSamplingStrategies { + return &PerOperationSamplingStrategies{} +} + +func (p *PerOperationSamplingStrategies) GetDefaultSamplingProbability() float64 { + return p.DefaultSamplingProbability +} + +func (p *PerOperationSamplingStrategies) GetDefaultLowerBoundTracesPerSecond() float64 { + return p.DefaultLowerBoundTracesPerSecond +} + +func (p *PerOperationSamplingStrategies) GetPerOperationStrategies() []*OperationSamplingStrategy { + return p.PerOperationStrategies +} + +var PerOperationSamplingStrategies_DefaultUpperBoundTracesPerSecond_DEFAULT float64 + +func (p *PerOperationSamplingStrategies) GetDefaultUpperBoundTracesPerSecond() float64 { + if !p.IsSetDefaultUpperBoundTracesPerSecond() { + return PerOperationSamplingStrategies_DefaultUpperBoundTracesPerSecond_DEFAULT + } + return *p.DefaultUpperBoundTracesPerSecond +} +func (p *PerOperationSamplingStrategies) IsSetDefaultUpperBoundTracesPerSecond() bool { + return p.DefaultUpperBoundTracesPerSecond != nil +} + +func (p *PerOperationSamplingStrategies) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetDefaultSamplingProbability bool = false + var issetDefaultLowerBoundTracesPerSecond bool = false + var issetPerOperationStrategies bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetDefaultSamplingProbability = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetDefaultLowerBoundTracesPerSecond = true + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + issetPerOperationStrategies = true + case 4: + if err := p.readField4(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetDefaultSamplingProbability { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field DefaultSamplingProbability is not set")) + } + if !issetDefaultLowerBoundTracesPerSecond { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field DefaultLowerBoundTracesPerSecond is not set")) + } + if !issetPerOperationStrategies { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field PerOperationStrategies is not set")) + } + return nil +} + +func (p *PerOperationSamplingStrategies) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadDouble(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.DefaultSamplingProbability = v + } + return nil +} + +func (p *PerOperationSamplingStrategies) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadDouble(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.DefaultLowerBoundTracesPerSecond = v + } + return nil +} + +func (p *PerOperationSamplingStrategies) readField3(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*OperationSamplingStrategy, 0, size) + p.PerOperationStrategies = tSlice + for i := 0; i < size; i++ { + _elem0 := &OperationSamplingStrategy{} + if err := _elem0.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem0), err) + } + p.PerOperationStrategies = append(p.PerOperationStrategies, _elem0) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *PerOperationSamplingStrategies) readField4(iprot thrift.TProtocol) error { + if v, err := iprot.ReadDouble(); err != nil { + return thrift.PrependError("error reading field 4: ", err) + } else { + p.DefaultUpperBoundTracesPerSecond = &v + } + return nil +} + +func (p *PerOperationSamplingStrategies) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("PerOperationSamplingStrategies"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := p.writeField4(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *PerOperationSamplingStrategies) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("defaultSamplingProbability", thrift.DOUBLE, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:defaultSamplingProbability: ", p), err) + } + if err := oprot.WriteDouble(float64(p.DefaultSamplingProbability)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.defaultSamplingProbability (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:defaultSamplingProbability: ", p), err) + } + return err +} + +func (p *PerOperationSamplingStrategies) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("defaultLowerBoundTracesPerSecond", thrift.DOUBLE, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:defaultLowerBoundTracesPerSecond: ", p), err) + } + if err := oprot.WriteDouble(float64(p.DefaultLowerBoundTracesPerSecond)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.defaultLowerBoundTracesPerSecond (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:defaultLowerBoundTracesPerSecond: ", p), err) + } + return err +} + +func (p *PerOperationSamplingStrategies) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("perOperationStrategies", thrift.LIST, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:perOperationStrategies: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.PerOperationStrategies)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.PerOperationStrategies { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:perOperationStrategies: ", p), err) + } + return err +} + +func (p *PerOperationSamplingStrategies) writeField4(oprot thrift.TProtocol) (err error) { + if p.IsSetDefaultUpperBoundTracesPerSecond() { + if err := oprot.WriteFieldBegin("defaultUpperBoundTracesPerSecond", thrift.DOUBLE, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:defaultUpperBoundTracesPerSecond: ", p), err) + } + if err := oprot.WriteDouble(float64(*p.DefaultUpperBoundTracesPerSecond)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.defaultUpperBoundTracesPerSecond (4) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:defaultUpperBoundTracesPerSecond: ", p), err) + } + } + return err +} + +func (p *PerOperationSamplingStrategies) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("PerOperationSamplingStrategies(%+v)", *p) +} + +// Attributes: +// - StrategyType +// - ProbabilisticSampling +// - RateLimitingSampling +// - OperationSampling +type SamplingStrategyResponse struct { + StrategyType SamplingStrategyType `thrift:"strategyType,1,required" json:"strategyType"` + ProbabilisticSampling *ProbabilisticSamplingStrategy `thrift:"probabilisticSampling,2" json:"probabilisticSampling,omitempty"` + RateLimitingSampling *RateLimitingSamplingStrategy `thrift:"rateLimitingSampling,3" json:"rateLimitingSampling,omitempty"` + OperationSampling *PerOperationSamplingStrategies `thrift:"operationSampling,4" json:"operationSampling,omitempty"` +} + +func NewSamplingStrategyResponse() *SamplingStrategyResponse { + return &SamplingStrategyResponse{} +} + +func (p *SamplingStrategyResponse) GetStrategyType() SamplingStrategyType { + return p.StrategyType +} + +var SamplingStrategyResponse_ProbabilisticSampling_DEFAULT *ProbabilisticSamplingStrategy + +func (p *SamplingStrategyResponse) GetProbabilisticSampling() *ProbabilisticSamplingStrategy { + if !p.IsSetProbabilisticSampling() { + return SamplingStrategyResponse_ProbabilisticSampling_DEFAULT + } + return p.ProbabilisticSampling +} + +var SamplingStrategyResponse_RateLimitingSampling_DEFAULT *RateLimitingSamplingStrategy + +func (p *SamplingStrategyResponse) GetRateLimitingSampling() *RateLimitingSamplingStrategy { + if !p.IsSetRateLimitingSampling() { + return SamplingStrategyResponse_RateLimitingSampling_DEFAULT + } + return p.RateLimitingSampling +} + +var SamplingStrategyResponse_OperationSampling_DEFAULT *PerOperationSamplingStrategies + +func (p *SamplingStrategyResponse) GetOperationSampling() *PerOperationSamplingStrategies { + if !p.IsSetOperationSampling() { + return SamplingStrategyResponse_OperationSampling_DEFAULT + } + return p.OperationSampling +} +func (p *SamplingStrategyResponse) IsSetProbabilisticSampling() bool { + return p.ProbabilisticSampling != nil +} + +func (p *SamplingStrategyResponse) IsSetRateLimitingSampling() bool { + return p.RateLimitingSampling != nil +} + +func (p *SamplingStrategyResponse) IsSetOperationSampling() bool { + return p.OperationSampling != nil +} + +func (p *SamplingStrategyResponse) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetStrategyType bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetStrategyType = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + case 4: + if err := p.readField4(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetStrategyType { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field StrategyType is not set")) + } + return nil +} + +func (p *SamplingStrategyResponse) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + temp := SamplingStrategyType(v) + p.StrategyType = temp + } + return nil +} + +func (p *SamplingStrategyResponse) readField2(iprot thrift.TProtocol) error { + p.ProbabilisticSampling = &ProbabilisticSamplingStrategy{} + if err := p.ProbabilisticSampling.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.ProbabilisticSampling), err) + } + return nil +} + +func (p *SamplingStrategyResponse) readField3(iprot thrift.TProtocol) error { + p.RateLimitingSampling = &RateLimitingSamplingStrategy{} + if err := p.RateLimitingSampling.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.RateLimitingSampling), err) + } + return nil +} + +func (p *SamplingStrategyResponse) readField4(iprot thrift.TProtocol) error { + p.OperationSampling = &PerOperationSamplingStrategies{} + if err := p.OperationSampling.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.OperationSampling), err) + } + return nil +} + +func (p *SamplingStrategyResponse) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("SamplingStrategyResponse"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := p.writeField4(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *SamplingStrategyResponse) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("strategyType", thrift.I32, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:strategyType: ", p), err) + } + if err := oprot.WriteI32(int32(p.StrategyType)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.strategyType (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:strategyType: ", p), err) + } + return err +} + +func (p *SamplingStrategyResponse) writeField2(oprot thrift.TProtocol) (err error) { + if p.IsSetProbabilisticSampling() { + if err := oprot.WriteFieldBegin("probabilisticSampling", thrift.STRUCT, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:probabilisticSampling: ", p), err) + } + if err := p.ProbabilisticSampling.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.ProbabilisticSampling), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:probabilisticSampling: ", p), err) + } + } + return err +} + +func (p *SamplingStrategyResponse) writeField3(oprot thrift.TProtocol) (err error) { + if p.IsSetRateLimitingSampling() { + if err := oprot.WriteFieldBegin("rateLimitingSampling", thrift.STRUCT, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:rateLimitingSampling: ", p), err) + } + if err := p.RateLimitingSampling.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.RateLimitingSampling), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:rateLimitingSampling: ", p), err) + } + } + return err +} + +func (p *SamplingStrategyResponse) writeField4(oprot thrift.TProtocol) (err error) { + if p.IsSetOperationSampling() { + if err := oprot.WriteFieldBegin("operationSampling", thrift.STRUCT, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:operationSampling: ", p), err) + } + if err := p.OperationSampling.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.OperationSampling), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:operationSampling: ", p), err) + } + } + return err +} + +func (p *SamplingStrategyResponse) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("SamplingStrategyResponse(%+v)", *p) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/constants.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/constants.go new file mode 100644 index 00000000..f05144bf --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/constants.go @@ -0,0 +1,32 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package zipkincore + +import ( + "bytes" + "fmt" + "github.com/uber/jaeger-client-go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +const CLIENT_SEND = "cs" +const CLIENT_RECV = "cr" +const SERVER_SEND = "ss" +const SERVER_RECV = "sr" +const WIRE_SEND = "ws" +const WIRE_RECV = "wr" +const CLIENT_SEND_FRAGMENT = "csf" +const CLIENT_RECV_FRAGMENT = "crf" +const SERVER_SEND_FRAGMENT = "ssf" +const SERVER_RECV_FRAGMENT = "srf" +const LOCAL_COMPONENT = "lc" +const CLIENT_ADDR = "ca" +const SERVER_ADDR = "sa" + +func init() { +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/ttypes.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/ttypes.go new file mode 100644 index 00000000..34b2b267 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/ttypes.go @@ -0,0 +1,1247 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package zipkincore + +import ( + "bytes" + "fmt" + "github.com/uber/jaeger-client-go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +var GoUnusedProtection__ int + +type AnnotationType int64 + +const ( + AnnotationType_BOOL AnnotationType = 0 + AnnotationType_BYTES AnnotationType = 1 + AnnotationType_I16 AnnotationType = 2 + AnnotationType_I32 AnnotationType = 3 + AnnotationType_I64 AnnotationType = 4 + AnnotationType_DOUBLE AnnotationType = 5 + AnnotationType_STRING AnnotationType = 6 +) + +func (p AnnotationType) String() string { + switch p { + case AnnotationType_BOOL: + return "BOOL" + case AnnotationType_BYTES: + return "BYTES" + case AnnotationType_I16: + return "I16" + case AnnotationType_I32: + return "I32" + case AnnotationType_I64: + return "I64" + case AnnotationType_DOUBLE: + return "DOUBLE" + case AnnotationType_STRING: + return "STRING" + } + return "" +} + +func AnnotationTypeFromString(s string) (AnnotationType, error) { + switch s { + case "BOOL": + return AnnotationType_BOOL, nil + case "BYTES": + return AnnotationType_BYTES, nil + case "I16": + return AnnotationType_I16, nil + case "I32": + return AnnotationType_I32, nil + case "I64": + return AnnotationType_I64, nil + case "DOUBLE": + return AnnotationType_DOUBLE, nil + case "STRING": + return AnnotationType_STRING, nil + } + return AnnotationType(0), fmt.Errorf("not a valid AnnotationType string") +} + +func AnnotationTypePtr(v AnnotationType) *AnnotationType { return &v } + +func (p AnnotationType) MarshalText() ([]byte, error) { + return []byte(p.String()), nil +} + +func (p *AnnotationType) UnmarshalText(text []byte) error { + q, err := AnnotationTypeFromString(string(text)) + if err != nil { + return err + } + *p = q + return nil +} + +// Indicates the network context of a service recording an annotation with two +// exceptions. +// +// When a BinaryAnnotation, and key is CLIENT_ADDR or SERVER_ADDR, +// the endpoint indicates the source or destination of an RPC. This exception +// allows zipkin to display network context of uninstrumented services, or +// clients such as web browsers. +// +// Attributes: +// - Ipv4: IPv4 host address packed into 4 bytes. +// +// Ex for the ip 1.2.3.4, it would be (1 << 24) | (2 << 16) | (3 << 8) | 4 +// - Port: IPv4 port +// +// Note: this is to be treated as an unsigned integer, so watch for negatives. +// +// Conventionally, when the port isn't known, port = 0. +// - ServiceName: Service name in lowercase, such as "memcache" or "zipkin-web" +// +// Conventionally, when the service name isn't known, service_name = "unknown". +type Endpoint struct { + Ipv4 int32 `thrift:"ipv4,1" json:"ipv4"` + Port int16 `thrift:"port,2" json:"port"` + ServiceName string `thrift:"service_name,3" json:"service_name"` +} + +func NewEndpoint() *Endpoint { + return &Endpoint{} +} + +func (p *Endpoint) GetIpv4() int32 { + return p.Ipv4 +} + +func (p *Endpoint) GetPort() int16 { + return p.Port +} + +func (p *Endpoint) GetServiceName() string { + return p.ServiceName +} +func (p *Endpoint) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *Endpoint) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Ipv4 = v + } + return nil +} + +func (p *Endpoint) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI16(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.Port = v + } + return nil +} + +func (p *Endpoint) readField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + p.ServiceName = v + } + return nil +} + +func (p *Endpoint) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Endpoint"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *Endpoint) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("ipv4", thrift.I32, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:ipv4: ", p), err) + } + if err := oprot.WriteI32(int32(p.Ipv4)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.ipv4 (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:ipv4: ", p), err) + } + return err +} + +func (p *Endpoint) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("port", thrift.I16, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:port: ", p), err) + } + if err := oprot.WriteI16(int16(p.Port)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.port (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:port: ", p), err) + } + return err +} + +func (p *Endpoint) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("service_name", thrift.STRING, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:service_name: ", p), err) + } + if err := oprot.WriteString(string(p.ServiceName)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.service_name (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:service_name: ", p), err) + } + return err +} + +func (p *Endpoint) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Endpoint(%+v)", *p) +} + +// An annotation is similar to a log statement. It includes a host field which +// allows these events to be attributed properly, and also aggregatable. +// +// Attributes: +// - Timestamp: Microseconds from epoch. +// +// This value should use the most precise value possible. For example, +// gettimeofday or syncing nanoTime against a tick of currentTimeMillis. +// - Value +// - Host: Always the host that recorded the event. By specifying the host you allow +// rollup of all events (such as client requests to a service) by IP address. +type Annotation struct { + Timestamp int64 `thrift:"timestamp,1" json:"timestamp"` + Value string `thrift:"value,2" json:"value"` + Host *Endpoint `thrift:"host,3" json:"host,omitempty"` +} + +func NewAnnotation() *Annotation { + return &Annotation{} +} + +func (p *Annotation) GetTimestamp() int64 { + return p.Timestamp +} + +func (p *Annotation) GetValue() string { + return p.Value +} + +var Annotation_Host_DEFAULT *Endpoint + +func (p *Annotation) GetHost() *Endpoint { + if !p.IsSetHost() { + return Annotation_Host_DEFAULT + } + return p.Host +} +func (p *Annotation) IsSetHost() bool { + return p.Host != nil +} + +func (p *Annotation) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *Annotation) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Timestamp = v + } + return nil +} + +func (p *Annotation) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.Value = v + } + return nil +} + +func (p *Annotation) readField3(iprot thrift.TProtocol) error { + p.Host = &Endpoint{} + if err := p.Host.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Host), err) + } + return nil +} + +func (p *Annotation) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Annotation"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *Annotation) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("timestamp", thrift.I64, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:timestamp: ", p), err) + } + if err := oprot.WriteI64(int64(p.Timestamp)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.timestamp (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:timestamp: ", p), err) + } + return err +} + +func (p *Annotation) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("value", thrift.STRING, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:value: ", p), err) + } + if err := oprot.WriteString(string(p.Value)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.value (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:value: ", p), err) + } + return err +} + +func (p *Annotation) writeField3(oprot thrift.TProtocol) (err error) { + if p.IsSetHost() { + if err := oprot.WriteFieldBegin("host", thrift.STRUCT, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:host: ", p), err) + } + if err := p.Host.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Host), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:host: ", p), err) + } + } + return err +} + +func (p *Annotation) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Annotation(%+v)", *p) +} + +// Binary annotations are tags applied to a Span to give it context. For +// example, a binary annotation of "http.uri" could the path to a resource in a +// RPC call. +// +// Binary annotations of type STRING are always queryable, though more a +// historical implementation detail than a structural concern. +// +// Binary annotations can repeat, and vary on the host. Similar to Annotation, +// the host indicates who logged the event. This allows you to tell the +// difference between the client and server side of the same key. For example, +// the key "http.uri" might be different on the client and server side due to +// rewriting, like "/api/v1/myresource" vs "/myresource. Via the host field, +// you can see the different points of view, which often help in debugging. +// +// Attributes: +// - Key +// - Value +// - AnnotationType +// - Host: The host that recorded tag, which allows you to differentiate between +// multiple tags with the same key. There are two exceptions to this. +// +// When the key is CLIENT_ADDR or SERVER_ADDR, host indicates the source or +// destination of an RPC. This exception allows zipkin to display network +// context of uninstrumented services, or clients such as web browsers. +type BinaryAnnotation struct { + Key string `thrift:"key,1" json:"key"` + Value []byte `thrift:"value,2" json:"value"` + AnnotationType AnnotationType `thrift:"annotation_type,3" json:"annotation_type"` + Host *Endpoint `thrift:"host,4" json:"host,omitempty"` +} + +func NewBinaryAnnotation() *BinaryAnnotation { + return &BinaryAnnotation{} +} + +func (p *BinaryAnnotation) GetKey() string { + return p.Key +} + +func (p *BinaryAnnotation) GetValue() []byte { + return p.Value +} + +func (p *BinaryAnnotation) GetAnnotationType() AnnotationType { + return p.AnnotationType +} + +var BinaryAnnotation_Host_DEFAULT *Endpoint + +func (p *BinaryAnnotation) GetHost() *Endpoint { + if !p.IsSetHost() { + return BinaryAnnotation_Host_DEFAULT + } + return p.Host +} +func (p *BinaryAnnotation) IsSetHost() bool { + return p.Host != nil +} + +func (p *BinaryAnnotation) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + case 4: + if err := p.readField4(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *BinaryAnnotation) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Key = v + } + return nil +} + +func (p *BinaryAnnotation) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadBinary(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.Value = v + } + return nil +} + +func (p *BinaryAnnotation) readField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + temp := AnnotationType(v) + p.AnnotationType = temp + } + return nil +} + +func (p *BinaryAnnotation) readField4(iprot thrift.TProtocol) error { + p.Host = &Endpoint{} + if err := p.Host.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Host), err) + } + return nil +} + +func (p *BinaryAnnotation) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("BinaryAnnotation"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := p.writeField4(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *BinaryAnnotation) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("key", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:key: ", p), err) + } + if err := oprot.WriteString(string(p.Key)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.key (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:key: ", p), err) + } + return err +} + +func (p *BinaryAnnotation) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("value", thrift.STRING, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:value: ", p), err) + } + if err := oprot.WriteBinary(p.Value); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.value (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:value: ", p), err) + } + return err +} + +func (p *BinaryAnnotation) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("annotation_type", thrift.I32, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:annotation_type: ", p), err) + } + if err := oprot.WriteI32(int32(p.AnnotationType)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.annotation_type (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:annotation_type: ", p), err) + } + return err +} + +func (p *BinaryAnnotation) writeField4(oprot thrift.TProtocol) (err error) { + if p.IsSetHost() { + if err := oprot.WriteFieldBegin("host", thrift.STRUCT, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:host: ", p), err) + } + if err := p.Host.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Host), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:host: ", p), err) + } + } + return err +} + +func (p *BinaryAnnotation) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("BinaryAnnotation(%+v)", *p) +} + +// A trace is a series of spans (often RPC calls) which form a latency tree. +// +// The root span is where trace_id = id and parent_id = Nil. The root span is +// usually the longest interval in the trace, starting with a SERVER_RECV +// annotation and ending with a SERVER_SEND. +// +// Attributes: +// - TraceID +// - Name: Span name in lowercase, rpc method for example +// +// Conventionally, when the span name isn't known, name = "unknown". +// - ID +// - ParentID +// - Annotations +// - BinaryAnnotations +// - Debug +// - Timestamp: Microseconds from epoch of the creation of this span. +// +// This value should be set directly by instrumentation, using the most +// precise value possible. For example, gettimeofday or syncing nanoTime +// against a tick of currentTimeMillis. +// +// For compatibilty with instrumentation that precede this field, collectors +// or span stores can derive this via Annotation.timestamp. +// For example, SERVER_RECV.timestamp or CLIENT_SEND.timestamp. +// +// This field is optional for compatibility with old data: first-party span +// stores are expected to support this at time of introduction. +// - Duration: Measurement of duration in microseconds, used to support queries. +// +// This value should be set directly, where possible. Doing so encourages +// precise measurement decoupled from problems of clocks, such as skew or NTP +// updates causing time to move backwards. +// +// For compatibilty with instrumentation that precede this field, collectors +// or span stores can derive this by subtracting Annotation.timestamp. +// For example, SERVER_SEND.timestamp - SERVER_RECV.timestamp. +// +// If this field is persisted as unset, zipkin will continue to work, except +// duration query support will be implementation-specific. Similarly, setting +// this field non-atomically is implementation-specific. +// +// This field is i64 vs i32 to support spans longer than 35 minutes. +type Span struct { + TraceID int64 `thrift:"trace_id,1" json:"trace_id"` + // unused field # 2 + Name string `thrift:"name,3" json:"name"` + ID int64 `thrift:"id,4" json:"id"` + ParentID *int64 `thrift:"parent_id,5" json:"parent_id,omitempty"` + Annotations []*Annotation `thrift:"annotations,6" json:"annotations"` + // unused field # 7 + BinaryAnnotations []*BinaryAnnotation `thrift:"binary_annotations,8" json:"binary_annotations"` + Debug bool `thrift:"debug,9" json:"debug,omitempty"` + Timestamp *int64 `thrift:"timestamp,10" json:"timestamp,omitempty"` + Duration *int64 `thrift:"duration,11" json:"duration,omitempty"` +} + +func NewSpan() *Span { + return &Span{} +} + +func (p *Span) GetTraceID() int64 { + return p.TraceID +} + +func (p *Span) GetName() string { + return p.Name +} + +func (p *Span) GetID() int64 { + return p.ID +} + +var Span_ParentID_DEFAULT int64 + +func (p *Span) GetParentID() int64 { + if !p.IsSetParentID() { + return Span_ParentID_DEFAULT + } + return *p.ParentID +} + +func (p *Span) GetAnnotations() []*Annotation { + return p.Annotations +} + +func (p *Span) GetBinaryAnnotations() []*BinaryAnnotation { + return p.BinaryAnnotations +} + +var Span_Debug_DEFAULT bool = false + +func (p *Span) GetDebug() bool { + return p.Debug +} + +var Span_Timestamp_DEFAULT int64 + +func (p *Span) GetTimestamp() int64 { + if !p.IsSetTimestamp() { + return Span_Timestamp_DEFAULT + } + return *p.Timestamp +} + +var Span_Duration_DEFAULT int64 + +func (p *Span) GetDuration() int64 { + if !p.IsSetDuration() { + return Span_Duration_DEFAULT + } + return *p.Duration +} +func (p *Span) IsSetParentID() bool { + return p.ParentID != nil +} + +func (p *Span) IsSetDebug() bool { + return p.Debug != Span_Debug_DEFAULT +} + +func (p *Span) IsSetTimestamp() bool { + return p.Timestamp != nil +} + +func (p *Span) IsSetDuration() bool { + return p.Duration != nil +} + +func (p *Span) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + case 4: + if err := p.readField4(iprot); err != nil { + return err + } + case 5: + if err := p.readField5(iprot); err != nil { + return err + } + case 6: + if err := p.readField6(iprot); err != nil { + return err + } + case 8: + if err := p.readField8(iprot); err != nil { + return err + } + case 9: + if err := p.readField9(iprot); err != nil { + return err + } + case 10: + if err := p.readField10(iprot); err != nil { + return err + } + case 11: + if err := p.readField11(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *Span) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.TraceID = v + } + return nil +} + +func (p *Span) readField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + p.Name = v + } + return nil +} + +func (p *Span) readField4(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 4: ", err) + } else { + p.ID = v + } + return nil +} + +func (p *Span) readField5(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 5: ", err) + } else { + p.ParentID = &v + } + return nil +} + +func (p *Span) readField6(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Annotation, 0, size) + p.Annotations = tSlice + for i := 0; i < size; i++ { + _elem0 := &Annotation{} + if err := _elem0.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem0), err) + } + p.Annotations = append(p.Annotations, _elem0) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *Span) readField8(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*BinaryAnnotation, 0, size) + p.BinaryAnnotations = tSlice + for i := 0; i < size; i++ { + _elem1 := &BinaryAnnotation{} + if err := _elem1.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem1), err) + } + p.BinaryAnnotations = append(p.BinaryAnnotations, _elem1) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *Span) readField9(iprot thrift.TProtocol) error { + if v, err := iprot.ReadBool(); err != nil { + return thrift.PrependError("error reading field 9: ", err) + } else { + p.Debug = v + } + return nil +} + +func (p *Span) readField10(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 10: ", err) + } else { + p.Timestamp = &v + } + return nil +} + +func (p *Span) readField11(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 11: ", err) + } else { + p.Duration = &v + } + return nil +} + +func (p *Span) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Span"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := p.writeField4(oprot); err != nil { + return err + } + if err := p.writeField5(oprot); err != nil { + return err + } + if err := p.writeField6(oprot); err != nil { + return err + } + if err := p.writeField8(oprot); err != nil { + return err + } + if err := p.writeField9(oprot); err != nil { + return err + } + if err := p.writeField10(oprot); err != nil { + return err + } + if err := p.writeField11(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *Span) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("trace_id", thrift.I64, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:trace_id: ", p), err) + } + if err := oprot.WriteI64(int64(p.TraceID)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.trace_id (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:trace_id: ", p), err) + } + return err +} + +func (p *Span) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("name", thrift.STRING, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:name: ", p), err) + } + if err := oprot.WriteString(string(p.Name)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.name (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:name: ", p), err) + } + return err +} + +func (p *Span) writeField4(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("id", thrift.I64, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:id: ", p), err) + } + if err := oprot.WriteI64(int64(p.ID)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.id (4) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:id: ", p), err) + } + return err +} + +func (p *Span) writeField5(oprot thrift.TProtocol) (err error) { + if p.IsSetParentID() { + if err := oprot.WriteFieldBegin("parent_id", thrift.I64, 5); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 5:parent_id: ", p), err) + } + if err := oprot.WriteI64(int64(*p.ParentID)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.parent_id (5) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 5:parent_id: ", p), err) + } + } + return err +} + +func (p *Span) writeField6(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("annotations", thrift.LIST, 6); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 6:annotations: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Annotations)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Annotations { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 6:annotations: ", p), err) + } + return err +} + +func (p *Span) writeField8(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("binary_annotations", thrift.LIST, 8); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 8:binary_annotations: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.BinaryAnnotations)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.BinaryAnnotations { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 8:binary_annotations: ", p), err) + } + return err +} + +func (p *Span) writeField9(oprot thrift.TProtocol) (err error) { + if p.IsSetDebug() { + if err := oprot.WriteFieldBegin("debug", thrift.BOOL, 9); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 9:debug: ", p), err) + } + if err := oprot.WriteBool(bool(p.Debug)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.debug (9) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 9:debug: ", p), err) + } + } + return err +} + +func (p *Span) writeField10(oprot thrift.TProtocol) (err error) { + if p.IsSetTimestamp() { + if err := oprot.WriteFieldBegin("timestamp", thrift.I64, 10); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 10:timestamp: ", p), err) + } + if err := oprot.WriteI64(int64(*p.Timestamp)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.timestamp (10) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 10:timestamp: ", p), err) + } + } + return err +} + +func (p *Span) writeField11(oprot thrift.TProtocol) (err error) { + if p.IsSetDuration() { + if err := oprot.WriteFieldBegin("duration", thrift.I64, 11); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 11:duration: ", p), err) + } + if err := oprot.WriteI64(int64(*p.Duration)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.duration (11) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 11:duration: ", p), err) + } + } + return err +} + +func (p *Span) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Span(%+v)", *p) +} + +// Attributes: +// - Ok +type Response struct { + Ok bool `thrift:"ok,1,required" json:"ok"` +} + +func NewResponse() *Response { + return &Response{} +} + +func (p *Response) GetOk() bool { + return p.Ok +} +func (p *Response) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetOk bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetOk = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetOk { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Ok is not set")) + } + return nil +} + +func (p *Response) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadBool(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Ok = v + } + return nil +} + +func (p *Response) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Response"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *Response) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("ok", thrift.BOOL, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:ok: ", p), err) + } + if err := oprot.WriteBool(bool(p.Ok)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.ok (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:ok: ", p), err) + } + return err +} + +func (p *Response) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Response(%+v)", *p) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/zipkincollector.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/zipkincollector.go new file mode 100644 index 00000000..417e883d --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/zipkincollector.go @@ -0,0 +1,446 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package zipkincore + +import ( + "bytes" + "fmt" + "github.com/uber/jaeger-client-go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +type ZipkinCollector interface { + // Parameters: + // - Spans + SubmitZipkinBatch(spans []*Span) (r []*Response, err error) +} + +type ZipkinCollectorClient struct { + Transport thrift.TTransport + ProtocolFactory thrift.TProtocolFactory + InputProtocol thrift.TProtocol + OutputProtocol thrift.TProtocol + SeqId int32 +} + +func NewZipkinCollectorClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *ZipkinCollectorClient { + return &ZipkinCollectorClient{Transport: t, + ProtocolFactory: f, + InputProtocol: f.GetProtocol(t), + OutputProtocol: f.GetProtocol(t), + SeqId: 0, + } +} + +func NewZipkinCollectorClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *ZipkinCollectorClient { + return &ZipkinCollectorClient{Transport: t, + ProtocolFactory: nil, + InputProtocol: iprot, + OutputProtocol: oprot, + SeqId: 0, + } +} + +// Parameters: +// - Spans +func (p *ZipkinCollectorClient) SubmitZipkinBatch(spans []*Span) (r []*Response, err error) { + if err = p.sendSubmitZipkinBatch(spans); err != nil { + return + } + return p.recvSubmitZipkinBatch() +} + +func (p *ZipkinCollectorClient) sendSubmitZipkinBatch(spans []*Span) (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("submitZipkinBatch", thrift.CALL, p.SeqId); err != nil { + return + } + args := ZipkinCollectorSubmitZipkinBatchArgs{ + Spans: spans, + } + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *ZipkinCollectorClient) recvSubmitZipkinBatch() (value []*Response, err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "submitZipkinBatch" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "submitZipkinBatch failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "submitZipkinBatch failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error2 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error3 error + error3, err = error2.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error3 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "submitZipkinBatch failed: invalid message type") + return + } + result := ZipkinCollectorSubmitZipkinBatchResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + value = result.GetSuccess() + return +} + +type ZipkinCollectorProcessor struct { + processorMap map[string]thrift.TProcessorFunction + handler ZipkinCollector +} + +func (p *ZipkinCollectorProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { + p.processorMap[key] = processor +} + +func (p *ZipkinCollectorProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { + processor, ok = p.processorMap[key] + return processor, ok +} + +func (p *ZipkinCollectorProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { + return p.processorMap +} + +func NewZipkinCollectorProcessor(handler ZipkinCollector) *ZipkinCollectorProcessor { + + self4 := &ZipkinCollectorProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} + self4.processorMap["submitZipkinBatch"] = &zipkinCollectorProcessorSubmitZipkinBatch{handler: handler} + return self4 +} + +func (p *ZipkinCollectorProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + name, _, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return false, err + } + if processor, ok := p.GetProcessorFunction(name); ok { + return processor.Process(seqId, iprot, oprot) + } + iprot.Skip(thrift.STRUCT) + iprot.ReadMessageEnd() + x5 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) + oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) + x5.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, x5 + +} + +type zipkinCollectorProcessorSubmitZipkinBatch struct { + handler ZipkinCollector +} + +func (p *zipkinCollectorProcessorSubmitZipkinBatch) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := ZipkinCollectorSubmitZipkinBatchArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("submitZipkinBatch", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := ZipkinCollectorSubmitZipkinBatchResult{} + var retval []*Response + var err2 error + if retval, err2 = p.handler.SubmitZipkinBatch(args.Spans); err2 != nil { + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing submitZipkinBatch: "+err2.Error()) + oprot.WriteMessageBegin("submitZipkinBatch", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } else { + result.Success = retval + } + if err2 = oprot.WriteMessageBegin("submitZipkinBatch", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +// HELPER FUNCTIONS AND STRUCTURES + +// Attributes: +// - Spans +type ZipkinCollectorSubmitZipkinBatchArgs struct { + Spans []*Span `thrift:"spans,1" json:"spans"` +} + +func NewZipkinCollectorSubmitZipkinBatchArgs() *ZipkinCollectorSubmitZipkinBatchArgs { + return &ZipkinCollectorSubmitZipkinBatchArgs{} +} + +func (p *ZipkinCollectorSubmitZipkinBatchArgs) GetSpans() []*Span { + return p.Spans +} +func (p *ZipkinCollectorSubmitZipkinBatchArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *ZipkinCollectorSubmitZipkinBatchArgs) readField1(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Span, 0, size) + p.Spans = tSlice + for i := 0; i < size; i++ { + _elem6 := &Span{} + if err := _elem6.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem6), err) + } + p.Spans = append(p.Spans, _elem6) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *ZipkinCollectorSubmitZipkinBatchArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("submitZipkinBatch_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *ZipkinCollectorSubmitZipkinBatchArgs) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("spans", thrift.LIST, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:spans: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Spans)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Spans { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:spans: ", p), err) + } + return err +} + +func (p *ZipkinCollectorSubmitZipkinBatchArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("ZipkinCollectorSubmitZipkinBatchArgs(%+v)", *p) +} + +// Attributes: +// - Success +type ZipkinCollectorSubmitZipkinBatchResult struct { + Success []*Response `thrift:"success,0" json:"success,omitempty"` +} + +func NewZipkinCollectorSubmitZipkinBatchResult() *ZipkinCollectorSubmitZipkinBatchResult { + return &ZipkinCollectorSubmitZipkinBatchResult{} +} + +var ZipkinCollectorSubmitZipkinBatchResult_Success_DEFAULT []*Response + +func (p *ZipkinCollectorSubmitZipkinBatchResult) GetSuccess() []*Response { + return p.Success +} +func (p *ZipkinCollectorSubmitZipkinBatchResult) IsSetSuccess() bool { + return p.Success != nil +} + +func (p *ZipkinCollectorSubmitZipkinBatchResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 0: + if err := p.readField0(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *ZipkinCollectorSubmitZipkinBatchResult) readField0(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Response, 0, size) + p.Success = tSlice + for i := 0; i < size; i++ { + _elem7 := &Response{} + if err := _elem7.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem7), err) + } + p.Success = append(p.Success, _elem7) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *ZipkinCollectorSubmitZipkinBatchResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("submitZipkinBatch_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField0(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *ZipkinCollectorSubmitZipkinBatchResult) writeField0(oprot thrift.TProtocol) (err error) { + if p.IsSetSuccess() { + if err := oprot.WriteFieldBegin("success", thrift.LIST, 0); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Success)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Success { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) + } + } + return err +} + +func (p *ZipkinCollectorSubmitZipkinBatchResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("ZipkinCollectorSubmitZipkinBatchResult(%+v)", *p) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/.nocover b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/.nocover new file mode 100644 index 00000000..e69de29b diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/README.md b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/README.md new file mode 100644 index 00000000..1d8e642e --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/README.md @@ -0,0 +1,7 @@ +# Apache Thrift + +This is a partial copy of Apache Thrift v0.10 (https://github.com/apache/thrift/commit/b2a4d4ae21c789b689dd162deb819665567f481c). + +It is vendored code to avoid compatibility issues introduced in Thrift v0.11. + +See https://github.com/jaegertracing/jaeger-client-go/pull/303. diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/application_exception.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/application_exception.go new file mode 100644 index 00000000..6655cc5a --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/application_exception.go @@ -0,0 +1,142 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +const ( + UNKNOWN_APPLICATION_EXCEPTION = 0 + UNKNOWN_METHOD = 1 + INVALID_MESSAGE_TYPE_EXCEPTION = 2 + WRONG_METHOD_NAME = 3 + BAD_SEQUENCE_ID = 4 + MISSING_RESULT = 5 + INTERNAL_ERROR = 6 + PROTOCOL_ERROR = 7 +) + +// Application level Thrift exception +type TApplicationException interface { + TException + TypeId() int32 + Read(iprot TProtocol) (TApplicationException, error) + Write(oprot TProtocol) error +} + +type tApplicationException struct { + message string + type_ int32 +} + +func (e tApplicationException) Error() string { + return e.message +} + +func NewTApplicationException(type_ int32, message string) TApplicationException { + return &tApplicationException{message, type_} +} + +func (p *tApplicationException) TypeId() int32 { + return p.type_ +} + +func (p *tApplicationException) Read(iprot TProtocol) (TApplicationException, error) { + _, err := iprot.ReadStructBegin() + if err != nil { + return nil, err + } + + message := "" + type_ := int32(UNKNOWN_APPLICATION_EXCEPTION) + + for { + _, ttype, id, err := iprot.ReadFieldBegin() + if err != nil { + return nil, err + } + if ttype == STOP { + break + } + switch id { + case 1: + if ttype == STRING { + if message, err = iprot.ReadString(); err != nil { + return nil, err + } + } else { + if err = SkipDefaultDepth(iprot, ttype); err != nil { + return nil, err + } + } + case 2: + if ttype == I32 { + if type_, err = iprot.ReadI32(); err != nil { + return nil, err + } + } else { + if err = SkipDefaultDepth(iprot, ttype); err != nil { + return nil, err + } + } + default: + if err = SkipDefaultDepth(iprot, ttype); err != nil { + return nil, err + } + } + if err = iprot.ReadFieldEnd(); err != nil { + return nil, err + } + } + return NewTApplicationException(type_, message), iprot.ReadStructEnd() +} + +func (p *tApplicationException) Write(oprot TProtocol) (err error) { + err = oprot.WriteStructBegin("TApplicationException") + if len(p.Error()) > 0 { + err = oprot.WriteFieldBegin("message", STRING, 1) + if err != nil { + return + } + err = oprot.WriteString(p.Error()) + if err != nil { + return + } + err = oprot.WriteFieldEnd() + if err != nil { + return + } + } + err = oprot.WriteFieldBegin("type", I32, 2) + if err != nil { + return + } + err = oprot.WriteI32(p.type_) + if err != nil { + return + } + err = oprot.WriteFieldEnd() + if err != nil { + return + } + err = oprot.WriteFieldStop() + if err != nil { + return + } + err = oprot.WriteStructEnd() + return +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/binary_protocol.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/binary_protocol.go new file mode 100644 index 00000000..690d3411 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/binary_protocol.go @@ -0,0 +1,514 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + "math" +) + +type TBinaryProtocol struct { + trans TRichTransport + origTransport TTransport + reader io.Reader + writer io.Writer + strictRead bool + strictWrite bool + buffer [64]byte +} + +type TBinaryProtocolFactory struct { + strictRead bool + strictWrite bool +} + +func NewTBinaryProtocolTransport(t TTransport) *TBinaryProtocol { + return NewTBinaryProtocol(t, false, true) +} + +func NewTBinaryProtocol(t TTransport, strictRead, strictWrite bool) *TBinaryProtocol { + p := &TBinaryProtocol{origTransport: t, strictRead: strictRead, strictWrite: strictWrite} + if et, ok := t.(TRichTransport); ok { + p.trans = et + } else { + p.trans = NewTRichTransport(t) + } + p.reader = p.trans + p.writer = p.trans + return p +} + +func NewTBinaryProtocolFactoryDefault() *TBinaryProtocolFactory { + return NewTBinaryProtocolFactory(false, true) +} + +func NewTBinaryProtocolFactory(strictRead, strictWrite bool) *TBinaryProtocolFactory { + return &TBinaryProtocolFactory{strictRead: strictRead, strictWrite: strictWrite} +} + +func (p *TBinaryProtocolFactory) GetProtocol(t TTransport) TProtocol { + return NewTBinaryProtocol(t, p.strictRead, p.strictWrite) +} + +/** + * Writing Methods + */ + +func (p *TBinaryProtocol) WriteMessageBegin(name string, typeId TMessageType, seqId int32) error { + if p.strictWrite { + version := uint32(VERSION_1) | uint32(typeId) + e := p.WriteI32(int32(version)) + if e != nil { + return e + } + e = p.WriteString(name) + if e != nil { + return e + } + e = p.WriteI32(seqId) + return e + } else { + e := p.WriteString(name) + if e != nil { + return e + } + e = p.WriteByte(int8(typeId)) + if e != nil { + return e + } + e = p.WriteI32(seqId) + return e + } + return nil +} + +func (p *TBinaryProtocol) WriteMessageEnd() error { + return nil +} + +func (p *TBinaryProtocol) WriteStructBegin(name string) error { + return nil +} + +func (p *TBinaryProtocol) WriteStructEnd() error { + return nil +} + +func (p *TBinaryProtocol) WriteFieldBegin(name string, typeId TType, id int16) error { + e := p.WriteByte(int8(typeId)) + if e != nil { + return e + } + e = p.WriteI16(id) + return e +} + +func (p *TBinaryProtocol) WriteFieldEnd() error { + return nil +} + +func (p *TBinaryProtocol) WriteFieldStop() error { + e := p.WriteByte(STOP) + return e +} + +func (p *TBinaryProtocol) WriteMapBegin(keyType TType, valueType TType, size int) error { + e := p.WriteByte(int8(keyType)) + if e != nil { + return e + } + e = p.WriteByte(int8(valueType)) + if e != nil { + return e + } + e = p.WriteI32(int32(size)) + return e +} + +func (p *TBinaryProtocol) WriteMapEnd() error { + return nil +} + +func (p *TBinaryProtocol) WriteListBegin(elemType TType, size int) error { + e := p.WriteByte(int8(elemType)) + if e != nil { + return e + } + e = p.WriteI32(int32(size)) + return e +} + +func (p *TBinaryProtocol) WriteListEnd() error { + return nil +} + +func (p *TBinaryProtocol) WriteSetBegin(elemType TType, size int) error { + e := p.WriteByte(int8(elemType)) + if e != nil { + return e + } + e = p.WriteI32(int32(size)) + return e +} + +func (p *TBinaryProtocol) WriteSetEnd() error { + return nil +} + +func (p *TBinaryProtocol) WriteBool(value bool) error { + if value { + return p.WriteByte(1) + } + return p.WriteByte(0) +} + +func (p *TBinaryProtocol) WriteByte(value int8) error { + e := p.trans.WriteByte(byte(value)) + return NewTProtocolException(e) +} + +func (p *TBinaryProtocol) WriteI16(value int16) error { + v := p.buffer[0:2] + binary.BigEndian.PutUint16(v, uint16(value)) + _, e := p.writer.Write(v) + return NewTProtocolException(e) +} + +func (p *TBinaryProtocol) WriteI32(value int32) error { + v := p.buffer[0:4] + binary.BigEndian.PutUint32(v, uint32(value)) + _, e := p.writer.Write(v) + return NewTProtocolException(e) +} + +func (p *TBinaryProtocol) WriteI64(value int64) error { + v := p.buffer[0:8] + binary.BigEndian.PutUint64(v, uint64(value)) + _, err := p.writer.Write(v) + return NewTProtocolException(err) +} + +func (p *TBinaryProtocol) WriteDouble(value float64) error { + return p.WriteI64(int64(math.Float64bits(value))) +} + +func (p *TBinaryProtocol) WriteString(value string) error { + e := p.WriteI32(int32(len(value))) + if e != nil { + return e + } + _, err := p.trans.WriteString(value) + return NewTProtocolException(err) +} + +func (p *TBinaryProtocol) WriteBinary(value []byte) error { + e := p.WriteI32(int32(len(value))) + if e != nil { + return e + } + _, err := p.writer.Write(value) + return NewTProtocolException(err) +} + +/** + * Reading methods + */ + +func (p *TBinaryProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqId int32, err error) { + size, e := p.ReadI32() + if e != nil { + return "", typeId, 0, NewTProtocolException(e) + } + if size < 0 { + typeId = TMessageType(size & 0x0ff) + version := int64(int64(size) & VERSION_MASK) + if version != VERSION_1 { + return name, typeId, seqId, NewTProtocolExceptionWithType(BAD_VERSION, fmt.Errorf("Bad version in ReadMessageBegin")) + } + name, e = p.ReadString() + if e != nil { + return name, typeId, seqId, NewTProtocolException(e) + } + seqId, e = p.ReadI32() + if e != nil { + return name, typeId, seqId, NewTProtocolException(e) + } + return name, typeId, seqId, nil + } + if p.strictRead { + return name, typeId, seqId, NewTProtocolExceptionWithType(BAD_VERSION, fmt.Errorf("Missing version in ReadMessageBegin")) + } + name, e2 := p.readStringBody(size) + if e2 != nil { + return name, typeId, seqId, e2 + } + b, e3 := p.ReadByte() + if e3 != nil { + return name, typeId, seqId, e3 + } + typeId = TMessageType(b) + seqId, e4 := p.ReadI32() + if e4 != nil { + return name, typeId, seqId, e4 + } + return name, typeId, seqId, nil +} + +func (p *TBinaryProtocol) ReadMessageEnd() error { + return nil +} + +func (p *TBinaryProtocol) ReadStructBegin() (name string, err error) { + return +} + +func (p *TBinaryProtocol) ReadStructEnd() error { + return nil +} + +func (p *TBinaryProtocol) ReadFieldBegin() (name string, typeId TType, seqId int16, err error) { + t, err := p.ReadByte() + typeId = TType(t) + if err != nil { + return name, typeId, seqId, err + } + if t != STOP { + seqId, err = p.ReadI16() + } + return name, typeId, seqId, err +} + +func (p *TBinaryProtocol) ReadFieldEnd() error { + return nil +} + +var invalidDataLength = NewTProtocolExceptionWithType(INVALID_DATA, errors.New("Invalid data length")) + +func (p *TBinaryProtocol) ReadMapBegin() (kType, vType TType, size int, err error) { + k, e := p.ReadByte() + if e != nil { + err = NewTProtocolException(e) + return + } + kType = TType(k) + v, e := p.ReadByte() + if e != nil { + err = NewTProtocolException(e) + return + } + vType = TType(v) + size32, e := p.ReadI32() + if e != nil { + err = NewTProtocolException(e) + return + } + if size32 < 0 { + err = invalidDataLength + return + } + size = int(size32) + return kType, vType, size, nil +} + +func (p *TBinaryProtocol) ReadMapEnd() error { + return nil +} + +func (p *TBinaryProtocol) ReadListBegin() (elemType TType, size int, err error) { + b, e := p.ReadByte() + if e != nil { + err = NewTProtocolException(e) + return + } + elemType = TType(b) + size32, e := p.ReadI32() + if e != nil { + err = NewTProtocolException(e) + return + } + if size32 < 0 { + err = invalidDataLength + return + } + size = int(size32) + + return +} + +func (p *TBinaryProtocol) ReadListEnd() error { + return nil +} + +func (p *TBinaryProtocol) ReadSetBegin() (elemType TType, size int, err error) { + b, e := p.ReadByte() + if e != nil { + err = NewTProtocolException(e) + return + } + elemType = TType(b) + size32, e := p.ReadI32() + if e != nil { + err = NewTProtocolException(e) + return + } + if size32 < 0 { + err = invalidDataLength + return + } + size = int(size32) + return elemType, size, nil +} + +func (p *TBinaryProtocol) ReadSetEnd() error { + return nil +} + +func (p *TBinaryProtocol) ReadBool() (bool, error) { + b, e := p.ReadByte() + v := true + if b != 1 { + v = false + } + return v, e +} + +func (p *TBinaryProtocol) ReadByte() (int8, error) { + v, err := p.trans.ReadByte() + return int8(v), err +} + +func (p *TBinaryProtocol) ReadI16() (value int16, err error) { + buf := p.buffer[0:2] + err = p.readAll(buf) + value = int16(binary.BigEndian.Uint16(buf)) + return value, err +} + +func (p *TBinaryProtocol) ReadI32() (value int32, err error) { + buf := p.buffer[0:4] + err = p.readAll(buf) + value = int32(binary.BigEndian.Uint32(buf)) + return value, err +} + +func (p *TBinaryProtocol) ReadI64() (value int64, err error) { + buf := p.buffer[0:8] + err = p.readAll(buf) + value = int64(binary.BigEndian.Uint64(buf)) + return value, err +} + +func (p *TBinaryProtocol) ReadDouble() (value float64, err error) { + buf := p.buffer[0:8] + err = p.readAll(buf) + value = math.Float64frombits(binary.BigEndian.Uint64(buf)) + return value, err +} + +func (p *TBinaryProtocol) ReadString() (value string, err error) { + size, e := p.ReadI32() + if e != nil { + return "", e + } + if size < 0 { + err = invalidDataLength + return + } + + return p.readStringBody(size) +} + +func (p *TBinaryProtocol) ReadBinary() ([]byte, error) { + size, e := p.ReadI32() + if e != nil { + return nil, e + } + if size < 0 { + return nil, invalidDataLength + } + if uint64(size) > p.trans.RemainingBytes() { + return nil, invalidDataLength + } + + isize := int(size) + buf := make([]byte, isize) + _, err := io.ReadFull(p.trans, buf) + return buf, NewTProtocolException(err) +} + +func (p *TBinaryProtocol) Flush() (err error) { + return NewTProtocolException(p.trans.Flush()) +} + +func (p *TBinaryProtocol) Skip(fieldType TType) (err error) { + return SkipDefaultDepth(p, fieldType) +} + +func (p *TBinaryProtocol) Transport() TTransport { + return p.origTransport +} + +func (p *TBinaryProtocol) readAll(buf []byte) error { + _, err := io.ReadFull(p.reader, buf) + return NewTProtocolException(err) +} + +const readLimit = 32768 + +func (p *TBinaryProtocol) readStringBody(size int32) (value string, err error) { + if size < 0 { + return "", nil + } + if uint64(size) > p.trans.RemainingBytes() { + return "", invalidDataLength + } + + var ( + buf bytes.Buffer + e error + b []byte + ) + + switch { + case int(size) <= len(p.buffer): + b = p.buffer[:size] // avoids allocation for small reads + case int(size) < readLimit: + b = make([]byte, size) + default: + b = make([]byte, readLimit) + } + + for size > 0 { + _, e = io.ReadFull(p.trans, b) + buf.Write(b) + if e != nil { + break + } + size -= readLimit + if size < readLimit && size > 0 { + b = b[:size] + } + } + return buf.String(), NewTProtocolException(e) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/compact_protocol.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/compact_protocol.go new file mode 100644 index 00000000..b9299f2f --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/compact_protocol.go @@ -0,0 +1,815 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "encoding/binary" + "fmt" + "io" + "math" +) + +const ( + COMPACT_PROTOCOL_ID = 0x082 + COMPACT_VERSION = 1 + COMPACT_VERSION_MASK = 0x1f + COMPACT_TYPE_MASK = 0x0E0 + COMPACT_TYPE_BITS = 0x07 + COMPACT_TYPE_SHIFT_AMOUNT = 5 +) + +type tCompactType byte + +const ( + COMPACT_BOOLEAN_TRUE = 0x01 + COMPACT_BOOLEAN_FALSE = 0x02 + COMPACT_BYTE = 0x03 + COMPACT_I16 = 0x04 + COMPACT_I32 = 0x05 + COMPACT_I64 = 0x06 + COMPACT_DOUBLE = 0x07 + COMPACT_BINARY = 0x08 + COMPACT_LIST = 0x09 + COMPACT_SET = 0x0A + COMPACT_MAP = 0x0B + COMPACT_STRUCT = 0x0C +) + +var ( + ttypeToCompactType map[TType]tCompactType +) + +func init() { + ttypeToCompactType = map[TType]tCompactType{ + STOP: STOP, + BOOL: COMPACT_BOOLEAN_TRUE, + BYTE: COMPACT_BYTE, + I16: COMPACT_I16, + I32: COMPACT_I32, + I64: COMPACT_I64, + DOUBLE: COMPACT_DOUBLE, + STRING: COMPACT_BINARY, + LIST: COMPACT_LIST, + SET: COMPACT_SET, + MAP: COMPACT_MAP, + STRUCT: COMPACT_STRUCT, + } +} + +type TCompactProtocolFactory struct{} + +func NewTCompactProtocolFactory() *TCompactProtocolFactory { + return &TCompactProtocolFactory{} +} + +func (p *TCompactProtocolFactory) GetProtocol(trans TTransport) TProtocol { + return NewTCompactProtocol(trans) +} + +type TCompactProtocol struct { + trans TRichTransport + origTransport TTransport + + // Used to keep track of the last field for the current and previous structs, + // so we can do the delta stuff. + lastField []int + lastFieldId int + + // If we encounter a boolean field begin, save the TField here so it can + // have the value incorporated. + booleanFieldName string + booleanFieldId int16 + booleanFieldPending bool + + // If we read a field header, and it's a boolean field, save the boolean + // value here so that readBool can use it. + boolValue bool + boolValueIsNotNull bool + buffer [64]byte +} + +// Create a TCompactProtocol given a TTransport +func NewTCompactProtocol(trans TTransport) *TCompactProtocol { + p := &TCompactProtocol{origTransport: trans, lastField: []int{}} + if et, ok := trans.(TRichTransport); ok { + p.trans = et + } else { + p.trans = NewTRichTransport(trans) + } + + return p + +} + +// +// Public Writing methods. +// + +// Write a message header to the wire. Compact Protocol messages contain the +// protocol version so we can migrate forwards in the future if need be. +func (p *TCompactProtocol) WriteMessageBegin(name string, typeId TMessageType, seqid int32) error { + err := p.writeByteDirect(COMPACT_PROTOCOL_ID) + if err != nil { + return NewTProtocolException(err) + } + err = p.writeByteDirect((COMPACT_VERSION & COMPACT_VERSION_MASK) | ((byte(typeId) << COMPACT_TYPE_SHIFT_AMOUNT) & COMPACT_TYPE_MASK)) + if err != nil { + return NewTProtocolException(err) + } + _, err = p.writeVarint32(seqid) + if err != nil { + return NewTProtocolException(err) + } + e := p.WriteString(name) + return e + +} + +func (p *TCompactProtocol) WriteMessageEnd() error { return nil } + +// Write a struct begin. This doesn't actually put anything on the wire. We +// use it as an opportunity to put special placeholder markers on the field +// stack so we can get the field id deltas correct. +func (p *TCompactProtocol) WriteStructBegin(name string) error { + p.lastField = append(p.lastField, p.lastFieldId) + p.lastFieldId = 0 + return nil +} + +// Write a struct end. This doesn't actually put anything on the wire. We use +// this as an opportunity to pop the last field from the current struct off +// of the field stack. +func (p *TCompactProtocol) WriteStructEnd() error { + p.lastFieldId = p.lastField[len(p.lastField)-1] + p.lastField = p.lastField[:len(p.lastField)-1] + return nil +} + +func (p *TCompactProtocol) WriteFieldBegin(name string, typeId TType, id int16) error { + if typeId == BOOL { + // we want to possibly include the value, so we'll wait. + p.booleanFieldName, p.booleanFieldId, p.booleanFieldPending = name, id, true + return nil + } + _, err := p.writeFieldBeginInternal(name, typeId, id, 0xFF) + return NewTProtocolException(err) +} + +// The workhorse of writeFieldBegin. It has the option of doing a +// 'type override' of the type header. This is used specifically in the +// boolean field case. +func (p *TCompactProtocol) writeFieldBeginInternal(name string, typeId TType, id int16, typeOverride byte) (int, error) { + // short lastField = lastField_.pop(); + + // if there's a type override, use that. + var typeToWrite byte + if typeOverride == 0xFF { + typeToWrite = byte(p.getCompactType(typeId)) + } else { + typeToWrite = typeOverride + } + // check if we can use delta encoding for the field id + fieldId := int(id) + written := 0 + if fieldId > p.lastFieldId && fieldId-p.lastFieldId <= 15 { + // write them together + err := p.writeByteDirect(byte((fieldId-p.lastFieldId)<<4) | typeToWrite) + if err != nil { + return 0, err + } + } else { + // write them separate + err := p.writeByteDirect(typeToWrite) + if err != nil { + return 0, err + } + err = p.WriteI16(id) + written = 1 + 2 + if err != nil { + return 0, err + } + } + + p.lastFieldId = fieldId + // p.lastField.Push(field.id); + return written, nil +} + +func (p *TCompactProtocol) WriteFieldEnd() error { return nil } + +func (p *TCompactProtocol) WriteFieldStop() error { + err := p.writeByteDirect(STOP) + return NewTProtocolException(err) +} + +func (p *TCompactProtocol) WriteMapBegin(keyType TType, valueType TType, size int) error { + if size == 0 { + err := p.writeByteDirect(0) + return NewTProtocolException(err) + } + _, err := p.writeVarint32(int32(size)) + if err != nil { + return NewTProtocolException(err) + } + err = p.writeByteDirect(byte(p.getCompactType(keyType))<<4 | byte(p.getCompactType(valueType))) + return NewTProtocolException(err) +} + +func (p *TCompactProtocol) WriteMapEnd() error { return nil } + +// Write a list header. +func (p *TCompactProtocol) WriteListBegin(elemType TType, size int) error { + _, err := p.writeCollectionBegin(elemType, size) + return NewTProtocolException(err) +} + +func (p *TCompactProtocol) WriteListEnd() error { return nil } + +// Write a set header. +func (p *TCompactProtocol) WriteSetBegin(elemType TType, size int) error { + _, err := p.writeCollectionBegin(elemType, size) + return NewTProtocolException(err) +} + +func (p *TCompactProtocol) WriteSetEnd() error { return nil } + +func (p *TCompactProtocol) WriteBool(value bool) error { + v := byte(COMPACT_BOOLEAN_FALSE) + if value { + v = byte(COMPACT_BOOLEAN_TRUE) + } + if p.booleanFieldPending { + // we haven't written the field header yet + _, err := p.writeFieldBeginInternal(p.booleanFieldName, BOOL, p.booleanFieldId, v) + p.booleanFieldPending = false + return NewTProtocolException(err) + } + // we're not part of a field, so just write the value. + err := p.writeByteDirect(v) + return NewTProtocolException(err) +} + +// Write a byte. Nothing to see here! +func (p *TCompactProtocol) WriteByte(value int8) error { + err := p.writeByteDirect(byte(value)) + return NewTProtocolException(err) +} + +// Write an I16 as a zigzag varint. +func (p *TCompactProtocol) WriteI16(value int16) error { + _, err := p.writeVarint32(p.int32ToZigzag(int32(value))) + return NewTProtocolException(err) +} + +// Write an i32 as a zigzag varint. +func (p *TCompactProtocol) WriteI32(value int32) error { + _, err := p.writeVarint32(p.int32ToZigzag(value)) + return NewTProtocolException(err) +} + +// Write an i64 as a zigzag varint. +func (p *TCompactProtocol) WriteI64(value int64) error { + _, err := p.writeVarint64(p.int64ToZigzag(value)) + return NewTProtocolException(err) +} + +// Write a double to the wire as 8 bytes. +func (p *TCompactProtocol) WriteDouble(value float64) error { + buf := p.buffer[0:8] + binary.LittleEndian.PutUint64(buf, math.Float64bits(value)) + _, err := p.trans.Write(buf) + return NewTProtocolException(err) +} + +// Write a string to the wire with a varint size preceding. +func (p *TCompactProtocol) WriteString(value string) error { + _, e := p.writeVarint32(int32(len(value))) + if e != nil { + return NewTProtocolException(e) + } + if len(value) > 0 { + } + _, e = p.trans.WriteString(value) + return e +} + +// Write a byte array, using a varint for the size. +func (p *TCompactProtocol) WriteBinary(bin []byte) error { + _, e := p.writeVarint32(int32(len(bin))) + if e != nil { + return NewTProtocolException(e) + } + if len(bin) > 0 { + _, e = p.trans.Write(bin) + return NewTProtocolException(e) + } + return nil +} + +// +// Reading methods. +// + +// Read a message header. +func (p *TCompactProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqId int32, err error) { + + protocolId, err := p.readByteDirect() + if err != nil { + return + } + + if protocolId != COMPACT_PROTOCOL_ID { + e := fmt.Errorf("Expected protocol id %02x but got %02x", COMPACT_PROTOCOL_ID, protocolId) + return "", typeId, seqId, NewTProtocolExceptionWithType(BAD_VERSION, e) + } + + versionAndType, err := p.readByteDirect() + if err != nil { + return + } + + version := versionAndType & COMPACT_VERSION_MASK + typeId = TMessageType((versionAndType >> COMPACT_TYPE_SHIFT_AMOUNT) & COMPACT_TYPE_BITS) + if version != COMPACT_VERSION { + e := fmt.Errorf("Expected version %02x but got %02x", COMPACT_VERSION, version) + err = NewTProtocolExceptionWithType(BAD_VERSION, e) + return + } + seqId, e := p.readVarint32() + if e != nil { + err = NewTProtocolException(e) + return + } + name, err = p.ReadString() + return +} + +func (p *TCompactProtocol) ReadMessageEnd() error { return nil } + +// Read a struct begin. There's nothing on the wire for this, but it is our +// opportunity to push a new struct begin marker onto the field stack. +func (p *TCompactProtocol) ReadStructBegin() (name string, err error) { + p.lastField = append(p.lastField, p.lastFieldId) + p.lastFieldId = 0 + return +} + +// Doesn't actually consume any wire data, just removes the last field for +// this struct from the field stack. +func (p *TCompactProtocol) ReadStructEnd() error { + // consume the last field we read off the wire. + p.lastFieldId = p.lastField[len(p.lastField)-1] + p.lastField = p.lastField[:len(p.lastField)-1] + return nil +} + +// Read a field header off the wire. +func (p *TCompactProtocol) ReadFieldBegin() (name string, typeId TType, id int16, err error) { + t, err := p.readByteDirect() + if err != nil { + return + } + + // if it's a stop, then we can return immediately, as the struct is over. + if (t & 0x0f) == STOP { + return "", STOP, 0, nil + } + + // mask off the 4 MSB of the type header. it could contain a field id delta. + modifier := int16((t & 0xf0) >> 4) + if modifier == 0 { + // not a delta. look ahead for the zigzag varint field id. + id, err = p.ReadI16() + if err != nil { + return + } + } else { + // has a delta. add the delta to the last read field id. + id = int16(p.lastFieldId) + modifier + } + typeId, e := p.getTType(tCompactType(t & 0x0f)) + if e != nil { + err = NewTProtocolException(e) + return + } + + // if this happens to be a boolean field, the value is encoded in the type + if p.isBoolType(t) { + // save the boolean value in a special instance variable. + p.boolValue = (byte(t)&0x0f == COMPACT_BOOLEAN_TRUE) + p.boolValueIsNotNull = true + } + + // push the new field onto the field stack so we can keep the deltas going. + p.lastFieldId = int(id) + return +} + +func (p *TCompactProtocol) ReadFieldEnd() error { return nil } + +// Read a map header off the wire. If the size is zero, skip reading the key +// and value type. This means that 0-length maps will yield TMaps without the +// "correct" types. +func (p *TCompactProtocol) ReadMapBegin() (keyType TType, valueType TType, size int, err error) { + size32, e := p.readVarint32() + if e != nil { + err = NewTProtocolException(e) + return + } + if size32 < 0 { + err = invalidDataLength + return + } + size = int(size32) + + keyAndValueType := byte(STOP) + if size != 0 { + keyAndValueType, err = p.readByteDirect() + if err != nil { + return + } + } + keyType, _ = p.getTType(tCompactType(keyAndValueType >> 4)) + valueType, _ = p.getTType(tCompactType(keyAndValueType & 0xf)) + return +} + +func (p *TCompactProtocol) ReadMapEnd() error { return nil } + +// Read a list header off the wire. If the list size is 0-14, the size will +// be packed into the element type header. If it's a longer list, the 4 MSB +// of the element type header will be 0xF, and a varint will follow with the +// true size. +func (p *TCompactProtocol) ReadListBegin() (elemType TType, size int, err error) { + size_and_type, err := p.readByteDirect() + if err != nil { + return + } + size = int((size_and_type >> 4) & 0x0f) + if size == 15 { + size2, e := p.readVarint32() + if e != nil { + err = NewTProtocolException(e) + return + } + if size2 < 0 { + err = invalidDataLength + return + } + size = int(size2) + } + elemType, e := p.getTType(tCompactType(size_and_type)) + if e != nil { + err = NewTProtocolException(e) + return + } + return +} + +func (p *TCompactProtocol) ReadListEnd() error { return nil } + +// Read a set header off the wire. If the set size is 0-14, the size will +// be packed into the element type header. If it's a longer set, the 4 MSB +// of the element type header will be 0xF, and a varint will follow with the +// true size. +func (p *TCompactProtocol) ReadSetBegin() (elemType TType, size int, err error) { + return p.ReadListBegin() +} + +func (p *TCompactProtocol) ReadSetEnd() error { return nil } + +// Read a boolean off the wire. If this is a boolean field, the value should +// already have been read during readFieldBegin, so we'll just consume the +// pre-stored value. Otherwise, read a byte. +func (p *TCompactProtocol) ReadBool() (value bool, err error) { + if p.boolValueIsNotNull { + p.boolValueIsNotNull = false + return p.boolValue, nil + } + v, err := p.readByteDirect() + return v == COMPACT_BOOLEAN_TRUE, err +} + +// Read a single byte off the wire. Nothing interesting here. +func (p *TCompactProtocol) ReadByte() (int8, error) { + v, err := p.readByteDirect() + if err != nil { + return 0, NewTProtocolException(err) + } + return int8(v), err +} + +// Read an i16 from the wire as a zigzag varint. +func (p *TCompactProtocol) ReadI16() (value int16, err error) { + v, err := p.ReadI32() + return int16(v), err +} + +// Read an i32 from the wire as a zigzag varint. +func (p *TCompactProtocol) ReadI32() (value int32, err error) { + v, e := p.readVarint32() + if e != nil { + return 0, NewTProtocolException(e) + } + value = p.zigzagToInt32(v) + return value, nil +} + +// Read an i64 from the wire as a zigzag varint. +func (p *TCompactProtocol) ReadI64() (value int64, err error) { + v, e := p.readVarint64() + if e != nil { + return 0, NewTProtocolException(e) + } + value = p.zigzagToInt64(v) + return value, nil +} + +// No magic here - just read a double off the wire. +func (p *TCompactProtocol) ReadDouble() (value float64, err error) { + longBits := p.buffer[0:8] + _, e := io.ReadFull(p.trans, longBits) + if e != nil { + return 0.0, NewTProtocolException(e) + } + return math.Float64frombits(p.bytesToUint64(longBits)), nil +} + +// Reads a []byte (via readBinary), and then UTF-8 decodes it. +func (p *TCompactProtocol) ReadString() (value string, err error) { + length, e := p.readVarint32() + if e != nil { + return "", NewTProtocolException(e) + } + if length < 0 { + return "", invalidDataLength + } + if uint64(length) > p.trans.RemainingBytes() { + return "", invalidDataLength + } + + if length == 0 { + return "", nil + } + var buf []byte + if length <= int32(len(p.buffer)) { + buf = p.buffer[0:length] + } else { + buf = make([]byte, length) + } + _, e = io.ReadFull(p.trans, buf) + return string(buf), NewTProtocolException(e) +} + +// Read a []byte from the wire. +func (p *TCompactProtocol) ReadBinary() (value []byte, err error) { + length, e := p.readVarint32() + if e != nil { + return nil, NewTProtocolException(e) + } + if length == 0 { + return []byte{}, nil + } + if length < 0 { + return nil, invalidDataLength + } + if uint64(length) > p.trans.RemainingBytes() { + return nil, invalidDataLength + } + + buf := make([]byte, length) + _, e = io.ReadFull(p.trans, buf) + return buf, NewTProtocolException(e) +} + +func (p *TCompactProtocol) Flush() (err error) { + return NewTProtocolException(p.trans.Flush()) +} + +func (p *TCompactProtocol) Skip(fieldType TType) (err error) { + return SkipDefaultDepth(p, fieldType) +} + +func (p *TCompactProtocol) Transport() TTransport { + return p.origTransport +} + +// +// Internal writing methods +// + +// Abstract method for writing the start of lists and sets. List and sets on +// the wire differ only by the type indicator. +func (p *TCompactProtocol) writeCollectionBegin(elemType TType, size int) (int, error) { + if size <= 14 { + return 1, p.writeByteDirect(byte(int32(size<<4) | int32(p.getCompactType(elemType)))) + } + err := p.writeByteDirect(0xf0 | byte(p.getCompactType(elemType))) + if err != nil { + return 0, err + } + m, err := p.writeVarint32(int32(size)) + return 1 + m, err +} + +// Write an i32 as a varint. Results in 1-5 bytes on the wire. +// TODO(pomack): make a permanent buffer like writeVarint64? +func (p *TCompactProtocol) writeVarint32(n int32) (int, error) { + i32buf := p.buffer[0:5] + idx := 0 + for { + if (n & ^0x7F) == 0 { + i32buf[idx] = byte(n) + idx++ + // p.writeByteDirect(byte(n)); + break + // return; + } else { + i32buf[idx] = byte((n & 0x7F) | 0x80) + idx++ + // p.writeByteDirect(byte(((n & 0x7F) | 0x80))); + u := uint32(n) + n = int32(u >> 7) + } + } + return p.trans.Write(i32buf[0:idx]) +} + +// Write an i64 as a varint. Results in 1-10 bytes on the wire. +func (p *TCompactProtocol) writeVarint64(n int64) (int, error) { + varint64out := p.buffer[0:10] + idx := 0 + for { + if (n & ^0x7F) == 0 { + varint64out[idx] = byte(n) + idx++ + break + } else { + varint64out[idx] = byte((n & 0x7F) | 0x80) + idx++ + u := uint64(n) + n = int64(u >> 7) + } + } + return p.trans.Write(varint64out[0:idx]) +} + +// Convert l into a zigzag long. This allows negative numbers to be +// represented compactly as a varint. +func (p *TCompactProtocol) int64ToZigzag(l int64) int64 { + return (l << 1) ^ (l >> 63) +} + +// Convert l into a zigzag long. This allows negative numbers to be +// represented compactly as a varint. +func (p *TCompactProtocol) int32ToZigzag(n int32) int32 { + return (n << 1) ^ (n >> 31) +} + +func (p *TCompactProtocol) fixedUint64ToBytes(n uint64, buf []byte) { + binary.LittleEndian.PutUint64(buf, n) +} + +func (p *TCompactProtocol) fixedInt64ToBytes(n int64, buf []byte) { + binary.LittleEndian.PutUint64(buf, uint64(n)) +} + +// Writes a byte without any possibility of all that field header nonsense. +// Used internally by other writing methods that know they need to write a byte. +func (p *TCompactProtocol) writeByteDirect(b byte) error { + return p.trans.WriteByte(b) +} + +// Writes a byte without any possibility of all that field header nonsense. +func (p *TCompactProtocol) writeIntAsByteDirect(n int) (int, error) { + return 1, p.writeByteDirect(byte(n)) +} + +// +// Internal reading methods +// + +// Read an i32 from the wire as a varint. The MSB of each byte is set +// if there is another byte to follow. This can read up to 5 bytes. +func (p *TCompactProtocol) readVarint32() (int32, error) { + // if the wire contains the right stuff, this will just truncate the i64 we + // read and get us the right sign. + v, err := p.readVarint64() + return int32(v), err +} + +// Read an i64 from the wire as a proper varint. The MSB of each byte is set +// if there is another byte to follow. This can read up to 10 bytes. +func (p *TCompactProtocol) readVarint64() (int64, error) { + shift := uint(0) + result := int64(0) + for { + b, err := p.readByteDirect() + if err != nil { + return 0, err + } + result |= int64(b&0x7f) << shift + if (b & 0x80) != 0x80 { + break + } + shift += 7 + } + return result, nil +} + +// Read a byte, unlike ReadByte that reads Thrift-byte that is i8. +func (p *TCompactProtocol) readByteDirect() (byte, error) { + return p.trans.ReadByte() +} + +// +// encoding helpers +// + +// Convert from zigzag int to int. +func (p *TCompactProtocol) zigzagToInt32(n int32) int32 { + u := uint32(n) + return int32(u>>1) ^ -(n & 1) +} + +// Convert from zigzag long to long. +func (p *TCompactProtocol) zigzagToInt64(n int64) int64 { + u := uint64(n) + return int64(u>>1) ^ -(n & 1) +} + +// Note that it's important that the mask bytes are long literals, +// otherwise they'll default to ints, and when you shift an int left 56 bits, +// you just get a messed up int. +func (p *TCompactProtocol) bytesToInt64(b []byte) int64 { + return int64(binary.LittleEndian.Uint64(b)) +} + +// Note that it's important that the mask bytes are long literals, +// otherwise they'll default to ints, and when you shift an int left 56 bits, +// you just get a messed up int. +func (p *TCompactProtocol) bytesToUint64(b []byte) uint64 { + return binary.LittleEndian.Uint64(b) +} + +// +// type testing and converting +// + +func (p *TCompactProtocol) isBoolType(b byte) bool { + return (b&0x0f) == COMPACT_BOOLEAN_TRUE || (b&0x0f) == COMPACT_BOOLEAN_FALSE +} + +// Given a tCompactType constant, convert it to its corresponding +// TType value. +func (p *TCompactProtocol) getTType(t tCompactType) (TType, error) { + switch byte(t) & 0x0f { + case STOP: + return STOP, nil + case COMPACT_BOOLEAN_FALSE, COMPACT_BOOLEAN_TRUE: + return BOOL, nil + case COMPACT_BYTE: + return BYTE, nil + case COMPACT_I16: + return I16, nil + case COMPACT_I32: + return I32, nil + case COMPACT_I64: + return I64, nil + case COMPACT_DOUBLE: + return DOUBLE, nil + case COMPACT_BINARY: + return STRING, nil + case COMPACT_LIST: + return LIST, nil + case COMPACT_SET: + return SET, nil + case COMPACT_MAP: + return MAP, nil + case COMPACT_STRUCT: + return STRUCT, nil + } + return STOP, TException(fmt.Errorf("don't know what type: %d", t&0x0f)) +} + +// Given a TType value, find the appropriate TCompactProtocol.Types constant. +func (p *TCompactProtocol) getCompactType(t TType) tCompactType { + return ttypeToCompactType[t] +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/exception.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/exception.go new file mode 100644 index 00000000..ea8d6f66 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/exception.go @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "errors" +) + +// Generic Thrift exception +type TException interface { + error +} + +// Prepends additional information to an error without losing the Thrift exception interface +func PrependError(prepend string, err error) error { + if t, ok := err.(TTransportException); ok { + return NewTTransportException(t.TypeId(), prepend+t.Error()) + } + if t, ok := err.(TProtocolException); ok { + return NewTProtocolExceptionWithType(t.TypeId(), errors.New(prepend+err.Error())) + } + if t, ok := err.(TApplicationException); ok { + return NewTApplicationException(t.TypeId(), prepend+t.Error()) + } + + return errors.New(prepend + err.Error()) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/memory_buffer.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/memory_buffer.go new file mode 100644 index 00000000..b62fd56f --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/memory_buffer.go @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "bytes" +) + +// Memory buffer-based implementation of the TTransport interface. +type TMemoryBuffer struct { + *bytes.Buffer + size int +} + +type TMemoryBufferTransportFactory struct { + size int +} + +func (p *TMemoryBufferTransportFactory) GetTransport(trans TTransport) TTransport { + if trans != nil { + t, ok := trans.(*TMemoryBuffer) + if ok && t.size > 0 { + return NewTMemoryBufferLen(t.size) + } + } + return NewTMemoryBufferLen(p.size) +} + +func NewTMemoryBufferTransportFactory(size int) *TMemoryBufferTransportFactory { + return &TMemoryBufferTransportFactory{size: size} +} + +func NewTMemoryBuffer() *TMemoryBuffer { + return &TMemoryBuffer{Buffer: &bytes.Buffer{}, size: 0} +} + +func NewTMemoryBufferLen(size int) *TMemoryBuffer { + buf := make([]byte, 0, size) + return &TMemoryBuffer{Buffer: bytes.NewBuffer(buf), size: size} +} + +func (p *TMemoryBuffer) IsOpen() bool { + return true +} + +func (p *TMemoryBuffer) Open() error { + return nil +} + +func (p *TMemoryBuffer) Close() error { + p.Buffer.Reset() + return nil +} + +// Flushing a memory buffer is a no-op +func (p *TMemoryBuffer) Flush() error { + return nil +} + +func (p *TMemoryBuffer) RemainingBytes() (num_bytes uint64) { + return uint64(p.Buffer.Len()) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/messagetype.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/messagetype.go new file mode 100644 index 00000000..25ab2e98 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/messagetype.go @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +// Message type constants in the Thrift protocol. +type TMessageType int32 + +const ( + INVALID_TMESSAGE_TYPE TMessageType = 0 + CALL TMessageType = 1 + REPLY TMessageType = 2 + EXCEPTION TMessageType = 3 + ONEWAY TMessageType = 4 +) diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/numeric.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/numeric.go new file mode 100644 index 00000000..aa8daa9b --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/numeric.go @@ -0,0 +1,164 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "math" + "strconv" +) + +type Numeric interface { + Int64() int64 + Int32() int32 + Int16() int16 + Byte() byte + Int() int + Float64() float64 + Float32() float32 + String() string + isNull() bool +} + +type numeric struct { + iValue int64 + dValue float64 + sValue string + isNil bool +} + +var ( + INFINITY Numeric + NEGATIVE_INFINITY Numeric + NAN Numeric + ZERO Numeric + NUMERIC_NULL Numeric +) + +func NewNumericFromDouble(dValue float64) Numeric { + if math.IsInf(dValue, 1) { + return INFINITY + } + if math.IsInf(dValue, -1) { + return NEGATIVE_INFINITY + } + if math.IsNaN(dValue) { + return NAN + } + iValue := int64(dValue) + sValue := strconv.FormatFloat(dValue, 'g', 10, 64) + isNil := false + return &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNil} +} + +func NewNumericFromI64(iValue int64) Numeric { + dValue := float64(iValue) + sValue := string(iValue) + isNil := false + return &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNil} +} + +func NewNumericFromI32(iValue int32) Numeric { + dValue := float64(iValue) + sValue := string(iValue) + isNil := false + return &numeric{iValue: int64(iValue), dValue: dValue, sValue: sValue, isNil: isNil} +} + +func NewNumericFromString(sValue string) Numeric { + if sValue == INFINITY.String() { + return INFINITY + } + if sValue == NEGATIVE_INFINITY.String() { + return NEGATIVE_INFINITY + } + if sValue == NAN.String() { + return NAN + } + iValue, _ := strconv.ParseInt(sValue, 10, 64) + dValue, _ := strconv.ParseFloat(sValue, 64) + isNil := len(sValue) == 0 + return &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNil} +} + +func NewNumericFromJSONString(sValue string, isNull bool) Numeric { + if isNull { + return NewNullNumeric() + } + if sValue == JSON_INFINITY { + return INFINITY + } + if sValue == JSON_NEGATIVE_INFINITY { + return NEGATIVE_INFINITY + } + if sValue == JSON_NAN { + return NAN + } + iValue, _ := strconv.ParseInt(sValue, 10, 64) + dValue, _ := strconv.ParseFloat(sValue, 64) + return &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNull} +} + +func NewNullNumeric() Numeric { + return &numeric{iValue: 0, dValue: 0.0, sValue: "", isNil: true} +} + +func (p *numeric) Int64() int64 { + return p.iValue +} + +func (p *numeric) Int32() int32 { + return int32(p.iValue) +} + +func (p *numeric) Int16() int16 { + return int16(p.iValue) +} + +func (p *numeric) Byte() byte { + return byte(p.iValue) +} + +func (p *numeric) Int() int { + return int(p.iValue) +} + +func (p *numeric) Float64() float64 { + return p.dValue +} + +func (p *numeric) Float32() float32 { + return float32(p.dValue) +} + +func (p *numeric) String() string { + return p.sValue +} + +func (p *numeric) isNull() bool { + return p.isNil +} + +func init() { + INFINITY = &numeric{iValue: 0, dValue: math.Inf(1), sValue: "Infinity", isNil: false} + NEGATIVE_INFINITY = &numeric{iValue: 0, dValue: math.Inf(-1), sValue: "-Infinity", isNil: false} + NAN = &numeric{iValue: 0, dValue: math.NaN(), sValue: "NaN", isNil: false} + ZERO = &numeric{iValue: 0, dValue: 0, sValue: "0", isNil: false} + NUMERIC_NULL = &numeric{iValue: 0, dValue: 0, sValue: "0", isNil: true} +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/processor.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/processor.go new file mode 100644 index 00000000..ca0d3faf --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/processor.go @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +// A processor is a generic object which operates upon an input stream and +// writes to some output stream. +type TProcessor interface { + Process(in, out TProtocol) (bool, TException) +} + +type TProcessorFunction interface { + Process(seqId int32, in, out TProtocol) (bool, TException) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/protocol.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/protocol.go new file mode 100644 index 00000000..45fa202e --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/protocol.go @@ -0,0 +1,175 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "errors" +) + +const ( + VERSION_MASK = 0xffff0000 + VERSION_1 = 0x80010000 +) + +type TProtocol interface { + WriteMessageBegin(name string, typeId TMessageType, seqid int32) error + WriteMessageEnd() error + WriteStructBegin(name string) error + WriteStructEnd() error + WriteFieldBegin(name string, typeId TType, id int16) error + WriteFieldEnd() error + WriteFieldStop() error + WriteMapBegin(keyType TType, valueType TType, size int) error + WriteMapEnd() error + WriteListBegin(elemType TType, size int) error + WriteListEnd() error + WriteSetBegin(elemType TType, size int) error + WriteSetEnd() error + WriteBool(value bool) error + WriteByte(value int8) error + WriteI16(value int16) error + WriteI32(value int32) error + WriteI64(value int64) error + WriteDouble(value float64) error + WriteString(value string) error + WriteBinary(value []byte) error + + ReadMessageBegin() (name string, typeId TMessageType, seqid int32, err error) + ReadMessageEnd() error + ReadStructBegin() (name string, err error) + ReadStructEnd() error + ReadFieldBegin() (name string, typeId TType, id int16, err error) + ReadFieldEnd() error + ReadMapBegin() (keyType TType, valueType TType, size int, err error) + ReadMapEnd() error + ReadListBegin() (elemType TType, size int, err error) + ReadListEnd() error + ReadSetBegin() (elemType TType, size int, err error) + ReadSetEnd() error + ReadBool() (value bool, err error) + ReadByte() (value int8, err error) + ReadI16() (value int16, err error) + ReadI32() (value int32, err error) + ReadI64() (value int64, err error) + ReadDouble() (value float64, err error) + ReadString() (value string, err error) + ReadBinary() (value []byte, err error) + + Skip(fieldType TType) (err error) + Flush() (err error) + + Transport() TTransport +} + +// The maximum recursive depth the skip() function will traverse +const DEFAULT_RECURSION_DEPTH = 64 + +// Skips over the next data element from the provided input TProtocol object. +func SkipDefaultDepth(prot TProtocol, typeId TType) (err error) { + return Skip(prot, typeId, DEFAULT_RECURSION_DEPTH) +} + +// Skips over the next data element from the provided input TProtocol object. +func Skip(self TProtocol, fieldType TType, maxDepth int) (err error) { + + if maxDepth <= 0 { + return NewTProtocolExceptionWithType( DEPTH_LIMIT, errors.New("Depth limit exceeded")) + } + + switch fieldType { + case STOP: + return + case BOOL: + _, err = self.ReadBool() + return + case BYTE: + _, err = self.ReadByte() + return + case I16: + _, err = self.ReadI16() + return + case I32: + _, err = self.ReadI32() + return + case I64: + _, err = self.ReadI64() + return + case DOUBLE: + _, err = self.ReadDouble() + return + case STRING: + _, err = self.ReadString() + return + case STRUCT: + if _, err = self.ReadStructBegin(); err != nil { + return err + } + for { + _, typeId, _, _ := self.ReadFieldBegin() + if typeId == STOP { + break + } + err := Skip(self, typeId, maxDepth-1) + if err != nil { + return err + } + self.ReadFieldEnd() + } + return self.ReadStructEnd() + case MAP: + keyType, valueType, size, err := self.ReadMapBegin() + if err != nil { + return err + } + for i := 0; i < size; i++ { + err := Skip(self, keyType, maxDepth-1) + if err != nil { + return err + } + self.Skip(valueType) + } + return self.ReadMapEnd() + case SET: + elemType, size, err := self.ReadSetBegin() + if err != nil { + return err + } + for i := 0; i < size; i++ { + err := Skip(self, elemType, maxDepth-1) + if err != nil { + return err + } + } + return self.ReadSetEnd() + case LIST: + elemType, size, err := self.ReadListBegin() + if err != nil { + return err + } + for i := 0; i < size; i++ { + err := Skip(self, elemType, maxDepth-1) + if err != nil { + return err + } + } + return self.ReadListEnd() + } + return nil +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/protocol_exception.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/protocol_exception.go new file mode 100644 index 00000000..6e357ee8 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/protocol_exception.go @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "encoding/base64" +) + +// Thrift Protocol exception +type TProtocolException interface { + TException + TypeId() int +} + +const ( + UNKNOWN_PROTOCOL_EXCEPTION = 0 + INVALID_DATA = 1 + NEGATIVE_SIZE = 2 + SIZE_LIMIT = 3 + BAD_VERSION = 4 + NOT_IMPLEMENTED = 5 + DEPTH_LIMIT = 6 +) + +type tProtocolException struct { + typeId int + message string +} + +func (p *tProtocolException) TypeId() int { + return p.typeId +} + +func (p *tProtocolException) String() string { + return p.message +} + +func (p *tProtocolException) Error() string { + return p.message +} + +func NewTProtocolException(err error) TProtocolException { + if err == nil { + return nil + } + if e,ok := err.(TProtocolException); ok { + return e + } + if _, ok := err.(base64.CorruptInputError); ok { + return &tProtocolException{INVALID_DATA, err.Error()} + } + return &tProtocolException{UNKNOWN_PROTOCOL_EXCEPTION, err.Error()} +} + +func NewTProtocolExceptionWithType(errType int, err error) TProtocolException { + if err == nil { + return nil + } + return &tProtocolException{errType, err.Error()} +} + diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/protocol_factory.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/protocol_factory.go new file mode 100644 index 00000000..c40f796d --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/protocol_factory.go @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +// Factory interface for constructing protocol instances. +type TProtocolFactory interface { + GetProtocol(trans TTransport) TProtocol +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/rich_transport.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/rich_transport.go new file mode 100644 index 00000000..8e296a99 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/rich_transport.go @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import "io" + +type RichTransport struct { + TTransport +} + +// Wraps Transport to provide TRichTransport interface +func NewTRichTransport(trans TTransport) *RichTransport { + return &RichTransport{trans} +} + +func (r *RichTransport) ReadByte() (c byte, err error) { + return readByte(r.TTransport) +} + +func (r *RichTransport) WriteByte(c byte) error { + return writeByte(r.TTransport, c) +} + +func (r *RichTransport) WriteString(s string) (n int, err error) { + return r.Write([]byte(s)) +} + +func (r *RichTransport) RemainingBytes() (num_bytes uint64) { + return r.TTransport.RemainingBytes() +} + +func readByte(r io.Reader) (c byte, err error) { + v := [1]byte{0} + n, err := r.Read(v[0:1]) + if n > 0 && (err == nil || err == io.EOF) { + return v[0], nil + } + if n > 0 && err != nil { + return v[0], err + } + if err != nil { + return 0, err + } + return v[0], nil +} + +func writeByte(w io.Writer, c byte) error { + v := [1]byte{c} + _, err := w.Write(v[0:1]) + return err +} + diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/serializer.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/serializer.go new file mode 100644 index 00000000..77122299 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/serializer.go @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +type TSerializer struct { + Transport *TMemoryBuffer + Protocol TProtocol +} + +type TStruct interface { + Write(p TProtocol) error + Read(p TProtocol) error +} + +func NewTSerializer() *TSerializer { + transport := NewTMemoryBufferLen(1024) + protocol := NewTBinaryProtocolFactoryDefault().GetProtocol(transport) + + return &TSerializer{ + transport, + protocol} +} + +func (t *TSerializer) WriteString(msg TStruct) (s string, err error) { + t.Transport.Reset() + + if err = msg.Write(t.Protocol); err != nil { + return + } + + if err = t.Protocol.Flush(); err != nil { + return + } + if err = t.Transport.Flush(); err != nil { + return + } + + return t.Transport.String(), nil +} + +func (t *TSerializer) Write(msg TStruct) (b []byte, err error) { + t.Transport.Reset() + + if err = msg.Write(t.Protocol); err != nil { + return + } + + if err = t.Protocol.Flush(); err != nil { + return + } + + if err = t.Transport.Flush(); err != nil { + return + } + + b = append(b, t.Transport.Bytes()...) + return +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/simple_json_protocol.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/simple_json_protocol.go new file mode 100644 index 00000000..412a482d --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/simple_json_protocol.go @@ -0,0 +1,1337 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "bufio" + "bytes" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "math" + "strconv" +) + +type _ParseContext int + +const ( + _CONTEXT_IN_TOPLEVEL _ParseContext = 1 + _CONTEXT_IN_LIST_FIRST _ParseContext = 2 + _CONTEXT_IN_LIST _ParseContext = 3 + _CONTEXT_IN_OBJECT_FIRST _ParseContext = 4 + _CONTEXT_IN_OBJECT_NEXT_KEY _ParseContext = 5 + _CONTEXT_IN_OBJECT_NEXT_VALUE _ParseContext = 6 +) + +func (p _ParseContext) String() string { + switch p { + case _CONTEXT_IN_TOPLEVEL: + return "TOPLEVEL" + case _CONTEXT_IN_LIST_FIRST: + return "LIST-FIRST" + case _CONTEXT_IN_LIST: + return "LIST" + case _CONTEXT_IN_OBJECT_FIRST: + return "OBJECT-FIRST" + case _CONTEXT_IN_OBJECT_NEXT_KEY: + return "OBJECT-NEXT-KEY" + case _CONTEXT_IN_OBJECT_NEXT_VALUE: + return "OBJECT-NEXT-VALUE" + } + return "UNKNOWN-PARSE-CONTEXT" +} + +// JSON protocol implementation for thrift. +// +// This protocol produces/consumes a simple output format +// suitable for parsing by scripting languages. It should not be +// confused with the full-featured TJSONProtocol. +// +type TSimpleJSONProtocol struct { + trans TTransport + + parseContextStack []int + dumpContext []int + + writer *bufio.Writer + reader *bufio.Reader +} + +// Constructor +func NewTSimpleJSONProtocol(t TTransport) *TSimpleJSONProtocol { + v := &TSimpleJSONProtocol{trans: t, + writer: bufio.NewWriter(t), + reader: bufio.NewReader(t), + } + v.parseContextStack = append(v.parseContextStack, int(_CONTEXT_IN_TOPLEVEL)) + v.dumpContext = append(v.dumpContext, int(_CONTEXT_IN_TOPLEVEL)) + return v +} + +// Factory +type TSimpleJSONProtocolFactory struct{} + +func (p *TSimpleJSONProtocolFactory) GetProtocol(trans TTransport) TProtocol { + return NewTSimpleJSONProtocol(trans) +} + +func NewTSimpleJSONProtocolFactory() *TSimpleJSONProtocolFactory { + return &TSimpleJSONProtocolFactory{} +} + +var ( + JSON_COMMA []byte + JSON_COLON []byte + JSON_LBRACE []byte + JSON_RBRACE []byte + JSON_LBRACKET []byte + JSON_RBRACKET []byte + JSON_QUOTE byte + JSON_QUOTE_BYTES []byte + JSON_NULL []byte + JSON_TRUE []byte + JSON_FALSE []byte + JSON_INFINITY string + JSON_NEGATIVE_INFINITY string + JSON_NAN string + JSON_INFINITY_BYTES []byte + JSON_NEGATIVE_INFINITY_BYTES []byte + JSON_NAN_BYTES []byte + json_nonbase_map_elem_bytes []byte +) + +func init() { + JSON_COMMA = []byte{','} + JSON_COLON = []byte{':'} + JSON_LBRACE = []byte{'{'} + JSON_RBRACE = []byte{'}'} + JSON_LBRACKET = []byte{'['} + JSON_RBRACKET = []byte{']'} + JSON_QUOTE = '"' + JSON_QUOTE_BYTES = []byte{'"'} + JSON_NULL = []byte{'n', 'u', 'l', 'l'} + JSON_TRUE = []byte{'t', 'r', 'u', 'e'} + JSON_FALSE = []byte{'f', 'a', 'l', 's', 'e'} + JSON_INFINITY = "Infinity" + JSON_NEGATIVE_INFINITY = "-Infinity" + JSON_NAN = "NaN" + JSON_INFINITY_BYTES = []byte{'I', 'n', 'f', 'i', 'n', 'i', 't', 'y'} + JSON_NEGATIVE_INFINITY_BYTES = []byte{'-', 'I', 'n', 'f', 'i', 'n', 'i', 't', 'y'} + JSON_NAN_BYTES = []byte{'N', 'a', 'N'} + json_nonbase_map_elem_bytes = []byte{']', ',', '['} +} + +func jsonQuote(s string) string { + b, _ := json.Marshal(s) + s1 := string(b) + return s1 +} + +func jsonUnquote(s string) (string, bool) { + s1 := new(string) + err := json.Unmarshal([]byte(s), s1) + return *s1, err == nil +} + +func mismatch(expected, actual string) error { + return fmt.Errorf("Expected '%s' but found '%s' while parsing JSON.", expected, actual) +} + +func (p *TSimpleJSONProtocol) WriteMessageBegin(name string, typeId TMessageType, seqId int32) error { + p.resetContextStack() // THRIFT-3735 + if e := p.OutputListBegin(); e != nil { + return e + } + if e := p.WriteString(name); e != nil { + return e + } + if e := p.WriteByte(int8(typeId)); e != nil { + return e + } + if e := p.WriteI32(seqId); e != nil { + return e + } + return nil +} + +func (p *TSimpleJSONProtocol) WriteMessageEnd() error { + return p.OutputListEnd() +} + +func (p *TSimpleJSONProtocol) WriteStructBegin(name string) error { + if e := p.OutputObjectBegin(); e != nil { + return e + } + return nil +} + +func (p *TSimpleJSONProtocol) WriteStructEnd() error { + return p.OutputObjectEnd() +} + +func (p *TSimpleJSONProtocol) WriteFieldBegin(name string, typeId TType, id int16) error { + if e := p.WriteString(name); e != nil { + return e + } + return nil +} + +func (p *TSimpleJSONProtocol) WriteFieldEnd() error { + //return p.OutputListEnd() + return nil +} + +func (p *TSimpleJSONProtocol) WriteFieldStop() error { return nil } + +func (p *TSimpleJSONProtocol) WriteMapBegin(keyType TType, valueType TType, size int) error { + if e := p.OutputListBegin(); e != nil { + return e + } + if e := p.WriteByte(int8(keyType)); e != nil { + return e + } + if e := p.WriteByte(int8(valueType)); e != nil { + return e + } + return p.WriteI32(int32(size)) +} + +func (p *TSimpleJSONProtocol) WriteMapEnd() error { + return p.OutputListEnd() +} + +func (p *TSimpleJSONProtocol) WriteListBegin(elemType TType, size int) error { + return p.OutputElemListBegin(elemType, size) +} + +func (p *TSimpleJSONProtocol) WriteListEnd() error { + return p.OutputListEnd() +} + +func (p *TSimpleJSONProtocol) WriteSetBegin(elemType TType, size int) error { + return p.OutputElemListBegin(elemType, size) +} + +func (p *TSimpleJSONProtocol) WriteSetEnd() error { + return p.OutputListEnd() +} + +func (p *TSimpleJSONProtocol) WriteBool(b bool) error { + return p.OutputBool(b) +} + +func (p *TSimpleJSONProtocol) WriteByte(b int8) error { + return p.WriteI32(int32(b)) +} + +func (p *TSimpleJSONProtocol) WriteI16(v int16) error { + return p.WriteI32(int32(v)) +} + +func (p *TSimpleJSONProtocol) WriteI32(v int32) error { + return p.OutputI64(int64(v)) +} + +func (p *TSimpleJSONProtocol) WriteI64(v int64) error { + return p.OutputI64(int64(v)) +} + +func (p *TSimpleJSONProtocol) WriteDouble(v float64) error { + return p.OutputF64(v) +} + +func (p *TSimpleJSONProtocol) WriteString(v string) error { + return p.OutputString(v) +} + +func (p *TSimpleJSONProtocol) WriteBinary(v []byte) error { + // JSON library only takes in a string, + // not an arbitrary byte array, to ensure bytes are transmitted + // efficiently we must convert this into a valid JSON string + // therefore we use base64 encoding to avoid excessive escaping/quoting + if e := p.OutputPreValue(); e != nil { + return e + } + if _, e := p.write(JSON_QUOTE_BYTES); e != nil { + return NewTProtocolException(e) + } + writer := base64.NewEncoder(base64.StdEncoding, p.writer) + if _, e := writer.Write(v); e != nil { + p.writer.Reset(p.trans) // THRIFT-3735 + return NewTProtocolException(e) + } + if e := writer.Close(); e != nil { + return NewTProtocolException(e) + } + if _, e := p.write(JSON_QUOTE_BYTES); e != nil { + return NewTProtocolException(e) + } + return p.OutputPostValue() +} + +// Reading methods. +func (p *TSimpleJSONProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqId int32, err error) { + p.resetContextStack() // THRIFT-3735 + if isNull, err := p.ParseListBegin(); isNull || err != nil { + return name, typeId, seqId, err + } + if name, err = p.ReadString(); err != nil { + return name, typeId, seqId, err + } + bTypeId, err := p.ReadByte() + typeId = TMessageType(bTypeId) + if err != nil { + return name, typeId, seqId, err + } + if seqId, err = p.ReadI32(); err != nil { + return name, typeId, seqId, err + } + return name, typeId, seqId, nil +} + +func (p *TSimpleJSONProtocol) ReadMessageEnd() error { + return p.ParseListEnd() +} + +func (p *TSimpleJSONProtocol) ReadStructBegin() (name string, err error) { + _, err = p.ParseObjectStart() + return "", err +} + +func (p *TSimpleJSONProtocol) ReadStructEnd() error { + return p.ParseObjectEnd() +} + +func (p *TSimpleJSONProtocol) ReadFieldBegin() (string, TType, int16, error) { + if err := p.ParsePreValue(); err != nil { + return "", STOP, 0, err + } + b, _ := p.reader.Peek(1) + if len(b) > 0 { + switch b[0] { + case JSON_RBRACE[0]: + return "", STOP, 0, nil + case JSON_QUOTE: + p.reader.ReadByte() + name, err := p.ParseStringBody() + // simplejson is not meant to be read back into thrift + // - see http://wiki.apache.org/thrift/ThriftUsageJava + // - use JSON instead + if err != nil { + return name, STOP, 0, err + } + return name, STOP, -1, p.ParsePostValue() + /* + if err = p.ParsePostValue(); err != nil { + return name, STOP, 0, err + } + if isNull, err := p.ParseListBegin(); isNull || err != nil { + return name, STOP, 0, err + } + bType, err := p.ReadByte() + thetype := TType(bType) + if err != nil { + return name, thetype, 0, err + } + id, err := p.ReadI16() + return name, thetype, id, err + */ + } + e := fmt.Errorf("Expected \"}\" or '\"', but found: '%s'", string(b)) + return "", STOP, 0, NewTProtocolExceptionWithType(INVALID_DATA, e) + } + return "", STOP, 0, NewTProtocolException(io.EOF) +} + +func (p *TSimpleJSONProtocol) ReadFieldEnd() error { + return nil + //return p.ParseListEnd() +} + +func (p *TSimpleJSONProtocol) ReadMapBegin() (keyType TType, valueType TType, size int, e error) { + if isNull, e := p.ParseListBegin(); isNull || e != nil { + return VOID, VOID, 0, e + } + + // read keyType + bKeyType, e := p.ReadByte() + keyType = TType(bKeyType) + if e != nil { + return keyType, valueType, size, e + } + + // read valueType + bValueType, e := p.ReadByte() + valueType = TType(bValueType) + if e != nil { + return keyType, valueType, size, e + } + + // read size + iSize, err := p.ReadI64() + size = int(iSize) + return keyType, valueType, size, err +} + +func (p *TSimpleJSONProtocol) ReadMapEnd() error { + return p.ParseListEnd() +} + +func (p *TSimpleJSONProtocol) ReadListBegin() (elemType TType, size int, e error) { + return p.ParseElemListBegin() +} + +func (p *TSimpleJSONProtocol) ReadListEnd() error { + return p.ParseListEnd() +} + +func (p *TSimpleJSONProtocol) ReadSetBegin() (elemType TType, size int, e error) { + return p.ParseElemListBegin() +} + +func (p *TSimpleJSONProtocol) ReadSetEnd() error { + return p.ParseListEnd() +} + +func (p *TSimpleJSONProtocol) ReadBool() (bool, error) { + var value bool + + if err := p.ParsePreValue(); err != nil { + return value, err + } + f, _ := p.reader.Peek(1) + if len(f) > 0 { + switch f[0] { + case JSON_TRUE[0]: + b := make([]byte, len(JSON_TRUE)) + _, err := p.reader.Read(b) + if err != nil { + return false, NewTProtocolException(err) + } + if string(b) == string(JSON_TRUE) { + value = true + } else { + e := fmt.Errorf("Expected \"true\" but found: %s", string(b)) + return value, NewTProtocolExceptionWithType(INVALID_DATA, e) + } + break + case JSON_FALSE[0]: + b := make([]byte, len(JSON_FALSE)) + _, err := p.reader.Read(b) + if err != nil { + return false, NewTProtocolException(err) + } + if string(b) == string(JSON_FALSE) { + value = false + } else { + e := fmt.Errorf("Expected \"false\" but found: %s", string(b)) + return value, NewTProtocolExceptionWithType(INVALID_DATA, e) + } + break + case JSON_NULL[0]: + b := make([]byte, len(JSON_NULL)) + _, err := p.reader.Read(b) + if err != nil { + return false, NewTProtocolException(err) + } + if string(b) == string(JSON_NULL) { + value = false + } else { + e := fmt.Errorf("Expected \"null\" but found: %s", string(b)) + return value, NewTProtocolExceptionWithType(INVALID_DATA, e) + } + default: + e := fmt.Errorf("Expected \"true\", \"false\", or \"null\" but found: %s", string(f)) + return value, NewTProtocolExceptionWithType(INVALID_DATA, e) + } + } + return value, p.ParsePostValue() +} + +func (p *TSimpleJSONProtocol) ReadByte() (int8, error) { + v, err := p.ReadI64() + return int8(v), err +} + +func (p *TSimpleJSONProtocol) ReadI16() (int16, error) { + v, err := p.ReadI64() + return int16(v), err +} + +func (p *TSimpleJSONProtocol) ReadI32() (int32, error) { + v, err := p.ReadI64() + return int32(v), err +} + +func (p *TSimpleJSONProtocol) ReadI64() (int64, error) { + v, _, err := p.ParseI64() + return v, err +} + +func (p *TSimpleJSONProtocol) ReadDouble() (float64, error) { + v, _, err := p.ParseF64() + return v, err +} + +func (p *TSimpleJSONProtocol) ReadString() (string, error) { + var v string + if err := p.ParsePreValue(); err != nil { + return v, err + } + f, _ := p.reader.Peek(1) + if len(f) > 0 && f[0] == JSON_QUOTE { + p.reader.ReadByte() + value, err := p.ParseStringBody() + v = value + if err != nil { + return v, err + } + } else if len(f) > 0 && f[0] == JSON_NULL[0] { + b := make([]byte, len(JSON_NULL)) + _, err := p.reader.Read(b) + if err != nil { + return v, NewTProtocolException(err) + } + if string(b) != string(JSON_NULL) { + e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(b)) + return v, NewTProtocolExceptionWithType(INVALID_DATA, e) + } + } else { + e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(f)) + return v, NewTProtocolExceptionWithType(INVALID_DATA, e) + } + return v, p.ParsePostValue() +} + +func (p *TSimpleJSONProtocol) ReadBinary() ([]byte, error) { + var v []byte + if err := p.ParsePreValue(); err != nil { + return nil, err + } + f, _ := p.reader.Peek(1) + if len(f) > 0 && f[0] == JSON_QUOTE { + p.reader.ReadByte() + value, err := p.ParseBase64EncodedBody() + v = value + if err != nil { + return v, err + } + } else if len(f) > 0 && f[0] == JSON_NULL[0] { + b := make([]byte, len(JSON_NULL)) + _, err := p.reader.Read(b) + if err != nil { + return v, NewTProtocolException(err) + } + if string(b) != string(JSON_NULL) { + e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(b)) + return v, NewTProtocolExceptionWithType(INVALID_DATA, e) + } + } else { + e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(f)) + return v, NewTProtocolExceptionWithType(INVALID_DATA, e) + } + + return v, p.ParsePostValue() +} + +func (p *TSimpleJSONProtocol) Flush() (err error) { + return NewTProtocolException(p.writer.Flush()) +} + +func (p *TSimpleJSONProtocol) Skip(fieldType TType) (err error) { + return SkipDefaultDepth(p, fieldType) +} + +func (p *TSimpleJSONProtocol) Transport() TTransport { + return p.trans +} + +func (p *TSimpleJSONProtocol) OutputPreValue() error { + cxt := _ParseContext(p.dumpContext[len(p.dumpContext)-1]) + switch cxt { + case _CONTEXT_IN_LIST, _CONTEXT_IN_OBJECT_NEXT_KEY: + if _, e := p.write(JSON_COMMA); e != nil { + return NewTProtocolException(e) + } + break + case _CONTEXT_IN_OBJECT_NEXT_VALUE: + if _, e := p.write(JSON_COLON); e != nil { + return NewTProtocolException(e) + } + break + } + return nil +} + +func (p *TSimpleJSONProtocol) OutputPostValue() error { + cxt := _ParseContext(p.dumpContext[len(p.dumpContext)-1]) + switch cxt { + case _CONTEXT_IN_LIST_FIRST: + p.dumpContext = p.dumpContext[:len(p.dumpContext)-1] + p.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_LIST)) + break + case _CONTEXT_IN_OBJECT_FIRST: + p.dumpContext = p.dumpContext[:len(p.dumpContext)-1] + p.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_OBJECT_NEXT_VALUE)) + break + case _CONTEXT_IN_OBJECT_NEXT_KEY: + p.dumpContext = p.dumpContext[:len(p.dumpContext)-1] + p.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_OBJECT_NEXT_VALUE)) + break + case _CONTEXT_IN_OBJECT_NEXT_VALUE: + p.dumpContext = p.dumpContext[:len(p.dumpContext)-1] + p.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_OBJECT_NEXT_KEY)) + break + } + return nil +} + +func (p *TSimpleJSONProtocol) OutputBool(value bool) error { + if e := p.OutputPreValue(); e != nil { + return e + } + var v string + if value { + v = string(JSON_TRUE) + } else { + v = string(JSON_FALSE) + } + switch _ParseContext(p.dumpContext[len(p.dumpContext)-1]) { + case _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY: + v = jsonQuote(v) + default: + } + if e := p.OutputStringData(v); e != nil { + return e + } + return p.OutputPostValue() +} + +func (p *TSimpleJSONProtocol) OutputNull() error { + if e := p.OutputPreValue(); e != nil { + return e + } + if _, e := p.write(JSON_NULL); e != nil { + return NewTProtocolException(e) + } + return p.OutputPostValue() +} + +func (p *TSimpleJSONProtocol) OutputF64(value float64) error { + if e := p.OutputPreValue(); e != nil { + return e + } + var v string + if math.IsNaN(value) { + v = string(JSON_QUOTE) + JSON_NAN + string(JSON_QUOTE) + } else if math.IsInf(value, 1) { + v = string(JSON_QUOTE) + JSON_INFINITY + string(JSON_QUOTE) + } else if math.IsInf(value, -1) { + v = string(JSON_QUOTE) + JSON_NEGATIVE_INFINITY + string(JSON_QUOTE) + } else { + v = strconv.FormatFloat(value, 'g', -1, 64) + switch _ParseContext(p.dumpContext[len(p.dumpContext)-1]) { + case _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY: + v = string(JSON_QUOTE) + v + string(JSON_QUOTE) + default: + } + } + if e := p.OutputStringData(v); e != nil { + return e + } + return p.OutputPostValue() +} + +func (p *TSimpleJSONProtocol) OutputI64(value int64) error { + if e := p.OutputPreValue(); e != nil { + return e + } + v := strconv.FormatInt(value, 10) + switch _ParseContext(p.dumpContext[len(p.dumpContext)-1]) { + case _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY: + v = jsonQuote(v) + default: + } + if e := p.OutputStringData(v); e != nil { + return e + } + return p.OutputPostValue() +} + +func (p *TSimpleJSONProtocol) OutputString(s string) error { + if e := p.OutputPreValue(); e != nil { + return e + } + if e := p.OutputStringData(jsonQuote(s)); e != nil { + return e + } + return p.OutputPostValue() +} + +func (p *TSimpleJSONProtocol) OutputStringData(s string) error { + _, e := p.write([]byte(s)) + return NewTProtocolException(e) +} + +func (p *TSimpleJSONProtocol) OutputObjectBegin() error { + if e := p.OutputPreValue(); e != nil { + return e + } + if _, e := p.write(JSON_LBRACE); e != nil { + return NewTProtocolException(e) + } + p.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_OBJECT_FIRST)) + return nil +} + +func (p *TSimpleJSONProtocol) OutputObjectEnd() error { + if _, e := p.write(JSON_RBRACE); e != nil { + return NewTProtocolException(e) + } + p.dumpContext = p.dumpContext[:len(p.dumpContext)-1] + if e := p.OutputPostValue(); e != nil { + return e + } + return nil +} + +func (p *TSimpleJSONProtocol) OutputListBegin() error { + if e := p.OutputPreValue(); e != nil { + return e + } + if _, e := p.write(JSON_LBRACKET); e != nil { + return NewTProtocolException(e) + } + p.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_LIST_FIRST)) + return nil +} + +func (p *TSimpleJSONProtocol) OutputListEnd() error { + if _, e := p.write(JSON_RBRACKET); e != nil { + return NewTProtocolException(e) + } + p.dumpContext = p.dumpContext[:len(p.dumpContext)-1] + if e := p.OutputPostValue(); e != nil { + return e + } + return nil +} + +func (p *TSimpleJSONProtocol) OutputElemListBegin(elemType TType, size int) error { + if e := p.OutputListBegin(); e != nil { + return e + } + if e := p.WriteByte(int8(elemType)); e != nil { + return e + } + if e := p.WriteI64(int64(size)); e != nil { + return e + } + return nil +} + +func (p *TSimpleJSONProtocol) ParsePreValue() error { + if e := p.readNonSignificantWhitespace(); e != nil { + return NewTProtocolException(e) + } + cxt := _ParseContext(p.parseContextStack[len(p.parseContextStack)-1]) + b, _ := p.reader.Peek(1) + switch cxt { + case _CONTEXT_IN_LIST: + if len(b) > 0 { + switch b[0] { + case JSON_RBRACKET[0]: + return nil + case JSON_COMMA[0]: + p.reader.ReadByte() + if e := p.readNonSignificantWhitespace(); e != nil { + return NewTProtocolException(e) + } + return nil + default: + e := fmt.Errorf("Expected \"]\" or \",\" in list context, but found \"%s\"", string(b)) + return NewTProtocolExceptionWithType(INVALID_DATA, e) + } + } + break + case _CONTEXT_IN_OBJECT_NEXT_KEY: + if len(b) > 0 { + switch b[0] { + case JSON_RBRACE[0]: + return nil + case JSON_COMMA[0]: + p.reader.ReadByte() + if e := p.readNonSignificantWhitespace(); e != nil { + return NewTProtocolException(e) + } + return nil + default: + e := fmt.Errorf("Expected \"}\" or \",\" in object context, but found \"%s\"", string(b)) + return NewTProtocolExceptionWithType(INVALID_DATA, e) + } + } + break + case _CONTEXT_IN_OBJECT_NEXT_VALUE: + if len(b) > 0 { + switch b[0] { + case JSON_COLON[0]: + p.reader.ReadByte() + if e := p.readNonSignificantWhitespace(); e != nil { + return NewTProtocolException(e) + } + return nil + default: + e := fmt.Errorf("Expected \":\" in object context, but found \"%s\"", string(b)) + return NewTProtocolExceptionWithType(INVALID_DATA, e) + } + } + break + } + return nil +} + +func (p *TSimpleJSONProtocol) ParsePostValue() error { + if e := p.readNonSignificantWhitespace(); e != nil { + return NewTProtocolException(e) + } + cxt := _ParseContext(p.parseContextStack[len(p.parseContextStack)-1]) + switch cxt { + case _CONTEXT_IN_LIST_FIRST: + p.parseContextStack = p.parseContextStack[:len(p.parseContextStack)-1] + p.parseContextStack = append(p.parseContextStack, int(_CONTEXT_IN_LIST)) + break + case _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY: + p.parseContextStack = p.parseContextStack[:len(p.parseContextStack)-1] + p.parseContextStack = append(p.parseContextStack, int(_CONTEXT_IN_OBJECT_NEXT_VALUE)) + break + case _CONTEXT_IN_OBJECT_NEXT_VALUE: + p.parseContextStack = p.parseContextStack[:len(p.parseContextStack)-1] + p.parseContextStack = append(p.parseContextStack, int(_CONTEXT_IN_OBJECT_NEXT_KEY)) + break + } + return nil +} + +func (p *TSimpleJSONProtocol) readNonSignificantWhitespace() error { + for { + b, _ := p.reader.Peek(1) + if len(b) < 1 { + return nil + } + switch b[0] { + case ' ', '\r', '\n', '\t': + p.reader.ReadByte() + continue + default: + break + } + break + } + return nil +} + +func (p *TSimpleJSONProtocol) ParseStringBody() (string, error) { + line, err := p.reader.ReadString(JSON_QUOTE) + if err != nil { + return "", NewTProtocolException(err) + } + l := len(line) + // count number of escapes to see if we need to keep going + i := 1 + for ; i < l; i++ { + if line[l-i-1] != '\\' { + break + } + } + if i&0x01 == 1 { + v, ok := jsonUnquote(string(JSON_QUOTE) + line) + if !ok { + return "", NewTProtocolException(err) + } + return v, nil + } + s, err := p.ParseQuotedStringBody() + if err != nil { + return "", NewTProtocolException(err) + } + str := string(JSON_QUOTE) + line + s + v, ok := jsonUnquote(str) + if !ok { + e := fmt.Errorf("Unable to parse as JSON string %s", str) + return "", NewTProtocolExceptionWithType(INVALID_DATA, e) + } + return v, nil +} + +func (p *TSimpleJSONProtocol) ParseQuotedStringBody() (string, error) { + line, err := p.reader.ReadString(JSON_QUOTE) + if err != nil { + return "", NewTProtocolException(err) + } + l := len(line) + // count number of escapes to see if we need to keep going + i := 1 + for ; i < l; i++ { + if line[l-i-1] != '\\' { + break + } + } + if i&0x01 == 1 { + return line, nil + } + s, err := p.ParseQuotedStringBody() + if err != nil { + return "", NewTProtocolException(err) + } + v := line + s + return v, nil +} + +func (p *TSimpleJSONProtocol) ParseBase64EncodedBody() ([]byte, error) { + line, err := p.reader.ReadBytes(JSON_QUOTE) + if err != nil { + return line, NewTProtocolException(err) + } + line2 := line[0 : len(line)-1] + l := len(line2) + if (l % 4) != 0 { + pad := 4 - (l % 4) + fill := [...]byte{'=', '=', '='} + line2 = append(line2, fill[:pad]...) + l = len(line2) + } + output := make([]byte, base64.StdEncoding.DecodedLen(l)) + n, err := base64.StdEncoding.Decode(output, line2) + return output[0:n], NewTProtocolException(err) +} + +func (p *TSimpleJSONProtocol) ParseI64() (int64, bool, error) { + if err := p.ParsePreValue(); err != nil { + return 0, false, err + } + var value int64 + var isnull bool + if p.safePeekContains(JSON_NULL) { + p.reader.Read(make([]byte, len(JSON_NULL))) + isnull = true + } else { + num, err := p.readNumeric() + isnull = (num == nil) + if !isnull { + value = num.Int64() + } + if err != nil { + return value, isnull, err + } + } + return value, isnull, p.ParsePostValue() +} + +func (p *TSimpleJSONProtocol) ParseF64() (float64, bool, error) { + if err := p.ParsePreValue(); err != nil { + return 0, false, err + } + var value float64 + var isnull bool + if p.safePeekContains(JSON_NULL) { + p.reader.Read(make([]byte, len(JSON_NULL))) + isnull = true + } else { + num, err := p.readNumeric() + isnull = (num == nil) + if !isnull { + value = num.Float64() + } + if err != nil { + return value, isnull, err + } + } + return value, isnull, p.ParsePostValue() +} + +func (p *TSimpleJSONProtocol) ParseObjectStart() (bool, error) { + if err := p.ParsePreValue(); err != nil { + return false, err + } + var b []byte + b, err := p.reader.Peek(1) + if err != nil { + return false, err + } + if len(b) > 0 && b[0] == JSON_LBRACE[0] { + p.reader.ReadByte() + p.parseContextStack = append(p.parseContextStack, int(_CONTEXT_IN_OBJECT_FIRST)) + return false, nil + } else if p.safePeekContains(JSON_NULL) { + return true, nil + } + e := fmt.Errorf("Expected '{' or null, but found '%s'", string(b)) + return false, NewTProtocolExceptionWithType(INVALID_DATA, e) +} + +func (p *TSimpleJSONProtocol) ParseObjectEnd() error { + if isNull, err := p.readIfNull(); isNull || err != nil { + return err + } + cxt := _ParseContext(p.parseContextStack[len(p.parseContextStack)-1]) + if (cxt != _CONTEXT_IN_OBJECT_FIRST) && (cxt != _CONTEXT_IN_OBJECT_NEXT_KEY) { + e := fmt.Errorf("Expected to be in the Object Context, but not in Object Context (%d)", cxt) + return NewTProtocolExceptionWithType(INVALID_DATA, e) + } + line, err := p.reader.ReadString(JSON_RBRACE[0]) + if err != nil { + return NewTProtocolException(err) + } + for _, char := range line { + switch char { + default: + e := fmt.Errorf("Expecting end of object \"}\", but found: \"%s\"", line) + return NewTProtocolExceptionWithType(INVALID_DATA, e) + case ' ', '\n', '\r', '\t', '}': + break + } + } + p.parseContextStack = p.parseContextStack[:len(p.parseContextStack)-1] + return p.ParsePostValue() +} + +func (p *TSimpleJSONProtocol) ParseListBegin() (isNull bool, err error) { + if e := p.ParsePreValue(); e != nil { + return false, e + } + var b []byte + b, err = p.reader.Peek(1) + if err != nil { + return false, err + } + if len(b) >= 1 && b[0] == JSON_LBRACKET[0] { + p.parseContextStack = append(p.parseContextStack, int(_CONTEXT_IN_LIST_FIRST)) + p.reader.ReadByte() + isNull = false + } else if p.safePeekContains(JSON_NULL) { + isNull = true + } else { + err = fmt.Errorf("Expected \"null\" or \"[\", received %q", b) + } + return isNull, NewTProtocolExceptionWithType(INVALID_DATA, err) +} + +func (p *TSimpleJSONProtocol) ParseElemListBegin() (elemType TType, size int, e error) { + if isNull, e := p.ParseListBegin(); isNull || e != nil { + return VOID, 0, e + } + bElemType, err := p.ReadByte() + elemType = TType(bElemType) + if err != nil { + return elemType, size, err + } + nSize, err2 := p.ReadI64() + size = int(nSize) + return elemType, size, err2 +} + +func (p *TSimpleJSONProtocol) ParseListEnd() error { + if isNull, err := p.readIfNull(); isNull || err != nil { + return err + } + cxt := _ParseContext(p.parseContextStack[len(p.parseContextStack)-1]) + if cxt != _CONTEXT_IN_LIST { + e := fmt.Errorf("Expected to be in the List Context, but not in List Context (%d)", cxt) + return NewTProtocolExceptionWithType(INVALID_DATA, e) + } + line, err := p.reader.ReadString(JSON_RBRACKET[0]) + if err != nil { + return NewTProtocolException(err) + } + for _, char := range line { + switch char { + default: + e := fmt.Errorf("Expecting end of list \"]\", but found: \"%s\"", line) + return NewTProtocolExceptionWithType(INVALID_DATA, e) + case ' ', '\n', '\r', '\t', rune(JSON_RBRACKET[0]): + break + } + } + p.parseContextStack = p.parseContextStack[:len(p.parseContextStack)-1] + if _ParseContext(p.parseContextStack[len(p.parseContextStack)-1]) == _CONTEXT_IN_TOPLEVEL { + return nil + } + return p.ParsePostValue() +} + +func (p *TSimpleJSONProtocol) readSingleValue() (interface{}, TType, error) { + e := p.readNonSignificantWhitespace() + if e != nil { + return nil, VOID, NewTProtocolException(e) + } + b, e := p.reader.Peek(1) + if len(b) > 0 { + c := b[0] + switch c { + case JSON_NULL[0]: + buf := make([]byte, len(JSON_NULL)) + _, e := p.reader.Read(buf) + if e != nil { + return nil, VOID, NewTProtocolException(e) + } + if string(JSON_NULL) != string(buf) { + e = mismatch(string(JSON_NULL), string(buf)) + return nil, VOID, NewTProtocolExceptionWithType(INVALID_DATA, e) + } + return nil, VOID, nil + case JSON_QUOTE: + p.reader.ReadByte() + v, e := p.ParseStringBody() + if e != nil { + return v, UTF8, NewTProtocolException(e) + } + if v == JSON_INFINITY { + return INFINITY, DOUBLE, nil + } else if v == JSON_NEGATIVE_INFINITY { + return NEGATIVE_INFINITY, DOUBLE, nil + } else if v == JSON_NAN { + return NAN, DOUBLE, nil + } + return v, UTF8, nil + case JSON_TRUE[0]: + buf := make([]byte, len(JSON_TRUE)) + _, e := p.reader.Read(buf) + if e != nil { + return true, BOOL, NewTProtocolException(e) + } + if string(JSON_TRUE) != string(buf) { + e := mismatch(string(JSON_TRUE), string(buf)) + return true, BOOL, NewTProtocolExceptionWithType(INVALID_DATA, e) + } + return true, BOOL, nil + case JSON_FALSE[0]: + buf := make([]byte, len(JSON_FALSE)) + _, e := p.reader.Read(buf) + if e != nil { + return false, BOOL, NewTProtocolException(e) + } + if string(JSON_FALSE) != string(buf) { + e := mismatch(string(JSON_FALSE), string(buf)) + return false, BOOL, NewTProtocolExceptionWithType(INVALID_DATA, e) + } + return false, BOOL, nil + case JSON_LBRACKET[0]: + _, e := p.reader.ReadByte() + return make([]interface{}, 0), LIST, NewTProtocolException(e) + case JSON_LBRACE[0]: + _, e := p.reader.ReadByte() + return make(map[string]interface{}), STRUCT, NewTProtocolException(e) + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'e', 'E', '.', '+', '-', JSON_INFINITY[0], JSON_NAN[0]: + // assume numeric + v, e := p.readNumeric() + return v, DOUBLE, e + default: + e := fmt.Errorf("Expected element in list but found '%s' while parsing JSON.", string(c)) + return nil, VOID, NewTProtocolExceptionWithType(INVALID_DATA, e) + } + } + e = fmt.Errorf("Cannot read a single element while parsing JSON.") + return nil, VOID, NewTProtocolExceptionWithType(INVALID_DATA, e) + +} + +func (p *TSimpleJSONProtocol) readIfNull() (bool, error) { + cont := true + for cont { + b, _ := p.reader.Peek(1) + if len(b) < 1 { + return false, nil + } + switch b[0] { + default: + return false, nil + case JSON_NULL[0]: + cont = false + break + case ' ', '\n', '\r', '\t': + p.reader.ReadByte() + break + } + } + if p.safePeekContains(JSON_NULL) { + p.reader.Read(make([]byte, len(JSON_NULL))) + return true, nil + } + return false, nil +} + +func (p *TSimpleJSONProtocol) readQuoteIfNext() { + b, _ := p.reader.Peek(1) + if len(b) > 0 && b[0] == JSON_QUOTE { + p.reader.ReadByte() + } +} + +func (p *TSimpleJSONProtocol) readNumeric() (Numeric, error) { + isNull, err := p.readIfNull() + if isNull || err != nil { + return NUMERIC_NULL, err + } + hasDecimalPoint := false + nextCanBeSign := true + hasE := false + MAX_LEN := 40 + buf := bytes.NewBuffer(make([]byte, 0, MAX_LEN)) + continueFor := true + inQuotes := false + for continueFor { + c, err := p.reader.ReadByte() + if err != nil { + if err == io.EOF { + break + } + return NUMERIC_NULL, NewTProtocolException(err) + } + switch c { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + buf.WriteByte(c) + nextCanBeSign = false + case '.': + if hasDecimalPoint { + e := fmt.Errorf("Unable to parse number with multiple decimal points '%s.'", buf.String()) + return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) + } + if hasE { + e := fmt.Errorf("Unable to parse number with decimal points in the exponent '%s.'", buf.String()) + return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) + } + buf.WriteByte(c) + hasDecimalPoint, nextCanBeSign = true, false + case 'e', 'E': + if hasE { + e := fmt.Errorf("Unable to parse number with multiple exponents '%s%c'", buf.String(), c) + return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) + } + buf.WriteByte(c) + hasE, nextCanBeSign = true, true + case '-', '+': + if !nextCanBeSign { + e := fmt.Errorf("Negative sign within number") + return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) + } + buf.WriteByte(c) + nextCanBeSign = false + case ' ', 0, '\t', '\n', '\r', JSON_RBRACE[0], JSON_RBRACKET[0], JSON_COMMA[0], JSON_COLON[0]: + p.reader.UnreadByte() + continueFor = false + case JSON_NAN[0]: + if buf.Len() == 0 { + buffer := make([]byte, len(JSON_NAN)) + buffer[0] = c + _, e := p.reader.Read(buffer[1:]) + if e != nil { + return NUMERIC_NULL, NewTProtocolException(e) + } + if JSON_NAN != string(buffer) { + e := mismatch(JSON_NAN, string(buffer)) + return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) + } + if inQuotes { + p.readQuoteIfNext() + } + return NAN, nil + } else { + e := fmt.Errorf("Unable to parse number starting with character '%c'", c) + return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) + } + case JSON_INFINITY[0]: + if buf.Len() == 0 || (buf.Len() == 1 && buf.Bytes()[0] == '+') { + buffer := make([]byte, len(JSON_INFINITY)) + buffer[0] = c + _, e := p.reader.Read(buffer[1:]) + if e != nil { + return NUMERIC_NULL, NewTProtocolException(e) + } + if JSON_INFINITY != string(buffer) { + e := mismatch(JSON_INFINITY, string(buffer)) + return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) + } + if inQuotes { + p.readQuoteIfNext() + } + return INFINITY, nil + } else if buf.Len() == 1 && buf.Bytes()[0] == JSON_NEGATIVE_INFINITY[0] { + buffer := make([]byte, len(JSON_NEGATIVE_INFINITY)) + buffer[0] = JSON_NEGATIVE_INFINITY[0] + buffer[1] = c + _, e := p.reader.Read(buffer[2:]) + if e != nil { + return NUMERIC_NULL, NewTProtocolException(e) + } + if JSON_NEGATIVE_INFINITY != string(buffer) { + e := mismatch(JSON_NEGATIVE_INFINITY, string(buffer)) + return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) + } + if inQuotes { + p.readQuoteIfNext() + } + return NEGATIVE_INFINITY, nil + } else { + e := fmt.Errorf("Unable to parse number starting with character '%c' due to existing buffer %s", c, buf.String()) + return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) + } + case JSON_QUOTE: + if !inQuotes { + inQuotes = true + } else { + break + } + default: + e := fmt.Errorf("Unable to parse number starting with character '%c'", c) + return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) + } + } + if buf.Len() == 0 { + e := fmt.Errorf("Unable to parse number from empty string ''") + return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) + } + return NewNumericFromJSONString(buf.String(), false), nil +} + +// Safely peeks into the buffer, reading only what is necessary +func (p *TSimpleJSONProtocol) safePeekContains(b []byte) bool { + for i := 0; i < len(b); i++ { + a, _ := p.reader.Peek(i + 1) + if len(a) == 0 || a[i] != b[i] { + return false + } + } + return true +} + +// Reset the context stack to its initial state. +func (p *TSimpleJSONProtocol) resetContextStack() { + p.parseContextStack = []int{int(_CONTEXT_IN_TOPLEVEL)} + p.dumpContext = []int{int(_CONTEXT_IN_TOPLEVEL)} +} + +func (p *TSimpleJSONProtocol) write(b []byte) (int, error) { + n, err := p.writer.Write(b) + if err != nil { + p.writer.Reset(p.trans) // THRIFT-3735 + } + return n, err +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/transport.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/transport.go new file mode 100644 index 00000000..45389965 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/transport.go @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "errors" + "io" +) + +var errTransportInterrupted = errors.New("Transport Interrupted") + +type Flusher interface { + Flush() (err error) +} + +type ReadSizeProvider interface { + RemainingBytes() (num_bytes uint64) +} + + +// Encapsulates the I/O layer +type TTransport interface { + io.ReadWriteCloser + Flusher + ReadSizeProvider + + // Opens the transport for communication + Open() error + + // Returns true if the transport is open + IsOpen() bool +} + +type stringWriter interface { + WriteString(s string) (n int, err error) +} + + +// This is "enchanced" transport with extra capabilities. You need to use one of these +// to construct protocol. +// Notably, TSocket does not implement this interface, and it is always a mistake to use +// TSocket directly in protocol. +type TRichTransport interface { + io.ReadWriter + io.ByteReader + io.ByteWriter + stringWriter + Flusher + ReadSizeProvider +} + diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/transport_exception.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/transport_exception.go new file mode 100644 index 00000000..9505b446 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/transport_exception.go @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "errors" + "io" +) + +type timeoutable interface { + Timeout() bool +} + +// Thrift Transport exception +type TTransportException interface { + TException + TypeId() int + Err() error +} + +const ( + UNKNOWN_TRANSPORT_EXCEPTION = 0 + NOT_OPEN = 1 + ALREADY_OPEN = 2 + TIMED_OUT = 3 + END_OF_FILE = 4 +) + +type tTransportException struct { + typeId int + err error +} + +func (p *tTransportException) TypeId() int { + return p.typeId +} + +func (p *tTransportException) Error() string { + return p.err.Error() +} + +func (p *tTransportException) Err() error { + return p.err +} + +func NewTTransportException(t int, e string) TTransportException { + return &tTransportException{typeId: t, err: errors.New(e)} +} + +func NewTTransportExceptionFromError(e error) TTransportException { + if e == nil { + return nil + } + + if t, ok := e.(TTransportException); ok { + return t + } + + switch v := e.(type) { + case TTransportException: + return v + case timeoutable: + if v.Timeout() { + return &tTransportException{typeId: TIMED_OUT, err: e} + } + } + + if e == io.EOF { + return &tTransportException{typeId: END_OF_FILE, err: e} + } + + return &tTransportException{typeId: UNKNOWN_TRANSPORT_EXCEPTION, err: e} +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/transport_factory.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/transport_factory.go new file mode 100644 index 00000000..533d1b43 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/transport_factory.go @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +// Factory class used to create wrapped instance of Transports. +// This is used primarily in servers, which get Transports from +// a ServerTransport and then may want to mutate them (i.e. create +// a BufferedTransport from the underlying base transport) +type TTransportFactory interface { + GetTransport(trans TTransport) TTransport +} + +type tTransportFactory struct{} + +// Return a wrapped instance of the base Transport. +func (p *tTransportFactory) GetTransport(trans TTransport) TTransport { + return trans +} + +func NewTTransportFactory() TTransportFactory { + return &tTransportFactory{} +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/type.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/type.go new file mode 100644 index 00000000..4292ffca --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/thrift/type.go @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +// Type constants in the Thrift protocol +type TType byte + +const ( + STOP = 0 + VOID = 1 + BOOL = 2 + BYTE = 3 + I08 = 3 + DOUBLE = 4 + I16 = 6 + I32 = 8 + I64 = 10 + STRING = 11 + UTF7 = 11 + STRUCT = 12 + MAP = 13 + SET = 14 + LIST = 15 + UTF8 = 16 + UTF16 = 17 + //BINARY = 18 wrong and unusued +) + +var typeNames = map[int]string{ + STOP: "STOP", + VOID: "VOID", + BOOL: "BOOL", + BYTE: "BYTE", + DOUBLE: "DOUBLE", + I16: "I16", + I32: "I32", + I64: "I64", + STRING: "STRING", + STRUCT: "STRUCT", + MAP: "MAP", + SET: "SET", + LIST: "LIST", + UTF8: "UTF8", + UTF16: "UTF16", +} + +func (p TType) String() string { + if s, ok := typeNames[int(p)]; ok { + return s + } + return "Unknown" +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/tracer.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/tracer.go new file mode 100644 index 00000000..198c32eb --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/tracer.go @@ -0,0 +1,431 @@ +// Copyright (c) 2017-2018 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "fmt" + "io" + "os" + "reflect" + "strconv" + "sync" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + + "github.com/uber/jaeger-client-go/internal/baggage" + "github.com/uber/jaeger-client-go/internal/throttler" + "github.com/uber/jaeger-client-go/log" + "github.com/uber/jaeger-client-go/utils" +) + +// Tracer implements opentracing.Tracer. +type Tracer struct { + serviceName string + hostIPv4 uint32 // this is for zipkin endpoint conversion + + sampler Sampler + reporter Reporter + metrics Metrics + logger log.Logger + + timeNow func() time.Time + randomNumber func() uint64 + + options struct { + poolSpans bool + gen128Bit bool // whether to generate 128bit trace IDs + zipkinSharedRPCSpan bool + highTraceIDGenerator func() uint64 // custom high trace ID generator + maxTagValueLength int + // more options to come + } + // pool for Span objects + spanPool sync.Pool + + injectors map[interface{}]Injector + extractors map[interface{}]Extractor + + observer compositeObserver + + tags []Tag + process Process + + baggageRestrictionManager baggage.RestrictionManager + baggageSetter *baggageSetter + + debugThrottler throttler.Throttler +} + +// NewTracer creates Tracer implementation that reports tracing to Jaeger. +// The returned io.Closer can be used in shutdown hooks to ensure that the internal +// queue of the Reporter is drained and all buffered spans are submitted to collectors. +func NewTracer( + serviceName string, + sampler Sampler, + reporter Reporter, + options ...TracerOption, +) (opentracing.Tracer, io.Closer) { + t := &Tracer{ + serviceName: serviceName, + sampler: sampler, + reporter: reporter, + injectors: make(map[interface{}]Injector), + extractors: make(map[interface{}]Extractor), + metrics: *NewNullMetrics(), + spanPool: sync.Pool{New: func() interface{} { + return &Span{} + }}, + } + + for _, option := range options { + option(t) + } + + // register default injectors/extractors unless they are already provided via options + textPropagator := newTextMapPropagator(getDefaultHeadersConfig(), t.metrics) + t.addCodec(opentracing.TextMap, textPropagator, textPropagator) + + httpHeaderPropagator := newHTTPHeaderPropagator(getDefaultHeadersConfig(), t.metrics) + t.addCodec(opentracing.HTTPHeaders, httpHeaderPropagator, httpHeaderPropagator) + + binaryPropagator := newBinaryPropagator(t) + t.addCodec(opentracing.Binary, binaryPropagator, binaryPropagator) + + // TODO remove after TChannel supports OpenTracing + interopPropagator := &jaegerTraceContextPropagator{tracer: t} + t.addCodec(SpanContextFormat, interopPropagator, interopPropagator) + + zipkinPropagator := &zipkinPropagator{tracer: t} + t.addCodec(ZipkinSpanFormat, zipkinPropagator, zipkinPropagator) + + if t.baggageRestrictionManager != nil { + t.baggageSetter = newBaggageSetter(t.baggageRestrictionManager, &t.metrics) + } else { + t.baggageSetter = newBaggageSetter(baggage.NewDefaultRestrictionManager(0), &t.metrics) + } + if t.debugThrottler == nil { + t.debugThrottler = throttler.DefaultThrottler{} + } + + if t.randomNumber == nil { + rng := utils.NewRand(time.Now().UnixNano()) + t.randomNumber = func() uint64 { + return uint64(rng.Int63()) + } + } + if t.timeNow == nil { + t.timeNow = time.Now + } + if t.logger == nil { + t.logger = log.NullLogger + } + // Set tracer-level tags + t.tags = append(t.tags, Tag{key: JaegerClientVersionTagKey, value: JaegerClientVersion}) + if hostname, err := os.Hostname(); err == nil { + t.tags = append(t.tags, Tag{key: TracerHostnameTagKey, value: hostname}) + } + if ip, err := utils.HostIP(); err == nil { + t.tags = append(t.tags, Tag{key: TracerIPTagKey, value: ip.String()}) + t.hostIPv4 = utils.PackIPAsUint32(ip) + } else { + t.logger.Error("Unable to determine this host's IP address: " + err.Error()) + } + + if t.options.gen128Bit { + if t.options.highTraceIDGenerator == nil { + t.options.highTraceIDGenerator = t.randomNumber + } + } else if t.options.highTraceIDGenerator != nil { + t.logger.Error("Overriding high trace ID generator but not generating " + + "128 bit trace IDs, consider enabling the \"Gen128Bit\" option") + } + if t.options.maxTagValueLength == 0 { + t.options.maxTagValueLength = DefaultMaxTagValueLength + } + t.process = Process{ + Service: serviceName, + UUID: strconv.FormatUint(t.randomNumber(), 16), + Tags: t.tags, + } + if throttler, ok := t.debugThrottler.(ProcessSetter); ok { + throttler.SetProcess(t.process) + } + + return t, t +} + +// addCodec adds registers injector and extractor for given propagation format if not already defined. +func (t *Tracer) addCodec(format interface{}, injector Injector, extractor Extractor) { + if _, ok := t.injectors[format]; !ok { + t.injectors[format] = injector + } + if _, ok := t.extractors[format]; !ok { + t.extractors[format] = extractor + } +} + +// StartSpan implements StartSpan() method of opentracing.Tracer. +func (t *Tracer) StartSpan( + operationName string, + options ...opentracing.StartSpanOption, +) opentracing.Span { + sso := opentracing.StartSpanOptions{} + for _, o := range options { + o.Apply(&sso) + } + return t.startSpanWithOptions(operationName, sso) +} + +func (t *Tracer) startSpanWithOptions( + operationName string, + options opentracing.StartSpanOptions, +) opentracing.Span { + if options.StartTime.IsZero() { + options.StartTime = t.timeNow() + } + + // Predicate whether the given span context is a valid reference + // which may be used as parent / debug ID / baggage items source + isValidReference := func(ctx SpanContext) bool { + return ctx.IsValid() || ctx.isDebugIDContainerOnly() || len(ctx.baggage) != 0 + } + + var references []Reference + var parent SpanContext + var hasParent bool // need this because `parent` is a value, not reference + for _, ref := range options.References { + ctx, ok := ref.ReferencedContext.(SpanContext) + if !ok { + t.logger.Error(fmt.Sprintf( + "Reference contains invalid type of SpanReference: %s", + reflect.ValueOf(ref.ReferencedContext))) + continue + } + if !isValidReference(ctx) { + continue + } + references = append(references, Reference{Type: ref.Type, Context: ctx}) + if !hasParent { + parent = ctx + hasParent = ref.Type == opentracing.ChildOfRef + } + } + if !hasParent && isValidReference(parent) { + // If ChildOfRef wasn't found but a FollowFromRef exists, use the context from + // the FollowFromRef as the parent + hasParent = true + } + + rpcServer := false + if v, ok := options.Tags[ext.SpanKindRPCServer.Key]; ok { + rpcServer = (v == ext.SpanKindRPCServerEnum || v == string(ext.SpanKindRPCServerEnum)) + } + + var samplerTags []Tag + var ctx SpanContext + newTrace := false + if !hasParent || !parent.IsValid() { + newTrace = true + ctx.traceID.Low = t.randomID() + if t.options.gen128Bit { + ctx.traceID.High = t.options.highTraceIDGenerator() + } + ctx.spanID = SpanID(ctx.traceID.Low) + ctx.parentID = 0 + ctx.flags = byte(0) + if hasParent && parent.isDebugIDContainerOnly() && t.isDebugAllowed(operationName) { + ctx.flags |= (flagSampled | flagDebug) + samplerTags = []Tag{{key: JaegerDebugHeader, value: parent.debugID}} + } else if sampled, tags := t.sampler.IsSampled(ctx.traceID, operationName); sampled { + ctx.flags |= flagSampled + samplerTags = tags + } + } else { + ctx.traceID = parent.traceID + if rpcServer && t.options.zipkinSharedRPCSpan { + // Support Zipkin's one-span-per-RPC model + ctx.spanID = parent.spanID + ctx.parentID = parent.parentID + } else { + ctx.spanID = SpanID(t.randomID()) + ctx.parentID = parent.spanID + } + ctx.flags = parent.flags + } + if hasParent { + // copy baggage items + if l := len(parent.baggage); l > 0 { + ctx.baggage = make(map[string]string, len(parent.baggage)) + for k, v := range parent.baggage { + ctx.baggage[k] = v + } + } + } + + sp := t.newSpan() + sp.context = ctx + sp.observer = t.observer.OnStartSpan(sp, operationName, options) + return t.startSpanInternal( + sp, + operationName, + options.StartTime, + samplerTags, + options.Tags, + newTrace, + rpcServer, + references, + ) +} + +// Inject implements Inject() method of opentracing.Tracer +func (t *Tracer) Inject(ctx opentracing.SpanContext, format interface{}, carrier interface{}) error { + c, ok := ctx.(SpanContext) + if !ok { + return opentracing.ErrInvalidSpanContext + } + if injector, ok := t.injectors[format]; ok { + return injector.Inject(c, carrier) + } + return opentracing.ErrUnsupportedFormat +} + +// Extract implements Extract() method of opentracing.Tracer +func (t *Tracer) Extract( + format interface{}, + carrier interface{}, +) (opentracing.SpanContext, error) { + if extractor, ok := t.extractors[format]; ok { + return extractor.Extract(carrier) + } + return nil, opentracing.ErrUnsupportedFormat +} + +// Close releases all resources used by the Tracer and flushes any remaining buffered spans. +func (t *Tracer) Close() error { + t.reporter.Close() + t.sampler.Close() + if mgr, ok := t.baggageRestrictionManager.(io.Closer); ok { + mgr.Close() + } + if throttler, ok := t.debugThrottler.(io.Closer); ok { + throttler.Close() + } + return nil +} + +// Tags returns a slice of tracer-level tags. +func (t *Tracer) Tags() []opentracing.Tag { + tags := make([]opentracing.Tag, len(t.tags)) + for i, tag := range t.tags { + tags[i] = opentracing.Tag{Key: tag.key, Value: tag.value} + } + return tags +} + +// newSpan returns an instance of a clean Span object. +// If options.PoolSpans is true, the spans are retrieved from an object pool. +func (t *Tracer) newSpan() *Span { + if !t.options.poolSpans { + return &Span{} + } + sp := t.spanPool.Get().(*Span) + sp.context = emptyContext + sp.tracer = nil + sp.tags = nil + sp.logs = nil + return sp +} + +func (t *Tracer) startSpanInternal( + sp *Span, + operationName string, + startTime time.Time, + internalTags []Tag, + tags opentracing.Tags, + newTrace bool, + rpcServer bool, + references []Reference, +) *Span { + sp.tracer = t + sp.operationName = operationName + sp.startTime = startTime + sp.duration = 0 + sp.references = references + sp.firstInProcess = rpcServer || sp.context.parentID == 0 + if len(tags) > 0 || len(internalTags) > 0 { + sp.tags = make([]Tag, len(internalTags), len(tags)+len(internalTags)) + copy(sp.tags, internalTags) + for k, v := range tags { + sp.observer.OnSetTag(k, v) + if k == string(ext.SamplingPriority) && !setSamplingPriority(sp, v) { + continue + } + sp.setTagNoLocking(k, v) + } + } + // emit metrics + if sp.context.IsSampled() { + t.metrics.SpansStartedSampled.Inc(1) + if newTrace { + // We cannot simply check for parentID==0 because in Zipkin model the + // server-side RPC span has the exact same trace/span/parent IDs as the + // calling client-side span, but obviously the server side span is + // no longer a root span of the trace. + t.metrics.TracesStartedSampled.Inc(1) + } else if sp.firstInProcess { + t.metrics.TracesJoinedSampled.Inc(1) + } + } else { + t.metrics.SpansStartedNotSampled.Inc(1) + if newTrace { + t.metrics.TracesStartedNotSampled.Inc(1) + } else if sp.firstInProcess { + t.metrics.TracesJoinedNotSampled.Inc(1) + } + } + return sp +} + +func (t *Tracer) reportSpan(sp *Span) { + t.metrics.SpansFinished.Inc(1) + if sp.context.IsSampled() { + t.reporter.Report(sp) + } + if t.options.poolSpans { + t.spanPool.Put(sp) + } +} + +// randomID generates a random trace/span ID, using tracer.random() generator. +// It never returns 0. +func (t *Tracer) randomID() uint64 { + val := t.randomNumber() + for val == 0 { + val = t.randomNumber() + } + return val +} + +// (NB) span must hold the lock before making this call +func (t *Tracer) setBaggage(sp *Span, key, value string) { + t.baggageSetter.setBaggage(sp, key, value) +} + +// (NB) span must hold the lock before making this call +func (t *Tracer) isDebugAllowed(operation string) bool { + return t.debugThrottler.IsAllowed(operation) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/tracer_options.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/tracer_options.go new file mode 100644 index 00000000..a90265f0 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/tracer_options.go @@ -0,0 +1,159 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "time" + + "github.com/opentracing/opentracing-go" + + "github.com/uber/jaeger-client-go/internal/baggage" + "github.com/uber/jaeger-client-go/internal/throttler" +) + +// TracerOption is a function that sets some option on the tracer +type TracerOption func(tracer *Tracer) + +// TracerOptions is a factory for all available TracerOption's +var TracerOptions tracerOptions + +type tracerOptions struct{} + +// Metrics creates a TracerOption that initializes Metrics on the tracer, +// which is used to emit statistics. +func (tracerOptions) Metrics(m *Metrics) TracerOption { + return func(tracer *Tracer) { + tracer.metrics = *m + } +} + +// Logger creates a TracerOption that gives the tracer a Logger. +func (tracerOptions) Logger(logger Logger) TracerOption { + return func(tracer *Tracer) { + tracer.logger = logger + } +} + +func (tracerOptions) CustomHeaderKeys(headerKeys *HeadersConfig) TracerOption { + return func(tracer *Tracer) { + if headerKeys == nil { + return + } + textPropagator := newTextMapPropagator(headerKeys.applyDefaults(), tracer.metrics) + tracer.addCodec(opentracing.TextMap, textPropagator, textPropagator) + + httpHeaderPropagator := newHTTPHeaderPropagator(headerKeys.applyDefaults(), tracer.metrics) + tracer.addCodec(opentracing.HTTPHeaders, httpHeaderPropagator, httpHeaderPropagator) + } +} + +// TimeNow creates a TracerOption that gives the tracer a function +// used to generate timestamps for spans. +func (tracerOptions) TimeNow(timeNow func() time.Time) TracerOption { + return func(tracer *Tracer) { + tracer.timeNow = timeNow + } +} + +// RandomNumber creates a TracerOption that gives the tracer +// a thread-safe random number generator function for generating trace IDs. +func (tracerOptions) RandomNumber(randomNumber func() uint64) TracerOption { + return func(tracer *Tracer) { + tracer.randomNumber = randomNumber + } +} + +// PoolSpans creates a TracerOption that tells the tracer whether it should use +// an object pool to minimize span allocations. +// This should be used with care, only if the service is not running any async tasks +// that can access parent spans after those spans have been finished. +func (tracerOptions) PoolSpans(poolSpans bool) TracerOption { + return func(tracer *Tracer) { + tracer.options.poolSpans = poolSpans + } +} + +// Deprecated: HostIPv4 creates a TracerOption that identifies the current service/process. +// If not set, the factory method will obtain the current IP address. +// The TracerOption is deprecated; the tracer will attempt to automatically detect the IP. +func (tracerOptions) HostIPv4(hostIPv4 uint32) TracerOption { + return func(tracer *Tracer) { + tracer.hostIPv4 = hostIPv4 + } +} + +func (tracerOptions) Injector(format interface{}, injector Injector) TracerOption { + return func(tracer *Tracer) { + tracer.injectors[format] = injector + } +} + +func (tracerOptions) Extractor(format interface{}, extractor Extractor) TracerOption { + return func(tracer *Tracer) { + tracer.extractors[format] = extractor + } +} + +func (t tracerOptions) Observer(observer Observer) TracerOption { + return t.ContribObserver(&oldObserver{obs: observer}) +} + +func (tracerOptions) ContribObserver(observer ContribObserver) TracerOption { + return func(tracer *Tracer) { + tracer.observer.append(observer) + } +} + +func (tracerOptions) Gen128Bit(gen128Bit bool) TracerOption { + return func(tracer *Tracer) { + tracer.options.gen128Bit = gen128Bit + } +} + +func (tracerOptions) HighTraceIDGenerator(highTraceIDGenerator func() uint64) TracerOption { + return func(tracer *Tracer) { + tracer.options.highTraceIDGenerator = highTraceIDGenerator + } +} + +func (tracerOptions) MaxTagValueLength(maxTagValueLength int) TracerOption { + return func(tracer *Tracer) { + tracer.options.maxTagValueLength = maxTagValueLength + } +} + +func (tracerOptions) ZipkinSharedRPCSpan(zipkinSharedRPCSpan bool) TracerOption { + return func(tracer *Tracer) { + tracer.options.zipkinSharedRPCSpan = zipkinSharedRPCSpan + } +} + +func (tracerOptions) Tag(key string, value interface{}) TracerOption { + return func(tracer *Tracer) { + tracer.tags = append(tracer.tags, Tag{key: key, value: value}) + } +} + +func (tracerOptions) BaggageRestrictionManager(mgr baggage.RestrictionManager) TracerOption { + return func(tracer *Tracer) { + tracer.baggageRestrictionManager = mgr + } +} + +func (tracerOptions) DebugThrottler(throttler throttler.Throttler) TracerOption { + return func(tracer *Tracer) { + tracer.debugThrottler = throttler + } +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/tracer_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/tracer_test.go new file mode 100644 index 00000000..bd592555 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/tracer_test.go @@ -0,0 +1,427 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "io" + "net/http" + "testing" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "github.com/uber/jaeger-lib/metrics" + "github.com/uber/jaeger-lib/metrics/testutils" + + "github.com/uber/jaeger-client-go/internal/baggage" + "github.com/uber/jaeger-client-go/log" + "github.com/uber/jaeger-client-go/utils" +) + +type tracerSuite struct { + suite.Suite + tracer opentracing.Tracer + closer io.Closer + metricsFactory *metrics.LocalFactory +} + +func (s *tracerSuite) SetupTest() { + s.metricsFactory = metrics.NewLocalFactory(0) + metrics := NewMetrics(s.metricsFactory, nil) + + s.tracer, s.closer = NewTracer("DOOP", // respect the classics, man! + NewConstSampler(true), + NewNullReporter(), + TracerOptions.Metrics(metrics), + TracerOptions.ZipkinSharedRPCSpan(true), + TracerOptions.BaggageRestrictionManager(baggage.NewDefaultRestrictionManager(0)), + ) + s.NotNil(s.tracer) +} + +func (s *tracerSuite) TearDownTest() { + if s.tracer != nil { + s.closer.Close() + s.tracer = nil + } +} + +func TestTracerSuite(t *testing.T) { + suite.Run(t, new(tracerSuite)) +} + +func (s *tracerSuite) TestBeginRootSpan() { + s.metricsFactory.Clear() + startTime := time.Now() + s.tracer.(*Tracer).timeNow = func() time.Time { return startTime } + someID := uint64(12345) + s.tracer.(*Tracer).randomNumber = func() uint64 { return someID } + + sp := s.tracer.StartSpan("get_name") + ext.SpanKindRPCServer.Set(sp) + ext.PeerService.Set(sp, "peer-service") + s.NotNil(sp) + ss := sp.(*Span) + s.NotNil(ss.tracer, "Tracer must be referenced from span") + s.Equal("get_name", ss.operationName) + s.Len(ss.tags, 4, "Span should have 2 sampler tags, span.kind tag and peer.service tag") + s.EqualValues(Tag{key: "span.kind", value: ext.SpanKindRPCServerEnum}, ss.tags[2], "Span must be server-side") + s.EqualValues(Tag{key: "peer.service", value: "peer-service"}, ss.tags[3], "Client is 'peer-service'") + + s.EqualValues(someID, ss.context.traceID.Low) + s.EqualValues(0, ss.context.parentID) + + s.Equal(startTime, ss.startTime) + + sp.Finish() + s.NotNil(ss.duration) + + testutils.AssertCounterMetrics(s.T(), s.metricsFactory, []testutils.ExpectedMetric{ + {Name: "jaeger.finished_spans", Value: 1}, + {Name: "jaeger.started_spans", Tags: map[string]string{"sampled": "y"}, Value: 1}, + {Name: "jaeger.traces", Tags: map[string]string{"sampled": "y", "state": "started"}, Value: 1}, + }...) +} + +func (s *tracerSuite) TestStartRootSpanWithOptions() { + ts := time.Now() + sp := s.tracer.StartSpan("get_address", opentracing.StartTime(ts)) + ss := sp.(*Span) + s.Equal("get_address", ss.operationName) + s.Equal(ts, ss.startTime) +} + +func (s *tracerSuite) TestStartChildSpan() { + s.metricsFactory.Clear() + sp1 := s.tracer.StartSpan("get_address") + sp2 := s.tracer.StartSpan("get_street", opentracing.ChildOf(sp1.Context())) + s.Equal(sp1.(*Span).context.spanID, sp2.(*Span).context.parentID) + sp2.Finish() + s.NotNil(sp2.(*Span).duration) + sp1.Finish() + testutils.AssertCounterMetrics(s.T(), s.metricsFactory, []testutils.ExpectedMetric{ + {Name: "jaeger.started_spans", Tags: map[string]string{"sampled": "y"}, Value: 2}, + {Name: "jaeger.traces", Tags: map[string]string{"sampled": "y", "state": "started"}, Value: 1}, + {Name: "jaeger.finished_spans", Value: 2}, + }...) +} + +type nonJaegerSpanContext struct{} + +func (c nonJaegerSpanContext) ForeachBaggageItem(handler func(k, v string) bool) {} + +func (s *tracerSuite) TestStartSpanWithMultipleReferences() { + s.metricsFactory.Clear() + sp1 := s.tracer.StartSpan("A") + sp2 := s.tracer.StartSpan("B") + sp3 := s.tracer.StartSpan("C") + sp4 := s.tracer.StartSpan( + "D", + opentracing.ChildOf(sp1.Context()), + opentracing.ChildOf(sp2.Context()), + opentracing.FollowsFrom(sp3.Context()), + opentracing.FollowsFrom(nonJaegerSpanContext{}), + opentracing.FollowsFrom(SpanContext{}), // Empty span context should be excluded + ) + // Should use the first ChildOf ref span as the parent + s.Equal(sp1.(*Span).context.spanID, sp4.(*Span).context.parentID) + sp4.Finish() + s.NotNil(sp4.(*Span).duration) + sp3.Finish() + sp2.Finish() + sp1.Finish() + testutils.AssertCounterMetrics(s.T(), s.metricsFactory, []testutils.ExpectedMetric{ + {Name: "jaeger.started_spans", Tags: map[string]string{"sampled": "y"}, Value: 4}, + {Name: "jaeger.traces", Tags: map[string]string{"sampled": "y", "state": "started"}, Value: 3}, + {Name: "jaeger.finished_spans", Value: 4}, + }...) + assert.Len(s.T(), sp4.(*Span).references, 3) +} + +func (s *tracerSuite) TestStartSpanWithOnlyFollowFromReference() { + s.metricsFactory.Clear() + sp1 := s.tracer.StartSpan("A") + sp2 := s.tracer.StartSpan( + "B", + opentracing.FollowsFrom(sp1.Context()), + ) + // Should use the first ChildOf ref span as the parent + s.Equal(sp1.(*Span).context.spanID, sp2.(*Span).context.parentID) + sp2.Finish() + s.NotNil(sp2.(*Span).duration) + sp1.Finish() + testutils.AssertCounterMetrics(s.T(), s.metricsFactory, []testutils.ExpectedMetric{ + {Name: "jaeger.started_spans", Tags: map[string]string{"sampled": "y"}, Value: 2}, + {Name: "jaeger.traces", Tags: map[string]string{"sampled": "y", "state": "started"}, Value: 1}, + {Name: "jaeger.finished_spans", Value: 2}, + }...) + assert.Len(s.T(), sp2.(*Span).references, 1) +} + +func (s *tracerSuite) TestTraceStartedOrJoinedMetrics() { + tests := []struct { + sampled bool + label string + }{ + {true, "y"}, + {false, "n"}, + } + for _, test := range tests { + s.metricsFactory.Clear() + s.tracer.(*Tracer).sampler = NewConstSampler(test.sampled) + sp1 := s.tracer.StartSpan("parent", ext.RPCServerOption(nil)) + sp2 := s.tracer.StartSpan("child1", opentracing.ChildOf(sp1.Context())) + sp3 := s.tracer.StartSpan("child2", ext.RPCServerOption(sp2.Context())) + s.Equal(sp2.(*Span).context.spanID, sp3.(*Span).context.spanID) + s.Equal(sp2.(*Span).context.parentID, sp3.(*Span).context.parentID) + sp3.Finish() + sp2.Finish() + sp1.Finish() + s.Equal(test.sampled, sp1.Context().(SpanContext).IsSampled()) + s.Equal(test.sampled, sp2.Context().(SpanContext).IsSampled()) + + testutils.AssertCounterMetrics(s.T(), s.metricsFactory, []testutils.ExpectedMetric{ + {Name: "jaeger.started_spans", Tags: map[string]string{"sampled": test.label}, Value: 3}, + {Name: "jaeger.finished_spans", Value: 3}, + {Name: "jaeger.traces", Tags: map[string]string{"sampled": test.label, "state": "started"}, Value: 1}, + {Name: "jaeger.traces", Tags: map[string]string{"sampled": test.label, "state": "joined"}, Value: 1}, + }...) + } +} + +func (s *tracerSuite) TestSetOperationName() { + sp1 := s.tracer.StartSpan("get_address") + sp1.SetOperationName("get_street") + s.Equal("get_street", sp1.(*Span).operationName) +} + +func (s *tracerSuite) TestSamplerEffects() { + s.tracer.(*Tracer).sampler = NewConstSampler(true) + sp := s.tracer.StartSpan("test") + flags := sp.(*Span).context.flags + s.EqualValues(flagSampled, flags&flagSampled) + + s.tracer.(*Tracer).sampler = NewConstSampler(false) + sp = s.tracer.StartSpan("test") + flags = sp.(*Span).context.flags + s.EqualValues(0, flags&flagSampled) +} + +func (s *tracerSuite) TestRandomIDNotZero() { + val := uint64(0) + s.tracer.(*Tracer).randomNumber = func() (r uint64) { + r = val + val++ + return + } + sp := s.tracer.StartSpan("get_name").(*Span) + s.EqualValues(TraceID{Low: 1}, sp.context.traceID) + + rng := utils.NewRand(0) + rng.Seed(1) // for test coverage +} + +func TestTracerOptions(t *testing.T) { + t1, e := time.Parse(time.RFC3339, "2012-11-01T22:08:41+00:00") + assert.NoError(t, e) + + timeNow := func() time.Time { + return t1 + } + rnd := func() uint64 { + return 1 + } + + openTracer, closer := NewTracer("DOOP", // respect the classics, man! + NewConstSampler(true), + NewNullReporter(), + TracerOptions.Logger(log.StdLogger), + TracerOptions.TimeNow(timeNow), + TracerOptions.RandomNumber(rnd), + TracerOptions.PoolSpans(true), + TracerOptions.Tag("tag_key", "tag_value"), + ) + defer closer.Close() + + tracer := openTracer.(*Tracer) + assert.Equal(t, log.StdLogger, tracer.logger) + assert.Equal(t, t1, tracer.timeNow()) + assert.Equal(t, uint64(1), tracer.randomNumber()) + assert.Equal(t, uint64(1), tracer.randomNumber()) + assert.Equal(t, uint64(1), tracer.randomNumber()) // always 1 + assert.Equal(t, true, tracer.options.poolSpans) + assert.Equal(t, opentracing.Tag{Key: "tag_key", Value: "tag_value"}, tracer.Tags()[0]) +} + +func TestInjectorExtractorOptions(t *testing.T) { + tracer, tc := NewTracer("x", NewConstSampler(true), NewNullReporter(), + TracerOptions.Injector("dummy", &dummyPropagator{}), + TracerOptions.Extractor("dummy", &dummyPropagator{}), + ) + defer tc.Close() + + sp := tracer.StartSpan("x") + c := &dummyCarrier{} + err := tracer.Inject(sp.Context(), "dummy", []int{}) + assert.Equal(t, opentracing.ErrInvalidCarrier, err) + err = tracer.Inject(sp.Context(), "dummy", c) + assert.NoError(t, err) + assert.True(t, c.ok) + + c.ok = false + _, err = tracer.Extract("dummy", []int{}) + assert.Equal(t, opentracing.ErrInvalidCarrier, err) + _, err = tracer.Extract("dummy", c) + assert.Equal(t, opentracing.ErrSpanContextNotFound, err) + c.ok = true + _, err = tracer.Extract("dummy", c) + assert.NoError(t, err) +} + +func TestEmptySpanContextAsParent(t *testing.T) { + tracer, tc := NewTracer("x", NewConstSampler(true), NewNullReporter()) + defer tc.Close() + + span := tracer.StartSpan("test", opentracing.ChildOf(emptyContext)) + ctx := span.Context().(SpanContext) + assert.True(t, ctx.traceID.IsValid()) + assert.True(t, ctx.IsValid()) +} + +func TestGen128Bit(t *testing.T) { + tracer, tc := NewTracer("x", NewConstSampler(true), NewNullReporter(), TracerOptions.Gen128Bit(true)) + defer tc.Close() + + span := tracer.StartSpan("test", opentracing.ChildOf(emptyContext)) + defer span.Finish() + traceID := span.Context().(SpanContext).TraceID() + assert.True(t, traceID.High != 0) + assert.True(t, traceID.Low != 0) +} + +func TestZipkinSharedRPCSpan(t *testing.T) { + tracer, tc := NewTracer("x", NewConstSampler(true), NewNullReporter(), TracerOptions.ZipkinSharedRPCSpan(false)) + + sp1 := tracer.StartSpan("client", ext.SpanKindRPCClient) + sp2 := tracer.StartSpan("server", opentracing.ChildOf(sp1.Context()), ext.SpanKindRPCServer) + assert.Equal(t, sp1.(*Span).context.spanID, sp2.(*Span).context.parentID) + assert.NotEqual(t, sp1.(*Span).context.spanID, sp2.(*Span).context.spanID) + sp2.Finish() + sp1.Finish() + tc.Close() + + tracer, tc = NewTracer("x", NewConstSampler(true), NewNullReporter(), TracerOptions.ZipkinSharedRPCSpan(true)) + + sp1 = tracer.StartSpan("client", ext.SpanKindRPCClient) + sp2 = tracer.StartSpan("server", opentracing.ChildOf(sp1.Context()), ext.SpanKindRPCServer) + assert.Equal(t, sp1.(*Span).context.spanID, sp2.(*Span).context.spanID) + assert.Equal(t, sp1.(*Span).context.parentID, sp2.(*Span).context.parentID) + sp2.Finish() + sp1.Finish() + tc.Close() +} + +type testDebugThrottler struct { + process Process +} + +func (t *testDebugThrottler) SetProcess(process Process) { + t.process = process +} + +func (t *testDebugThrottler) Close() error { + return nil +} + +func (t *testDebugThrottler) IsAllowed(operation string) bool { + return true +} + +func TestDebugThrottler(t *testing.T) { + throttler := &testDebugThrottler{} + opentracingTracer, tc := NewTracer("x", NewConstSampler(true), NewNullReporter(), TracerOptions.DebugThrottler(throttler)) + assert.NoError(t, tc.Close()) + tracer := opentracingTracer.(*Tracer) + assert.Equal(t, tracer.process, throttler.process) +} + +func TestThrottling_SamplingPriority(t *testing.T) { + tracer, closer := NewTracer("DOOP", NewConstSampler(true), NewNullReporter()) + + sp1 := tracer.StartSpan("s1", opentracing.Tags{string(ext.SamplingPriority): 0}).(*Span) + assert.False(t, sp1.context.IsDebug()) + + sp1 = tracer.StartSpan("s1", opentracing.Tags{string(ext.SamplingPriority): uint16(1)}).(*Span) + assert.True(t, sp1.context.IsDebug()) + + assert.NotNil(t, findDomainTag(sp1, "sampling.priority"), "sampling.priority tag should be added") + closer.Close() + + tracer, closer = NewTracer("DOOP", NewConstSampler(true), NewNullReporter(), + TracerOptions.DebugThrottler(testThrottler{allowAll: false})) + defer closer.Close() + + sp1 = tracer.StartSpan("s1", opentracing.Tags{string(ext.SamplingPriority): uint16(1)}).(*Span) + ext.SamplingPriority.Set(sp1, 1) + assert.False(t, sp1.context.IsDebug(), "debug should not be allowed by the throttler") +} + +func TestThrottling_DebugHeader(t *testing.T) { + tracer, closer := NewTracer("DOOP", NewConstSampler(true), NewNullReporter()) + + h := http.Header{} + h.Add(JaegerDebugHeader, "x") + ctx, err := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(h)) + require.NoError(t, err) + + sp := tracer.StartSpan("root", opentracing.ChildOf(ctx)).(*Span) + assert.True(t, sp.context.IsDebug()) + closer.Close() + + tracer, closer = NewTracer("DOOP", NewConstSampler(true), NewNullReporter(), + TracerOptions.DebugThrottler(testThrottler{allowAll: false})) + defer closer.Close() + + sp = tracer.StartSpan("root", opentracing.ChildOf(ctx)).(*Span) + assert.False(t, sp.context.IsDebug(), "debug should not be allowed by the throttler") +} + +type dummyPropagator struct{} +type dummyCarrier struct { + ok bool +} + +func (p *dummyPropagator) Inject(ctx SpanContext, carrier interface{}) error { + c, ok := carrier.(*dummyCarrier) + if !ok { + return opentracing.ErrInvalidCarrier + } + c.ok = true + return nil +} + +func (p *dummyPropagator) Extract(carrier interface{}) (SpanContext, error) { + c, ok := carrier.(*dummyCarrier) + if !ok { + return emptyContext, opentracing.ErrInvalidCarrier + } + if c.ok { + return emptyContext, nil + } + return emptyContext, opentracing.ErrSpanContextNotFound +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/transport.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/transport.go new file mode 100644 index 00000000..c5f5b195 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/transport.go @@ -0,0 +1,38 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "io" +) + +// Transport abstracts the method of sending spans out of process. +// Implementations are NOT required to be thread-safe; the RemoteReporter +// is expected to only call methods on the Transport from the same go-routine. +type Transport interface { + // Append converts the span to the wire representation and adds it + // to sender's internal buffer. If the buffer exceeds its designated + // size, the transport should call Flush() and return the number of spans + // flushed, otherwise return 0. If error is returned, the returned number + // of spans is treated as failed span, and reported to metrics accordingly. + Append(span *Span) (int, error) + + // Flush submits the internal buffer to the remote server. It returns the + // number of spans flushed. If error is returned, the returned number of + // spans is treated as failed span, and reported to metrics accordingly. + Flush() (int, error) + + io.Closer +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/transport/doc.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/transport/doc.go new file mode 100644 index 00000000..6b961fb6 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/transport/doc.go @@ -0,0 +1,23 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +// Package transport defines various transports that can be used with +// RemoteReporter to send spans out of process. Transport is responsible +// for serializing the spans into a specific format suitable for sending +// to the tracing backend. Examples may include Thrift over UDP, Thrift +// or JSON over HTTP, Thrift over Kafka, etc. +// +// Implementations are NOT required to be thread-safe; the RemoteReporter +// is expected to only call methods on the Transport from the same go-routine. +package transport diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/transport/http.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/transport/http.go new file mode 100644 index 00000000..846fc4e6 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/transport/http.go @@ -0,0 +1,155 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package transport + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "net/http" + "time" + + "github.com/uber/jaeger-client-go/thrift" + + "github.com/uber/jaeger-client-go" + j "github.com/uber/jaeger-client-go/thrift-gen/jaeger" +) + +// Default timeout for http request in seconds +const defaultHTTPTimeout = time.Second * 5 + +// HTTPTransport implements Transport by forwarding spans to a http server. +type HTTPTransport struct { + url string + client *http.Client + batchSize int + spans []*j.Span + process *j.Process + httpCredentials *HTTPBasicAuthCredentials +} + +// HTTPBasicAuthCredentials stores credentials for HTTP basic auth. +type HTTPBasicAuthCredentials struct { + username string + password string +} + +// HTTPOption sets a parameter for the HttpCollector +type HTTPOption func(c *HTTPTransport) + +// HTTPTimeout sets maximum timeout for http request. +func HTTPTimeout(duration time.Duration) HTTPOption { + return func(c *HTTPTransport) { c.client.Timeout = duration } +} + +// HTTPBatchSize sets the maximum batch size, after which a collect will be +// triggered. The default batch size is 100 spans. +func HTTPBatchSize(n int) HTTPOption { + return func(c *HTTPTransport) { c.batchSize = n } +} + +// HTTPBasicAuth sets the credentials required to perform HTTP basic auth +func HTTPBasicAuth(username string, password string) HTTPOption { + return func(c *HTTPTransport) { + c.httpCredentials = &HTTPBasicAuthCredentials{username: username, password: password} + } +} + +// NewHTTPTransport returns a new HTTP-backend transport. url should be an http +// url of the collector to handle POST request, typically something like: +// http://hostname:14268/api/traces?format=jaeger.thrift +func NewHTTPTransport(url string, options ...HTTPOption) *HTTPTransport { + c := &HTTPTransport{ + url: url, + client: &http.Client{Timeout: defaultHTTPTimeout}, + batchSize: 100, + spans: []*j.Span{}, + } + + for _, option := range options { + option(c) + } + return c +} + +// Append implements Transport. +func (c *HTTPTransport) Append(span *jaeger.Span) (int, error) { + if c.process == nil { + c.process = jaeger.BuildJaegerProcessThrift(span) + } + jSpan := jaeger.BuildJaegerThrift(span) + c.spans = append(c.spans, jSpan) + if len(c.spans) >= c.batchSize { + return c.Flush() + } + return 0, nil +} + +// Flush implements Transport. +func (c *HTTPTransport) Flush() (int, error) { + count := len(c.spans) + if count == 0 { + return 0, nil + } + err := c.send(c.spans) + c.spans = c.spans[:0] + return count, err +} + +// Close implements Transport. +func (c *HTTPTransport) Close() error { + return nil +} + +func (c *HTTPTransport) send(spans []*j.Span) error { + batch := &j.Batch{ + Spans: spans, + Process: c.process, + } + body, err := serializeThrift(batch) + if err != nil { + return err + } + req, err := http.NewRequest("POST", c.url, body) + if err != nil { + return err + } + req.Header.Set("Content-Type", "application/x-thrift") + + if c.httpCredentials != nil { + req.SetBasicAuth(c.httpCredentials.username, c.httpCredentials.password) + } + + resp, err := c.client.Do(req) + if err != nil { + return err + } + io.Copy(ioutil.Discard, resp.Body) + resp.Body.Close() + if resp.StatusCode >= http.StatusBadRequest { + return fmt.Errorf("error from collector: %d", resp.StatusCode) + } + return nil +} + +func serializeThrift(obj thrift.TStruct) (*bytes.Buffer, error) { + t := thrift.NewTMemoryBuffer() + p := thrift.NewTBinaryProtocolTransport(t) + if err := obj.Write(p); err != nil { + return nil, err + } + return t.Buffer, nil +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/transport/http_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/transport/http_test.go new file mode 100644 index 00000000..85cc8f47 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/transport/http_test.go @@ -0,0 +1,156 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package transport + +import ( + "io/ioutil" + "net/http" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/uber/jaeger-client-go/thrift" + + "github.com/uber/jaeger-client-go" + j "github.com/uber/jaeger-client-go/thrift-gen/jaeger" +) + +func TestHTTPTransport(t *testing.T) { + server := newHTTPServer(t) + httpUsername := "Bender" + httpPassword := "Rodriguez" + sender := NewHTTPTransport( + "http://localhost:10001/api/v1/spans", + HTTPBatchSize(1), + HTTPBasicAuth(httpUsername, httpPassword), + ) + + tracer, closer := jaeger.NewTracer( + "test", + jaeger.NewConstSampler(true), + jaeger.NewRemoteReporter(sender), + ) + defer closer.Close() + + span := tracer.StartSpan("root") + span.Finish() + + // Need to yield to the select loop to accept the send request, and then + // yield again to the send operation to write to the socket. I think the + // best way to do that is just give it some time. + + deadline := time.Now().Add(2 * time.Second) + for { + if time.Now().After(deadline) { + t.Fatal("never received a span") + } + if want, have := 1, len(server.getBatches()); want != have { + time.Sleep(time.Millisecond) + continue + } + break + } + + srcSpanCtx := span.Context().(jaeger.SpanContext) + gotBatch := server.getBatches()[0] + assert.Len(t, gotBatch.Spans, 1) + assert.Equal(t, "test", gotBatch.Process.ServiceName) + gotSpan := gotBatch.Spans[0] + assert.Equal(t, "root", gotSpan.OperationName) + assert.EqualValues(t, srcSpanCtx.TraceID().High, gotSpan.TraceIdHigh) + assert.EqualValues(t, srcSpanCtx.TraceID().Low, gotSpan.TraceIdLow) + assert.EqualValues(t, srcSpanCtx.SpanID(), gotSpan.SpanId) + assert.Equal(t, + &HTTPBasicAuthCredentials{username: httpUsername, password: httpPassword}, + server.authCredentials[0], + ) +} + +func TestHTTPOptions(t *testing.T) { + sender := NewHTTPTransport( + "some url", + HTTPBatchSize(123), + HTTPTimeout(123*time.Millisecond), + ) + assert.Equal(t, 123, sender.batchSize) + assert.Equal(t, 123*time.Millisecond, sender.client.Timeout) +} + +type httpServer struct { + t *testing.T + batches []*j.Batch + authCredentials []*HTTPBasicAuthCredentials + mutex sync.RWMutex +} + +func (s *httpServer) getBatches() []*j.Batch { + s.mutex.RLock() + defer s.mutex.RUnlock() + return s.batches +} + +func (s *httpServer) credentials() []*HTTPBasicAuthCredentials { + s.mutex.RLock() + defer s.mutex.RUnlock() + return s.authCredentials +} + +// TODO this function and zipkin/http_test.go#newHTTPServer look like twins lost at birth +func newHTTPServer(t *testing.T) *httpServer { + server := &httpServer{ + t: t, + batches: make([]*j.Batch, 0), + authCredentials: make([]*HTTPBasicAuthCredentials, 0), + mutex: sync.RWMutex{}, + } + http.HandleFunc("/api/v1/spans", func(w http.ResponseWriter, r *http.Request) { + contextType := r.Header.Get("Content-Type") + if contextType != "application/x-thrift" { + t.Errorf( + "except Content-Type should be application/x-thrift, but is %s", + contextType) + return + } + + body, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Error(err) + return + } + buffer := thrift.NewTMemoryBuffer() + if _, err = buffer.Write(body); err != nil { + t.Error(err) + return + } + transport := thrift.NewTBinaryProtocolTransport(buffer) + batch := &j.Batch{} + if err = batch.Read(transport); err != nil { + t.Error(err) + return + } + server.mutex.Lock() + defer server.mutex.Unlock() + server.batches = append(server.batches, batch) + u, p, _ := r.BasicAuth() + server.authCredentials = append(server.authCredentials, &HTTPBasicAuthCredentials{username: u, password: p}) + }) + + go func() { + http.ListenAndServe(":10001", nil) + }() + + return server +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/transport/zipkin/doc.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/transport/zipkin/doc.go new file mode 100644 index 00000000..64abb539 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/transport/zipkin/doc.go @@ -0,0 +1,17 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +// Package zipkin provides various Transports that can be used +// with RemoteReporter for submitting traces to Zipkin backend. +package zipkin diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/transport/zipkin/example_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/transport/zipkin/example_test.go new file mode 100644 index 00000000..bf853809 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/transport/zipkin/example_test.go @@ -0,0 +1,46 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package zipkin_test + +import ( + "log" + + "github.com/opentracing/opentracing-go" + "github.com/uber/jaeger-client-go" + jlog "github.com/uber/jaeger-client-go/log" + "github.com/uber/jaeger-client-go/transport/zipkin" +) + +func ExampleNewHTTPTransport() { + // assume this is your main() + + transport, err := zipkin.NewHTTPTransport( + "http://localhost:9411/api/v1/spans", + zipkin.HTTPBatchSize(10), + zipkin.HTTPLogger(jlog.StdLogger), + ) + if err != nil { + log.Fatalf("Cannot initialize Zipkin HTTP transport: %v", err) + } + tracer, closer := jaeger.NewTracer( + "my-service-name", + jaeger.NewConstSampler(true), + jaeger.NewRemoteReporter(transport, nil), + ) + defer closer.Close() + opentracing.SetGlobalTracer(tracer) + + // initialize servers +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/transport/zipkin/http.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/transport/zipkin/http.go new file mode 100644 index 00000000..57ff9b1a --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/transport/zipkin/http.go @@ -0,0 +1,166 @@ +// Copyright (c) 2017 The OpenTracing Authors +// Copyright (c) 2016 Bas van Beek +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zipkin + +// This code is adapted from 'collector-http.go' from +// https://github.com/openzipkin/zipkin-go-opentracing/ + +import ( + "bytes" + "net/http" + "time" + + "github.com/uber/jaeger-client-go/thrift" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/log" + "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" +) + +// Default timeout for http request in seconds +const defaultHTTPTimeout = time.Second * 5 + +// HTTPTransport implements Transport by forwarding spans to a http server. +type HTTPTransport struct { + logger jaeger.Logger + url string + client *http.Client + batchSize int + batch []*zipkincore.Span + httpCredentials *HTTPBasicAuthCredentials +} + +// HTTPBasicAuthCredentials stores credentials for HTTP basic auth. +type HTTPBasicAuthCredentials struct { + username string + password string +} + +// HTTPOption sets a parameter for the HttpCollector +type HTTPOption func(c *HTTPTransport) + +// HTTPLogger sets the logger used to report errors in the collection +// process. By default, a no-op logger is used, i.e. no errors are logged +// anywhere. It's important to set this option in a production service. +func HTTPLogger(logger jaeger.Logger) HTTPOption { + return func(c *HTTPTransport) { c.logger = logger } +} + +// HTTPTimeout sets maximum timeout for http request. +func HTTPTimeout(duration time.Duration) HTTPOption { + return func(c *HTTPTransport) { c.client.Timeout = duration } +} + +// HTTPBatchSize sets the maximum batch size, after which a collect will be +// triggered. The default batch size is 100 spans. +func HTTPBatchSize(n int) HTTPOption { + return func(c *HTTPTransport) { c.batchSize = n } +} + +// HTTPBasicAuth sets the credentials required to perform HTTP basic auth +func HTTPBasicAuth(username string, password string) HTTPOption { + return func(c *HTTPTransport) { + c.httpCredentials = &HTTPBasicAuthCredentials{username: username, password: password} + } +} + +// NewHTTPTransport returns a new HTTP-backend transport. url should be an http +// url to handle post request, typically something like: +// http://hostname:9411/api/v1/spans +func NewHTTPTransport(url string, options ...HTTPOption) (*HTTPTransport, error) { + c := &HTTPTransport{ + logger: log.NullLogger, + url: url, + client: &http.Client{Timeout: defaultHTTPTimeout}, + batchSize: 100, + batch: []*zipkincore.Span{}, + } + + for _, option := range options { + option(c) + } + return c, nil +} + +// Append implements Transport. +func (c *HTTPTransport) Append(span *jaeger.Span) (int, error) { + zSpan := jaeger.BuildZipkinThrift(span) + c.batch = append(c.batch, zSpan) + if len(c.batch) >= c.batchSize { + return c.Flush() + } + return 0, nil +} + +// Flush implements Transport. +func (c *HTTPTransport) Flush() (int, error) { + count := len(c.batch) + if count == 0 { + return 0, nil + } + err := c.send(c.batch) + c.batch = c.batch[:0] + return count, err +} + +// Close implements Transport. +func (c *HTTPTransport) Close() error { + return nil +} + +func httpSerialize(spans []*zipkincore.Span) (*bytes.Buffer, error) { + t := thrift.NewTMemoryBuffer() + p := thrift.NewTBinaryProtocolTransport(t) + if err := p.WriteListBegin(thrift.STRUCT, len(spans)); err != nil { + return nil, err + } + for _, s := range spans { + if err := s.Write(p); err != nil { + return nil, err + } + } + if err := p.WriteListEnd(); err != nil { + return nil, err + } + return t.Buffer, nil +} + +func (c *HTTPTransport) send(spans []*zipkincore.Span) error { + body, err := httpSerialize(spans) + if err != nil { + return err + } + req, err := http.NewRequest("POST", c.url, body) + if err != nil { + return err + } + req.Header.Set("Content-Type", "application/x-thrift") + + if c.httpCredentials != nil { + req.SetBasicAuth(c.httpCredentials.username, c.httpCredentials.password) + } + + _, err = c.client.Do(req) + + return err +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/transport/zipkin/http_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/transport/zipkin/http_test.go new file mode 100644 index 00000000..5e70379e --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/transport/zipkin/http_test.go @@ -0,0 +1,178 @@ +// Copyright (c) 2017 The OpenTracing Authors +// Copyright (c) 2016 Bas van Beek +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zipkin + +// This code is adapted from 'collector-http_test.go' from +// https://github.com/openzipkin/zipkin-go-opentracing/ + +import ( + "io/ioutil" + "net/http" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber/jaeger-client-go/thrift" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/log" + "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" +) + +func TestHttpTransport(t *testing.T) { + server := newHTTPServer(t) + httpUsername := "Aphex" + httpPassword := "Twin" + sender, err := NewHTTPTransport("http://localhost:10000/api/v1/spans", HTTPBasicAuth(httpUsername, httpPassword)) + require.NoError(t, err) + + tracer, closer := jaeger.NewTracer( + "test", + jaeger.NewConstSampler(true), + jaeger.NewRemoteReporter(sender), + ) + + span := tracer.StartSpan("root") + span.Finish() + + closer.Close() + + // Need to yield to the select loop to accept the send request, and then + // yield again to the send operation to write to the socket. I think the + // best way to do that is just give it some time. + + deadline := time.Now().Add(2 * time.Second) + for { + if time.Now().After(deadline) { + t.Fatal("never received a span") + } + if want, have := 1, len(server.spans()); want != have { + time.Sleep(time.Millisecond) + continue + } + break + } + + srcSpanCtx := span.Context().(jaeger.SpanContext) + gotSpan := server.spans()[0] + assert.Equal(t, "root", gotSpan.Name) + assert.EqualValues(t, srcSpanCtx.TraceID().Low, gotSpan.TraceID) + assert.EqualValues(t, srcSpanCtx.SpanID(), gotSpan.ID) + assert.Equal(t, &HTTPBasicAuthCredentials{username: httpUsername, password: httpPassword}, server.authCredentials[0]) +} + +func TestHTTPOptions(t *testing.T) { + sender, err := NewHTTPTransport( + "some url", + HTTPLogger(log.StdLogger), + HTTPBatchSize(123), + HTTPTimeout(123*time.Millisecond), + HTTPBasicAuth("urundai", "kuzhambu"), + ) + require.NoError(t, err) + assert.Equal(t, log.StdLogger, sender.logger) + assert.Equal(t, 123, sender.batchSize) + assert.Equal(t, 123*time.Millisecond, sender.client.Timeout) + assert.Equal(t, "urundai", sender.httpCredentials.username) + assert.Equal(t, "kuzhambu", sender.httpCredentials.password) +} + +type httpServer struct { + t *testing.T + zipkinSpans []*zipkincore.Span + authCredentials []*HTTPBasicAuthCredentials + mutex sync.RWMutex +} + +func (s *httpServer) spans() []*zipkincore.Span { + s.mutex.RLock() + defer s.mutex.RUnlock() + return s.zipkinSpans +} + +func (s *httpServer) credentials() []*HTTPBasicAuthCredentials { + s.mutex.RLock() + defer s.mutex.RUnlock() + return s.authCredentials +} + +func newHTTPServer(t *testing.T) *httpServer { + server := &httpServer{ + t: t, + zipkinSpans: make([]*zipkincore.Span, 0), + authCredentials: make([]*HTTPBasicAuthCredentials, 0), + mutex: sync.RWMutex{}, + } + http.HandleFunc("/api/v1/spans", func(w http.ResponseWriter, r *http.Request) { + contextType := r.Header.Get("Content-Type") + if contextType != "application/x-thrift" { + t.Errorf( + "except Content-Type should be application/x-thrift, but is %s", + contextType) + return + } + + body, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Error(err) + return + } + buffer := thrift.NewTMemoryBuffer() + if _, err = buffer.Write(body); err != nil { + t.Error(err) + return + } + transport := thrift.NewTBinaryProtocolTransport(buffer) + _, size, err := transport.ReadListBegin() + if err != nil { + t.Error(err) + return + } + var spans []*zipkincore.Span + for i := 0; i < size; i++ { + zs := &zipkincore.Span{} + if err = zs.Read(transport); err != nil { + t.Error(err) + return + } + spans = append(spans, zs) + } + if err := transport.ReadListEnd(); err != nil { + t.Error(err) + return + } + server.mutex.Lock() + defer server.mutex.Unlock() + server.zipkinSpans = append(server.zipkinSpans, spans...) + u, p, _ := r.BasicAuth() + server.authCredentials = append(server.authCredentials, &HTTPBasicAuthCredentials{username: u, password: p}) + }) + + go func() { + http.ListenAndServe(":10000", nil) + }() + + return server +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/transport_udp.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/transport_udp.go new file mode 100644 index 00000000..7b9ccf93 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/transport_udp.go @@ -0,0 +1,131 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "errors" + "fmt" + + "github.com/uber/jaeger-client-go/thrift" + + j "github.com/uber/jaeger-client-go/thrift-gen/jaeger" + "github.com/uber/jaeger-client-go/utils" +) + +// Empirically obtained constant for how many bytes in the message are used for envelope. +// The total datagram size is: +// sizeof(Span) * numSpans + processByteSize + emitBatchOverhead <= maxPacketSize +// There is a unit test `TestEmitBatchOverhead` that validates this number. +// Note that due to the use of Compact Thrift protocol, overhead grows with the number of spans +// in the batch, because the length of the list is encoded as varint32, as well as SeqId. +const emitBatchOverhead = 30 + +var errSpanTooLarge = errors.New("Span is too large") + +type udpSender struct { + client *utils.AgentClientUDP + maxPacketSize int // max size of datagram in bytes + maxSpanBytes int // max number of bytes to record spans (excluding envelope) in the datagram + byteBufferSize int // current number of span bytes accumulated in the buffer + spanBuffer []*j.Span // spans buffered before a flush + thriftBuffer *thrift.TMemoryBuffer // buffer used to calculate byte size of a span + thriftProtocol thrift.TProtocol + process *j.Process + processByteSize int +} + +// NewUDPTransport creates a reporter that submits spans to jaeger-agent +func NewUDPTransport(hostPort string, maxPacketSize int) (Transport, error) { + if len(hostPort) == 0 { + hostPort = fmt.Sprintf("%s:%d", DefaultUDPSpanServerHost, DefaultUDPSpanServerPort) + } + if maxPacketSize == 0 { + maxPacketSize = utils.UDPPacketMaxLength + } + + protocolFactory := thrift.NewTCompactProtocolFactory() + + // Each span is first written to thriftBuffer to determine its size in bytes. + thriftBuffer := thrift.NewTMemoryBufferLen(maxPacketSize) + thriftProtocol := protocolFactory.GetProtocol(thriftBuffer) + + client, err := utils.NewAgentClientUDP(hostPort, maxPacketSize) + if err != nil { + return nil, err + } + + sender := &udpSender{ + client: client, + maxSpanBytes: maxPacketSize - emitBatchOverhead, + thriftBuffer: thriftBuffer, + thriftProtocol: thriftProtocol} + return sender, nil +} + +func (s *udpSender) calcSizeOfSerializedThrift(thriftStruct thrift.TStruct) int { + s.thriftBuffer.Reset() + thriftStruct.Write(s.thriftProtocol) + return s.thriftBuffer.Len() +} + +func (s *udpSender) Append(span *Span) (int, error) { + if s.process == nil { + s.process = BuildJaegerProcessThrift(span) + s.processByteSize = s.calcSizeOfSerializedThrift(s.process) + s.byteBufferSize += s.processByteSize + } + jSpan := BuildJaegerThrift(span) + spanSize := s.calcSizeOfSerializedThrift(jSpan) + if spanSize > s.maxSpanBytes { + return 1, errSpanTooLarge + } + + s.byteBufferSize += spanSize + if s.byteBufferSize <= s.maxSpanBytes { + s.spanBuffer = append(s.spanBuffer, jSpan) + if s.byteBufferSize < s.maxSpanBytes { + return 0, nil + } + return s.Flush() + } + // the latest span did not fit in the buffer + n, err := s.Flush() + s.spanBuffer = append(s.spanBuffer, jSpan) + s.byteBufferSize = spanSize + s.processByteSize + return n, err +} + +func (s *udpSender) Flush() (int, error) { + n := len(s.spanBuffer) + if n == 0 { + return 0, nil + } + err := s.client.EmitBatch(&j.Batch{Process: s.process, Spans: s.spanBuffer}) + s.resetBuffers() + + return n, err +} + +func (s *udpSender) Close() error { + return s.client.Close() +} + +func (s *udpSender) resetBuffers() { + for i := range s.spanBuffer { + s.spanBuffer[i] = nil + } + s.spanBuffer = s.spanBuffer[:0] + s.byteBufferSize = s.processByteSize +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/transport_udp_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/transport_udp_test.go new file mode 100644 index 00000000..7af7b53e --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/transport_udp_test.go @@ -0,0 +1,220 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/uber/jaeger-client-go/testutils" + "github.com/uber/jaeger-client-go/thrift" + j "github.com/uber/jaeger-client-go/thrift-gen/jaeger" +) + +var ( + testTracer, _ = NewTracer("svcName", NewConstSampler(false), NewNullReporter()) + jaegerTracer = testTracer.(*Tracer) +) + +func getThriftSpanByteLength(t *testing.T, span *Span) int { + jSpan := BuildJaegerThrift(span) + transport := thrift.NewTMemoryBufferLen(1000) + protocolFactory := thrift.NewTCompactProtocolFactory() + err := jSpan.Write(protocolFactory.GetProtocol(transport)) + require.NoError(t, err) + return transport.Len() +} + +func getThriftProcessByteLengthFromTracer(t *testing.T, tracer *Tracer) int { + process := buildJaegerProcessThrift(tracer) + return getThriftProcessByteLength(t, process) +} + +func getThriftProcessByteLength(t *testing.T, process *j.Process) int { + transport := thrift.NewTMemoryBufferLen(1000) + protocolFactory := thrift.NewTCompactProtocolFactory() + err := process.Write(protocolFactory.GetProtocol(transport)) + require.NoError(t, err) + return transport.Len() +} + +func TestEmitBatchOverhead(t *testing.T) { + transport := thrift.NewTMemoryBufferLen(1000) + protocolFactory := thrift.NewTCompactProtocolFactory() + client := j.NewAgentClientFactory(transport, protocolFactory) + + span := &Span{operationName: "test-span", tracer: jaegerTracer} + spanSize := getThriftSpanByteLength(t, span) + + tests := []int{1, 2, 14, 15, 377, 500, 65000, 0xFFFF} + for i, n := range tests { + transport.Reset() + batch := make([]*j.Span, n) + processTags := make([]*j.Tag, n) + for x := 0; x < n; x++ { + batch[x] = BuildJaegerThrift(span) + processTags[x] = &j.Tag{} + } + process := &j.Process{ServiceName: "svcName", Tags: processTags} + client.SeqId = -2 // this causes the longest encoding of varint32 as 5 bytes + err := client.EmitBatch(&j.Batch{Process: process, Spans: batch}) + processSize := getThriftProcessByteLength(t, process) + require.NoError(t, err) + overhead := transport.Len() - n*spanSize - processSize + assert.True(t, overhead <= emitBatchOverhead, + "test %d, n=%d, expected overhead %d <= %d", i, n, overhead, emitBatchOverhead) + t.Logf("span count: %d, overhead: %d", n, overhead) + } +} + +func TestUDPSenderFlush(t *testing.T) { + agent, err := testutils.StartMockAgent() + require.NoError(t, err) + defer agent.Close() + + span := &Span{operationName: "test-span", tracer: jaegerTracer} + spanSize := getThriftSpanByteLength(t, span) + processSize := getThriftProcessByteLengthFromTracer(t, jaegerTracer) + + sender, err := NewUDPTransport(agent.SpanServerAddr(), 5*spanSize+processSize+emitBatchOverhead) + require.NoError(t, err) + udpSender := sender.(*udpSender) + + // test empty flush + n, err := sender.Flush() + require.NoError(t, err) + assert.Equal(t, 0, n) + + // test early flush + n, err = sender.Append(span) + require.NoError(t, err) + assert.Equal(t, 0, n, "span should be in buffer, not flushed") + buffer := udpSender.spanBuffer + require.Equal(t, 1, len(buffer), "span should be in buffer, not flushed") + assert.Equal(t, BuildJaegerThrift(span), buffer[0], "span should be in buffer, not flushed") + + n, err = sender.Flush() + require.NoError(t, err) + assert.Equal(t, 1, n) + assert.Equal(t, 0, len(udpSender.spanBuffer), "buffer should become empty") + assert.Equal(t, processSize, udpSender.byteBufferSize, "buffer size counter should be equal to the processSize") + assert.Nil(t, buffer[0], "buffer should not keep reference to the span") + + for i := 0; i < 10000; i++ { + batches := agent.GetJaegerBatches() + if len(batches) > 0 { + break + } + time.Sleep(1 * time.Millisecond) + } + batches := agent.GetJaegerBatches() + require.Equal(t, 1, len(batches), "agent should have received the batch") + require.Equal(t, 1, len(batches[0].Spans)) + assert.Equal(t, span.operationName, batches[0].Spans[0].OperationName) +} + +func TestUDPSenderAppend(t *testing.T) { + agent, err := testutils.StartMockAgent() + require.NoError(t, err) + defer agent.Close() + + span := &Span{operationName: "test-span", tracer: jaegerTracer} + spanSize := getThriftSpanByteLength(t, span) + processSize := getThriftProcessByteLengthFromTracer(t, jaegerTracer) + + tests := []struct { + bufferSizeOffset int + expectFlush bool + expectSpansFlushed int + expectBatchesFlushed int + manualFlush bool + expectSpansFlushed2 int + expectBatchesFlushed2 int + description string + }{ + {1, false, 0, 0, true, 5, 1, "in test: buffer bigger than 5 spans"}, + {0, true, 5, 1, false, 0, 0, "in test: buffer fits exactly 5 spans"}, + {-1, true, 4, 1, true, 1, 1, "in test: buffer smaller than 5 spans"}, + } + + for _, test := range tests { + bufferSize := 5*spanSize + test.bufferSizeOffset + processSize + emitBatchOverhead + sender, err := NewUDPTransport(agent.SpanServerAddr(), bufferSize) + require.NoError(t, err, test.description) + + agent.ResetJaegerBatches() + for i := 0; i < 5; i++ { + n, err := sender.Append(span) + require.NoError(t, err, test.description) + if i < 4 { + assert.Equal(t, 0, n, test.description) + } else { + assert.Equal(t, test.expectSpansFlushed, n, test.description) + } + } + if test.expectFlush { + time.Sleep(5 * time.Millisecond) + } + batches := agent.GetJaegerBatches() + require.Equal(t, test.expectBatchesFlushed, len(batches), test.description) + var spans []*j.Span + if test.expectBatchesFlushed > 0 { + spans = batches[0].Spans + } + require.Equal(t, test.expectSpansFlushed, len(spans), test.description) + for i := 0; i < test.expectSpansFlushed; i++ { + assert.Equal(t, span.operationName, spans[i].OperationName, test.description) + } + + if test.manualFlush { + agent.ResetJaegerBatches() + n, err := sender.Flush() + require.NoError(t, err, test.description) + assert.Equal(t, test.expectSpansFlushed2, n, test.description) + + time.Sleep(5 * time.Millisecond) + batches = agent.GetJaegerBatches() + require.Equal(t, test.expectBatchesFlushed2, len(batches), test.description) + spans = []*j.Span{} + if test.expectBatchesFlushed2 > 0 { + spans = batches[0].Spans + } + require.Equal(t, test.expectSpansFlushed2, len(spans), test.description) + for i := 0; i < test.expectSpansFlushed2; i++ { + assert.Equal(t, span.operationName, spans[i].OperationName, test.description) + } + } + + } +} + +func TestUDPSenderHugeSpan(t *testing.T) { + agent, err := testutils.StartMockAgent() + require.NoError(t, err) + defer agent.Close() + + span := &Span{operationName: "test-span", tracer: jaegerTracer} + spanSize := getThriftSpanByteLength(t, span) + + sender, err := NewUDPTransport(agent.SpanServerAddr(), spanSize/2+emitBatchOverhead) + require.NoError(t, err) + + n, err := sender.Append(span) + assert.Equal(t, errSpanTooLarge, err) + assert.Equal(t, 1, n) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/travis/build-crossdock.sh b/template/faaschain/vendor/github.com/uber/jaeger-client-go/travis/build-crossdock.sh new file mode 100644 index 00000000..8e98d081 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/travis/build-crossdock.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +set -e + +make crossdock + +export REPO=jaegertracing/xdock-go +export BRANCH=$(if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then echo $TRAVIS_BRANCH; else echo $TRAVIS_PULL_REQUEST_BRANCH; fi) +export TAG=`if [ "$BRANCH" == "master" ]; then echo "latest"; else echo "${BRANCH///}"; fi` +echo "TRAVIS_BRANCH=$TRAVIS_BRANCH, REPO=$REPO, PR=$PR, BRANCH=$BRANCH, TAG=$TAG" + +# Only push the docker container to Docker Hub for master branch +if [[ "$BRANCH" == "master" && "$TRAVIS_SECURE_ENV_VARS" == "true" ]]; then echo 'upload to Docker Hub'; else echo 'skip docker upload for PR'; exit 0; fi + +docker login -u $DOCKER_USER -p $DOCKER_PASS + +set -x + +docker build -f crossdock/Dockerfile -t $REPO:$COMMIT . + +docker tag $REPO:$COMMIT $REPO:$TAG +docker tag $REPO:$COMMIT $REPO:travis-$TRAVIS_BUILD_NUMBER +docker push $REPO diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/travis/install-crossdock-deps.sh b/template/faaschain/vendor/github.com/uber/jaeger-client-go/travis/install-crossdock-deps.sh new file mode 100644 index 00000000..f2e15fe7 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/travis/install-crossdock-deps.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +set -e + +docker version + +# Install docker-compose +sudo rm -f /usr/local/bin/docker-compose +curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose +chmod +x docker-compose +sudo mv docker-compose /usr/local/bin +docker-compose version diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/http_json.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/http_json.go new file mode 100644 index 00000000..237211f8 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/http_json.go @@ -0,0 +1,54 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package utils + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" +) + +// GetJSON makes an HTTP call to the specified URL and parses the returned JSON into `out`. +func GetJSON(url string, out interface{}) error { + resp, err := http.Get(url) + if err != nil { + return err + } + return ReadJSON(resp, out) +} + +// ReadJSON reads JSON from http.Response and parses it into `out` +func ReadJSON(resp *http.Response, out interface{}) error { + defer resp.Body.Close() + + if resp.StatusCode >= 400 { + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + + return fmt.Errorf("StatusCode: %d, Body: %s", resp.StatusCode, body) + } + + if out == nil { + io.Copy(ioutil.Discard, resp.Body) + return nil + } + + decoder := json.NewDecoder(resp.Body) + return decoder.Decode(out) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/http_json_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/http_json_test.go new file mode 100644 index 00000000..6ee984a9 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/http_json_test.go @@ -0,0 +1,58 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package utils + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testJSONStruct struct { + Name string + Age int +} + +func TestGetJSON(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("Content-Type", "application/json") + w.Write([]byte("{\"name\": \"Bender\", \"age\": 3}")) + })) + defer server.Close() + + var s testJSONStruct + err := GetJSON(server.URL, &s) + require.NoError(t, err) + + assert.Equal(t, "Bender", s.Name) + assert.Equal(t, 3, s.Age) +} + +func TestGetJSONErrors(t *testing.T) { + var s testJSONStruct + err := GetJSON("localhost:0", &s) + assert.Error(t, err) + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.Error(w, "some error", http.StatusInternalServerError) + })) + defer server.Close() + + err = GetJSON(server.URL, &s) + assert.Error(t, err) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/localip.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/localip.go new file mode 100644 index 00000000..b51af771 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/localip.go @@ -0,0 +1,84 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package utils + +import ( + "errors" + "net" +) + +// This code is borrowed from https://github.com/uber/tchannel-go/blob/dev/localip.go + +// scoreAddr scores how likely the given addr is to be a remote address and returns the +// IP to use when listening. Any address which receives a negative score should not be used. +// Scores are calculated as: +// -1 for any unknown IP addresses. +// +300 for IPv4 addresses +// +100 for non-local addresses, extra +100 for "up" interaces. +func scoreAddr(iface net.Interface, addr net.Addr) (int, net.IP) { + var ip net.IP + if netAddr, ok := addr.(*net.IPNet); ok { + ip = netAddr.IP + } else if netIP, ok := addr.(*net.IPAddr); ok { + ip = netIP.IP + } else { + return -1, nil + } + + var score int + if ip.To4() != nil { + score += 300 + } + if iface.Flags&net.FlagLoopback == 0 && !ip.IsLoopback() { + score += 100 + if iface.Flags&net.FlagUp != 0 { + score += 100 + } + } + return score, ip +} + +// HostIP tries to find an IP that can be used by other machines to reach this machine. +func HostIP() (net.IP, error) { + interfaces, err := net.Interfaces() + if err != nil { + return nil, err + } + + bestScore := -1 + var bestIP net.IP + // Select the highest scoring IP as the best IP. + for _, iface := range interfaces { + addrs, err := iface.Addrs() + if err != nil { + // Skip this interface if there is an error. + continue + } + + for _, addr := range addrs { + score, ip := scoreAddr(iface, addr) + if score > bestScore { + bestScore = score + bestIP = ip + } + } + } + + if bestScore == -1 { + return nil, errors.New("no addresses to listen on") + } + + return bestIP, nil +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/rand.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/rand.go new file mode 100644 index 00000000..9875f7f5 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/rand.go @@ -0,0 +1,46 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package utils + +import ( + "math/rand" + "sync" +) + +// lockedSource allows a random number generator to be used by multiple goroutines concurrently. +// The code is very similar to math/rand.lockedSource, which is unfortunately not exposed. +type lockedSource struct { + mut sync.Mutex + src rand.Source +} + +// NewRand returns a rand.Rand that is threadsafe. +func NewRand(seed int64) *rand.Rand { + return rand.New(&lockedSource{src: rand.NewSource(seed)}) +} + +func (r *lockedSource) Int63() (n int64) { + r.mut.Lock() + n = r.src.Int63() + r.mut.Unlock() + return +} + +// Seed implements Seed() of Source +func (r *lockedSource) Seed(seed int64) { + r.mut.Lock() + r.src.Seed(seed) + r.mut.Unlock() +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/rate_limiter.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/rate_limiter.go new file mode 100644 index 00000000..1b8db975 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/rate_limiter.go @@ -0,0 +1,77 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package utils + +import ( + "sync" + "time" +) + +// RateLimiter is a filter used to check if a message that is worth itemCost units is within the rate limits. +type RateLimiter interface { + CheckCredit(itemCost float64) bool +} + +type rateLimiter struct { + sync.Mutex + + creditsPerSecond float64 + balance float64 + maxBalance float64 + lastTick time.Time + + timeNow func() time.Time +} + +// NewRateLimiter creates a new rate limiter based on leaky bucket algorithm, formulated in terms of a +// credits balance that is replenished every time CheckCredit() method is called (tick) by the amount proportional +// to the time elapsed since the last tick, up to max of creditsPerSecond. A call to CheckCredit() takes a cost +// of an item we want to pay with the balance. If the balance exceeds the cost of the item, the item is "purchased" +// and the balance reduced, indicated by returned value of true. Otherwise the balance is unchanged and return false. +// +// This can be used to limit a rate of messages emitted by a service by instantiating the Rate Limiter with the +// max number of messages a service is allowed to emit per second, and calling CheckCredit(1.0) for each message +// to determine if the message is within the rate limit. +// +// It can also be used to limit the rate of traffic in bytes, by setting creditsPerSecond to desired throughput +// as bytes/second, and calling CheckCredit() with the actual message size. +func NewRateLimiter(creditsPerSecond, maxBalance float64) RateLimiter { + return &rateLimiter{ + creditsPerSecond: creditsPerSecond, + balance: maxBalance, + maxBalance: maxBalance, + lastTick: time.Now(), + timeNow: time.Now} +} + +func (b *rateLimiter) CheckCredit(itemCost float64) bool { + b.Lock() + defer b.Unlock() + // calculate how much time passed since the last tick, and update current tick + currentTime := b.timeNow() + elapsedTime := currentTime.Sub(b.lastTick) + b.lastTick = currentTime + // calculate how much credit have we accumulated since the last tick + b.balance += elapsedTime.Seconds() * b.creditsPerSecond + if b.balance > b.maxBalance { + b.balance = b.maxBalance + } + // if we have enough credits to pay for current item, then reduce balance and allow + if b.balance >= itemCost { + b.balance -= itemCost + return true + } + return false +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/rate_limiter_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/rate_limiter_test.go new file mode 100644 index 00000000..a075afb4 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/rate_limiter_test.go @@ -0,0 +1,75 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package utils + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestRateLimiter(t *testing.T) { + limiter := NewRateLimiter(2.0, 2.0) + // stop time + ts := time.Now() + limiter.(*rateLimiter).lastTick = ts + limiter.(*rateLimiter).timeNow = func() time.Time { + return ts + } + assert.True(t, limiter.CheckCredit(1.0)) + assert.True(t, limiter.CheckCredit(1.0)) + assert.False(t, limiter.CheckCredit(1.0)) + // move time 250ms forward, not enough credits to pay for 1.0 item + limiter.(*rateLimiter).timeNow = func() time.Time { + return ts.Add(time.Second / 4) + } + assert.False(t, limiter.CheckCredit(1.0)) + // move time 500ms forward, now enough credits to pay for 1.0 item + limiter.(*rateLimiter).timeNow = func() time.Time { + return ts.Add(time.Second/4 + time.Second/2) + } + assert.True(t, limiter.CheckCredit(1.0)) + assert.False(t, limiter.CheckCredit(1.0)) + // move time 5s forward, enough to accumulate credits for 10 messages, but it should still be capped at 2 + limiter.(*rateLimiter).lastTick = ts + limiter.(*rateLimiter).timeNow = func() time.Time { + return ts.Add(5 * time.Second) + } + assert.True(t, limiter.CheckCredit(1.0)) + assert.True(t, limiter.CheckCredit(1.0)) + assert.False(t, limiter.CheckCredit(1.0)) + assert.False(t, limiter.CheckCredit(1.0)) + assert.False(t, limiter.CheckCredit(1.0)) +} + +func TestMaxBalance(t *testing.T) { + limiter := NewRateLimiter(0.1, 1.0) + // stop time + ts := time.Now() + limiter.(*rateLimiter).lastTick = ts + limiter.(*rateLimiter).timeNow = func() time.Time { + return ts + } + // on initialization, should have enough credits for 1 message + assert.True(t, limiter.CheckCredit(1.0)) + + // move time 20s forward, enough to accumulate credits for 2 messages, but it should still be capped at 1 + limiter.(*rateLimiter).timeNow = func() time.Time { + return ts.Add(time.Second * 20) + } + assert.True(t, limiter.CheckCredit(1.0)) + assert.False(t, limiter.CheckCredit(1.0)) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/udp_client.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/udp_client.go new file mode 100644 index 00000000..6f042073 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/udp_client.go @@ -0,0 +1,98 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package utils + +import ( + "errors" + "fmt" + "io" + "net" + + "github.com/uber/jaeger-client-go/thrift" + + "github.com/uber/jaeger-client-go/thrift-gen/agent" + "github.com/uber/jaeger-client-go/thrift-gen/jaeger" + "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" +) + +// UDPPacketMaxLength is the max size of UDP packet we want to send, synced with jaeger-agent +const UDPPacketMaxLength = 65000 + +// AgentClientUDP is a UDP client to Jaeger agent that implements agent.Agent interface. +type AgentClientUDP struct { + agent.Agent + io.Closer + + connUDP *net.UDPConn + client *agent.AgentClient + maxPacketSize int // max size of datagram in bytes + thriftBuffer *thrift.TMemoryBuffer // buffer used to calculate byte size of a span +} + +// NewAgentClientUDP creates a client that sends spans to Jaeger Agent over UDP. +func NewAgentClientUDP(hostPort string, maxPacketSize int) (*AgentClientUDP, error) { + if maxPacketSize == 0 { + maxPacketSize = UDPPacketMaxLength + } + + thriftBuffer := thrift.NewTMemoryBufferLen(maxPacketSize) + protocolFactory := thrift.NewTCompactProtocolFactory() + client := agent.NewAgentClientFactory(thriftBuffer, protocolFactory) + + destAddr, err := net.ResolveUDPAddr("udp", hostPort) + if err != nil { + return nil, err + } + + connUDP, err := net.DialUDP(destAddr.Network(), nil, destAddr) + if err != nil { + return nil, err + } + if err := connUDP.SetWriteBuffer(maxPacketSize); err != nil { + return nil, err + } + + clientUDP := &AgentClientUDP{ + connUDP: connUDP, + client: client, + maxPacketSize: maxPacketSize, + thriftBuffer: thriftBuffer} + return clientUDP, nil +} + +// EmitZipkinBatch implements EmitZipkinBatch() of Agent interface +func (a *AgentClientUDP) EmitZipkinBatch(spans []*zipkincore.Span) error { + return errors.New("Not implemented") +} + +// EmitBatch implements EmitBatch() of Agent interface +func (a *AgentClientUDP) EmitBatch(batch *jaeger.Batch) error { + a.thriftBuffer.Reset() + a.client.SeqId = 0 // we have no need for distinct SeqIds for our one-way UDP messages + if err := a.client.EmitBatch(batch); err != nil { + return err + } + if a.thriftBuffer.Len() > a.maxPacketSize { + return fmt.Errorf("Data does not fit within one UDP packet; size %d, max %d, spans %d", + a.thriftBuffer.Len(), a.maxPacketSize, len(batch.Spans)) + } + _, err := a.connUDP.Write(a.thriftBuffer.Bytes()) + return err +} + +// Close implements Close() of io.Closer and closes the underlying UDP connection. +func (a *AgentClientUDP) Close() error { + return a.connUDP.Close() +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/utils.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/utils.go new file mode 100644 index 00000000..ac3c325d --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/utils.go @@ -0,0 +1,87 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package utils + +import ( + "encoding/binary" + "errors" + "net" + "strconv" + "strings" + "time" +) + +var ( + // ErrEmptyIP an error for empty ip strings + ErrEmptyIP = errors.New("empty string given for ip") + + // ErrNotHostColonPort an error for invalid host port string + ErrNotHostColonPort = errors.New("expecting host:port") + + // ErrNotFourOctets an error for the wrong number of octets after splitting a string + ErrNotFourOctets = errors.New("Wrong number of octets") +) + +// ParseIPToUint32 converts a string ip (e.g. "x.y.z.w") to an uint32 +func ParseIPToUint32(ip string) (uint32, error) { + if ip == "" { + return 0, ErrEmptyIP + } + + if ip == "localhost" { + return 127<<24 | 1, nil + } + + octets := strings.Split(ip, ".") + if len(octets) != 4 { + return 0, ErrNotFourOctets + } + + var intIP uint32 + for i := 0; i < 4; i++ { + octet, err := strconv.Atoi(octets[i]) + if err != nil { + return 0, err + } + intIP = (intIP << 8) | uint32(octet) + } + + return intIP, nil +} + +// ParsePort converts port number from string to uin16 +func ParsePort(portString string) (uint16, error) { + port, err := strconv.ParseUint(portString, 10, 16) + return uint16(port), err +} + +// PackIPAsUint32 packs an IPv4 as uint32 +func PackIPAsUint32(ip net.IP) uint32 { + if ipv4 := ip.To4(); ipv4 != nil { + return binary.BigEndian.Uint32(ipv4) + } + return 0 +} + +// TimeToMicrosecondsSinceEpochInt64 converts Go time.Time to a long +// representing time since epoch in microseconds, which is used expected +// in the Jaeger spans encoded as Thrift. +func TimeToMicrosecondsSinceEpochInt64(t time.Time) int64 { + // ^^^ Passing time.Time by value is faster than passing a pointer! + // BenchmarkTimeByValue-8 2000000000 1.37 ns/op + // BenchmarkTimeByPtr-8 2000000000 1.98 ns/op + + return t.UnixNano() / 1000 +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/utils_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/utils_test.go new file mode 100644 index 00000000..1df8e0b3 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/utils/utils_test.go @@ -0,0 +1,91 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package utils + +import ( + "net" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetLocalIP(t *testing.T) { + ip, _ := HostIP() + assert.NotNil(t, ip, "assert we have an ip") +} + +func TestParseIPToUint32(t *testing.T) { + tests := []struct { + in string + out uint32 + err error + }{ + {"1.2.3.4", 1<<24 | 2<<16 | 3<<8 | 4, nil}, + {"127.0.0.1", 127<<24 | 1, nil}, + {"localhost", 127<<24 | 1, nil}, + {"127.xxx.0.1", 0, nil}, + {"", 0, ErrEmptyIP}, + {"hostname", 0, ErrNotFourOctets}, + } + + for _, test := range tests { + intIP, err := ParseIPToUint32(test.in) + if test.err != nil { + assert.Equal(t, test.err, err) + } else { + assert.Equal(t, test.out, intIP) + } + + } +} + +func TestParsePort(t *testing.T) { + tests := []struct { + in string + out uint16 + err bool + }{ + {"123", 123, false}, + {"77777", 0, true}, // too large for 16bit + {"bad-wolf", 0, true}, + } + for _, test := range tests { + p, err := ParsePort(test.in) + if test.err { + assert.Error(t, err) + } else { + assert.Equal(t, test.out, p) + } + } +} + +func TestPackIPAsUint32(t *testing.T) { + ipv6a := net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 1, 2, 3, 4} + ipv6b := net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334") + assert.NotNil(t, ipv6a) + + tests := []struct { + in net.IP + out uint32 + }{ + {net.IPv4(1, 2, 3, 4), 1<<24 | 2<<16 | 3<<8 | 4}, + {ipv6a, 1<<24 | 2<<16 | 3<<8 | 4}, // IPv6 but convertible to IPv4 + {ipv6b, 0}, + } + for _, test := range tests { + ip := PackIPAsUint32(test.in) + assert.Equal(t, test.out, ip) + } +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin.go new file mode 100644 index 00000000..636952b7 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin.go @@ -0,0 +1,76 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "github.com/opentracing/opentracing-go" +) + +// ZipkinSpanFormat is an OpenTracing carrier format constant +const ZipkinSpanFormat = "zipkin-span-format" + +// ExtractableZipkinSpan is a type of Carrier used for integration with Zipkin-aware +// RPC frameworks (like TChannel). It does not support baggage, only trace IDs. +type ExtractableZipkinSpan interface { + TraceID() uint64 + SpanID() uint64 + ParentID() uint64 + Flags() byte +} + +// InjectableZipkinSpan is a type of Carrier used for integration with Zipkin-aware +// RPC frameworks (like TChannel). It does not support baggage, only trace IDs. +type InjectableZipkinSpan interface { + SetTraceID(traceID uint64) + SetSpanID(spanID uint64) + SetParentID(parentID uint64) + SetFlags(flags byte) +} + +type zipkinPropagator struct { + tracer *Tracer +} + +func (p *zipkinPropagator) Inject( + ctx SpanContext, + abstractCarrier interface{}, +) error { + carrier, ok := abstractCarrier.(InjectableZipkinSpan) + if !ok { + return opentracing.ErrInvalidCarrier + } + + carrier.SetTraceID(ctx.TraceID().Low) // TODO this cannot work with 128bit IDs + carrier.SetSpanID(uint64(ctx.SpanID())) + carrier.SetParentID(uint64(ctx.ParentID())) + carrier.SetFlags(ctx.flags) + return nil +} + +func (p *zipkinPropagator) Extract(abstractCarrier interface{}) (SpanContext, error) { + carrier, ok := abstractCarrier.(ExtractableZipkinSpan) + if !ok { + return emptyContext, opentracing.ErrInvalidCarrier + } + if carrier.TraceID() == 0 { + return emptyContext, opentracing.ErrSpanContextNotFound + } + var ctx SpanContext + ctx.traceID.Low = carrier.TraceID() + ctx.spanID = SpanID(carrier.SpanID()) + ctx.parentID = SpanID(carrier.ParentID()) + ctx.flags = carrier.Flags() + return ctx, nil +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin/README.md b/template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin/README.md new file mode 100644 index 00000000..6e51f10d --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin/README.md @@ -0,0 +1,91 @@ +# Zipkin compatibility features + +## `NewZipkinB3HTTPHeaderPropagator()` + +Adds support for injecting and extracting Zipkin B3 Propagation HTTP headers, +for use with other Zipkin collectors. + +```go + +// ... +import ( + opentracing "github.com/opentracing/opentracing-go" + jaeger "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/zipkin" +) + +func main() { + // ... + + zipkinPropagator := zipkin.NewZipkinB3HTTPHeaderPropagator() + injector := jaeger.TracerOptions.Injector(opentracing.HTTPHeaders, zipkinPropagator) + extractor := jaeger.TracerOptions.Extractor(opentracing.HTTPHeaders, zipkinPropagator) + + // Zipkin shares span ID between client and server spans; it must be enabled via the following option. + zipkinSharedRPCSpan := jaeger.TracerOptions.ZipkinSharedRPCSpan(true) + + // create Jaeger tracer + tracer, closer := jaeger.NewTracer( + "myService", + mySampler, // as usual + myReporter // as usual + injector, + extractor, + zipkinSharedRPCSpan, + ) + + opentracing.SetGlobalTracer(tracer) + + // continue main() +} +``` + +If you'd like to follow the official guides from https://godoc.org/github.com/uber/jaeger-client-go/config#example-Configuration-InitGlobalTracer-Production, here is an example. + +```go +import ( + "time" + + opentracing "github.com/opentracing/opentracing-go" + "github.com/uber/jaeger-client-go" + jaegerClientConfig "github.com/uber/jaeger-client-go/config" + "github.com/uber/jaeger-client-go/zipkin" + "github.com/uber/jaeger-client-go/log" + "github.com/uber/jaeger-lib/metrics" +) + +func main(){ + //... + + // Recommended configuration for production. + cfg := jaegercfg.Configuration{} + + // Example logger and metrics factory. Use github.com/uber/jaeger-client-go/log + // and github.com/uber/jaeger-lib/metrics respectively to bind to real logging and metrics + // frameworks. + jLogger := jaegerlog.StdLogger + jMetricsFactory := metrics.NullFactory + + // Zipkin shares span ID between client and server spans; it must be enabled via the following option. + zipkinPropagator := zipkin.NewZipkinB3HTTPHeaderPropagator() + + // Create tracer and then initialize global tracer + closer, err := cfg.InitGlobalTracer( + serviceName, + jaegercfg.Logger(jLogger), + jaegercfg.Metrics(jMetricsFactory), + jaegercfg.Injector(opentracing.HTTPHeaders, zipkinPropagator), + jaegercfg.Extractor(opentracing.HTTPHeaders, zipkinPropagator), + jaegercfg.ZipkinSharedRPCSpan(true), + ) + + if err != nil { + log.Printf("Could not initialize jaeger tracer: %s", err.Error()) + return + } + defer closer.Close() + + // continue main() +} + +``` diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin/doc.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin/doc.go new file mode 100644 index 00000000..11357f8a --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin/doc.go @@ -0,0 +1,16 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +// Package zipkin comprises Zipkin functionality for Zipkin compatiblity. +package zipkin diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin/propagation.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin/propagation.go new file mode 100644 index 00000000..000c52f7 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin/propagation.go @@ -0,0 +1,95 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package zipkin + +import ( + "strconv" + "strings" + + opentracing "github.com/opentracing/opentracing-go" + + "github.com/uber/jaeger-client-go" +) + +// Propagator is an Injector and Extractor +type Propagator struct{} + +// NewZipkinB3HTTPHeaderPropagator creates a Propagator for extracting and injecting +// Zipkin HTTP B3 headers into SpanContexts. +func NewZipkinB3HTTPHeaderPropagator() Propagator { + return Propagator{} +} + +// Inject conforms to the Injector interface for decoding Zipkin HTTP B3 headers +func (p Propagator) Inject( + sc jaeger.SpanContext, + abstractCarrier interface{}, +) error { + textMapWriter, ok := abstractCarrier.(opentracing.TextMapWriter) + if !ok { + return opentracing.ErrInvalidCarrier + } + + // TODO this needs to change to support 128bit IDs + textMapWriter.Set("x-b3-traceid", strconv.FormatUint(sc.TraceID().Low, 16)) + if sc.ParentID() != 0 { + textMapWriter.Set("x-b3-parentspanid", strconv.FormatUint(uint64(sc.ParentID()), 16)) + } + textMapWriter.Set("x-b3-spanid", strconv.FormatUint(uint64(sc.SpanID()), 16)) + if sc.IsSampled() { + textMapWriter.Set("x-b3-sampled", "1") + } else { + textMapWriter.Set("x-b3-sampled", "0") + } + return nil +} + +// Extract conforms to the Extractor interface for encoding Zipkin HTTP B3 headers +func (p Propagator) Extract(abstractCarrier interface{}) (jaeger.SpanContext, error) { + textMapReader, ok := abstractCarrier.(opentracing.TextMapReader) + if !ok { + return jaeger.SpanContext{}, opentracing.ErrInvalidCarrier + } + var traceID uint64 + var spanID uint64 + var parentID uint64 + sampled := false + err := textMapReader.ForeachKey(func(rawKey, value string) error { + key := strings.ToLower(rawKey) // TODO not necessary for plain TextMap + var err error + if key == "x-b3-traceid" { + traceID, err = strconv.ParseUint(value, 16, 64) + } else if key == "x-b3-parentspanid" { + parentID, err = strconv.ParseUint(value, 16, 64) + } else if key == "x-b3-spanid" { + spanID, err = strconv.ParseUint(value, 16, 64) + } else if key == "x-b3-sampled" && value == "1" { + sampled = true + } + return err + }) + + if err != nil { + return jaeger.SpanContext{}, err + } + if traceID == 0 { + return jaeger.SpanContext{}, opentracing.ErrSpanContextNotFound + } + return jaeger.NewSpanContext( + jaeger.TraceID{Low: traceID}, + jaeger.SpanID(spanID), + jaeger.SpanID(parentID), + sampled, nil), nil +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin/propagation_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin/propagation_test.go new file mode 100644 index 00000000..8ef4f3cf --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin/propagation_test.go @@ -0,0 +1,113 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package zipkin + +import ( + "testing" + + opentracing "github.com/opentracing/opentracing-go" + "github.com/stretchr/testify/assert" + "github.com/uber/jaeger-client-go" +) + +var ( + rootSampled = newSpanContext(1, 2, 0, true) + nonRootSampled = newSpanContext(1, 2, 1, true) + nonRootNonSampled = newSpanContext(1, 2, 1, false) +) + +var ( + rootSampledHeader = opentracing.TextMapCarrier{ + "x-b3-traceid": "1", + "x-b3-spanid": "2", + "x-b3-sampled": "1", + } + nonRootSampledHeader = opentracing.TextMapCarrier{ + "x-b3-traceid": "1", + "x-b3-spanid": "2", + "x-b3-parentspanid": "1", + "x-b3-sampled": "1", + } + nonRootNonSampledHeader = opentracing.TextMapCarrier{ + "x-b3-traceid": "1", + "x-b3-spanid": "2", + "x-b3-parentspanid": "1", + "x-b3-sampled": "0", + } + invalidHeader = opentracing.TextMapCarrier{ + "x-b3-traceid": "jdkafhsd", + "x-b3-spanid": "afsdfsdf", + "x-b3-parentspanid": "hiagggdf", + "x-b3-sampled": "sdfgsdfg", + } +) + +var ( + propagator = NewZipkinB3HTTPHeaderPropagator() +) + +func newSpanContext(traceID, spanID, parentID uint64, sampled bool) jaeger.SpanContext { + return jaeger.NewSpanContext( + jaeger.TraceID{Low: traceID}, + jaeger.SpanID(spanID), + jaeger.SpanID(parentID), + sampled, + nil, + ) +} + +func TestExtractorInvalid(t *testing.T) { + _, err := propagator.Extract(invalidHeader) + assert.Error(t, err) +} + +func TestExtractorRootSampled(t *testing.T) { + ctx, err := propagator.Extract(rootSampledHeader) + assert.Nil(t, err) + assert.EqualValues(t, rootSampled, ctx) +} + +func TestExtractorNonRootSampled(t *testing.T) { + ctx, err := propagator.Extract(nonRootSampledHeader) + assert.Nil(t, err) + assert.EqualValues(t, nonRootSampled, ctx) +} + +func TestExtractorNonRootNonSampled(t *testing.T) { + ctx, err := propagator.Extract(nonRootNonSampledHeader) + assert.Nil(t, err) + assert.EqualValues(t, nonRootNonSampled, ctx) +} + +func TestInjectorRootSampled(t *testing.T) { + hdr := opentracing.TextMapCarrier{} + err := propagator.Inject(rootSampled, hdr) + assert.Nil(t, err) + assert.EqualValues(t, rootSampledHeader, hdr) +} + +func TestInjectorNonRootSampled(t *testing.T) { + hdr := opentracing.TextMapCarrier{} + err := propagator.Inject(nonRootSampled, hdr) + assert.Nil(t, err) + assert.EqualValues(t, nonRootSampledHeader, hdr) +} + +func TestInjectorNonRootNonSampled(t *testing.T) { + hdr := opentracing.TextMapCarrier{} + err := propagator.Inject(nonRootNonSampled, hdr) + assert.Nil(t, err) + assert.EqualValues(t, nonRootNonSampledHeader, hdr) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin_test.go new file mode 100644 index 00000000..2d1d464e --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin_test.go @@ -0,0 +1,68 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "testing" + + "github.com/opentracing/opentracing-go/ext" + "github.com/stretchr/testify/assert" +) + +func TestZipkinPropagator(t *testing.T) { + tracer, tCloser := NewTracer("x", NewConstSampler(true), NewNullReporter(), TracerOptions.ZipkinSharedRPCSpan(true)) + defer tCloser.Close() + + carrier := &TestZipkinSpan{} + sp := tracer.StartSpan("y") + + // Note: we intentionally use string as format, as that's what TChannel would need to do + if err := tracer.Inject(sp.Context(), "zipkin-span-format", carrier); err != nil { + t.Fatalf("Inject failed: %+v", err) + } + sp1 := sp.(*Span) + assert.Equal(t, sp1.context.traceID, TraceID{Low: carrier.traceID}) + assert.Equal(t, sp1.context.spanID, SpanID(carrier.spanID)) + assert.Equal(t, sp1.context.parentID, SpanID(carrier.parentID)) + assert.Equal(t, sp1.context.flags, carrier.flags) + + sp2ctx, err := tracer.Extract("zipkin-span-format", carrier) + if err != nil { + t.Fatalf("Extract failed: %+v", err) + } + sp2 := tracer.StartSpan("x", ext.RPCServerOption(sp2ctx)) + sp3 := sp2.(*Span) + assert.Equal(t, sp1.context.traceID, sp3.context.traceID) + assert.Equal(t, sp1.context.spanID, sp3.context.spanID) + assert.Equal(t, sp1.context.parentID, sp3.context.parentID) + assert.Equal(t, sp1.context.flags, sp3.context.flags) +} + +// TestZipkinSpan is a mock-up of TChannel's internal Span struct +type TestZipkinSpan struct { + traceID uint64 + parentID uint64 + spanID uint64 + flags byte +} + +func (s TestZipkinSpan) TraceID() uint64 { return s.traceID } +func (s TestZipkinSpan) ParentID() uint64 { return s.parentID } +func (s TestZipkinSpan) SpanID() uint64 { return s.spanID } +func (s TestZipkinSpan) Flags() byte { return s.flags } +func (s *TestZipkinSpan) SetTraceID(traceID uint64) { s.traceID = traceID } +func (s *TestZipkinSpan) SetSpanID(spanID uint64) { s.spanID = spanID } +func (s *TestZipkinSpan) SetParentID(parentID uint64) { s.parentID = parentID } +func (s *TestZipkinSpan) SetFlags(flags byte) { s.flags = flags } diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin_thrift_span.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin_thrift_span.go new file mode 100644 index 00000000..dce58b43 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin_thrift_span.go @@ -0,0 +1,322 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "encoding/binary" + "fmt" + "time" + + "github.com/opentracing/opentracing-go/ext" + + "github.com/uber/jaeger-client-go/internal/spanlog" + z "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" + "github.com/uber/jaeger-client-go/utils" +) + +const ( + // Zipkin UI does not work well with non-string tag values + allowPackedNumbers = false +) + +var specialTagHandlers = map[string]func(*zipkinSpan, interface{}){ + string(ext.SpanKind): setSpanKind, + string(ext.PeerHostIPv4): setPeerIPv4, + string(ext.PeerPort): setPeerPort, + string(ext.PeerService): setPeerService, + TracerIPTagKey: removeTag, +} + +// BuildZipkinThrift builds thrift span based on internal span. +func BuildZipkinThrift(s *Span) *z.Span { + span := &zipkinSpan{Span: s} + span.handleSpecialTags() + parentID := int64(span.context.parentID) + var ptrParentID *int64 + if parentID != 0 { + ptrParentID = &parentID + } + timestamp := utils.TimeToMicrosecondsSinceEpochInt64(span.startTime) + duration := span.duration.Nanoseconds() / int64(time.Microsecond) + endpoint := &z.Endpoint{ + ServiceName: span.tracer.serviceName, + Ipv4: int32(span.tracer.hostIPv4)} + thriftSpan := &z.Span{ + TraceID: int64(span.context.traceID.Low), // TODO upgrade zipkin thrift and use TraceIdHigh + ID: int64(span.context.spanID), + ParentID: ptrParentID, + Name: span.operationName, + Timestamp: ×tamp, + Duration: &duration, + Debug: span.context.IsDebug(), + Annotations: buildAnnotations(span, endpoint), + BinaryAnnotations: buildBinaryAnnotations(span, endpoint)} + return thriftSpan +} + +func buildAnnotations(span *zipkinSpan, endpoint *z.Endpoint) []*z.Annotation { + // automatically adding 2 Zipkin CoreAnnotations + annotations := make([]*z.Annotation, 0, 2+len(span.logs)) + var startLabel, endLabel string + if span.spanKind == string(ext.SpanKindRPCClientEnum) { + startLabel, endLabel = z.CLIENT_SEND, z.CLIENT_RECV + } else if span.spanKind == string(ext.SpanKindRPCServerEnum) { + startLabel, endLabel = z.SERVER_RECV, z.SERVER_SEND + } + if !span.startTime.IsZero() && startLabel != "" { + start := &z.Annotation{ + Timestamp: utils.TimeToMicrosecondsSinceEpochInt64(span.startTime), + Value: startLabel, + Host: endpoint} + annotations = append(annotations, start) + if span.duration != 0 { + endTs := span.startTime.Add(span.duration) + end := &z.Annotation{ + Timestamp: utils.TimeToMicrosecondsSinceEpochInt64(endTs), + Value: endLabel, + Host: endpoint} + annotations = append(annotations, end) + } + } + for _, log := range span.logs { + anno := &z.Annotation{ + Timestamp: utils.TimeToMicrosecondsSinceEpochInt64(log.Timestamp), + Host: endpoint} + if content, err := spanlog.MaterializeWithJSON(log.Fields); err == nil { + anno.Value = truncateString(string(content), span.tracer.options.maxTagValueLength) + } else { + anno.Value = err.Error() + } + annotations = append(annotations, anno) + } + return annotations +} + +func buildBinaryAnnotations(span *zipkinSpan, endpoint *z.Endpoint) []*z.BinaryAnnotation { + // automatically adding local component or server/client address tag, and client version + annotations := make([]*z.BinaryAnnotation, 0, 2+len(span.tags)) + + if span.peerDefined() && span.isRPC() { + peer := z.Endpoint{ + Ipv4: span.peer.Ipv4, + Port: span.peer.Port, + ServiceName: span.peer.ServiceName} + label := z.CLIENT_ADDR + if span.isRPCClient() { + label = z.SERVER_ADDR + } + anno := &z.BinaryAnnotation{ + Key: label, + Value: []byte{1}, + AnnotationType: z.AnnotationType_BOOL, + Host: &peer} + annotations = append(annotations, anno) + } + if !span.isRPC() { + componentName := endpoint.ServiceName + for _, tag := range span.tags { + if tag.key == string(ext.Component) { + componentName = stringify(tag.value) + break + } + } + local := &z.BinaryAnnotation{ + Key: z.LOCAL_COMPONENT, + Value: []byte(componentName), + AnnotationType: z.AnnotationType_STRING, + Host: endpoint} + annotations = append(annotations, local) + } + for _, tag := range span.tags { + // "Special tags" are already handled by this point, we'd be double reporting the + // tags if we don't skip here + if _, ok := specialTagHandlers[tag.key]; ok { + continue + } + if anno := buildBinaryAnnotation(tag.key, tag.value, span.tracer.options.maxTagValueLength, nil); anno != nil { + annotations = append(annotations, anno) + } + } + return annotations +} + +func buildBinaryAnnotation(key string, val interface{}, maxTagValueLength int, endpoint *z.Endpoint) *z.BinaryAnnotation { + bann := &z.BinaryAnnotation{Key: key, Host: endpoint} + if value, ok := val.(string); ok { + bann.Value = []byte(truncateString(value, maxTagValueLength)) + bann.AnnotationType = z.AnnotationType_STRING + } else if value, ok := val.([]byte); ok { + if len(value) > maxTagValueLength { + value = value[:maxTagValueLength] + } + bann.Value = value + bann.AnnotationType = z.AnnotationType_BYTES + } else if value, ok := val.(int32); ok && allowPackedNumbers { + bann.Value = int32ToBytes(value) + bann.AnnotationType = z.AnnotationType_I32 + } else if value, ok := val.(int64); ok && allowPackedNumbers { + bann.Value = int64ToBytes(value) + bann.AnnotationType = z.AnnotationType_I64 + } else if value, ok := val.(int); ok && allowPackedNumbers { + bann.Value = int64ToBytes(int64(value)) + bann.AnnotationType = z.AnnotationType_I64 + } else if value, ok := val.(bool); ok { + bann.Value = []byte{boolToByte(value)} + bann.AnnotationType = z.AnnotationType_BOOL + } else { + value := stringify(val) + bann.Value = []byte(truncateString(value, maxTagValueLength)) + bann.AnnotationType = z.AnnotationType_STRING + } + return bann +} + +func stringify(value interface{}) string { + if s, ok := value.(string); ok { + return s + } + return fmt.Sprintf("%+v", value) +} + +func truncateString(value string, maxLength int) string { + // we ignore the problem of utf8 runes possibly being sliced in the middle, + // as it is rather expensive to iterate through each tag just to find rune + // boundaries. + if len(value) > maxLength { + return value[:maxLength] + } + return value +} + +func boolToByte(b bool) byte { + if b { + return 1 + } + return 0 +} + +// int32ToBytes converts int32 to bytes. +func int32ToBytes(i int32) []byte { + buf := make([]byte, 4) + binary.BigEndian.PutUint32(buf, uint32(i)) + return buf +} + +// int64ToBytes converts int64 to bytes. +func int64ToBytes(i int64) []byte { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, uint64(i)) + return buf +} + +type zipkinSpan struct { + *Span + + // peer points to the peer service participating in this span, + // e.g. the Client if this span is a server span, + // or Server if this span is a client span + peer struct { + Ipv4 int32 + Port int16 + ServiceName string + } + + // used to distinguish local vs. RPC Server vs. RPC Client spans + spanKind string +} + +func (s *zipkinSpan) handleSpecialTags() { + s.Lock() + defer s.Unlock() + if s.firstInProcess { + // append the process tags + s.tags = append(s.tags, s.tracer.tags...) + } + filteredTags := make([]Tag, 0, len(s.tags)) + for _, tag := range s.tags { + if handler, ok := specialTagHandlers[tag.key]; ok { + handler(s, tag.value) + } else { + filteredTags = append(filteredTags, tag) + } + } + s.tags = filteredTags +} + +func setSpanKind(s *zipkinSpan, value interface{}) { + if val, ok := value.(string); ok { + s.spanKind = val + return + } + if val, ok := value.(ext.SpanKindEnum); ok { + s.spanKind = string(val) + } +} + +func setPeerIPv4(s *zipkinSpan, value interface{}) { + if val, ok := value.(string); ok { + if ip, err := utils.ParseIPToUint32(val); err == nil { + s.peer.Ipv4 = int32(ip) + return + } + } + if val, ok := value.(uint32); ok { + s.peer.Ipv4 = int32(val) + return + } + if val, ok := value.(int32); ok { + s.peer.Ipv4 = val + } +} + +func setPeerPort(s *zipkinSpan, value interface{}) { + if val, ok := value.(string); ok { + if port, err := utils.ParsePort(val); err == nil { + s.peer.Port = int16(port) + return + } + } + if val, ok := value.(uint16); ok { + s.peer.Port = int16(val) + return + } + if val, ok := value.(int); ok { + s.peer.Port = int16(val) + } +} + +func setPeerService(s *zipkinSpan, value interface{}) { + if val, ok := value.(string); ok { + s.peer.ServiceName = val + } +} + +func removeTag(s *zipkinSpan, value interface{}) {} + +func (s *zipkinSpan) peerDefined() bool { + return s.peer.ServiceName != "" || s.peer.Ipv4 != 0 || s.peer.Port != 0 +} + +func (s *zipkinSpan) isRPC() bool { + s.RLock() + defer s.RUnlock() + return s.spanKind == string(ext.SpanKindRPCClientEnum) || s.spanKind == string(ext.SpanKindRPCServerEnum) +} + +func (s *zipkinSpan) isRPCClient() bool { + s.RLock() + defer s.RUnlock() + return s.spanKind == string(ext.SpanKindRPCClientEnum) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin_thrift_span_test.go b/template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin_thrift_span_test.go new file mode 100644 index 00000000..08c00a18 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-client-go/zipkin_thrift_span_test.go @@ -0,0 +1,359 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "errors" + "fmt" + "strconv" + "testing" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/opentracing/opentracing-go/log" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" + "github.com/uber/jaeger-client-go/utils" +) + +func TestThriftFirstInProcessSpan(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter()) + defer closer.Close() + + sp1 := tracer.StartSpan("s1").(*Span) + sp2 := tracer.StartSpan("sp2", opentracing.ChildOf(sp1.Context())).(*Span) + sp2.Finish() + sp1.Finish() + + tests := []struct { + span *Span + wantTags bool + }{ + {sp1, true}, + {sp2, false}, + } + + for _, test := range tests { + var check func(assert.TestingT, interface{}, ...interface{}) bool + if test.wantTags { + check = assert.NotNil + } else { + check = assert.Nil + } + thriftSpan := BuildZipkinThrift(test.span) + version := findBinaryAnnotation(thriftSpan, JaegerClientVersionTagKey) + hostname := findBinaryAnnotation(thriftSpan, TracerHostnameTagKey) + check(t, version) + check(t, hostname) + } +} + +func TestThriftForceSampled(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(false), // sample nothing + NewNullReporter()) + defer closer.Close() + + sp := tracer.StartSpan("s1").(*Span) + ext.SamplingPriority.Set(sp, 1) + assert.True(t, sp.context.IsSampled()) + assert.True(t, sp.context.IsDebug()) + thriftSpan := BuildZipkinThrift(sp) + assert.True(t, thriftSpan.Debug) +} + +func TestThriftSpanLogs(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter()) + defer closer.Close() + root := tracer.StartSpan("s1") + + someTime := time.Now().Add(-time.Minute) + someTimeInt64 := utils.TimeToMicrosecondsSinceEpochInt64(someTime) + + fields := func(fields ...log.Field) []log.Field { + return fields + } + tests := []struct { + fields []log.Field + logFunc func(sp opentracing.Span) + expected string + expectedTimestamp int64 + disableSampling bool + }{ + {fields: fields(log.String("event", "happened")), expected: "happened"}, + {fields: fields(log.String("something", "happened")), expected: `{"something":"happened"}`}, + {fields: fields(log.Bool("something", true)), expected: `{"something":"true"}`}, + {fields: fields(log.Int("something", 123)), expected: `{"something":"123"}`}, + {fields: fields(log.Int32("something", 123)), expected: `{"something":"123"}`}, + {fields: fields(log.Int64("something", 123)), expected: `{"something":"123"}`}, + {fields: fields(log.Uint32("something", 123)), expected: `{"something":"123"}`}, + {fields: fields(log.Uint64("something", 123)), expected: `{"something":"123"}`}, + {fields: fields(log.Float32("something", 123)), expected: `{"something":"123.000000"}`}, + {fields: fields(log.Float64("something", 123)), expected: `{"something":"123.000000"}`}, + {fields: fields(log.Error(errors.New("drugs are baaad, m-k"))), + expected: `{"error":"drugs are baaad, m-k"}`}, + {fields: fields(log.Object("something", 123)), expected: `{"something":"123"}`}, + { + fields: fields(log.Lazy(func(fv log.Encoder) { + fv.EmitBool("something", true) + })), + expected: `{"something":"true"}`, + }, + { + logFunc: func(sp opentracing.Span) { + sp.LogKV("event", "something") + }, + expected: "something", + }, + { + logFunc: func(sp opentracing.Span) { + sp.LogKV("non-even number of arguments") + }, + // this is a bit fragile, but ¯\_(ツ)_/¯ + expected: `{"error":"non-even keyValues len: 1","function":"LogKV"}`, + }, + { + logFunc: func(sp opentracing.Span) { + sp.LogEvent("something") + }, + expected: "something", + }, + { + logFunc: func(sp opentracing.Span) { + sp.LogEventWithPayload("something", "payload") + }, + expected: `{"event":"something","payload":"payload"}`, + }, + { + logFunc: func(sp opentracing.Span) { + sp.Log(opentracing.LogData{Event: "something"}) + }, + expected: "something", + }, + { + logFunc: func(sp opentracing.Span) { + sp.Log(opentracing.LogData{Event: "something", Payload: "payload"}) + }, + expected: `{"event":"something","payload":"payload"}`, + }, + { + logFunc: func(sp opentracing.Span) { + sp.FinishWithOptions(opentracing.FinishOptions{ + LogRecords: []opentracing.LogRecord{ + { + Timestamp: someTime, + Fields: fields(log.String("event", "happened")), + }, + }, + }) + }, + expected: "happened", + expectedTimestamp: someTimeInt64, + }, + { + logFunc: func(sp opentracing.Span) { + sp.FinishWithOptions(opentracing.FinishOptions{ + BulkLogData: []opentracing.LogData{ + { + Timestamp: someTime, + Event: "happened", + }, + }, + }) + }, + expected: "happened", + expectedTimestamp: someTimeInt64, + }, + { + logFunc: func(sp opentracing.Span) { + sp.FinishWithOptions(opentracing.FinishOptions{ + BulkLogData: []opentracing.LogData{ + { + Timestamp: someTime, + Event: "happened", + Payload: "payload", + }, + }, + }) + }, + expected: `{"event":"happened","payload":"payload"}`, + expectedTimestamp: someTimeInt64, + }, + { + disableSampling: true, + fields: fields(log.String("event", "happened")), + expected: "", + }, + { + disableSampling: true, + logFunc: func(sp opentracing.Span) { + sp.LogKV("event", "something") + }, + expected: "", + }, + } + + for i, test := range tests { + testName := fmt.Sprintf("test-%02d", i) + sp := tracer.StartSpan(testName, opentracing.ChildOf(root.Context())) + if test.disableSampling { + ext.SamplingPriority.Set(sp, 0) + } + if test.logFunc != nil { + test.logFunc(sp) + } else if len(test.fields) > 0 { + sp.LogFields(test.fields...) + } + thriftSpan := BuildZipkinThrift(sp.(*Span)) + if test.disableSampling { + assert.Equal(t, 0, len(thriftSpan.Annotations), testName) + continue + } + assert.Equal(t, 1, len(thriftSpan.Annotations), testName) + assert.Equal(t, test.expected, thriftSpan.Annotations[0].Value, testName) + if test.expectedTimestamp != 0 { + assert.Equal(t, test.expectedTimestamp, thriftSpan.Annotations[0].Timestamp, testName) + } + } +} + +func TestThriftLocalComponentSpan(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter()) + defer closer.Close() + + tests := []struct { + addComponentTag bool + wantAnnotation string + }{ + {false, "DOOP"}, // Without COMPONENT tag the value is the service name + {true, "c1"}, + } + + for _, test := range tests { + sp := tracer.StartSpan("s1").(*Span) + if test.addComponentTag { + ext.Component.Set(sp, "c1") + } + sp.Finish() + thriftSpan := BuildZipkinThrift(sp) + + anno := findBinaryAnnotation(thriftSpan, "lc") + require.NotNil(t, anno) + assert.EqualValues(t, test.wantAnnotation, anno.Value) + } +} + +func TestSpecialTags(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter()) + defer closer.Close() + + sp := tracer.StartSpan("s1").(*Span) + ext.SpanKindRPCServer.Set(sp) + ext.PeerService.Set(sp, "peer") + ext.PeerPort.Set(sp, 80) + ext.PeerHostIPv4.Set(sp, 2130706433) + sp.Finish() + + thriftSpan := BuildZipkinThrift(sp) + // Special tags should not be copied over to binary annotations + assert.Nil(t, findBinaryAnnotation(thriftSpan, "span.kind")) + assert.Nil(t, findBinaryAnnotation(thriftSpan, "peer.service")) + assert.Nil(t, findBinaryAnnotation(thriftSpan, "peer.port")) + assert.Nil(t, findBinaryAnnotation(thriftSpan, "peer.ipv4")) + assert.Nil(t, findBinaryAnnotation(thriftSpan, "ip")) + + anno := findBinaryAnnotation(thriftSpan, "ca") + assert.NotNil(t, anno) + assert.NotNil(t, anno.Host) + assert.EqualValues(t, 80, anno.Host.Port) + assert.EqualValues(t, 2130706433, anno.Host.Ipv4) + assert.EqualValues(t, "peer", anno.Host.ServiceName) + + assert.NotNil(t, findAnnotation(thriftSpan, "sr")) + assert.NotNil(t, findAnnotation(thriftSpan, "ss")) +} + +func TestBaggageLogs(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter()) + defer closer.Close() + + sp := tracer.StartSpan("s1").(*Span) + sp.SetBaggageItem("auth.token", "token") + ext.SpanKindRPCServer.Set(sp) + sp.Finish() + + thriftSpan := BuildZipkinThrift(sp) + assert.NotNil(t, findAnnotation(thriftSpan, `{"event":"baggage","key":"auth.token","value":"token"}`)) +} + +func TestMaxTagValueLength(t *testing.T) { + value := make([]byte, 512) + tests := []struct { + tagValueLength int + value []byte + expected []byte + }{ + {256, value, value[:256]}, + {512, value, value}, + } + + for _, test := range tests { + t.Run(strconv.Itoa(test.tagValueLength), func(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter(), + TracerOptions.MaxTagValueLength(test.tagValueLength)) + defer closer.Close() + sp := tracer.StartSpan("s1").(*Span) + sp.SetTag("tag.string", string(test.value)) + sp.SetTag("tag.bytes", test.value) + sp.Finish() + thriftSpan := BuildZipkinThrift(sp) + assert.Equal(t, test.expected, findBinaryAnnotation(thriftSpan, "tag.string").Value) + assert.Equal(t, test.expected, findBinaryAnnotation(thriftSpan, "tag.bytes").Value) + }) + } +} + +func findAnnotation(span *zipkincore.Span, name string) *zipkincore.Annotation { + for _, a := range span.Annotations { + if a.Value == name { + return a + } + } + return nil +} + +func findBinaryAnnotation(span *zipkincore.Span, name string) *zipkincore.BinaryAnnotation { + for _, a := range span.BinaryAnnotations { + if a.Key == name { + return a + } + } + return nil +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/.github/ISSUE_TEMPLATE.md b/template/faaschain/vendor/github.com/uber/jaeger-lib/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..7a5851ba --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,28 @@ + + +## Requirement - what kind of business use case are you trying to solve? + + + +## Problem - what in Jaeger blocks you from solving the requirement? + + + + +## Proposal - what do you suggest to solve the problem or improve the existing situation? + + + +## Any open questions to address + + diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/.github/PULL_REQUEST_TEMPLATE.md b/template/faaschain/vendor/github.com/uber/jaeger-lib/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..2564119c --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,18 @@ + + +## Which problem is this PR solving? +- + +## Short description of the changes +- diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/.gitignore b/template/faaschain/vendor/github.com/uber/jaeger-lib/.gitignore new file mode 100644 index 00000000..9cc72383 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/.gitignore @@ -0,0 +1,11 @@ +*.out +*.test +*.xml +*.swp +.idea/ +.tmp/ +*.iml +*.cov +*.html +*.log +vendor/ diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/.travis.yml b/template/faaschain/vendor/github.com/uber/jaeger-lib/.travis.yml new file mode 100644 index 00000000..b0dcc013 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/.travis.yml @@ -0,0 +1,36 @@ +sudo: required + +services: + - docker + +language: go +go_import_path: github.com/uber/jaeger-lib + +matrix: + include: + - go: 1.7 + env: + - COVERAGE=true + - go: 1.9 + env: + - TEST=true + - go: 1.7 + env: + - TEST=true + - USE_DEP=true + +env: + global: + - GO15VENDOREXPERIMENT=1 + +install: + - if [ "$USE_DEP" == true ]; then make install-dep-ci ; else echo 'skipping installing dep'; fi + - make install-ci + +script: + - if [ "$COVERAGE" == true ]; then make test-ci ; else echo 'skipping tests'; fi + - if [ "$TEST" == true ]; then make test-only-ci ; else echo 'skipping tests'; fi + +after_success: + - if [ "$COVERAGE" == true ]; then mv cover.out coverage.txt ; else echo 'skipping coverage'; fi + - if [ "$COVERAGE" == true ]; then bash <(curl -s https://codecov.io/bash) ; else echo 'skipping coverage'; fi diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/CHANGELOG.md b/template/faaschain/vendor/github.com/uber/jaeger-lib/CHANGELOG.md new file mode 100644 index 00000000..182d8293 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/CHANGELOG.md @@ -0,0 +1,56 @@ +Changes by Version +================== + +1.5.1 (unreleased) +------------------ + +- Nothing yet. + + +1.5.0 (2018-05-11) +------------------ + +- Change default metrics namespace separator from colon to underscore (#43) +- Use an interface to be compatible with Prometheus 0.9.x (#42) + + +1.4.0 (2018-03-05) +------------------ + +- Reimplement expvar metrics to be tolerant of duplicates (#40) + + +1.3.1 (2018-01-12) +------------------- + +- Add Gopkg.toml to allow using the lib with `dep` + + +1.3.0 (2018-01-08) +------------------ + +- Move rate limiter from client to jaeger-lib [#35](https://github.com/jaegertracing/jaeger-lib/pull/35) + + +1.2.1 (2017-11-14) +------------------ + +- *breaking* Change prometheus.New() to accept options instead of fixed arguments + + +1.2.0 (2017-11-12) +------------------ + +- Support Prometheus metrics directly [#29](https://github.com/jaegertracing/jaeger-lib/pull/29). + + +1.1.0 (2017-09-10) +------------------ + +- Re-releasing the project under Apache 2.0 license. + + +1.0.0 (2017-08-22) +------------------ + +- First semver release. diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/CONTRIBUTING.md b/template/faaschain/vendor/github.com/uber/jaeger-lib/CONTRIBUTING.md new file mode 100644 index 00000000..78007908 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/CONTRIBUTING.md @@ -0,0 +1,169 @@ +# How to Contribute to `jaeger-lib` + +We'd love your help! + +Jaeger is [Apache 2.0 licensed](LICENSE) and accepts contributions via GitHub +pull requests. This document outlines some of the conventions on development +workflow, commit message formatting, contact points and other resources to make +it easier to get your contribution accepted. + +We gratefully welcome improvements to documentation as well as to code. + +# Certificate of Origin + +By contributing to this project you agree to the [Developer Certificate of +Origin](https://developercertificate.org/) (DCO). This document was created +by the Linux Kernel community and is a simple statement that you, as a +contributor, have the legal right to make the contribution. See the [DCO](DCO) +file for details. + +## Getting Started + +This library uses [glide](https://github.com/Masterminds/glide) to manage dependencies. + +To get started, make sure you clone the Git repository into the correct location +`github.com/uber/jaeger-lib` relative to `$GOPATH`: + +``` +mkdir -p $GOPATH/src/github.com/uber +cd $GOPATH/src/github.com/uber +git clone git@github.com:jaegertracing/jaeger-lib.git jaeger-lib +cd jaeger-lib +``` + +Then install dependencies and run the tests: + +``` +git submodule update --init --recursive +glide install +make test +``` + +## Imports grouping + +This projects follows the following pattern for grouping imports in Go files: + * imports from standard library + * imports from other projects + * imports from this `jaeger-lib` project + +For example: + +```go +import ( + "fmt" + + "go.uber.org/zap" + + "github.com/uber/jaeger-lib/metrics" +) +``` + +## Making A Change + +*Before making any significant changes, please [open an +issue](https://github.com/uber/jaeger-lib/issues).* Discussing your proposed +changes ahead of time will make the contribution process smooth for everyone. + +Once we've discussed your changes and you've got your code ready, make sure +that tests are passing (`make test` or `make cover`) and open your PR. Your +pull request is most likely to be accepted if it: + +* Includes tests for new functionality. +* Follows the guidelines in [Effective + Go](https://golang.org/doc/effective_go.html) and the [Go team's common code + review comments](https://github.com/golang/go/wiki/CodeReviewComments). +* Has a [good commit message](https://chris.beams.io/posts/git-commit/): + * Separate subject from body with a blank line + * Limit the subject line to 50 characters + * Capitalize the subject line + * Do not end the subject line with a period + * Use the imperative mood in the subject line + * Wrap the body at 72 characters + * Use the body to explain _what_ and _why_ instead of _how_ +* Each commit must be signed by the author ([see below](#sign-your-work)). + +## License + +By contributing your code, you agree to license your contribution under the terms +of the [Apache License](LICENSE). + +If you are adding a new file it should have a header like below. The easiest +way to add such header is to run `make fmt`. + +``` +// Copyright (c) 2017 The Jaeger Authors. +// +// 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. +``` + +## Sign your work + +The sign-off is a simple line at the end of the explanation for the +patch, which certifies that you wrote it or otherwise have the right to +pass it on as an open-source patch. The rules are pretty simple: if you +can certify the below (from +[developercertificate.org](http://developercertificate.org/)): + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +660 York Street, Suite 102, +San Francisco, CA 94110 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` + +then you just add a line to every git commit message: + + Signed-off-by: Joe Smith + +using your real name (sorry, no pseudonyms or anonymous contributions.) + +You can add the sign off when creating the git commit via `git commit -s`. + +If you want this to be automatic you can set up some aliases: + +``` +git config --add alias.amend "commit -s --amend" +git config --add alias.c "commit -s" +``` diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/DCO b/template/faaschain/vendor/github.com/uber/jaeger-lib/DCO new file mode 100644 index 00000000..068953d4 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/DCO @@ -0,0 +1,37 @@ +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +660 York Street, Suite 102, +San Francisco, CA 94110 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. + diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/Gopkg.lock b/template/faaschain/vendor/github.com/uber/jaeger-lib/Gopkg.lock new file mode 100644 index 00000000..c42a7f0e --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/Gopkg.lock @@ -0,0 +1,114 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + name = "github.com/VividCortex/gohistogram" + packages = ["."] + revision = "51564d9861991fb0ad0f531c99ef602d0f9866e6" + version = "v1.0.0" + +[[projects]] + branch = "master" + name = "github.com/beorn7/perks" + packages = ["quantile"] + revision = "4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9" + +[[projects]] + name = "github.com/codahale/hdrhistogram" + packages = ["."] + revision = "f8ad88b59a584afeee9d334eff879b104439117b" + +[[projects]] + name = "github.com/davecgh/go-spew" + packages = ["spew"] + revision = "04cdfd42973bb9c8589fd6a731800cf222fde1a9" + +[[projects]] + branch = "master" + name = "github.com/facebookgo/clock" + packages = ["."] + revision = "600d898af40aa09a7a93ecb9265d87b0504b6f03" + +[[projects]] + name = "github.com/go-kit/kit" + packages = ["log","log/level","metrics","metrics/expvar","metrics/generic","metrics/influx","metrics/internal/lv","metrics/prometheus"] + revision = "a9ca6725cbbea455e61c6bc8a1ed28e81eb3493b" + version = "v0.5.0" + +[[projects]] + name = "github.com/go-logfmt/logfmt" + packages = ["."] + revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5" + version = "v0.3.0" + +[[projects]] + name = "github.com/go-stack/stack" + packages = ["."] + revision = "817915b46b97fd7bb80e8ab6b69f01a53ac3eebf" + version = "v1.6.0" + +[[projects]] + name = "github.com/golang/protobuf" + packages = ["proto"] + revision = "7cc19b78d562895b13596ddce7aafb59dd789318" + +[[projects]] + name = "github.com/influxdata/influxdb" + packages = ["client/v2","models","pkg/escape"] + revision = "f3f30726d822c4be8cd00137ba66b6e4fd68cca1" + +[[projects]] + branch = "master" + name = "github.com/kr/logfmt" + packages = ["."] + revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0" + +[[projects]] + branch = "master" + name = "github.com/matttproud/golang_protobuf_extensions" + packages = ["pbutil"] + revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" + +[[projects]] + name = "github.com/pmezard/go-difflib" + packages = ["difflib"] + revision = "d8ed2627bdf02c080bf22230dbb337003b7aba2d" + +[[projects]] + name = "github.com/prometheus/client_golang" + packages = ["prometheus"] + revision = "c5b7fccd204277076155f10851dad72b76a49317" + version = "v0.8.0" + +[[projects]] + name = "github.com/prometheus/client_model" + packages = ["go"] + revision = "6f3806018612930941127f2a7c6c453ba2c527d2" + +[[projects]] + name = "github.com/prometheus/common" + packages = ["expfmt","internal/bitbucket.org/ww/goautoneg","model"] + revision = "49fee292b27bfff7f354ee0f64e1bc4850462edf" + +[[projects]] + name = "github.com/prometheus/procfs" + packages = [".","xfs"] + revision = "a1dba9ce8baed984a2495b658c82687f8157b98f" + +[[projects]] + name = "github.com/stretchr/testify" + packages = ["assert","require"] + revision = "05e8a0eda380579888eb53c394909df027f06991" + +[[projects]] + name = "github.com/uber-go/tally" + packages = ["."] + revision = "be9e53c77349ae2dd4b8c03a6dc20ed9a88b9927" + version = "v3.2.0" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "6a38cb6e727212ac18ae744a1a3f17dcd79235e1947fac5f63b41d3162328e1f" + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/Gopkg.toml b/template/faaschain/vendor/github.com/uber/jaeger-lib/Gopkg.toml new file mode 100644 index 00000000..b97e1e39 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/Gopkg.toml @@ -0,0 +1,23 @@ +[[constraint]] + name = "github.com/codahale/hdrhistogram" + +[[constraint]] + name = "github.com/go-kit/kit" + version = "0.5.0" + +[[constraint]] + name = "github.com/influxdata/influxdb" + +[[constraint]] + name = "github.com/prometheus/client_golang" + version = "0.8.0" + +[[constraint]] + name = "github.com/prometheus/client_model" + +[[constraint]] + name = "github.com/stretchr/testify" + +[[constraint]] + name = "github.com/uber-go/tally" + version = ">=2.1.0, <4.0.0" diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/LICENSE b/template/faaschain/vendor/github.com/uber/jaeger-lib/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor 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, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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/template/faaschain/vendor/github.com/uber/jaeger-lib/Makefile b/template/faaschain/vendor/github.com/uber/jaeger-lib/Makefile new file mode 100644 index 00000000..73b957b6 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/Makefile @@ -0,0 +1,98 @@ +PROJECT_ROOT=github.com/uber/jaeger-lib +PACKAGES := $(shell glide novendor | grep -v ./thrift-gen/...) +# all .go files that don't exist in hidden directories +ALL_SRC := $(shell find . -name "*.go" | grep -v -e vendor -e thrift-gen \ + -e ".*/\..*" \ + -e ".*/_.*" \ + -e ".*/mocks.*") + +export GO15VENDOREXPERIMENT=1 + +GOTEST=go test -v $(RACE) +GOLINT=golint +GOVET=go vet +GOFMT=gofmt +FMT_LOG=fmt.log +LINT_LOG=lint.log + +THRIFT_VER=0.9.3 +THRIFT_IMG=thrift:$(THRIFT_VER) +THRIFT=docker run -v "${PWD}:/data" $(THRIFT_IMG) thrift +THRIFT_GO_ARGS=thrift_import="github.com/apache/thrift/lib/go/thrift" +THRIFT_GEN_DIR=thrift-gen + +PASS=$(shell printf "\033[32mPASS\033[0m") +FAIL=$(shell printf "\033[31mFAIL\033[0m") +COLORIZE=sed ''/PASS/s//$(PASS)/'' | sed ''/FAIL/s//$(FAIL)/'' + +.DEFAULT_GOAL := test-and-lint + +.PHONY: test-and-lint +test-and-lint: test fmt lint + +.PHONY: test +test: + $(GOTEST) $(PACKAGES) | $(COLORIZE) + +.PHONY: fmt +fmt: + $(GOFMT) -e -s -l -w $(ALL_SRC) + ./scripts/updateLicenses.sh + +.PHONY: lint +lint: + $(GOVET) $(PACKAGES) + @cat /dev/null > $(LINT_LOG) + @$(foreach pkg, $(PACKAGES), $(GOLINT) $(pkg) | grep -v crossdock/thrift >> $(LINT_LOG) || true;) + @[ ! -s "$(LINT_LOG)" ] || (echo "Lint Failures" | cat - $(LINT_LOG) && false) + @$(GOFMT) -e -s -l $(ALL_SRC) > $(FMT_LOG) + @./scripts/updateLicenses.sh >> $(FMT_LOG) + @[ ! -s "$(FMT_LOG)" ] || (echo "go fmt or license check failures, run 'make fmt'" | cat - $(FMT_LOG) && false) + + +.PHONY: install +install: + glide --version || go get github.com/Masterminds/glide +ifeq ($(USE_DEP),true) + dep ensure + dep status +else + glide install +endif + + +.PHONY: cover +cover: + ./scripts/cover.sh $(shell go list $(PACKAGES)) + go tool cover -html=cover.out -o cover.html + + +idl-submodule: + git submodule init + git submodule update + +thrift-image: + $(THRIFT) -version + +.PHONY: install-dep-ci +install-dep-ci: + - curl -L -s https://github.com/golang/dep/releases/download/v0.3.2/dep-linux-amd64 -o $$GOPATH/bin/dep + - chmod +x $$GOPATH/bin/dep + +.PHONY: install-ci +install-ci: install + go get github.com/wadey/gocovmerge + go get github.com/mattn/goveralls + go get golang.org/x/tools/cmd/cover + go get github.com/golang/lint/golint + + +.PHONY: test-ci +test-ci: + ./scripts/cover.sh $(shell go list $(PACKAGES)) + make lint + +.PHONY: test-only-ci +test-only-ci: + go test -cover $(PACKAGES) + make lint diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/README.md b/template/faaschain/vendor/github.com/uber/jaeger-lib/README.md new file mode 100644 index 00000000..144ed1fb --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/README.md @@ -0,0 +1,27 @@ +[![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] + + +# jaeger-lib + +A collection of shared infrastructure libraries used by different +components of [Jaeger](https://github.com/uber/jaeger) backend and [jaeger-client-go](https://github.com/uber/jaeger-client-go). +This library is *not intended to be used standalone*, and provides *no guarantees of backwards compatibility*. + +The library's import path is `github.com/uber/jaeger-lib`. + +## How to Contribute + +Please see [CONTRIBUTING.md](CONTRIBUTING.md). + +## License + +[Apache 2.0 License](./LICENSE). + + +[doc-img]: https://godoc.org/github.com/uber/jaeger-lib?status.svg +[doc]: https://godoc.org/github.com/uber/jaeger-lib +[ci-img]: https://travis-ci.org/jaegertracing/jaeger-lib.svg?branch=master +[ci]: https://travis-ci.org/jaegertracing/jaeger-lib +[cov-img]: https://coveralls.io/repos/jaegertracing/jaeger-lib/badge.svg?branch=master&service=github +[cov]: https://coveralls.io/github/jaegertracing/jaeger-lib?branch=master + diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/client/log/go-kit/logger.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/client/log/go-kit/logger.go new file mode 100644 index 00000000..d7fc19ec --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/client/log/go-kit/logger.go @@ -0,0 +1,64 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package xkit + +import ( + "fmt" + + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" +) + +// LoggerOption sets a parameter for the Logger. +type LoggerOption func(*Logger) + +// MessageKey sets the key for the actual log message. By default, it's "msg". +func MessageKey(key string) LoggerOption { + return func(l *Logger) { l.messageKey = key } +} + +// Logger wraps a go-kit logger instance in a Jaeger client compatible one. +type Logger struct { + infoLogger log.Logger + errorLogger log.Logger + + messageKey string +} + +// NewLogger creates a new Jaeger client logger from a go-kit one. +func NewLogger(kitlogger log.Logger, options ...LoggerOption) *Logger { + logger := &Logger{ + infoLogger: level.Info(kitlogger), + errorLogger: level.Error(kitlogger), + + messageKey: "msg", + } + + for _, option := range options { + option(logger) + } + + return logger +} + +// Error implements the github.com/uber/jaeger-client-go/log.Logger interface. +func (l *Logger) Error(msg string) { + l.errorLogger.Log(l.messageKey, msg) +} + +// Infof implements the github.com/uber/jaeger-client-go/log.Logger interface. +func (l *Logger) Infof(msg string, args ...interface{}) { + l.infoLogger.Log(l.messageKey, fmt.Sprintf(msg, args...)) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/client/log/go-kit/logger_test.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/client/log/go-kit/logger_test.go new file mode 100644 index 00000000..23189e69 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/client/log/go-kit/logger_test.go @@ -0,0 +1,36 @@ +package xkit + +import ( + "bytes" + "testing" + + "github.com/go-kit/kit/log" + "github.com/stretchr/testify/assert" +) + +func TestLogger_Infof(t *testing.T) { + buf := &bytes.Buffer{} + logger := NewLogger(log.NewLogfmtLogger(buf)) + + logger.Infof("Formatted %s", "string") + + assert.Equal(t, "level=info msg=\"Formatted string\"\n", buf.String()) +} + +func TestLogger_Error(t *testing.T) { + buf := &bytes.Buffer{} + logger := NewLogger(log.NewLogfmtLogger(buf)) + + logger.Error("Something really bad happened") + + assert.Equal(t, "level=error msg=\"Something really bad happened\"\n", buf.String()) +} + +func TestMessageKey(t *testing.T) { + buf := &bytes.Buffer{} + logger := NewLogger(log.NewLogfmtLogger(buf), MessageKey("message")) + + logger.Error("Something really bad happened") + + assert.Equal(t, "level=error message=\"Something really bad happened\"\n", buf.String()) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/glide.lock b/template/faaschain/vendor/github.com/uber/jaeger-lib/glide.lock new file mode 100644 index 00000000..3974d38c --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/glide.lock @@ -0,0 +1,78 @@ +hash: 8ca2ebd4305a4aaead18ee8cc5a84da42e95c202d53c1bd78d11436aeb8be8e4 +updated: 2017-09-20T01:48:48.588894144+02:00 +imports: +- name: github.com/beorn7/perks + version: 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9 + subpackages: + - quantile +- name: github.com/codahale/hdrhistogram + version: f8ad88b59a584afeee9d334eff879b104439117b +- name: github.com/davecgh/go-spew + version: 04cdfd42973bb9c8589fd6a731800cf222fde1a9 + subpackages: + - spew +- name: github.com/facebookgo/clock + version: 600d898af40aa09a7a93ecb9265d87b0504b6f03 +- name: github.com/go-kit/kit + version: a9ca6725cbbea455e61c6bc8a1ed28e81eb3493b + subpackages: + - log + - log/level + - metrics + - metrics/expvar + - metrics/generic + - metrics/influx + - metrics/internal/lv + - metrics/prometheus +- name: github.com/go-logfmt/logfmt + version: 390ab7935ee28ec6b286364bba9b4dd6410cb3d5 +- name: github.com/go-stack/stack + version: 817915b46b97fd7bb80e8ab6b69f01a53ac3eebf +- name: github.com/golang/protobuf + version: 7cc19b78d562895b13596ddce7aafb59dd789318 + subpackages: + - proto +- name: github.com/influxdata/influxdb + version: f3f30726d822c4be8cd00137ba66b6e4fd68cca1 + subpackages: + - client/v2 + - models + - pkg/escape +- name: github.com/kr/logfmt + version: b84e30acd515aadc4b783ad4ff83aff3299bdfe0 +- name: github.com/matttproud/golang_protobuf_extensions + version: c12348ce28de40eed0136aa2b644d0ee0650e56c + subpackages: + - pbutil +- name: github.com/pmezard/go-difflib + version: d8ed2627bdf02c080bf22230dbb337003b7aba2d + subpackages: + - difflib +- name: github.com/prometheus/client_golang + version: c5b7fccd204277076155f10851dad72b76a49317 + subpackages: + - prometheus +- name: github.com/prometheus/client_model + version: 6f3806018612930941127f2a7c6c453ba2c527d2 + subpackages: + - go +- name: github.com/prometheus/common + version: 49fee292b27bfff7f354ee0f64e1bc4850462edf + subpackages: + - expfmt + - internal/bitbucket.org/ww/goautoneg + - model +- name: github.com/prometheus/procfs + version: a1dba9ce8baed984a2495b658c82687f8157b98f + subpackages: + - xfs +- name: github.com/stretchr/testify + version: 05e8a0eda380579888eb53c394909df027f06991 + subpackages: + - assert + - require +- name: github.com/uber-go/tally + version: be9e53c77349ae2dd4b8c03a6dc20ed9a88b9927 +- name: github.com/VividCortex/gohistogram + version: 51564d9861991fb0ad0f531c99ef602d0f9866e6 +testImports: [] diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/glide.yaml b/template/faaschain/vendor/github.com/uber/jaeger-lib/glide.yaml new file mode 100644 index 00000000..e134d1de --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/glide.yaml @@ -0,0 +1,13 @@ +package: github.com/uber/jaeger-lib +import: +- package: github.com/codahale/hdrhistogram +- package: github.com/go-kit/kit + version: v0.5.0 + subpackages: + - metrics/influx +- package: github.com/uber-go/tally + version: '>= 2.1.0, < 4' +- package: github.com/prometheus/client_golang + version: v0.8.0 +testImport: +- package: github.com/stretchr/testify diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/adapters/cache.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/adapters/cache.go new file mode 100644 index 00000000..6f2bf2e3 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/adapters/cache.go @@ -0,0 +1,69 @@ +// Copyright (c) 2018 Uber Technologies, Inc. +// +// 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. + +package adapters + +import ( + "sync" + + "github.com/uber/jaeger-lib/metrics" +) + +type cache struct { + lock sync.Mutex + counters map[string]metrics.Counter + gauges map[string]metrics.Gauge + timers map[string]metrics.Timer +} + +func newCache() *cache { + return &cache{ + counters: make(map[string]metrics.Counter), + gauges: make(map[string]metrics.Gauge), + timers: make(map[string]metrics.Timer), + } +} + +func (r *cache) getOrSetCounter(name string, create func() metrics.Counter) metrics.Counter { + r.lock.Lock() + defer r.lock.Unlock() + c, ok := r.counters[name] + if !ok { + c = create() + r.counters[name] = c + } + return c +} + +func (r *cache) getOrSetGauge(name string, create func() metrics.Gauge) metrics.Gauge { + r.lock.Lock() + defer r.lock.Unlock() + g, ok := r.gauges[name] + if !ok { + g = create() + r.gauges[name] = g + } + return g +} + +func (r *cache) getOrSetTimer(name string, create func() metrics.Timer) metrics.Timer { + r.lock.Lock() + defer r.lock.Unlock() + t, ok := r.timers[name] + if !ok { + t = create() + r.timers[name] = t + } + return t +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/adapters/cache_test.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/adapters/cache_test.go new file mode 100644 index 00000000..38704f36 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/adapters/cache_test.go @@ -0,0 +1,46 @@ +// Copyright (c) 2018 Uber Technologies, Inc. +// +// 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. + +package adapters + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/uber/jaeger-lib/metrics" +) + +func TestCache(t *testing.T) { + f := metrics.NewLocalFactory(100 * time.Second) + c1 := f.Counter("x", nil) + g1 := f.Gauge("y", nil) + t1 := f.Timer("z", nil) + + c := newCache() + + c2 := c.getOrSetCounter("x", func() metrics.Counter { return c1 }) + assert.Equal(t, c1, c2) + g2 := c.getOrSetGauge("y", func() metrics.Gauge { return g1 }) + assert.Equal(t, g1, g2) + t2 := c.getOrSetTimer("z", func() metrics.Timer { return t1 }) + assert.Equal(t, t1, t2) + + c3 := c.getOrSetCounter("x", func() metrics.Counter { panic("c1") }) + assert.Equal(t, c1, c3) + g3 := c.getOrSetGauge("y", func() metrics.Gauge { panic("g1") }) + assert.Equal(t, g1, g3) + t3 := c.getOrSetTimer("z", func() metrics.Timer { panic("t1") }) + assert.Equal(t, t1, t3) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/adapters/factory.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/adapters/factory.go new file mode 100644 index 00000000..bc2e137c --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/adapters/factory.go @@ -0,0 +1,123 @@ +// Copyright (c) 2018 Uber Technologies, Inc. +// +// 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. + +package adapters + +import ( + "github.com/uber/jaeger-lib/metrics" +) + +// FactoryWithTags creates metrics with fully qualified name and tags. +type FactoryWithTags interface { + Counter(name string, tags map[string]string) metrics.Counter + Gauge(name string, tags map[string]string) metrics.Gauge + Timer(name string, tags map[string]string) metrics.Timer +} + +// Options affect how the adapter factory behaves. +type Options struct { + ScopeSep string + TagsSep string + TagKVSep string +} + +func defaultOptions(options Options) Options { + o := options + if o.ScopeSep == "" { + o.ScopeSep = "." + } + if o.TagsSep == "" { + o.TagsSep = "." + } + if o.TagKVSep == "" { + o.TagKVSep = "_" + } + return o +} + +// WrapFactoryWithTags creates a real metrics.Factory that supports subscopes. +func WrapFactoryWithTags(f FactoryWithTags, options Options) metrics.Factory { + return &factory{ + Options: defaultOptions(options), + factory: f, + cache: newCache(), + } +} + +type factory struct { + Options + factory FactoryWithTags + scope string + tags map[string]string + cache *cache +} + +func (f *factory) Counter(name string, tags map[string]string) metrics.Counter { + fullName, fullTags, key := f.getKey(name, tags) + return f.cache.getOrSetCounter(key, func() metrics.Counter { + return f.factory.Counter(fullName, fullTags) + }) +} + +func (f *factory) Gauge(name string, tags map[string]string) metrics.Gauge { + fullName, fullTags, key := f.getKey(name, tags) + return f.cache.getOrSetGauge(key, func() metrics.Gauge { + return f.factory.Gauge(fullName, fullTags) + }) +} + +func (f *factory) Timer(name string, tags map[string]string) metrics.Timer { + fullName, fullTags, key := f.getKey(name, tags) + return f.cache.getOrSetTimer(key, func() metrics.Timer { + return f.factory.Timer(fullName, fullTags) + }) +} + +func (f *factory) Namespace(name string, tags map[string]string) metrics.Factory { + return &factory{ + cache: f.cache, + scope: f.subScope(name), + tags: f.mergeTags(tags), + factory: f.factory, + Options: f.Options, + } +} + +func (f *factory) getKey(name string, tags map[string]string) (fullName string, fullTags map[string]string, key string) { + fullName = f.subScope(name) + fullTags = f.mergeTags(tags) + key = metrics.GetKey(fullName, fullTags, f.TagsSep, f.TagKVSep) + return +} + +func (f *factory) mergeTags(tags map[string]string) map[string]string { + ret := make(map[string]string, len(f.tags)+len(tags)) + for k, v := range f.tags { + ret[k] = v + } + for k, v := range tags { + ret[k] = v + } + return ret +} + +func (f *factory) subScope(name string) string { + if f.scope == "" { + return name + } + if name == "" { + return f.scope + } + return f.scope + f.ScopeSep + name +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/adapters/factory_test.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/adapters/factory_test.go new file mode 100644 index 00000000..1985c929 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/adapters/factory_test.go @@ -0,0 +1,119 @@ +// Copyright (c) 2018 Uber Technologies, Inc. +// +// 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. + +package adapters + +import ( + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/uber/jaeger-lib/metrics" +) + +func TestDefaultOptions(t *testing.T) { + o := defaultOptions(Options{}) + assert.Equal(t, ".", o.ScopeSep) + assert.Equal(t, ".", o.TagsSep) + assert.Equal(t, "_", o.TagKVSep) +} + +func TestSubScope(t *testing.T) { + f := &factory{ + Options: defaultOptions(Options{}), + } + assert.Equal(t, "", f.subScope("")) + assert.Equal(t, "x", f.subScope("x")) + f.scope = "x" + assert.Equal(t, "x", f.subScope("")) + assert.Equal(t, "x.y", f.subScope("y")) +} + +func TestFactory(t *testing.T) { + var ( + counterPrefix = "counter_" + gaugePrefix = "gauge_" + timerPrefix = "timer_" + + tagsA = map[string]string{"a": "b"} + tagsX = map[string]string{"x": "y"} + ) + + testCases := []struct { + name string + tags map[string]string + namespace string + nsTags map[string]string + fullName string + expectedCounter string + }{ + {name: "x", fullName: "%sx"}, + {tags: tagsX, fullName: "%s.x_y"}, + {name: "x", tags: tagsA, fullName: "%sx.a_b"}, + {namespace: "y", fullName: "y.%s"}, + {nsTags: tagsA, fullName: "%s.a_b"}, + {namespace: "y", nsTags: tagsX, fullName: "y.%s.x_y"}, + {name: "x", namespace: "y", nsTags: tagsX, fullName: "y.%sx.x_y"}, + {name: "x", tags: tagsX, namespace: "y", nsTags: tagsX, fullName: "y.%sx.x_y", expectedCounter: "84"}, + {name: "x", tags: tagsA, namespace: "y", nsTags: tagsX, fullName: "y.%sx.a_b.x_y"}, + {name: "x", tags: tagsX, namespace: "y", nsTags: tagsA, fullName: "y.%sx.a_b.x_y", expectedCounter: "84"}, + } + local := metrics.NewLocalFactory(100 * time.Second) + for _, testCase := range testCases { + t.Run("", func(t *testing.T) { + if testCase.expectedCounter == "" { + testCase.expectedCounter = "42" + } + ff := &fakeTagless{factory: local} + f := WrapFactoryWithoutTags(ff, Options{}) + if testCase.namespace != "" || testCase.nsTags != nil { + f = f.Namespace(testCase.namespace, testCase.nsTags) + } + counter := f.Counter(counterPrefix+testCase.name, testCase.tags) + gauge := f.Gauge(gaugePrefix+testCase.name, testCase.tags) + timer := f.Timer(timerPrefix+testCase.name, testCase.tags) + + assert.Equal(t, counter, f.Counter(counterPrefix+testCase.name, testCase.tags)) + assert.Equal(t, gauge, f.Gauge(gaugePrefix+testCase.name, testCase.tags)) + assert.Equal(t, timer, f.Timer(timerPrefix+testCase.name, testCase.tags)) + + assert.Equal(t, fmt.Sprintf(testCase.fullName, counterPrefix), ff.counter) + assert.Equal(t, fmt.Sprintf(testCase.fullName, gaugePrefix), ff.gauge) + assert.Equal(t, fmt.Sprintf(testCase.fullName, timerPrefix), ff.timer) + }) + } +} + +type fakeTagless struct { + factory metrics.Factory + counter string + gauge string + timer string +} + +func (f *fakeTagless) Counter(name string) metrics.Counter { + f.counter = name + return f.factory.Counter(name, nil) +} + +func (f *fakeTagless) Gauge(name string) metrics.Gauge { + f.gauge = name + return f.factory.Gauge(name, nil) +} + +func (f *fakeTagless) Timer(name string) metrics.Timer { + f.timer = name + return f.factory.Timer(name, nil) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/adapters/tagless.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/adapters/tagless.go new file mode 100644 index 00000000..a393b0a2 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/adapters/tagless.go @@ -0,0 +1,61 @@ +// Copyright (c) 2018 Uber Technologies, Inc. +// +// 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. + +package adapters + +import "github.com/uber/jaeger-lib/metrics" + +// FactoryWithoutTags creates metrics based on name only, without tags. +// Suitable for integrating with statsd-like backends that don't support tags. +type FactoryWithoutTags interface { + Counter(name string) metrics.Counter + Gauge(name string) metrics.Gauge + Timer(name string) metrics.Timer +} + +// WrapFactoryWithoutTags creates a real metrics.Factory that supports subscopes. +func WrapFactoryWithoutTags(f FactoryWithoutTags, options Options) metrics.Factory { + return WrapFactoryWithTags( + &tagless{ + Options: defaultOptions(options), + factory: f, + }, + options, + ) +} + +// tagless implements FactoryWithTags +type tagless struct { + Options + factory FactoryWithoutTags +} + +func (f *tagless) Counter(name string, tags map[string]string) metrics.Counter { + fullName := f.getFullName(name, tags) + return f.factory.Counter(fullName) +} + +func (f *tagless) Gauge(name string, tags map[string]string) metrics.Gauge { + fullName := f.getFullName(name, tags) + return f.factory.Gauge(fullName) +} + +func (f *tagless) Timer(name string, tags map[string]string) metrics.Timer { + fullName := f.getFullName(name, tags) + return f.factory.Timer(fullName) +} + +func (f *tagless) getFullName(name string, tags map[string]string) string { + return metrics.GetKey(name, tags, f.TagsSep, f.TagKVSep) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/counter.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/counter.go new file mode 100644 index 00000000..2a6a43ef --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/counter.go @@ -0,0 +1,28 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package metrics + +// Counter tracks the number of times an event has occurred +type Counter interface { + // Inc adds the given value to the counter. + Inc(int64) +} + +// NullCounter counter that does nothing +var NullCounter Counter = nullCounter{} + +type nullCounter struct{} + +func (nullCounter) Inc(int64) {} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/expvar/factory.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/expvar/factory.go new file mode 100644 index 00000000..114f092e --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/expvar/factory.go @@ -0,0 +1,49 @@ +// Copyright (c) 2018 Uber Technologies, Inc. +// +// 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. + +package expvar + +import ( + "github.com/uber/jaeger-lib/metrics" + "github.com/uber/jaeger-lib/metrics/adapters" + xkit "github.com/uber/jaeger-lib/metrics/go-kit" + "github.com/uber/jaeger-lib/metrics/go-kit/expvar" +) + +// NewFactory creates a new metrics factory using go-kit expvar package. +// buckets is the number of buckets to be used in histograms. +func NewFactory(buckets int) metrics.Factory { + return adapters.WrapFactoryWithoutTags( + &factory{ + factory: expvar.NewFactory(buckets), + }, + adapters.Options{}, + ) +} + +type factory struct { + factory xkit.Factory +} + +func (f *factory) Counter(name string) metrics.Counter { + return xkit.NewCounter(f.factory.Counter(name)) +} + +func (f *factory) Gauge(name string) metrics.Gauge { + return xkit.NewGauge(f.factory.Gauge(name)) +} + +func (f *factory) Timer(name string) metrics.Timer { + return xkit.NewTimer(f.factory.Histogram(name)) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/expvar/factory_test.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/expvar/factory_test.go new file mode 100644 index 00000000..a13f8f08 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/expvar/factory_test.go @@ -0,0 +1,104 @@ +// Copyright (c) 2018 Uber Technologies, Inc. +// +// 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. + +package expvar + +import ( + "expvar" + "fmt" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +var ( + id = time.Now().UnixNano() + prefix = fmt.Sprintf("test_%d", id) + counterPrefix = prefix + "_counter_" + gaugePrefix = prefix + "_gauge_" + timerPrefix = prefix + "_timer_" + + tagsA = map[string]string{"a": "b"} + tagsX = map[string]string{"x": "y"} +) + +func TestFactory(t *testing.T) { + testCases := []struct { + name string + tags map[string]string + namespace string + nsTags map[string]string + fullName string + expectedCounter string + }{ + {name: "x", fullName: "%sx"}, + {tags: tagsX, fullName: "%s.x_y"}, + {name: "x", tags: tagsA, fullName: "%sx.a_b"}, + {namespace: "y", fullName: "y.%s"}, + {nsTags: tagsA, fullName: "%s.a_b"}, + {namespace: "y", nsTags: tagsX, fullName: "y.%s.x_y"}, + {name: "x", namespace: "y", nsTags: tagsX, fullName: "y.%sx.x_y"}, + {name: "x", tags: tagsX, namespace: "y", nsTags: tagsX, fullName: "y.%sx.x_y", expectedCounter: "84"}, + {name: "x", tags: tagsA, namespace: "y", nsTags: tagsX, fullName: "y.%sx.a_b.x_y"}, + {name: "x", tags: tagsX, namespace: "y", nsTags: tagsA, fullName: "y.%sx.a_b.x_y", expectedCounter: "84"}, + } + f := NewFactory(2) + for _, testCase := range testCases { + t.Run("", func(t *testing.T) { + if testCase.expectedCounter == "" { + testCase.expectedCounter = "42" + } + ff := f + if testCase.namespace != "" || testCase.nsTags != nil { + ff = f.Namespace(testCase.namespace, testCase.nsTags) + } + counter := ff.Counter(counterPrefix+testCase.name, testCase.tags) + gauge := ff.Gauge(gaugePrefix+testCase.name, testCase.tags) + timer := ff.Timer(timerPrefix+testCase.name, testCase.tags) + + // register second time, should not panic + ff.Counter(counterPrefix+testCase.name, testCase.tags) + ff.Gauge(gaugePrefix+testCase.name, testCase.tags) + ff.Timer(timerPrefix+testCase.name, testCase.tags) + + counter.Inc(42) + gauge.Update(42) + timer.Record(42 * time.Millisecond) + + assertExpvar(t, fmt.Sprintf(testCase.fullName, counterPrefix), testCase.expectedCounter) + assertExpvar(t, fmt.Sprintf(testCase.fullName, gaugePrefix), "42") + assertExpvar(t, fmt.Sprintf(testCase.fullName, timerPrefix)+".p99", "0.042") + }) + } +} + +func assertExpvar(t *testing.T, fullName string, value string) { + var found expvar.KeyValue + expvar.Do(func(kv expvar.KeyValue) { + if kv.Key == fullName { + found = kv + } + }) + if !assert.Equal(t, fullName, found.Key) { + expvar.Do(func(kv expvar.KeyValue) { + if strings.HasPrefix(kv.Key, prefix) { + // t.Log(kv) + } + }) + return + } + assert.Equal(t, value, found.Value.String(), fullName) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/factory.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/factory.go new file mode 100644 index 00000000..a744a890 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/factory.go @@ -0,0 +1,35 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package metrics + +// Factory creates new metrics +type Factory interface { + Counter(name string, tags map[string]string) Counter + Timer(name string, tags map[string]string) Timer + Gauge(name string, tags map[string]string) Gauge + + // Namespace returns a nested metrics factory. + Namespace(name string, tags map[string]string) Factory +} + +// NullFactory is a metrics factory that returns NullCounter, NullTimer, and NullGauge. +var NullFactory Factory = nullFactory{} + +type nullFactory struct{} + +func (nullFactory) Counter(name string, tags map[string]string) Counter { return NullCounter } +func (nullFactory) Timer(name string, tags map[string]string) Timer { return NullTimer } +func (nullFactory) Gauge(name string, tags map[string]string) Gauge { return NullGauge } +func (nullFactory) Namespace(name string, tags map[string]string) Factory { return NullFactory } diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/gauge.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/gauge.go new file mode 100644 index 00000000..3c606391 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/gauge.go @@ -0,0 +1,28 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package metrics + +// Gauge returns instantaneous measurements of something as an int64 value +type Gauge interface { + // Update the gauge to the value passed in. + Update(int64) +} + +// NullGauge gauge that does nothing +var NullGauge Gauge = nullGauge{} + +type nullGauge struct{} + +func (nullGauge) Update(int64) {} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/expvar/factory.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/expvar/factory.go new file mode 100644 index 00000000..199eb8a0 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/expvar/factory.go @@ -0,0 +1,58 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package expvar + +import ( + "github.com/go-kit/kit/metrics" + "github.com/go-kit/kit/metrics/expvar" + + "github.com/uber/jaeger-lib/metrics/go-kit" +) + +// NewFactory creates a new metrics factory using go-kit expvar package. +// buckets is the number of buckets to be used in histograms. +// +// Deprecated: the recommended way is to use metrics/expvar module: +// +// "github.com/uber/jaeger-lib/metrics/expvar" +// +// var numBuckets = 10 +// var metricsFactory = expvar.NewFactory(numBuckets) +// +func NewFactory(buckets int) xkit.Factory { + return factory{ + buckets: buckets, + } +} + +type factory struct { + buckets int +} + +func (f factory) Counter(name string) metrics.Counter { + return expvar.NewCounter(name) +} + +func (f factory) Histogram(name string) metrics.Histogram { + return expvar.NewHistogram(name, f.buckets) +} + +func (f factory) Gauge(name string) metrics.Gauge { + return expvar.NewGauge(name) +} + +func (f factory) Capabilities() xkit.Capabilities { + return xkit.Capabilities{Tagging: false} +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/expvar/factory_test.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/expvar/factory_test.go new file mode 100644 index 00000000..30a0d594 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/expvar/factory_test.go @@ -0,0 +1,43 @@ +package expvar + +import ( + "expvar" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCounter(t *testing.T) { + f := NewFactory(10) + assert.False(t, f.Capabilities().Tagging) + c := f.Counter("gokit_expvar_counter") + c.Add(42) + kv := findExpvar("gokit_expvar_counter") + assert.Equal(t, "42", kv.Value.String()) +} + +func TestGauge(t *testing.T) { + f := NewFactory(10) + g := f.Gauge("gokit_expvar_gauge") + g.Set(42) + kv := findExpvar("gokit_expvar_gauge") + assert.Equal(t, "42", kv.Value.String()) +} + +func TestHistogram(t *testing.T) { + f := NewFactory(10) + hist := f.Histogram("gokit_expvar_hist") + hist.Observe(10.5) + kv := findExpvar("gokit_expvar_hist.p50") + assert.Equal(t, "10.5", kv.Value.String()) +} + +func findExpvar(key string) *expvar.KeyValue { + var kv *expvar.KeyValue + expvar.Do(func(v expvar.KeyValue) { + if v.Key == key { + kv = &v + } + }) + return kv +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/factory.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/factory.go new file mode 100644 index 00000000..d19c23c6 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/factory.go @@ -0,0 +1,161 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package xkit + +import ( + kit "github.com/go-kit/kit/metrics" + + "github.com/uber/jaeger-lib/metrics" +) + +// Factory provides a unified interface for creating named metrics +// from various go-kit metrics implementations. +type Factory interface { + Counter(name string) kit.Counter + Gauge(name string) kit.Gauge + Histogram(name string) kit.Histogram + Capabilities() Capabilities +} + +// Capabilities describes capabilities of a specific metrics factory. +type Capabilities struct { + // Tagging indicates whether the factory has the capability for tagged metrics + Tagging bool +} + +// FactoryOption is a function that adjusts some parameters of the factory. +type FactoryOption func(*factory) + +// Wrap is used to create an adapter from xkit.Factory to metrics.Factory. +func Wrap(namespace string, f Factory, options ...FactoryOption) metrics.Factory { + factory := &factory{ + scope: namespace, + factory: f, + scopeSep: ".", + tagsSep: ".", + tagKVSep: "_", + } + for i := range options { + options[i](factory) + } + return factory +} + +// ScopeSeparator returns an option that overrides default scope separator. +func ScopeSeparator(scopeSep string) FactoryOption { + return func(f *factory) { + f.scopeSep = scopeSep + } +} + +// TagsSeparator returns an option that overrides default tags separator. +func TagsSeparator(tagsSep string) FactoryOption { + return func(f *factory) { + f.tagsSep = tagsSep + } +} + +type factory struct { + scope string + tags map[string]string + factory Factory + scopeSep string + tagsSep string + tagKVSep string +} + +func (f *factory) subScope(name string) string { + if f.scope == "" { + return name + } + if name == "" { + return f.scope + } + return f.scope + f.scopeSep + name +} + +// nameAndTagsList returns a name and tags list for the new metrics. +// The name is a concatenation of nom and the current factory scope. +// The tags list is a flattened list of passed tags merged with factory tags. +// If the underlying factory does not support tags, then the tags are +// transformed into a string and appended to the name. +func (f *factory) nameAndTagsList(nom string, tags map[string]string) (name string, tagsList []string) { + mergedTags := f.mergeTags(tags) + name = f.subScope(nom) + tagsList = f.tagsList(mergedTags) + if len(tagsList) == 0 || f.factory.Capabilities().Tagging { + return + } + name = metrics.GetKey(name, mergedTags, f.tagsSep, f.tagKVSep) + tagsList = nil + return +} + +func (f *factory) Counter(name string, tags map[string]string) metrics.Counter { + name, tagsList := f.nameAndTagsList(name, tags) + counter := f.factory.Counter(name) + if len(tagsList) > 0 { + counter = counter.With(tagsList...) + } + return NewCounter(counter) +} + +func (f *factory) Timer(name string, tags map[string]string) metrics.Timer { + name, tagsList := f.nameAndTagsList(name, tags) + hist := f.factory.Histogram(name) + if len(tagsList) > 0 { + hist = hist.With(tagsList...) + } + return NewTimer(hist) +} + +func (f *factory) Gauge(name string, tags map[string]string) metrics.Gauge { + name, tagsList := f.nameAndTagsList(name, tags) + gauge := f.factory.Gauge(name) + if len(tagsList) > 0 { + gauge = gauge.With(tagsList...) + } + return NewGauge(gauge) +} + +func (f *factory) Namespace(name string, tags map[string]string) metrics.Factory { + return &factory{ + scope: f.subScope(name), + tags: f.mergeTags(tags), + factory: f.factory, + scopeSep: f.scopeSep, + tagsSep: f.tagsSep, + tagKVSep: f.tagKVSep, + } +} + +func (f *factory) tagsList(a map[string]string) []string { + ret := make([]string, 0, 2*len(a)) + for k, v := range a { + ret = append(ret, k, v) + } + return ret +} + +func (f *factory) mergeTags(tags map[string]string) map[string]string { + ret := make(map[string]string, len(f.tags)+len(tags)) + for k, v := range f.tags { + ret[k] = v + } + for k, v := range tags { + ret[k] = v + } + return ret +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/factory_test.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/factory_test.go new file mode 100644 index 00000000..d5e7d0ee --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/factory_test.go @@ -0,0 +1,202 @@ +package xkit + +import ( + "sort" + "testing" + "time" + + kit "github.com/go-kit/kit/metrics" + "github.com/go-kit/kit/metrics/generic" + "github.com/stretchr/testify/assert" + + "github.com/uber/jaeger-lib/metrics" +) + +type genericFactory struct{} + +func (f genericFactory) Counter(name string) kit.Counter { return generic.NewCounter(name) } +func (f genericFactory) Gauge(name string) kit.Gauge { return generic.NewGauge(name) } +func (f genericFactory) Histogram(name string) kit.Histogram { return generic.NewHistogram(name, 10) } +func (f genericFactory) Capabilities() Capabilities { return Capabilities{Tagging: true} } + +// noTagsFactory is similar to genericFactory but overrides With() methods to no-op +type noTagsFactory struct{} + +func (f noTagsFactory) Counter(name string) kit.Counter { + return noTagsCounter{generic.NewCounter(name)} +} +func (f noTagsFactory) Gauge(name string) kit.Gauge { return generic.NewGauge(name) } +func (f noTagsFactory) Histogram(name string) kit.Histogram { return generic.NewHistogram(name, 10) } +func (f noTagsFactory) Capabilities() Capabilities { return Capabilities{Tagging: false} } + +type noTagsCounter struct { + counter *generic.Counter +} + +func (c noTagsCounter) Add(delta float64) { c.counter.Add(delta) } +func (c noTagsCounter) With(labelValues ...string) kit.Counter { return c } + +type noTagsGauge struct { + gauge *generic.Gauge +} + +func (g noTagsGauge) Set(value float64) { g.gauge.Set(value) } +func (g noTagsGauge) Add(delta float64) { g.gauge.Add(delta) } +func (g noTagsGauge) With(labelValues ...string) kit.Gauge { return g } + +type noTagsHistogram struct { + hist *generic.Histogram +} + +func (h noTagsHistogram) Observe(value float64) { h.hist.Observe(value) } +func (h noTagsHistogram) With(labelValues ...string) kit.Histogram { return h } + +type Tags map[string]string +type metricFunc func(t *testing.T, testCase testCase, f metrics.Factory) (name func() string, labels func() []string) + +type testCase struct { + f Factory + + prefix string + name string + tags Tags + + useNamespace bool + namespace string + namespaceTags Tags + options []FactoryOption + + expName string + expTags []string +} + +func TestFactoryScoping(t *testing.T) { + genericFactory := genericFactory{} + noTagsFactory := noTagsFactory{} + testSuites := []struct { + metricType string + metricFunc metricFunc + }{ + {"counter", testCounter}, + {"gauge", testGauge}, + {"timer", testTimer}, + } + for _, ts := range testSuites { + testSuite := ts // capture loop var + testCases := []testCase{ + {f: genericFactory, prefix: "x", name: "", expName: "x"}, + {f: genericFactory, prefix: "", name: "y", expName: "y"}, + {f: genericFactory, prefix: "x", name: "y", expName: "x.y"}, + {f: genericFactory, prefix: "x", name: "z", expName: "x.z", tags: Tags{"a": "b"}, expTags: []string{"a", "b"}}, + { + f: genericFactory, + name: "x", + useNamespace: true, + namespace: "w", + expName: "w.x", + }, + { + f: genericFactory, + name: "y", + useNamespace: true, + namespace: "w", + namespaceTags: Tags{"a": "b"}, + expName: "w.y", + expTags: []string{"a", "b"}, + }, + { + f: genericFactory, + name: "z", + tags: Tags{"a": "b"}, + useNamespace: true, + namespace: "w", + namespaceTags: Tags{"c": "d"}, + expName: "w.z", + expTags: []string{"a", "b", "c", "d"}, + }, + {f: noTagsFactory, prefix: "x", name: "", expName: "x"}, + {f: noTagsFactory, prefix: "", name: "y", expName: "y"}, + {f: noTagsFactory, prefix: "x", name: "y", expName: "x.y"}, + {f: noTagsFactory, prefix: "x", name: "z", expName: "x.z.a_b", tags: Tags{"a": "b"}}, + { + f: noTagsFactory, + name: "x", + useNamespace: true, + namespace: "w", + expName: "w.x", + }, + { + f: noTagsFactory, + name: "y", + useNamespace: true, + namespace: "w", + namespaceTags: Tags{"a": "b"}, + expName: "w.y.a_b", + }, + { + f: noTagsFactory, + options: []FactoryOption{ScopeSeparator(":"), TagsSeparator(":")}, + name: "z", + tags: Tags{"a": "b"}, + useNamespace: true, + namespace: "w", + namespaceTags: Tags{"c": "d"}, + expName: "w:z:a_b:c_d", + }, + } + for _, tc := range testCases { + testCase := tc // capture loop var + factoryName := "genericFactory" + if testCase.f == noTagsFactory { + factoryName = "noTagsFactory" + } + t.Run(factoryName+"_"+testSuite.metricType+"_"+testCase.expName, func(t *testing.T) { + f := Wrap(testCase.prefix, testCase.f, testCase.options...) + if testCase.useNamespace { + f = f.Namespace(testCase.namespace, testCase.namespaceTags) + } + name, labels := testSuite.metricFunc(t, testCase, f) + assert.Equal(t, testCase.expName, name()) + actualTags := labels() + sort.Strings(actualTags) + assert.Equal(t, testCase.expTags, actualTags) + }) + } + } +} + +func testCounter(t *testing.T, testCase testCase, f metrics.Factory) (name func() string, labels func() []string) { + c := f.Counter(testCase.name, testCase.tags) + c.Inc(123) + kc := c.(*Counter).counter + var gc *generic.Counter + if c, ok := kc.(*generic.Counter); ok { + gc = c + } else { + gc = kc.(noTagsCounter).counter + } + assert.EqualValues(t, 123.0, gc.Value()) + name = func() string { return gc.Name } + labels = gc.LabelValues + return +} + +func testGauge(t *testing.T, testCase testCase, f metrics.Factory) (name func() string, labels func() []string) { + g := f.Gauge(testCase.name, testCase.tags) + g.Update(123) + gg := g.(*Gauge).gauge.(*generic.Gauge) + assert.EqualValues(t, 123.0, gg.Value()) + name = func() string { return gg.Name } + labels = gg.LabelValues + return +} + +func testTimer(t *testing.T, testCase testCase, f metrics.Factory) (name func() string, labels func() []string) { + tm := f.Timer(testCase.name, testCase.tags) + tm.Record(123 * time.Millisecond) + gt := tm.(*Timer).hist.(*generic.Histogram) + assert.InDelta(t, 0.123, gt.Quantile(0.9), 0.00001) + name = func() string { return gt.Name } + labels = gt.LabelValues + return +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/influx/factory.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/influx/factory.go new file mode 100644 index 00000000..d9249321 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/influx/factory.go @@ -0,0 +1,49 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package influx + +import ( + "github.com/go-kit/kit/metrics" + "github.com/go-kit/kit/metrics/influx" + + "github.com/uber/jaeger-lib/metrics/go-kit" +) + +// NewFactory creates a new metrics factory using go-kit influx package. +func NewFactory(client *influx.Influx) xkit.Factory { + return factory{ + client: client, + } +} + +type factory struct { + client *influx.Influx +} + +func (f factory) Counter(name string) metrics.Counter { + return f.client.NewCounter(name) +} + +func (f factory) Histogram(name string) metrics.Histogram { + return f.client.NewHistogram(name) +} + +func (f factory) Gauge(name string) metrics.Gauge { + return f.client.NewGauge(name) +} + +func (f factory) Capabilities() xkit.Capabilities { + return xkit.Capabilities{Tagging: true} +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/influx/factory_test.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/influx/factory_test.go new file mode 100644 index 00000000..f4dc71ca --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/influx/factory_test.go @@ -0,0 +1,87 @@ +package influx + +import ( + "bytes" + "fmt" + "testing" + "time" + + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/metrics/influx" + influxdb "github.com/influxdata/influxdb/client/v2" + "github.com/stretchr/testify/assert" + + "github.com/uber/jaeger-lib/metrics/go-kit" +) + +func TestCounter(t *testing.T) { + in := influx.New(map[string]string{}, influxdb.BatchPointsConfig{}, log.NewNopLogger()) + inf := NewFactory(in) + wf := xkit.Wrap("namespace", inf) + + c := wf.Counter("gokit.infl-counter", map[string]string{"label": "val1"}) + c.Inc(7) + + assert.Contains(t, reportToString(in), "namespace.gokit.infl-counter,label=val1 count=7") +} + +func TestGauge(t *testing.T) { + in := influx.New(map[string]string{}, influxdb.BatchPointsConfig{}, log.NewNopLogger()) + inf := NewFactory(in) + wf := xkit.Wrap("namespace", inf) + + g := wf.Gauge("gokit.infl-gauge", map[string]string{"x": "y"}) + g.Update(42) + + assert.Contains(t, reportToString(in), "namespace.gokit.infl-gauge,x=y value=42") +} + +func TestTimer(t *testing.T) { + in := influx.New(map[string]string{}, influxdb.BatchPointsConfig{}, log.NewNopLogger()) + inf := NewFactory(in) + wf := xkit.Wrap("namespace", inf) + + timer := wf.Timer("gokit.infl-timer", map[string]string{"x": "y"}) + timer.Record(time.Second * 1) + timer.Record(time.Second * 1) + timer.Record(time.Second * 10) + + assert.Contains(t, reportToString(in), "namespace.gokit.infl-timer,x=y p50=1,p90=10,p95=10,p99=10") +} + +func TestWrapperNamespaces(t *testing.T) { + in := influx.New(map[string]string{}, influxdb.BatchPointsConfig{}, log.NewNopLogger()) + inf := NewFactory(in) + wf := xkit.Wrap("namespace", inf) + + wf = wf.Namespace("bar", map[string]string{"bar_tag": "bar_tag"}) + + c := wf.Counter("gokit.prom-wrapped-counter", map[string]string{"x": "y"}) + c.Inc(42) + + assert.Contains(t, reportToString(in), "namespace.bar.gokit.prom-wrapped-counter,bar_tag=bar_tag,x=y count=42") +} + +func TestCapabilities(t *testing.T) { + in := influx.New(map[string]string{}, influxdb.BatchPointsConfig{}, log.NewNopLogger()) + inf := NewFactory(in) + + assert.True(t, inf.Capabilities().Tagging) +} + +func reportToString(in *influx.Influx) string { + client := &bufWriter{} + in.WriteTo(client) + return client.buf.String() +} + +type bufWriter struct { + buf bytes.Buffer +} + +func (w *bufWriter) Write(bp influxdb.BatchPoints) error { + for _, p := range bp.Points() { + fmt.Fprintf(&w.buf, p.String()+"\n") + } + return nil +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/metrics.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/metrics.go new file mode 100644 index 00000000..3b55b8ba --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/metrics.go @@ -0,0 +1,66 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package xkit + +import ( + "time" + + kit "github.com/go-kit/kit/metrics" +) + +// Counter is an adapter from go-kit Counter to jaeger-lib Counter +type Counter struct { + counter kit.Counter +} + +// NewCounter creates a new Counter +func NewCounter(counter kit.Counter) *Counter { + return &Counter{counter: counter} +} + +// Inc adds the given value to the counter. +func (c *Counter) Inc(delta int64) { + c.counter.Add(float64(delta)) +} + +// Gauge is an adapter from go-kit Gauge to jaeger-lib Gauge +type Gauge struct { + gauge kit.Gauge +} + +// NewGauge creates a new Gauge +func NewGauge(gauge kit.Gauge) *Gauge { + return &Gauge{gauge: gauge} +} + +// Update the gauge to the value passed in. +func (g *Gauge) Update(value int64) { + g.gauge.Set(float64(value)) +} + +// Timer is an adapter from go-kit Histogram to jaeger-lib Timer +type Timer struct { + hist kit.Histogram +} + +// NewTimer creates a new Timer +func NewTimer(hist kit.Histogram) *Timer { + return &Timer{hist: hist} +} + +// Record saves the time passed in. +func (t *Timer) Record(delta time.Duration) { + t.hist.Observe(delta.Seconds()) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/metrics_test.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/metrics_test.go new file mode 100644 index 00000000..b62c08af --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/metrics_test.go @@ -0,0 +1,32 @@ +package xkit + +import ( + "testing" + "time" + + "github.com/go-kit/kit/metrics/generic" + "github.com/stretchr/testify/assert" + + "github.com/uber/jaeger-lib/metrics" +) + +func TestCounter(t *testing.T) { + kitCounter := generic.NewCounter("abc") + var counter metrics.Counter = NewCounter(kitCounter) + counter.Inc(123) + assert.EqualValues(t, 123, kitCounter.Value()) +} + +func TestGauge(t *testing.T) { + kitGauge := generic.NewGauge("abc") + var gauge metrics.Gauge = NewGauge(kitGauge) + gauge.Update(123) + assert.EqualValues(t, 123, kitGauge.Value()) +} + +func TestTimer(t *testing.T) { + kitHist := generic.NewHistogram("abc", 10) + var timer metrics.Timer = NewTimer(kitHist) + timer.Record(100*time.Millisecond + 500*time.Microsecond) // 100.5 milliseconds + assert.EqualValues(t, 0.1005, kitHist.Quantile(0.9)) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/prometheus/factory.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/prometheus/factory.go new file mode 100644 index 00000000..c9f8ec9a --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/prometheus/factory.go @@ -0,0 +1,93 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package prometheus + +import ( + "strings" + + "github.com/go-kit/kit/metrics" + kitprom "github.com/go-kit/kit/metrics/prometheus" + "github.com/prometheus/client_golang/prometheus" + + "github.com/uber/jaeger-lib/metrics/go-kit" +) + +var normalizer = strings.NewReplacer( + ".", "_", + "-", "_", +) + +// NewFactory creates a new metrics factory using go-kit prometheus package. +// buckets define the buckets into which histogram observations are counted. +// If buckets == nil, the default value prometheus.DefBuckets is used. +// +// Deprecated: the recommended way is to use metrics/prometheus module: +// +// import ( +// "github.com/prometheus/client_golang/prometheus" +// xprom "github.com/uber/jaeger-lib/metrics/prometheus" +// ) +// +// registry := prometheus.NewPedanticRegistry() +// factory := xprom.New(xprom.WithRegisterer(registry)) +// +func NewFactory(namespace, subsystem string, buckets []float64) xkit.Factory { + return &factory{ + namespace: namespace, + subsystem: subsystem, + buckets: buckets, + } +} + +type factory struct { + namespace string + subsystem string + buckets []float64 +} + +func (f *factory) Counter(name string) metrics.Counter { + opts := prometheus.CounterOpts{ + Namespace: f.namespace, + Subsystem: f.subsystem, + Name: normalizer.Replace(name), + Help: name, + } + return kitprom.NewCounterFrom(opts, nil) +} + +func (f *factory) Histogram(name string) metrics.Histogram { + opts := prometheus.HistogramOpts{ + Namespace: f.namespace, + Subsystem: f.subsystem, + Name: normalizer.Replace(name), + Help: name, + Buckets: f.buckets, + } + return kitprom.NewHistogramFrom(opts, nil) +} + +func (f *factory) Gauge(name string) metrics.Gauge { + opts := prometheus.GaugeOpts{ + Namespace: f.namespace, + Subsystem: f.subsystem, + Name: normalizer.Replace(name), + Help: name, + } + return kitprom.NewGaugeFrom(opts, nil) +} + +func (f *factory) Capabilities() xkit.Capabilities { + return xkit.Capabilities{Tagging: true} +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/prometheus/factory_test.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/prometheus/factory_test.go new file mode 100644 index 00000000..6aacfd29 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/go-kit/prometheus/factory_test.go @@ -0,0 +1,64 @@ +package prometheus + +import ( + "testing" + + "github.com/prometheus/client_golang/prometheus" + dto "github.com/prometheus/client_model/go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + xkit "github.com/uber/jaeger-lib/metrics/go-kit" +) + +func TestCounter(t *testing.T) { + f := NewFactory("namespace", "subsystem", nil) + assert.True(t, f.Capabilities().Tagging) + c := f.Counter("gokit.prom-counter") + c.Add(42) + m := findMetric(t, "namespace_subsystem_gokit_prom_counter") + require.NotNil(t, m) + assert.Equal(t, 42.0, m[0].GetCounter().GetValue()) +} + +func TestGauge(t *testing.T) { + f := NewFactory("namespace", "subsystem", nil) + g := f.Gauge("gokit.prom-gauge") + g.Set(42) + m := findMetric(t, "namespace_subsystem_gokit_prom_gauge") + require.NotNil(t, m) + assert.Equal(t, 42.0, m[0].GetGauge().GetValue()) +} + +func TestHistogram(t *testing.T) { + f := NewFactory("namespace", "subsystem", nil) + hist := f.Histogram("gokit.prom-hist") + hist.Observe(10.5) + m := findMetric(t, "namespace_subsystem_gokit_prom_hist") + require.NotNil(t, m) + assert.Equal(t, 10.5, m[0].GetHistogram().GetSampleSum()) +} + +func TestWrapper(t *testing.T) { + f := NewFactory("", "", nil) + wf := xkit.Wrap("foo", f, xkit.ScopeSeparator(":")) + wf = wf.Namespace("bar", nil) + c := wf.Counter("gokit.prom-wrapped-counter", nil) + // TODO tags currently do not work because Prometheus requires to predeclate tag keys. + // c := wf.Counter("gokit.prom-wrapped-counter", map[string]string{"x": "y"}) + c.Inc(42) + m := findMetric(t, "foo:bar:gokit_prom_wrapped_counter") + require.NotNil(t, m) + assert.Equal(t, 42.0, m[0].GetCounter().GetValue()) +} + +func findMetric(t *testing.T, key string) []*dto.Metric { + nf, err := prometheus.DefaultGatherer.Gather() + require.NoError(t, err) + for _, nf := range nf { + if nf.GetName() == key { + return nf.GetMetric() + } + } + return nil +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/local.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/local.go new file mode 100644 index 00000000..217d3060 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/local.go @@ -0,0 +1,337 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package metrics + +import ( + "sort" + "sync" + "sync/atomic" + "time" + + "github.com/codahale/hdrhistogram" +) + +// This is intentionally very similar to github.com/codahale/metrics, the +// main difference being that counters/gauges are scoped to the provider +// rather than being global (to facilitate testing). + +// A LocalBackend is a metrics provider which aggregates data in-vm, and +// allows exporting snapshots to shove the data into a remote collector +type LocalBackend struct { + cm sync.Mutex + gm sync.Mutex + tm sync.Mutex + counters map[string]*int64 + gauges map[string]*int64 + timers map[string]*localBackendTimer + stop chan struct{} + wg sync.WaitGroup + TagsSep string + TagKVSep string +} + +// NewLocalBackend returns a new LocalBackend. The collectionInterval is the histogram +// time window for each timer. +func NewLocalBackend(collectionInterval time.Duration) *LocalBackend { + b := &LocalBackend{ + counters: make(map[string]*int64), + gauges: make(map[string]*int64), + timers: make(map[string]*localBackendTimer), + stop: make(chan struct{}), + TagsSep: "|", + TagKVSep: "=", + } + if collectionInterval == 0 { + // Use one histogram time window for all timers + return b + } + b.wg.Add(1) + go b.runLoop(collectionInterval) + return b +} + +// Clear discards accumulated stats +func (b *LocalBackend) Clear() { + b.cm.Lock() + defer b.cm.Unlock() + b.gm.Lock() + defer b.gm.Unlock() + b.tm.Lock() + defer b.tm.Unlock() + b.counters = make(map[string]*int64) + b.gauges = make(map[string]*int64) + b.timers = make(map[string]*localBackendTimer) +} + +func (b *LocalBackend) runLoop(collectionInterval time.Duration) { + defer b.wg.Done() + ticker := time.NewTicker(collectionInterval) + for { + select { + case <-ticker.C: + b.tm.Lock() + timers := make(map[string]*localBackendTimer, len(b.timers)) + for timerName, timer := range b.timers { + timers[timerName] = timer + } + b.tm.Unlock() + + for _, t := range timers { + t.Lock() + t.hist.Rotate() + t.Unlock() + } + case <-b.stop: + ticker.Stop() + return + } + } +} + +// IncCounter increments a counter value +func (b *LocalBackend) IncCounter(name string, tags map[string]string, delta int64) { + name = GetKey(name, tags, b.TagsSep, b.TagKVSep) + b.cm.Lock() + defer b.cm.Unlock() + counter := b.counters[name] + if counter == nil { + b.counters[name] = new(int64) + *b.counters[name] = delta + return + } + atomic.AddInt64(counter, delta) +} + +// UpdateGauge updates the value of a gauge +func (b *LocalBackend) UpdateGauge(name string, tags map[string]string, value int64) { + name = GetKey(name, tags, b.TagsSep, b.TagKVSep) + b.gm.Lock() + defer b.gm.Unlock() + gauge := b.gauges[name] + if gauge == nil { + b.gauges[name] = new(int64) + *b.gauges[name] = value + return + } + atomic.StoreInt64(gauge, value) +} + +// RecordTimer records a timing duration +func (b *LocalBackend) RecordTimer(name string, tags map[string]string, d time.Duration) { + name = GetKey(name, tags, b.TagsSep, b.TagKVSep) + timer := b.findOrCreateTimer(name) + timer.Lock() + timer.hist.Current.RecordValue(int64(d / time.Millisecond)) + timer.Unlock() +} + +func (b *LocalBackend) findOrCreateTimer(name string) *localBackendTimer { + b.tm.Lock() + defer b.tm.Unlock() + if t, ok := b.timers[name]; ok { + return t + } + + t := &localBackendTimer{ + hist: hdrhistogram.NewWindowed(5, 0, int64((5*time.Minute)/time.Millisecond), 1), + } + b.timers[name] = t + return t +} + +type localBackendTimer struct { + sync.Mutex + hist *hdrhistogram.WindowedHistogram +} + +var ( + percentiles = map[string]float64{ + "P50": 50, + "P75": 75, + "P90": 90, + "P95": 95, + "P99": 99, + "P999": 99.9, + } +) + +// Snapshot captures a snapshot of the current counter and gauge values +func (b *LocalBackend) Snapshot() (counters, gauges map[string]int64) { + b.cm.Lock() + defer b.cm.Unlock() + + counters = make(map[string]int64, len(b.counters)) + for name, value := range b.counters { + counters[name] = atomic.LoadInt64(value) + } + + b.gm.Lock() + defer b.gm.Unlock() + + gauges = make(map[string]int64, len(b.gauges)) + for name, value := range b.gauges { + gauges[name] = atomic.LoadInt64(value) + } + + b.tm.Lock() + timers := make(map[string]*localBackendTimer) + for timerName, timer := range b.timers { + timers[timerName] = timer + } + b.tm.Unlock() + + for timerName, timer := range timers { + timer.Lock() + hist := timer.hist.Merge() + timer.Unlock() + for name, q := range percentiles { + gauges[timerName+"."+name] = hist.ValueAtQuantile(q) + } + } + + return +} + +// Stop cleanly closes the background goroutine spawned by NewLocalBackend. +func (b *LocalBackend) Stop() { + close(b.stop) + b.wg.Wait() +} + +// GetKey converts name+tags into a single string of the form +// "name|tag1=value1|...|tagN=valueN", where tag names are +// sorted alphabetically. +func GetKey(name string, tags map[string]string, tagsSep string, tagKVSep string) string { + keys := make([]string, 0, len(tags)) + for k := range tags { + keys = append(keys, k) + } + sort.Strings(keys) + key := name + for _, k := range keys { + key = key + tagsSep + k + tagKVSep + tags[k] + } + return key +} + +type stats struct { + name string + tags map[string]string + localBackend *LocalBackend +} + +type localTimer struct { + stats +} + +func (l *localTimer) Record(d time.Duration) { + l.localBackend.RecordTimer(l.name, l.tags, d) +} + +type localCounter struct { + stats +} + +func (l *localCounter) Inc(delta int64) { + l.localBackend.IncCounter(l.name, l.tags, delta) +} + +type localGauge struct { + stats +} + +func (l *localGauge) Update(value int64) { + l.localBackend.UpdateGauge(l.name, l.tags, value) +} + +// LocalFactory stats factory that creates metrics that are stored locally +type LocalFactory struct { + *LocalBackend + namespace string + tags map[string]string +} + +// NewLocalFactory returns a new LocalMetricsFactory +func NewLocalFactory(collectionInterval time.Duration) *LocalFactory { + return &LocalFactory{ + LocalBackend: NewLocalBackend(collectionInterval), + } +} + +// appendTags adds the tags to the namespace tags and returns a combined map. +func (l *LocalFactory) appendTags(tags map[string]string) map[string]string { + newTags := make(map[string]string) + for k, v := range l.tags { + newTags[k] = v + } + for k, v := range tags { + newTags[k] = v + } + return newTags +} + +func (l *LocalFactory) newNamespace(name string) string { + if l.namespace == "" { + return name + } + + if name == "" { + return l.namespace + } + + return l.namespace + "." + name +} + +// Counter returns a local stats counter +func (l *LocalFactory) Counter(name string, tags map[string]string) Counter { + return &localCounter{ + stats{ + name: l.newNamespace(name), + tags: l.appendTags(tags), + localBackend: l.LocalBackend, + }, + } +} + +// Timer returns a local stats timer. +func (l *LocalFactory) Timer(name string, tags map[string]string) Timer { + return &localTimer{ + stats{ + name: l.newNamespace(name), + tags: l.appendTags(tags), + localBackend: l.LocalBackend, + }, + } +} + +// Gauge returns a local stats gauge. +func (l *LocalFactory) Gauge(name string, tags map[string]string) Gauge { + return &localGauge{ + stats{ + name: l.newNamespace(name), + tags: l.appendTags(tags), + localBackend: l.LocalBackend, + }, + } +} + +// Namespace returns a new namespace. +func (l *LocalFactory) Namespace(name string, tags map[string]string) Factory { + return &LocalFactory{ + namespace: l.newNamespace(name), + tags: l.appendTags(tags), + LocalBackend: l.LocalBackend, + } +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/local_test.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/local_test.go new file mode 100644 index 00000000..22c84ee8 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/local_test.go @@ -0,0 +1,118 @@ +package metrics + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestLocalMetrics(t *testing.T) { + tags := map[string]string{ + "x": "y", + } + + f := NewLocalFactory(0) + defer f.Stop() + f.Counter("my-counter", tags).Inc(4) + f.Counter("my-counter", tags).Inc(6) + f.Counter("my-counter", nil).Inc(6) + f.Counter("other-counter", nil).Inc(8) + f.Gauge("my-gauge", nil).Update(25) + f.Gauge("my-gauge", nil).Update(43) + f.Gauge("other-gauge", nil).Update(74) + f.Namespace("namespace", tags).Counter("my-counter", nil).Inc(7) + f.Namespace("ns.subns", nil).Counter("", map[string]string{"service": "a-service"}).Inc(9) + + timings := map[string][]time.Duration{ + "foo-latency": { + time.Second * 35, + time.Second * 6, + time.Millisecond * 576, + time.Second * 12, + }, + "bar-latency": { + time.Minute*4 + time.Second*34, + time.Minute*7 + time.Second*12, + time.Second * 625, + time.Second * 12, + }, + } + + for metric, timing := range timings { + for _, d := range timing { + f.Timer(metric, nil).Record(d) + } + } + + c, g := f.Snapshot() + require.NotNil(t, c) + require.NotNil(t, g) + + assert.Equal(t, map[string]int64{ + "my-counter|x=y": 10, + "my-counter": 6, + "other-counter": 8, + "namespace.my-counter|x=y": 7, + "ns.subns|service=a-service": 9, + }, c) + + assert.Equal(t, map[string]int64{ + "bar-latency.P50": 278527, + "bar-latency.P75": 278527, + "bar-latency.P90": 442367, + "bar-latency.P95": 442367, + "bar-latency.P99": 442367, + "bar-latency.P999": 442367, + "foo-latency.P50": 6143, + "foo-latency.P75": 12287, + "foo-latency.P90": 36863, + "foo-latency.P95": 36863, + "foo-latency.P99": 36863, + "foo-latency.P999": 36863, + "my-gauge": 43, + "other-gauge": 74, + }, g) + + f.Clear() + c, g = f.Snapshot() + require.Empty(t, c) + require.Empty(t, g) +} + +func TestLocalMetricsInterval(t *testing.T) { + refreshInterval := time.Millisecond + const relativeCheckFrequency = 5 // check 5 times per refreshInterval + const maxChecks = 2 * relativeCheckFrequency + checkInterval := (refreshInterval * relativeCheckFrequency) / maxChecks + + f := NewLocalFactory(refreshInterval) + defer f.Stop() + + f.Timer("timer", nil).Record(1) + + f.tm.Lock() + timer := f.timers["timer"] + f.tm.Unlock() + assert.NotNil(t, timer) + + // timer.hist.Current is modified on every Rotate(), which is called by LocalBackend after every refreshInterval + getCurr := func() interface{} { + timer.Lock() + defer timer.Unlock() + return timer.hist.Current + } + + curr := getCurr() + + // wait for twice as long as the refresh interval + for i := 0; i < maxChecks; i++ { + time.Sleep(checkInterval) + + if getCurr() != curr { + return + } + } + t.Fail() +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/metrics.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/metrics.go new file mode 100644 index 00000000..0b97707b --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/metrics.go @@ -0,0 +1,85 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package metrics + +import ( + "fmt" + "reflect" + "strings" +) + +// Init initializes the passed in metrics and initializes its fields using the passed in factory. +func Init(metrics interface{}, factory Factory, globalTags map[string]string) { + if err := initMetrics(metrics, factory, globalTags); err != nil { + panic(err.Error()) + } +} + +// initMetrics uses reflection to initialize a struct containing metrics fields +// by assigning new Counter/Gauge/Timer values with the metric name retrieved +// from the `metric` tag and stats tags retrieved from the `tags` tag. +// +// Note: all fields of the struct must be exported, have a `metric` tag, and be +// of type Counter or Gauge or Timer. +func initMetrics(m interface{}, factory Factory, globalTags map[string]string) error { + // Allow user to opt out of reporting metrics by passing in nil. + if factory == nil { + factory = NullFactory + } + + counterPtrType := reflect.TypeOf((*Counter)(nil)).Elem() + gaugePtrType := reflect.TypeOf((*Gauge)(nil)).Elem() + timerPtrType := reflect.TypeOf((*Timer)(nil)).Elem() + + v := reflect.ValueOf(m).Elem() + t := v.Type() + for i := 0; i < t.NumField(); i++ { + tags := make(map[string]string) + for k, v := range globalTags { + tags[k] = v + } + field := t.Field(i) + metric := field.Tag.Get("metric") + if metric == "" { + return fmt.Errorf("Field %s is missing a tag 'metric'", field.Name) + } + if tagString := field.Tag.Get("tags"); tagString != "" { + tagPairs := strings.Split(tagString, ",") + for _, tagPair := range tagPairs { + tag := strings.Split(tagPair, "=") + if len(tag) != 2 { + return fmt.Errorf( + "Field [%s]: Tag [%s] is not of the form key=value in 'tags' string [%s]", + field.Name, tagPair, tagString) + } + tags[tag[0]] = tag[1] + } + } + var obj interface{} + if field.Type.AssignableTo(counterPtrType) { + obj = factory.Counter(metric, tags) + } else if field.Type.AssignableTo(gaugePtrType) { + obj = factory.Gauge(metric, tags) + } else if field.Type.AssignableTo(timerPtrType) { + obj = factory.Timer(metric, tags) + } else { + return fmt.Errorf( + "Field %s is not a pointer to timer, gauge, or counter", + field.Name) + } + v.Field(i).Set(reflect.ValueOf(obj)) + } + return nil +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/metrics_test.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/metrics_test.go new file mode 100644 index 00000000..7d9226c6 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/metrics_test.go @@ -0,0 +1,89 @@ +package metrics + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestInitMetrics(t *testing.T) { + testMetrics := struct { + Gauge Gauge `metric:"gauge" tags:"1=one,2=two"` + Counter Counter `metric:"counter"` + Timer Timer `metric:"timer"` + }{} + + f := NewLocalFactory(0) + defer f.Stop() + + globalTags := map[string]string{"key": "value"} + + err := initMetrics(&testMetrics, f, globalTags) + assert.NoError(t, err) + + testMetrics.Gauge.Update(10) + testMetrics.Counter.Inc(5) + testMetrics.Timer.Record(time.Duration(time.Second * 35)) + + // wait for metrics + for i := 0; i < 1000; i++ { + c, _ := f.Snapshot() + if _, ok := c["counter"]; ok { + break + } + time.Sleep(1 * time.Millisecond) + } + + c, g := f.Snapshot() + + assert.EqualValues(t, 5, c["counter|key=value"]) + assert.EqualValues(t, 10, g["gauge|1=one|2=two|key=value"]) + assert.EqualValues(t, 36863, g["timer|key=value.P50"]) + + stopwatch := StartStopwatch(testMetrics.Timer) + stopwatch.Stop() + assert.True(t, 0 < stopwatch.ElapsedTime()) +} + +var ( + noMetricTag = struct { + NoMetricTag Counter + }{} + + badTags = struct { + BadTags Counter `metric:"counter" tags:"1=one,noValue"` + }{} + + invalidMetricType = struct { + InvalidMetricType int64 `metric:"counter"` + }{} +) + +func TestInitMetricsFailures(t *testing.T) { + assert.EqualError(t, initMetrics(&noMetricTag, nil, nil), "Field NoMetricTag is missing a tag 'metric'") + + assert.EqualError(t, initMetrics(&badTags, nil, nil), + "Field [BadTags]: Tag [noValue] is not of the form key=value in 'tags' string [1=one,noValue]") + + assert.EqualError(t, initMetrics(&invalidMetricType, nil, nil), + "Field InvalidMetricType is not a pointer to timer, gauge, or counter") +} + +func TestInitPanic(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Error("The code did not panic") + } + }() + + Init(&noMetricTag, NullFactory, nil) +} + +func TestNullMetrics(t *testing.T) { + // This test is just for cover + NullFactory.Timer("name", nil).Record(0) + NullFactory.Counter("name", nil).Inc(0) + NullFactory.Gauge("name", nil).Update(0) + NullFactory.Namespace("name", nil).Gauge("name2", nil).Update(0) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/multi/multi.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/multi/multi.go new file mode 100644 index 00000000..c3791b60 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/multi/multi.go @@ -0,0 +1,107 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package multi + +import ( + "time" + + "github.com/uber/jaeger-lib/metrics" +) + +// Factory is a metrics factory that dispatches to multiple metrics backends. +type Factory struct { + factories []metrics.Factory +} + +// New creates a new multi.Factory that will dispatch to multiple metrics backends. +func New(factories ...metrics.Factory) *Factory { + return &Factory{ + factories: factories, + } +} + +type counter struct { + counters []metrics.Counter +} + +func (c *counter) Inc(delta int64) { + for _, counter := range c.counters { + counter.Inc(delta) + } +} + +// Counter implements metrics.Factory interface +func (f *Factory) Counter(name string, tags map[string]string) metrics.Counter { + counter := &counter{ + counters: make([]metrics.Counter, len(f.factories)), + } + for i, factory := range f.factories { + counter.counters[i] = factory.Counter(name, tags) + } + return counter +} + +type timer struct { + timers []metrics.Timer +} + +func (t *timer) Record(delta time.Duration) { + for _, timer := range t.timers { + timer.Record(delta) + } +} + +// Timer implements metrics.Factory interface +func (f *Factory) Timer(name string, tags map[string]string) metrics.Timer { + timer := &timer{ + timers: make([]metrics.Timer, len(f.factories)), + } + for i, factory := range f.factories { + timer.timers[i] = factory.Timer(name, tags) + } + return timer +} + +type gauge struct { + gauges []metrics.Gauge +} + +func (t *gauge) Update(value int64) { + for _, gauge := range t.gauges { + gauge.Update(value) + } +} + +// Gauge implements metrics.Factory interface +func (f *Factory) Gauge(name string, tags map[string]string) metrics.Gauge { + gauge := &gauge{ + gauges: make([]metrics.Gauge, len(f.factories)), + } + for i, factory := range f.factories { + gauge.gauges[i] = factory.Gauge(name, tags) + } + return gauge +} + +// Namespace implements metrics.Factory interface +func (f *Factory) Namespace(name string, tags map[string]string) metrics.Factory { + newFactory := &Factory{ + factories: make([]metrics.Factory, len(f.factories)), + } + for i, factory := range f.factories { + newFactory.factories[i] = factory.Namespace(name, tags) + } + return newFactory +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/multi/multi_test.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/multi/multi_test.go new file mode 100644 index 00000000..24ecb733 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/multi/multi_test.go @@ -0,0 +1,32 @@ +package multi + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/uber/jaeger-lib/metrics" + "github.com/uber/jaeger-lib/metrics/testutils" +) + +var _ metrics.Factory = &Factory{} // API check + +func TestMultiFactory(t *testing.T) { + f1 := metrics.NewLocalFactory(time.Second) + f2 := metrics.NewLocalFactory(time.Second) + multi1 := New(f1, f2) + multi2 := multi1.Namespace("ns2", nil) + tags := map[string]string{"x": "y"} + multi2.Counter("counter", tags).Inc(42) + multi2.Gauge("gauge", tags).Update(42) + multi2.Timer("timer", tags).Record(42 * time.Millisecond) + + for _, f := range []*metrics.LocalFactory{f1, f2} { + testutils.AssertCounterMetrics(t, f, + testutils.ExpectedMetric{Name: "ns2.counter", Tags: tags, Value: 42}) + testutils.AssertGaugeMetrics(t, f, + testutils.ExpectedMetric{Name: "ns2.gauge", Tags: tags, Value: 42}) + _, g := f.Snapshot() + assert.EqualValues(t, 43, g["ns2.timer|x=y.P99"]) + } +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/prometheus/cache.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/prometheus/cache.go new file mode 100644 index 00000000..40791ebb --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/prometheus/cache.go @@ -0,0 +1,86 @@ +// Copyright (c) 2017 The Jaeger Authors. +// +// 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. + +package prometheus + +import ( + "strings" + "sync" + + "github.com/prometheus/client_golang/prometheus" +) + +// vectorCache is used to avoid creating Prometheus vectors with the same set of labels more than once. +type vectorCache struct { + registerer prometheus.Registerer + lock sync.Mutex + cVecs map[string]*prometheus.CounterVec + gVecs map[string]*prometheus.GaugeVec + hVecs map[string]*prometheus.HistogramVec +} + +func newVectorCache(registerer prometheus.Registerer) *vectorCache { + return &vectorCache{ + registerer: registerer, + cVecs: make(map[string]*prometheus.CounterVec), + gVecs: make(map[string]*prometheus.GaugeVec), + hVecs: make(map[string]*prometheus.HistogramVec), + } +} + +func (c *vectorCache) getOrMakeCounterVec(opts prometheus.CounterOpts, labelNames []string) *prometheus.CounterVec { + c.lock.Lock() + defer c.lock.Unlock() + + cacheKey := c.getCacheKey(opts.Name, labelNames) + cv, cvExists := c.cVecs[cacheKey] + if !cvExists { + cv = prometheus.NewCounterVec(opts, labelNames) + c.registerer.MustRegister(cv) + c.cVecs[cacheKey] = cv + } + return cv +} + +func (c *vectorCache) getOrMakeGaugeVec(opts prometheus.GaugeOpts, labelNames []string) *prometheus.GaugeVec { + c.lock.Lock() + defer c.lock.Unlock() + + cacheKey := c.getCacheKey(opts.Name, labelNames) + gv, gvExists := c.gVecs[cacheKey] + if !gvExists { + gv = prometheus.NewGaugeVec(opts, labelNames) + c.registerer.MustRegister(gv) + c.gVecs[cacheKey] = gv + } + return gv +} + +func (c *vectorCache) getOrMakeHistogramVec(opts prometheus.HistogramOpts, labelNames []string) *prometheus.HistogramVec { + c.lock.Lock() + defer c.lock.Unlock() + + cacheKey := c.getCacheKey(opts.Name, labelNames) + hv, hvExists := c.hVecs[cacheKey] + if !hvExists { + hv = prometheus.NewHistogramVec(opts, labelNames) + c.registerer.MustRegister(hv) + c.hVecs[cacheKey] = hv + } + return hv +} + +func (c *vectorCache) getCacheKey(name string, labels []string) string { + return strings.Join(append([]string{name}, labels...), "||") +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/prometheus/factory.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/prometheus/factory.go new file mode 100644 index 00000000..b5791f4d --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/prometheus/factory.go @@ -0,0 +1,246 @@ +// Copyright (c) 2017 The Jaeger Authors. +// +// 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. + +package prometheus + +import ( + "sort" + "strings" + "time" + + "github.com/prometheus/client_golang/prometheus" + + "github.com/uber/jaeger-lib/metrics" +) + +// Factory implements metrics.Factory backed by Prometheus registry. +type Factory struct { + scope string + tags map[string]string + cache *vectorCache + buckets []float64 + normalizer *strings.Replacer + separator Separator +} + +type options struct { + registerer prometheus.Registerer + buckets []float64 + separator Separator +} + +// Separator represents the namespace separator to use +type Separator rune + +const ( + // SeparatorUnderscore uses an underscore as separator + SeparatorUnderscore Separator = '_' + + // SeparatorColon uses a colon as separator + SeparatorColon = ':' +) + +// Option is a function that sets some option for the Factory constructor. +type Option func(*options) + +// WithRegisterer returns an option that sets the registerer. +// If not used we fallback to prometheus.DefaultRegisterer. +func WithRegisterer(registerer prometheus.Registerer) Option { + return func(opts *options) { + opts.registerer = registerer + } +} + +// WithBuckets returns an option that sets the default buckets for histogram. +// If not used, we fallback to default Prometheus buckets. +func WithBuckets(buckets []float64) Option { + return func(opts *options) { + opts.buckets = buckets + } +} + +// WithSeparator returns an option that sets the default separator for the namespace +// If not used, we fallback to underscore. +func WithSeparator(separator Separator) Option { + return func(opts *options) { + opts.separator = separator + } +} + +func applyOptions(opts []Option) *options { + options := new(options) + for _, o := range opts { + o(options) + } + if options.registerer == nil { + options.registerer = prometheus.DefaultRegisterer + } + if options.separator == '\x00' { + options.separator = SeparatorUnderscore + } + return options +} + +// New creates a Factory backed by Prometheus registry. +// Typically the first argument should be prometheus.DefaultRegisterer. +// +// Parameter buckets defines the buckets into which Timer observations are counted. +// Each element in the slice is the upper inclusive bound of a bucket. The +// values must be sorted in strictly increasing order. There is no need +// to add a highest bucket with +Inf bound, it will be added +// implicitly. The default value is prometheus.DefBuckets. +func New(opts ...Option) *Factory { + options := applyOptions(opts) + return newFactory( + &Factory{ // dummy struct to be discarded + cache: newVectorCache(options.registerer), + buckets: options.buckets, + normalizer: strings.NewReplacer(".", "_", "-", "_"), + separator: options.separator, + }, + "", // scope + nil) // tags +} + +func newFactory(parent *Factory, scope string, tags map[string]string) *Factory { + return &Factory{ + cache: parent.cache, + buckets: parent.buckets, + normalizer: parent.normalizer, + separator: parent.separator, + scope: scope, + tags: tags, + } +} + +// Counter implements Counter of metrics.Factory. +func (f *Factory) Counter(name string, tags map[string]string) metrics.Counter { + name = f.subScope(name) + tags = f.mergeTags(tags) + labelNames := f.tagNames(tags) + opts := prometheus.CounterOpts{ + Name: name, + Help: name, + } + cv := f.cache.getOrMakeCounterVec(opts, labelNames) + return &counter{ + counter: cv.WithLabelValues(f.tagsAsLabelValues(labelNames, tags)...), + } +} + +// Gauge implements Gauge of metrics.Factory. +func (f *Factory) Gauge(name string, tags map[string]string) metrics.Gauge { + name = f.subScope(name) + tags = f.mergeTags(tags) + labelNames := f.tagNames(tags) + opts := prometheus.GaugeOpts{ + Name: name, + Help: name, + } + gv := f.cache.getOrMakeGaugeVec(opts, labelNames) + return &gauge{ + gauge: gv.WithLabelValues(f.tagsAsLabelValues(labelNames, tags)...), + } +} + +// Timer implements Timer of metrics.Factory. +func (f *Factory) Timer(name string, tags map[string]string) metrics.Timer { + name = f.subScope(name) + tags = f.mergeTags(tags) + labelNames := f.tagNames(tags) + opts := prometheus.HistogramOpts{ + Name: name, + Help: name, + Buckets: f.buckets, + } + hv := f.cache.getOrMakeHistogramVec(opts, labelNames) + return &timer{ + histogram: hv.WithLabelValues(f.tagsAsLabelValues(labelNames, tags)...), + } +} + +// Namespace implements Namespace of metrics.Factory. +func (f *Factory) Namespace(name string, tags map[string]string) metrics.Factory { + return newFactory(f, f.subScope(name), f.mergeTags(tags)) +} + +type counter struct { + counter prometheus.Counter +} + +func (c *counter) Inc(v int64) { + c.counter.Add(float64(v)) +} + +type gauge struct { + gauge prometheus.Gauge +} + +func (g *gauge) Update(v int64) { + g.gauge.Set(float64(v)) +} + +type observer interface { + Observe(v float64) +} + +type timer struct { + histogram observer +} + +func (t *timer) Record(v time.Duration) { + t.histogram.Observe(float64(v.Nanoseconds()) / float64(time.Second/time.Nanosecond)) +} + +func (f *Factory) subScope(name string) string { + if f.scope == "" { + return f.normalize(name) + } + if name == "" { + return f.normalize(f.scope) + } + return f.normalize(f.scope + string(f.separator) + name) +} + +func (f *Factory) normalize(v string) string { + return f.normalizer.Replace(v) +} + +func (f *Factory) mergeTags(tags map[string]string) map[string]string { + ret := make(map[string]string, len(f.tags)+len(tags)) + for k, v := range f.tags { + ret[k] = v + } + for k, v := range tags { + ret[k] = v + } + return ret +} + +func (f *Factory) tagNames(tags map[string]string) []string { + ret := make([]string, 0, len(tags)) + for k := range tags { + ret = append(ret, k) + } + sort.Strings(ret) + return ret +} + +func (f *Factory) tagsAsLabelValues(labels []string, tags map[string]string) []string { + ret := make([]string, 0, len(tags)) + for _, l := range labels { + ret = append(ret, tags[l]) + } + return ret +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/prometheus/factory_test.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/prometheus/factory_test.go new file mode 100644 index 00000000..a583c7f9 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/prometheus/factory_test.go @@ -0,0 +1,185 @@ +// Copyright (c) 2017 The Jaeger Authors. +// +// 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. + +package prometheus_test + +import ( + "testing" + "time" + + "github.com/prometheus/client_golang/prometheus" + promModel "github.com/prometheus/client_model/go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/uber/jaeger-lib/metrics" + . "github.com/uber/jaeger-lib/metrics/prometheus" +) + +var _ metrics.Factory = new(Factory) + +func TestOptions(t *testing.T) { + f1 := New() + assert.NotNil(t, f1) +} + +func TestSeparator(t *testing.T) { + registry := prometheus.NewPedanticRegistry() + f1 := New(WithRegisterer(registry), WithSeparator(SeparatorColon)) + c1 := f1.Namespace("bender", nil).Counter("rodriguez", map[string]string{"a": "b"}) + c1.Inc(1) + snapshot, err := registry.Gather() + require.NoError(t, err) + m1 := findMetric(t, snapshot, "bender:rodriguez", map[string]string{"a": "b"}) + assert.EqualValues(t, 1, m1.GetCounter().GetValue(), "%+v", m1) +} + +func TestCounter(t *testing.T) { + registry := prometheus.NewPedanticRegistry() + f1 := New(WithRegisterer(registry)) + fDummy := f1.Namespace("", nil) + f2 := fDummy.Namespace("bender", map[string]string{"a": "b"}) + f3 := f2.Namespace("", nil) + + c1 := f2.Counter("rodriguez", map[string]string{"x": "y"}) + c2 := f2.Counter("rodriguez", map[string]string{"x": "z"}) + c3 := f3.Counter("rodriguez", map[string]string{"x": "z"}) // same tags as c2, but from f3 + c1.Inc(1) + c1.Inc(2) + c2.Inc(3) + c3.Inc(4) + + snapshot, err := registry.Gather() + require.NoError(t, err) + + m1 := findMetric(t, snapshot, "bender_rodriguez", map[string]string{"a": "b", "x": "y"}) + assert.EqualValues(t, 3, m1.GetCounter().GetValue(), "%+v", m1) + + m2 := findMetric(t, snapshot, "bender_rodriguez", map[string]string{"a": "b", "x": "z"}) + assert.EqualValues(t, 7, m2.GetCounter().GetValue(), "%+v", m2) +} + +func TestGauge(t *testing.T) { + registry := prometheus.NewPedanticRegistry() + f1 := New(WithRegisterer(registry)) + f2 := f1.Namespace("bender", map[string]string{"a": "b"}) + f3 := f2.Namespace("", map[string]string{"a": "b"}) // essentially same as f2 + g1 := f2.Gauge("rodriguez", map[string]string{"x": "y"}) + g2 := f2.Gauge("rodriguez", map[string]string{"x": "z"}) + g3 := f3.Gauge("rodriguez", map[string]string{"x": "z"}) // same as g2, but from f3 + g1.Update(1) + g1.Update(2) + g2.Update(3) + g3.Update(4) + + snapshot, err := registry.Gather() + require.NoError(t, err) + + m1 := findMetric(t, snapshot, "bender_rodriguez", map[string]string{"a": "b", "x": "y"}) + assert.EqualValues(t, 2, m1.GetGauge().GetValue(), "%+v", m1) + + m2 := findMetric(t, snapshot, "bender_rodriguez", map[string]string{"a": "b", "x": "z"}) + assert.EqualValues(t, 4, m2.GetGauge().GetValue(), "%+v", m2) +} + +func TestTimer(t *testing.T) { + registry := prometheus.NewPedanticRegistry() + f1 := New(WithRegisterer(registry)) + f2 := f1.Namespace("bender", map[string]string{"a": "b"}) + f3 := f2.Namespace("", map[string]string{"a": "b"}) // essentially same as f2 + t1 := f2.Timer("rodriguez", map[string]string{"x": "y"}) + t2 := f2.Timer("rodriguez", map[string]string{"x": "z"}) + t3 := f3.Timer("rodriguez", map[string]string{"x": "z"}) // same as t2, but from f3 + t1.Record(1 * time.Second) + t1.Record(2 * time.Second) + t2.Record(3 * time.Second) + t3.Record(4 * time.Second) + + snapshot, err := registry.Gather() + require.NoError(t, err) + + m1 := findMetric(t, snapshot, "bender_rodriguez", map[string]string{"a": "b", "x": "y"}) + assert.EqualValues(t, 2, m1.GetHistogram().GetSampleCount(), "%+v", m1) + assert.EqualValues(t, 3, m1.GetHistogram().GetSampleSum(), "%+v", m1) + for _, bucket := range m1.GetHistogram().GetBucket() { + if bucket.GetUpperBound() < 1 { + assert.EqualValues(t, 0, bucket.GetCumulativeCount()) + } else if bucket.GetUpperBound() < 2 { + assert.EqualValues(t, 1, bucket.GetCumulativeCount()) + } else { + assert.EqualValues(t, 2, bucket.GetCumulativeCount()) + } + } + + m2 := findMetric(t, snapshot, "bender_rodriguez", map[string]string{"a": "b", "x": "z"}) + assert.EqualValues(t, 2, m2.GetHistogram().GetSampleCount(), "%+v", m2) + assert.EqualValues(t, 7, m2.GetHistogram().GetSampleSum(), "%+v", m2) + for _, bucket := range m2.GetHistogram().GetBucket() { + if bucket.GetUpperBound() < 3 { + assert.EqualValues(t, 0, bucket.GetCumulativeCount()) + } else if bucket.GetUpperBound() < 4 { + assert.EqualValues(t, 1, bucket.GetCumulativeCount()) + } else { + assert.EqualValues(t, 2, bucket.GetCumulativeCount()) + } + } +} + +func TestTimerCustomBuckets(t *testing.T) { + registry := prometheus.NewPedanticRegistry() + f1 := New(WithRegisterer(registry), WithBuckets([]float64{1.5})) + // dot and dash in the metric name will be replaced with underscore + t1 := f1.Timer("bender.bending-rodriguez", map[string]string{"x": "y"}) + t1.Record(1 * time.Second) + t1.Record(2 * time.Second) + + snapshot, err := registry.Gather() + require.NoError(t, err) + + m1 := findMetric(t, snapshot, "bender_bending_rodriguez", map[string]string{"x": "y"}) + assert.EqualValues(t, 2, m1.GetHistogram().GetSampleCount(), "%+v", m1) + assert.EqualValues(t, 3, m1.GetHistogram().GetSampleSum(), "%+v", m1) + assert.Len(t, m1.GetHistogram().GetBucket(), 1) +} + +func findMetric(t *testing.T, snapshot []*promModel.MetricFamily, name string, tags map[string]string) *promModel.Metric { + for _, mf := range snapshot { + if mf.GetName() != name { + continue + } + for _, m := range mf.GetMetric() { + if len(m.GetLabel()) != len(tags) { + t.Fatalf("Mismatching labels for metric %v: want %v, have %v", name, tags, m.GetLabel()) + } + match := true + for _, l := range m.GetLabel() { + if v, ok := tags[l.GetName()]; !ok || v != l.GetValue() { + match = false + } + } + if match { + return m + } + } + } + t.Logf("Cannot find metric %v %v", name, tags) + for _, nf := range snapshot { + t.Logf("Family: %v", nf.GetName()) + for _, m := range nf.GetMetric() { + t.Logf("==> %v", m) + } + } + t.FailNow() + return nil +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/stopwatch.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/stopwatch.go new file mode 100644 index 00000000..4a8abdb5 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/stopwatch.go @@ -0,0 +1,43 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package metrics + +import ( + "time" +) + +// StartStopwatch begins recording the executing time of an event, returning +// a Stopwatch that should be used to stop the recording the time for +// that event. Multiple events can be occurring simultaneously each +// represented by different active Stopwatches +func StartStopwatch(timer Timer) Stopwatch { + return Stopwatch{t: timer, start: time.Now()} +} + +// A Stopwatch tracks the execution time of a specific event +type Stopwatch struct { + t Timer + start time.Time +} + +// Stop stops executing of the stopwatch and records the amount of elapsed time +func (s Stopwatch) Stop() { + s.t.Record(s.ElapsedTime()) +} + +// ElapsedTime returns the amount of elapsed time (in time.Duration) +func (s Stopwatch) ElapsedTime() time.Duration { + return time.Since(s.start) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/tally/factory.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/tally/factory.go new file mode 100644 index 00000000..79237729 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/tally/factory.go @@ -0,0 +1,63 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package tally + +import ( + "github.com/uber-go/tally" + + "github.com/uber/jaeger-lib/metrics" +) + +// Wrap takes a tally Scope and returns jaeger-lib metrics.Factory. +func Wrap(scope tally.Scope) metrics.Factory { + return &factory{ + tally: scope, + } +} + +// TODO implement support for tags if tally.Scope does not support them +type factory struct { + tally tally.Scope +} + +func (f *factory) Counter(name string, tags map[string]string) metrics.Counter { + scope := f.tally + if len(tags) > 0 { + scope = scope.Tagged(tags) + } + return NewCounter(scope.Counter(name)) +} + +func (f *factory) Gauge(name string, tags map[string]string) metrics.Gauge { + scope := f.tally + if len(tags) > 0 { + scope = scope.Tagged(tags) + } + return NewGauge(scope.Gauge(name)) +} + +func (f *factory) Timer(name string, tags map[string]string) metrics.Timer { + scope := f.tally + if len(tags) > 0 { + scope = scope.Tagged(tags) + } + return NewTimer(scope.Timer(name)) +} + +func (f *factory) Namespace(name string, tags map[string]string) metrics.Factory { + return &factory{ + tally: f.tally.SubScope(name).Tagged(tags), + } +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/tally/factory_test.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/tally/factory_test.go new file mode 100644 index 00000000..fbbbe20a --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/tally/factory_test.go @@ -0,0 +1,47 @@ +package tally + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/uber-go/tally" +) + +func TestFactory(t *testing.T) { + testScope := tally.NewTestScope("pre", map[string]string{"a": "b"}) + factory := Wrap(testScope).Namespace("fix", map[string]string{"c": "d"}) + counter := factory.Counter("counter", map[string]string{"x": "y"}) + counter.Inc(42) + gauge := factory.Gauge("gauge", map[string]string{"x": "y"}) + gauge.Update(42) + timer := factory.Timer("timer", map[string]string{"x": "y"}) + timer.Record(42 * time.Millisecond) + snapshot := testScope.Snapshot() + + // tally v3 includes tags in the name, so look + c := snapshot.Counters()["pre.fix.counter"] + if c == nil { + // tally v3 includes tags in the name. + c = snapshot.Counters()["pre.fix.counter+a=b,c=d,x=y"] + } + + g := snapshot.Gauges()["pre.fix.gauge"] + if g == nil { + g = snapshot.Gauges()["pre.fix.gauge+a=b,c=d,x=y"] + } + + h := snapshot.Timers()["pre.fix.timer"] + if h == nil { + h = snapshot.Timers()["pre.fix.timer+a=b,c=d,x=y"] + } + + expectedTags := map[string]string{"a": "b", "c": "d", "x": "y"} + assert.EqualValues(t, 42, c.Value()) + assert.EqualValues(t, expectedTags, c.Tags()) + assert.EqualValues(t, 42, g.Value()) + assert.EqualValues(t, expectedTags, g.Tags()) + assert.Equal(t, []time.Duration{42 * time.Millisecond}, h.Values()) + assert.EqualValues(t, expectedTags, h.Tags()) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/tally/metrics.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/tally/metrics.go new file mode 100644 index 00000000..f8621c66 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/tally/metrics.go @@ -0,0 +1,66 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package tally + +import ( + "time" + + "github.com/uber-go/tally" +) + +// Counter is an adapter from go-tally Counter to jaeger-lib Counter +type Counter struct { + counter tally.Counter +} + +// NewCounter creates a new Counter +func NewCounter(counter tally.Counter) *Counter { + return &Counter{counter: counter} +} + +// Inc adds the given value to the counter. +func (c *Counter) Inc(delta int64) { + c.counter.Inc(delta) +} + +// Gauge is an adapter from go-tally Gauge to jaeger-lib Gauge +type Gauge struct { + gauge tally.Gauge +} + +// NewGauge creates a new Gauge +func NewGauge(gauge tally.Gauge) *Gauge { + return &Gauge{gauge: gauge} +} + +// Update the gauge to the value passed in. +func (g *Gauge) Update(value int64) { + g.gauge.Update(float64(value)) +} + +// Timer is an adapter from go-tally Histogram to jaeger-lib Timer +type Timer struct { + timer tally.Timer +} + +// NewTimer creates a new Timer +func NewTimer(timer tally.Timer) *Timer { + return &Timer{timer: timer} +} + +// Record saves the time passed in. +func (t *Timer) Record(delta time.Duration) { + t.timer.Record(delta) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/testutils/testutils.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/testutils/testutils.go new file mode 100644 index 00000000..66134b6c --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/testutils/testutils.go @@ -0,0 +1,55 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package testutils + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/uber/jaeger-lib/metrics" +) + +// ExpectedMetric contains metrics under test. +type ExpectedMetric struct { + Name string + Tags map[string]string + Value int +} + +// TODO do something similar for Timers + +// AssertCounterMetrics checks if counter metrics exist. +func AssertCounterMetrics(t *testing.T, f *metrics.LocalFactory, expectedMetrics ...ExpectedMetric) { + counters, _ := f.Snapshot() + assertMetrics(t, counters, expectedMetrics...) +} + +// AssertGaugeMetrics checks if gauge metrics exist. +func AssertGaugeMetrics(t *testing.T, f *metrics.LocalFactory, expectedMetrics ...ExpectedMetric) { + _, gauges := f.Snapshot() + assertMetrics(t, gauges, expectedMetrics...) +} + +func assertMetrics(t *testing.T, actualMetrics map[string]int64, expectedMetrics ...ExpectedMetric) { + for _, expected := range expectedMetrics { + key := metrics.GetKey(expected.Name, expected.Tags, "|", "=") + assert.EqualValues(t, + expected.Value, + actualMetrics[key], + "expected metric name: %s, tags: %+v", expected.Name, expected.Tags, + ) + } +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/testutils/testutils_test.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/testutils/testutils_test.go new file mode 100644 index 00000000..9ebd24e8 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/testutils/testutils_test.go @@ -0,0 +1,17 @@ +package testutils + +import ( + "testing" + + "github.com/uber/jaeger-lib/metrics" +) + +func TestAssertMetrics(t *testing.T) { + f := metrics.NewLocalFactory(0) + tags := map[string]string{"key": "value"} + f.IncCounter("counter", tags, 1) + f.UpdateGauge("gauge", tags, 11) + + AssertCounterMetrics(t, f, ExpectedMetric{Name: "counter", Tags: tags, Value: 1}) + AssertGaugeMetrics(t, f, ExpectedMetric{Name: "gauge", Tags: tags, Value: 11}) +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/timer.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/timer.go new file mode 100644 index 00000000..e18d222a --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/metrics/timer.go @@ -0,0 +1,33 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package metrics + +import ( + "time" +) + +// Timer accumulates observations about how long some operation took, +// and also maintains a historgam of percentiles. +type Timer interface { + // Records the time passed in. + Record(time.Duration) +} + +// NullTimer timer that does nothing +var NullTimer Timer = nullTimer{} + +type nullTimer struct{} + +func (nullTimer) Record(time.Duration) {} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/sample/sample.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/sample/sample.go new file mode 100644 index 00000000..be6b97dd --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/sample/sample.go @@ -0,0 +1,24 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package sample + +import ( + "fmt" +) + +// SayHello is a sample function +func SayHello() { + fmt.Println("Hello, playground") +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/sample/sample_test.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/sample/sample_test.go new file mode 100644 index 00000000..19326b06 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/sample/sample_test.go @@ -0,0 +1,12 @@ +package sample + +import "testing" + +func ExampleSayHello() { + SayHello() + // Output: Hello, playground +} + +func TestSayHello(t *testing.T) { + SayHello() +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/scripts/cover.sh b/template/faaschain/vendor/github.com/uber/jaeger-lib/scripts/cover.sh new file mode 100755 index 00000000..77c1aef4 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/scripts/cover.sh @@ -0,0 +1,64 @@ +#!/bin/bash + +set -e + +COVER=.cover +ROOT_PKG=github.com/uber/jaeger-lib/ + +if [[ -d "$COVER" ]]; then + rm -rf "$COVER" +fi +mkdir -p "$COVER" + +# If a package directory has a .nocover file, don't count it when calculating +# coverage. +filter="" +for pkg in "$@"; do + if [[ -f "$GOPATH/src/$pkg/.nocover" ]]; then + if [[ -n "$filter" ]]; then + filter="$filter, " + fi + filter="\"$pkg\": true" + fi +done + +if [[ "$filter" = "" ]]; then + # make up some name to avoid breaking jq's select(in({})) + filter='"no-filter": true' +fi + +i=0 +for pkg in "$@"; do + i=$((i + 1)) + + extracoverpkg="" + if [[ -f "$GOPATH/src/$pkg/.extra-coverpkg" ]]; then + extracoverpkg=$( \ + sed -e "s|^|$pkg/|g" < "$GOPATH/src/$pkg/.extra-coverpkg" \ + | tr '\n' ',') + fi + + coverpkg=$(go list -json "$pkg" | jq -r ' + .Deps + | . + ["'"$pkg"'"] + | map + ( select(startswith("'"$ROOT_PKG"'")) + | select(contains("/vendor/") | not) + | select(in({'"$filter"'}) | not) + ) + | join(",") + ') + if [[ -n "$extracoverpkg" ]]; then + coverpkg="$extracoverpkg$coverpkg" + fi + + args="" + if [[ -n "$coverpkg" ]]; then + args="-coverprofile $COVER/cover.${i}.out" # -coverpkg $coverpkg" + fi + + echo go test -v -race "$pkg" + go test $args -v -race "$pkg" +done + +gocovmerge "$COVER"/*.out > cover.out diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/scripts/updateLicense.py b/template/faaschain/vendor/github.com/uber/jaeger-lib/scripts/updateLicense.py new file mode 100644 index 00000000..6503a6f5 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/scripts/updateLicense.py @@ -0,0 +1,93 @@ +from __future__ import ( + absolute_import, print_function, division, unicode_literals +) + +import logging +import re +import sys +from datetime import datetime + +logging.basicConfig(level=logging.DEBUG) +logger = logging.getLogger(__name__) + +CURRENT_YEAR = datetime.today().year + +LICENSE_BLOB = """Copyright (c) %d The Jaeger Authors. + +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.""" % CURRENT_YEAR + +LICENSE_BLOB_LINES_GO = [ + ('// ' + l).strip() + '\n' for l in LICENSE_BLOB.split('\n') +] + +COPYRIGHT_RE = re.compile(r'Copyright \(c\) (\d+)', re.I) + + +def update_go_license(name, force=False): + with open(name) as f: + orig_lines = list(f) + lines = list(orig_lines) + + found = False + changed = False + for i, line in enumerate(lines[:5]): + m = COPYRIGHT_RE.search(line) + if not m: + continue + + found = True + year = int(m.group(1)) + if year == CURRENT_YEAR: + break + + # Avoid updating the copyright year. + # + # new_line = COPYRIGHT_RE.sub('Copyright (c) %d' % CURRENT_YEAR, line) + # assert line != new_line, ('Could not change year in: %s' % line) + # lines[i] = new_line + # changed = True + break + + if not found: + if 'Code generated by' in lines[0]: + lines[1:1] = ['\n'] + LICENSE_BLOB_LINES_GO + else: + lines[0:0] = LICENSE_BLOB_LINES_GO + ['\n'] + changed = True + + if changed: + with open(name, 'w') as f: + for line in lines: + f.write(line) + print(name) + + +def main(): + if len(sys.argv) == 1: + print('USAGE: %s FILE ...' % sys.argv[0]) + sys.exit(1) + + for name in sys.argv[1:]: + if name.endswith('.go'): + try: + update_go_license(name) + except Exception as error: + logger.error('Failed to process file %s', name) + logger.exception(error) + raise error + else: + raise NotImplementedError('Unsupported file type: %s' % name) + + +if __name__ == "__main__": + main() diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/scripts/updateLicenses.sh b/template/faaschain/vendor/github.com/uber/jaeger-lib/scripts/updateLicenses.sh new file mode 100755 index 00000000..2074752a --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/scripts/updateLicenses.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +set -e +set -x + +python scripts/updateLicense.py $(go list -json $(glide nv) | jq -r '.Dir + "/" + (.GoFiles | .[])') diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/utils/rate_limiter.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/utils/rate_limiter.go new file mode 100644 index 00000000..1b8db975 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/utils/rate_limiter.go @@ -0,0 +1,77 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package utils + +import ( + "sync" + "time" +) + +// RateLimiter is a filter used to check if a message that is worth itemCost units is within the rate limits. +type RateLimiter interface { + CheckCredit(itemCost float64) bool +} + +type rateLimiter struct { + sync.Mutex + + creditsPerSecond float64 + balance float64 + maxBalance float64 + lastTick time.Time + + timeNow func() time.Time +} + +// NewRateLimiter creates a new rate limiter based on leaky bucket algorithm, formulated in terms of a +// credits balance that is replenished every time CheckCredit() method is called (tick) by the amount proportional +// to the time elapsed since the last tick, up to max of creditsPerSecond. A call to CheckCredit() takes a cost +// of an item we want to pay with the balance. If the balance exceeds the cost of the item, the item is "purchased" +// and the balance reduced, indicated by returned value of true. Otherwise the balance is unchanged and return false. +// +// This can be used to limit a rate of messages emitted by a service by instantiating the Rate Limiter with the +// max number of messages a service is allowed to emit per second, and calling CheckCredit(1.0) for each message +// to determine if the message is within the rate limit. +// +// It can also be used to limit the rate of traffic in bytes, by setting creditsPerSecond to desired throughput +// as bytes/second, and calling CheckCredit() with the actual message size. +func NewRateLimiter(creditsPerSecond, maxBalance float64) RateLimiter { + return &rateLimiter{ + creditsPerSecond: creditsPerSecond, + balance: maxBalance, + maxBalance: maxBalance, + lastTick: time.Now(), + timeNow: time.Now} +} + +func (b *rateLimiter) CheckCredit(itemCost float64) bool { + b.Lock() + defer b.Unlock() + // calculate how much time passed since the last tick, and update current tick + currentTime := b.timeNow() + elapsedTime := currentTime.Sub(b.lastTick) + b.lastTick = currentTime + // calculate how much credit have we accumulated since the last tick + b.balance += elapsedTime.Seconds() * b.creditsPerSecond + if b.balance > b.maxBalance { + b.balance = b.maxBalance + } + // if we have enough credits to pay for current item, then reduce balance and allow + if b.balance >= itemCost { + b.balance -= itemCost + return true + } + return false +} diff --git a/template/faaschain/vendor/github.com/uber/jaeger-lib/utils/rate_limiter_test.go b/template/faaschain/vendor/github.com/uber/jaeger-lib/utils/rate_limiter_test.go new file mode 100644 index 00000000..a075afb4 --- /dev/null +++ b/template/faaschain/vendor/github.com/uber/jaeger-lib/utils/rate_limiter_test.go @@ -0,0 +1,75 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package utils + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestRateLimiter(t *testing.T) { + limiter := NewRateLimiter(2.0, 2.0) + // stop time + ts := time.Now() + limiter.(*rateLimiter).lastTick = ts + limiter.(*rateLimiter).timeNow = func() time.Time { + return ts + } + assert.True(t, limiter.CheckCredit(1.0)) + assert.True(t, limiter.CheckCredit(1.0)) + assert.False(t, limiter.CheckCredit(1.0)) + // move time 250ms forward, not enough credits to pay for 1.0 item + limiter.(*rateLimiter).timeNow = func() time.Time { + return ts.Add(time.Second / 4) + } + assert.False(t, limiter.CheckCredit(1.0)) + // move time 500ms forward, now enough credits to pay for 1.0 item + limiter.(*rateLimiter).timeNow = func() time.Time { + return ts.Add(time.Second/4 + time.Second/2) + } + assert.True(t, limiter.CheckCredit(1.0)) + assert.False(t, limiter.CheckCredit(1.0)) + // move time 5s forward, enough to accumulate credits for 10 messages, but it should still be capped at 2 + limiter.(*rateLimiter).lastTick = ts + limiter.(*rateLimiter).timeNow = func() time.Time { + return ts.Add(5 * time.Second) + } + assert.True(t, limiter.CheckCredit(1.0)) + assert.True(t, limiter.CheckCredit(1.0)) + assert.False(t, limiter.CheckCredit(1.0)) + assert.False(t, limiter.CheckCredit(1.0)) + assert.False(t, limiter.CheckCredit(1.0)) +} + +func TestMaxBalance(t *testing.T) { + limiter := NewRateLimiter(0.1, 1.0) + // stop time + ts := time.Now() + limiter.(*rateLimiter).lastTick = ts + limiter.(*rateLimiter).timeNow = func() time.Time { + return ts + } + // on initialization, should have enough credits for 1 message + assert.True(t, limiter.CheckCredit(1.0)) + + // move time 20s forward, enough to accumulate credits for 2 messages, but it should still be capped at 1 + limiter.(*rateLimiter).timeNow = func() time.Time { + return ts.Add(time.Second * 20) + } + assert.True(t, limiter.CheckCredit(1.0)) + assert.False(t, limiter.CheckCredit(1.0)) +} From bdabc3787c72f47ad2ea6aff72a3078f029d1284 Mon Sep 17 00:00:00 2001 From: Swarvanu Sengupta Date: Wed, 15 Aug 2018 18:33:17 +0900 Subject: [PATCH 2/8] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 093d2cea..eb4193a6 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![Build Status](https://travis-ci.org/s8sg/faaschain.svg?branch=master)](https://travis-ci.org/s8sg/faaschain) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![GoDoc](https://godoc.org/github.com/s8sg/faaschain?status.svg)](https://godoc.org/github.com/s8sg/faaschain) +[![OpenTracing-1.0](http://opentracing.io) [![OpenFaaS](https://img.shields.io/badge/openfaas-serverless-blue.svg)](https://www.openfaas.com) > **Pure FaaS** From 111c14c7a08d943aa040123502db5ee885774411 Mon Sep 17 00:00:00 2001 From: Swarvanu Sengupta Date: Wed, 15 Aug 2018 18:35:50 +0900 Subject: [PATCH 3/8] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eb4193a6..83f2ece8 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Build Status](https://travis-ci.org/s8sg/faaschain.svg?branch=master)](https://travis-ci.org/s8sg/faaschain) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![GoDoc](https://godoc.org/github.com/s8sg/faaschain?status.svg)](https://godoc.org/github.com/s8sg/faaschain) -[![OpenTracing-1.0](http://opentracing.io) +[![OpenTracing Badge](https://img.shields.io/badge/OpenTracing-enabled-blue.svg)](http://opentracing.io) [![OpenFaaS](https://img.shields.io/badge/openfaas-serverless-blue.svg)](https://www.openfaas.com) > **Pure FaaS** From 4043c7d540b42181b4cdbf51a9e47984c73c3ad0 Mon Sep 17 00:00:00 2001 From: Swarvanu Sengupta Date: Wed, 15 Aug 2018 18:47:07 +0900 Subject: [PATCH 4/8] Update README.md --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 83f2ece8..53c23a2f 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,8 @@ faas-cli new test-chain --lang faaschain write_timeout: 120 write_debug: true combine_output: false + enable_tracing: true + trace_server: "jaegertracing:5775" ``` > `gateway` : We need to tell faaschain the address of openfaas gateway > ``` @@ -86,8 +88,15 @@ faas-cli new test-chain --lang faaschain > `write_timeout` : A value larger than `max` phase execution time. > `write_debug`: It enables the debug msg in logs. > `combine_output` : It allows debug msg to be excluded from `output`. +> `enable_tracing` : It ebales the opentracing for requests and their phases. +> `trace_server` : The address of opentracing backend jaeger. - +#### Start The Trace Server (jaeger - opentracing-1.x) +To start the trace server we run `jaegertracing/all-in-one` as a service. +``` +cd tracer; ./deploy_swarm.sh; cd .. +``` + #### **Edit the `test-chain/handler.go`:**. ```go chain.Apply("myfunc1", map[string]string{"method": "post"}, nil). From ed6f820cf38c7199404d2760816e1c8bdbb0d781 Mon Sep 17 00:00:00 2001 From: Swarvanu Sengupta Date: Wed, 15 Aug 2018 18:48:26 +0900 Subject: [PATCH 5/8] Update README.md --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 53c23a2f..3eb20530 100644 --- a/README.md +++ b/README.md @@ -93,8 +93,12 @@ faas-cli new test-chain --lang faaschain #### Start The Trace Server (jaeger - opentracing-1.x) To start the trace server we run `jaegertracing/all-in-one` as a service. -``` -cd tracer; ./deploy_swarm.sh; cd .. +```bash +docker service rm jaegertracing +docker pull jaegertracing/all-in-one:latest +docker service create --constraint="node.role==manager" --detach=true \ + --network func_functions --name jaegertracing -p 5775:5775/udp -p 16686:16686 \ + jaegertracing/all-in-one:latest ``` #### **Edit the `test-chain/handler.go`:**. From 360eae8e8cfa1264329c892471da4162d0914bff Mon Sep 17 00:00:00 2001 From: Swarvanu Sengupta Date: Thu, 16 Aug 2018 18:20:11 +0900 Subject: [PATCH 6/8] Add jaeger opentracing deploy script Signed-off-by: Swarvanu Sengupta --- tracer/deploy_swarm.sh | 5 +++++ 1 file changed, 5 insertions(+) create mode 100755 tracer/deploy_swarm.sh diff --git a/tracer/deploy_swarm.sh b/tracer/deploy_swarm.sh new file mode 100755 index 00000000..4e221697 --- /dev/null +++ b/tracer/deploy_swarm.sh @@ -0,0 +1,5 @@ +docker service rm jaegertracing +docker pull jaegertracing/all-in-one:latest +docker service create --constraint="node.role==manager" --detach=true \ + --network func_functions --name jaegertracing -p 5775:5775/udp -p 16686:16686 \ + jaegertracing/all-in-one:latest From 4aceea0a3d5929ca47dfa59ecffde0724af79d78 Mon Sep 17 00:00:00 2001 From: Swarvanu Sengupta Date: Fri, 17 Aug 2018 15:59:27 +0900 Subject: [PATCH 7/8] Add async tracing across multiple phases Due to some limitation we can't distribute the request span accross multiple phases Theres an open issue at: https://github.com/opentracing/specification/issues/81 Once its resolved as intended we will be good to go For now we make a work around where we pass the last phase spanContext to show an waterfall pattern Signed-off-by: Swarvanu Sengupta --- example/upload-chain-async/handler.go | 1 + template/faaschain/handle_chain.go | 25 ++++-- template/faaschain/tracer.go | 125 ++++++++++++++++++++++++-- 3 files changed, 140 insertions(+), 11 deletions(-) diff --git a/example/upload-chain-async/handler.go b/example/upload-chain-async/handler.go index 11dd12d4..385ba70e 100644 --- a/example/upload-chain-async/handler.go +++ b/example/upload-chain-async/handler.go @@ -58,6 +58,7 @@ func Define(chain *faaschain.Fchain) (err error) { // Define Chain chain.Apply("colorization", map[string]string{"method": "post"}, nil). + ApplyAsync("image-resizer", map[string]string{"method": "post"}, nil). ApplyAsync("image-resizer", map[string]string{"method": "post"}, nil). ApplyModifier(func(data []byte) ([]byte, error) { client := &http.Client{} diff --git a/template/faaschain/handle_chain.go b/template/faaschain/handle_chain.go index 40bc44f9..72b745eb 100644 --- a/template/faaschain/handle_chain.go +++ b/template/faaschain/handle_chain.go @@ -114,6 +114,9 @@ func buildChain(data []byte) (fchain *faaschain.Fchain, requestData []byte) { // Set execution Pos fchain.GetChain().ExecutionPosition = 0 requestData = data + + // trace req - mark as start of req + startReqSpan(requestId) } else { // Get the request ID requestId = request.GetID() @@ -130,13 +133,13 @@ func buildChain(data []byte) (fchain *faaschain.Fchain, requestData []byte) { // Set execution Pos fchain.GetChain().ExecutionPosition = executionPos requestData = request.GetData() + + // Continue request span + continueReqSpan(requestId) } // set request ID fchain.SetId(requestId) - // trace req - mark as start of req - startReqSpan(requestId) - return } @@ -251,6 +254,9 @@ func forwardAsync(fchain *faaschain.Fchain, result []byte) ([]byte, error) { httpreq.Header.Add("Accept", "application/json") httpreq.Header.Add("Content-Type", "application/json") + // extend req span for async call + extendReqSpan(chain.ExecutionPosition-1, fchain.GetAsyncUrl(), httpreq) + client := &http.Client{} res, resErr := client.Do(httpreq) resdata, _ := ioutil.ReadAll(res.Body) @@ -275,6 +281,7 @@ func handleResponse(fchain *faaschain.Fchain, result []byte) ([]byte, error) { // If chain has only one phase or if the chain has completed excution return case chain.CountPhases() == 1 || chain.GetCurrentPhase() == nil: log.Printf("[request `%s`] Finished successfully ", fchain.GetId()) + return result, nil // In default case we forward the partial chain to same chain-function @@ -282,9 +289,13 @@ func handleResponse(fchain *faaschain.Fchain, result []byte) ([]byte, error) { // forward the chain request resp, forwardErr := forwardAsync(fchain, result) if forwardErr != nil { - return nil, fmt.Errorf("Phase(%d): error: %v, %s, url %s", chain.ExecutionPosition, forwardErr, string(resp), fchain.GetAsyncUrl()) + return nil, fmt.Errorf("Phase(%d): error: %v, %s, url %s", + chain.ExecutionPosition, forwardErr, + string(resp), fchain.GetAsyncUrl()) } - log.Printf("[Request `%s`] Phase %d request submitted", chain.ExecutionPosition) + + log.Printf("[Request `%s`] Phase %d request submitted", + fchain.GetId(), chain.ExecutionPosition) } return []byte(""), nil @@ -297,7 +308,7 @@ func handleChain(data []byte) string { // Get chain name chainName = getChainName() if chainName == "" { - fmt.Errorf("Error: chain name must be provided when chain deployed as function") + fmt.Errorf("Error: chain name must be provided when deployed as function") os.Exit(1) } @@ -331,7 +342,9 @@ func handleChain(data []byte) string { } } + // stop req span if request has finished stopReqSpan() + // flash any pending trace item if tracing enabled flushTracer() diff --git a/template/faaschain/tracer.go b/template/faaschain/tracer.go index 85910bbb..eade18ec 100644 --- a/template/faaschain/tracer.go +++ b/template/faaschain/tracer.go @@ -3,9 +3,12 @@ package main import ( "fmt" "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + //"github.com/opentracing/opentracing-go/log" "github.com/uber/jaeger-client-go" "github.com/uber/jaeger-client-go/config" "io" + "net/http" "os" "strings" "time" @@ -14,9 +17,52 @@ import ( var ( closer io.Closer reqSpan opentracing.Span + reqSpanCtx opentracing.SpanContext phaseSpans map[int]opentracing.Span ) +// EnvHeadersCarrier satisfies both TextMapWriter and TextMapReader +type EnvHeadersCarrier struct { + envMap map[string]string +} + +// buildEnvHeadersCarrier builds a EnvHeadersCarrier from env +func buildEnvHeadersCarrier() *EnvHeadersCarrier { + carrier := &EnvHeadersCarrier{} + carrier.envMap = make(map[string]string) + + for _, e := range os.Environ() { + if i := strings.Index(e, "="); i >= 0 { + if e[:i] == "Http_Uber_Trace_Id" { + key := "uber-trace-id" + carrier.envMap[key] = e[i+1:] + break + } + } + } + + return carrier +} + +// ForeachKey conforms to the TextMapReader interface +func (c *EnvHeadersCarrier) ForeachKey(handler func(key, val string) error) error { + for key, value := range c.envMap { + err := handler(key, value) + if err != nil { + fmt.Fprintf(os.Stderr, "ForeachKey key %s value %s, error %v", + key, value, err) + return err + } + } + return nil +} + +// Set conforms to the TextMapWriter interface +func (c *EnvHeadersCarrier) Set(key, val string) { + c.envMap[key] = val +} + +// isTracingEnabled check if tracing enabled for the function func isTracingEnabled() bool { tracing := os.Getenv("enable_tracing") if strings.ToUpper(tracing) == "TRUE" { @@ -25,6 +71,7 @@ func isTracingEnabled() bool { return false } +// getTraceServer get the traceserver address func getTraceServer() string { traceServer := os.Getenv("trace_server") if traceServer == "" { @@ -33,6 +80,7 @@ func getTraceServer() string { return traceServer } +// initGlobalTracer init global trace with configuration func initGlobalTracer(chainName string) error { if !isTracingEnabled() { @@ -73,35 +121,101 @@ func initGlobalTracer(chainName string) error { return nil } -// TODO: make use of context.Background() +// startReqSpan starts a request span func startReqSpan(reqId string) { if !isTracingEnabled() { return } reqSpan = opentracing.GlobalTracer().StartSpan(reqId) + reqSpan.SetTag("request", reqId) + + reqSpanCtx = reqSpan.Context() +} + +// continueReqSpan continue request span +func continueReqSpan(reqId string) { + var err error + + if !isTracingEnabled() { + return + } + + carrier := buildEnvHeadersCarrier() + reqSpanCtx, err = opentracing.GlobalTracer().Extract( + opentracing.HTTPHeaders, + carrier, + ) + if err != nil { + fmt.Fprintf(os.Stderr, "failed to continue req span tracing, error %v\n", err) + return + } + + reqSpan = nil + // TODO: Its not Supported to get span from spanContext as of now + // https://github.com/opentracing/specification/issues/81 + // it will support us to extend the request span for phases + //reqSpan = opentracing.SpanFromContext(reqSpanCtx) +} + +// extendReqSpan extend req span over a request +// func extendReqSpan(url string, req *http.Request) { +func extendReqSpan(lastPhaseRef int, url string, req *http.Request) { + if !isTracingEnabled() { + return + } + + // TODO: as requestSpan can't be regenerated with the span context we + // forward the phaseSpan's SpanContext + // span := reqSpan + span := phaseSpans[lastPhaseRef] + + ext.SpanKindRPCClient.Set(span) + ext.HTTPUrl.Set(span, url) + ext.HTTPMethod.Set(span, "POST") + span.Tracer().Inject( + span.Context(), + opentracing.HTTPHeaders, + opentracing.HTTPHeadersCarrier(req.Header), + ) } +// stopReqSpan terminate a request span func stopReqSpan() { if !isTracingEnabled() { return } + if reqSpan == nil { + return + } + reqSpan.Finish() } -// TODO: make use of context.Background() +// startPhaseSpan starts a phase span func startPhaseSpan(phase int, reqId string) { + var phaseSpan opentracing.Span if !isTracingEnabled() { return } - phasename := fmt.Sprintf("%s-phase-%d", reqId, phase) - phaseSpan := opentracing.GlobalTracer().StartSpan( - phasename, opentracing.ChildOf(reqSpan.Context())) + phasename := fmt.Sprintf("%d", phase) + // TODO: Now its always true + if reqSpan == nil { + phaseSpan = opentracing.GlobalTracer().StartSpan( + phasename, ext.RPCServerOption(reqSpanCtx)) + phaseSpan.SetTag("async", 1) + } else { + phaseSpan = opentracing.GlobalTracer().StartSpan( + phasename, opentracing.ChildOf(reqSpan.Context())) + } + phaseSpan.SetTag("request", reqId) + phaseSpan.SetTag("phase", phase) phaseSpans[phase] = phaseSpan } +// stopPhaseSpan terminates a phase span func stopPhaseSpan(phase int) { if !isTracingEnabled() { return @@ -110,6 +224,7 @@ func stopPhaseSpan(phase int) { phaseSpans[phase].Finish() } +// flushTracer flush all pending traces func flushTracer() { if !isTracingEnabled() { return From e9cda20e5357bfce9f5b7f742574e9c92b2e2d0e Mon Sep 17 00:00:00 2001 From: Swarvanu Sengupta Date: Fri, 17 Aug 2018 16:22:16 +0900 Subject: [PATCH 8/8] Add diagram and tweaks ti fix client mode Signed-off-by: Swarvanu Sengupta --- README.md | 16 +++++++++++++++- doc/tracing.png | Bin 0 -> 176294 bytes template/faaschain/tracer.go | 19 ++++++++++--------- 3 files changed, 25 insertions(+), 10 deletions(-) create mode 100644 doc/tracing.png diff --git a/README.md b/README.md index 3eb20530..a14a5089 100644 --- a/README.md +++ b/README.md @@ -146,13 +146,27 @@ cat data | faas-cli invoke --async -f test-chain.yml test-chain Function submitted asynchronously. ``` -## Request Tracking +## Request Tracking by ID Request can be tracked from the log by `RequestId`. For each new Request a unique `RequestId` is generated. ```bash 2018/08/13 07:51:59 [request `bdojh7oi7u6bl8te4r0g`] Created 2018/08/13 07:52:03 [Request `bdojh7oi7u6bl8te4r0g`] Received ``` +## Request Tracing by Open-Tracing +Request tracing can be enabled by providing by specifying +```yaml + enable_tracing: true + trace_server: "jaegertracing:5775" +``` +Below is an example of tracing for an async request with 3 phases +![alt multi phase](https://github.com/s8sg/faaschain/blob/tracing/doc/tracing.png) + +> *Due to some unresolved issue we can't extend the req span over multiple phases +> we use the waterfall model as an workaround +> For more details visit : https://github.com/opentracing/specification/issues/81 + + ## TODO: - [ ] Export support for Debug > Request Execution Status. diff --git a/doc/tracing.png b/doc/tracing.png new file mode 100644 index 0000000000000000000000000000000000000000..68467cae35beb3e8fdaf2e8024b61ad6f048fc77 GIT binary patch literal 176294 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

J>8< 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%0DgMT*;muT2l1wS3y~ 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¬hC)oUy7$u1uyKM{z}Ki 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 literal 0 HcmV?d00001 diff --git a/template/faaschain/tracer.go b/template/faaschain/tracer.go index eade18ec..49cf7c4c 100644 --- a/template/faaschain/tracer.go +++ b/template/faaschain/tracer.go @@ -201,15 +201,16 @@ func startPhaseSpan(phase int, reqId string) { } phasename := fmt.Sprintf("%d", phase) - // TODO: Now its always true - if reqSpan == nil { - phaseSpan = opentracing.GlobalTracer().StartSpan( - phasename, ext.RPCServerOption(reqSpanCtx)) - phaseSpan.SetTag("async", 1) - } else { - phaseSpan = opentracing.GlobalTracer().StartSpan( - phasename, opentracing.ChildOf(reqSpan.Context())) - } + phaseSpan = opentracing.GlobalTracer().StartSpan( + phasename, ext.RPCServerOption(reqSpanCtx)) + phaseSpan.SetTag("async", 1) + /* + if reqSpan == nil { + + } else { + phaseSpan = opentracing.GlobalTracer().StartSpan( + phasename, opentracing.ChildOf(reqSpan.Context())) + }*/ phaseSpan.SetTag("request", reqId) phaseSpan.SetTag("phase", phase) phaseSpans[phase] = phaseSpan