diff --git a/.circleci/config.yml b/.circleci/config.yml index 54268aa8..48f01909 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,13 +1,34 @@ version: 2 jobs: - build: + system_testing: machine: true steps: - checkout - run: - name: Build nginx-opentracing-ci Docker image - command: docker build -t nginx-opentracing-ci ci + name: system.testing + command: | + pyenv global 3.5.2 + python3 --version + pip3.5 --version + pip3.5 install setuptools + pip3.5 install docker + pip3.5 install docker-compose + sudo mkdir /test-log + sudo chmod a+rwx /test-log + export LOG_DIR=/test-log + ./ci/do_ci.sh system.testing + - store_artifacts: + path: /test-log + docker_image: + machine: true + steps: + - checkout - run: - name: Build and Test - command: ./ci/run_ci_docker.sh './ci/do_ci.sh' + command: docker build -t opentracing/nginx-opentracing . +workflows: + version: 2 + build_test_and_deploy: + jobs: + - system_testing + - docker_image diff --git a/docker/Dockerfile b/Dockerfile similarity index 84% rename from docker/Dockerfile rename to Dockerfile index 662ad670..7374c7d4 100644 --- a/docker/Dockerfile +++ b/Dockerfile @@ -2,12 +2,13 @@ ARG NGINX_LABEL=latest FROM nginx:${NGINX_LABEL} -ARG OPENTRACING_CPP_VERSION=v1.2.0 -ARG ZIPKIN_CPP_VERSION=v0.2.0 -ARG LIGHTSTEP_VERSION=v0.6.1 -ARG JAEGER_CPP_VERSION=v0.2.0 +ARG OPENTRACING_CPP_VERSION=v1.4.0 +ARG ZIPKIN_CPP_VERSION=v0.3.1 +ARG LIGHTSTEP_VERSION=v0.7.0 +ARG JAEGER_CPP_VERSION=v0.4.1 ARG GRPC_VERSION=v1.4.x -ARG NGINX_OPENTRACING_VERSION=v0.2.1 + +COPY . /src RUN set -x \ # install nginx-opentracing package dependencies @@ -59,16 +60,18 @@ RUN set -x \ && cmake -DBUILD_SHARED_LIBS=1 -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=OFF .. \ && make && make install \ && cd "$tempDir" \ + && ln -s /usr/local/lib/libzipkin_opentracing.so /usr/local/lib/libzipkin_opentracing_plugin.so \ ### Build Jaeger cpp-client && git clone -b $JAEGER_CPP_VERSION https://github.com/jaegertracing/cpp-client.git jaeger-cpp-client \ && cd jaeger-cpp-client \ && mkdir .build && cd .build \ && cmake -DCMAKE_BUILD_TYPE=Release \ -DBUILD_TESTING=OFF \ - -DJAEGERTRACING_WITH_YAML_CPP=OFF .. \ + -DJAEGERTRACING_WITH_YAML_CPP=ON .. \ && make && make install \ && export HUNTER_INSTALL_DIR=$(cat _3rdParty/Hunter/install-root-dir) \ && cd "$tempDir" \ + && ln -s /usr/local/lib/libjaegertracing.so /usr/local/lib/libjaegertracing_plugin.so \ ### Build gRPC && git clone -b $GRPC_VERSION https://github.com/grpc/grpc \ && cd grpc \ @@ -83,8 +86,8 @@ RUN set -x \ && cmake -DBUILD_SHARED_LIBS=1 -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=OFF .. \ && make && make install \ && cd "$tempDir" \ + && ln -s /usr/local/lib/liblightstep_tracer.so /usr/local/lib/liblightstep_tracer_plugin.so \ ### Build nginx-opentracing modules - && git clone -b $NGINX_OPENTRACING_VERSION https://github.com/opentracing-contrib/nginx-opentracing.git \ && NGINX_VERSION=`nginx -v 2>&1` && NGINX_VERSION=${NGINX_VERSION#*nginx/} \ && echo "deb-src http://nginx.org/packages/mainline/debian/ stretch nginx" >> /etc/apt/sources.list \ && apt-get update \ @@ -95,18 +98,14 @@ RUN set -x \ && NGINX_MODULES_PATH=$(nginx -V 2>&1 | grep -oP "modules-path=\K[^\s]*") \ && auto/configure \ --with-compat \ - --add-dynamic-module=${tempDir}/nginx-opentracing/opentracing \ - --add-dynamic-module=${tempDir}/nginx-opentracing/zipkin \ - --add-dynamic-module=${tempDir}/nginx-opentracing/lightstep \ - --add-dynamic-module=${tempDir}/nginx-opentracing/jaeger \ + --add-dynamic-module=/src/opentracing \ --with-cc-opt="-I$HUNTER_INSTALL_DIR/include" \ --with-ld-opt="-L$HUNTER_INSTALL_DIR/lib" \ + --with-debug \ && make modules \ && cp objs/ngx_http_opentracing_module.so $NGINX_MODULES_PATH/ \ - && cp objs/ngx_http_zipkin_module.so $NGINX_MODULES_PATH/ \ - && cp objs/ngx_http_lightstep_module.so $NGINX_MODULES_PATH/ \ - && cp objs/ngx_http_jaeger_module.so $NGINX_MODULES_PATH/ \ # if we have leftovers from building, let's purge them (including extra, unnecessary build deps) + && rm -rf /src \ && rm -rf $HOME/.hunter \ && if [ -n "$tempDir" ]; then \ apt-get purge -y --auto-remove \ diff --git a/Dockerfile-test b/Dockerfile-test new file mode 100644 index 00000000..910bb4c8 --- /dev/null +++ b/Dockerfile-test @@ -0,0 +1,55 @@ +FROM ubuntu:18.04 + +ARG OPENTRACING_CPP_VERSION=v1.4.0 +ARG NGINX_VERSION=1.13.12 + +COPY . /src + +RUN set -x \ + && apt-get update \ + && apt-get install --no-install-recommends --no-install-suggests -y \ + build-essential \ + gettext \ + cmake \ + git \ + gnupg2 \ + software-properties-common \ + curl \ + python3 \ + jq \ + ca-certificates \ + wget \ + libpcre3 libpcre3-dev \ + zlib1g-dev \ + gcc-8 \ + g++-8 \ +### Use gcc-8 (the default gcc has this problem when using with address sanitizer: +### https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84428) + && update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 800 --slave /usr/bin/g++ g++ /usr/bin/g++-8 \ +### Build opentracing-cpp + && cd / \ + && git clone -b $OPENTRACING_CPP_VERSION https://github.com/opentracing/opentracing-cpp.git \ + && cd opentracing-cpp \ + && mkdir .build && cd .build \ + && cmake -DCMAKE_BUILD_TYPE=Debug \ + -DCMAKE_CXX_FLAGS="-fno-omit-frame-pointer -fsanitize=address" \ + -DCMAKE_SHARED_LINKER_FLAGS="-fno-omit-frame-pointer -fsanitize=address" \ + -DCMAKE_EXE_LINKER_FLAGS="-fno-omit-frame-pointer -fsanitize=address" \ + -DBUILD_TESTING=OFF .. \ + && make && make install \ +### Build nginx + && cd /src \ + && wget -O nginx-release-${NGINX_VERSION}.tar.gz https://github.com/nginx/nginx/archive/release-${NGINX_VERSION}.tar.gz \ + && tar zxf nginx-release-${NGINX_VERSION}.tar.gz \ + && cd /src/nginx-release-${NGINX_VERSION} \ + # Temporarily disable leak sanitizer to get around false positives in build + && export ASAN_OPTIONS=detect_leaks=0 \ + && export CFLAGS="-Wno-error" \ + && auto/configure \ + --with-compat \ + --with-debug \ + --with-cc-opt="-O1 -g -fno-omit-frame-pointer -fsanitize=address" \ + --with-ld-opt="-g -fno-omit-frame-pointer -fsanitize=address" \ + --add-dynamic-module=/src/opentracing \ + && make && make install +CMD ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"] diff --git a/README.md b/README.md index aae789c5..7c83f7c3 100644 --- a/README.md +++ b/README.md @@ -23,42 +23,68 @@ Building ``` $ tar zxvf nginx-1.9.x.tar.gz $ cd nginx-1.9.x -$ ./configure --add-dynamic-module=/absolute/path/to/nginx-opentracing/opentracing \ - # To enable tracing with Jaeger - --add-dynamic-module=/absolute/path/to/nginx-opentracing/jaeger \ - # To enable tracing with Zipkin - --add-dynamic-module=/absolute/path/to/nginx-opentracing/zipkin \ - # To enable tracing with LightStep - --add-dynamic-module=/absolute/path/to/nginx-opentracing/lightstep +$ ./configure --add-dynamic-module=/absolute/path/to/nginx-opentracing/opentracing $ make && sudo make install ``` +You will also need to install a C++ tracer for either [Jaeger](https://github.com/jaegertracing/jaeger-client-cpp), [LightStep]( +https://github.com/lightstep/lightstep-tracer-cpp), or [Zipkin](https://github.com/rnburn/zipkin-cpp-opentracing). For linux x86-64, portable binary plugins are available: +``` +# Jaeger +wget https://github.com/jaegertracing/jaeger-client-cpp/releases/download/v0.4.0/libjaegertracing_plugin.linux_amd64.so -O /usr/local/lib/libjaegertracing_plugin.so + +# LightStep +wget -O - https://github.com/lightstep/lightstep-tracer-cpp/releases/download/v0.7.0/linux-amd64-liblightstep_tracer_plugin.so.gz | gunzip -c > /usr/local/lib/liblightstep_tracer_plugin.so + +# Zipkin +wget -O - https://github.com/rnburn/zipkin-cpp-opentracing/releases/download/v0.3.1/linux-amd64-libzipkin_opentracing_plugin.so.gz gunzip -c > /usr/local/lib/libzipkin_opentracing_plugin.so + +``` Getting Started --------------- +First, write a configuration for the tracer used. Below's an example of what +a Jaeger configuration might look like: + +/etc/jaeger-nginx-config.json +``` +{ + "service_name": "nginx", + "sampler": { + "type": "const", + "param": 1 + }, + "reporter": { + "localAgentHostPort": "jaeger:6831" + }, + "headers": { + "jaegerDebugHeader": "jaeger-debug-id", + "jaegerBaggageHeader": "jaeger-baggage", + "traceBaggageHeaderPrefix": "uberctx-", + }, + "baggage_restrictions": { + "denyBaggageOnInitializationFailure": false, + "hostPort": "" + } +} +``` + +See the vendor documentation for details on what options are available. + +You can then set up NGINX for distributed tracing by adding the following to +nginx.conf: ``` # Load the OpenTracing dynamic module. load_module modules/ngx_http_opentracing_module.so; -# Load a vendor OpenTracing dynamic module. -# For example, -# load_module modules/ngx_http_jaeger_module.so; -# or -# load_module modules/ngx_http_zipkin_module.so; -# or -# load_module modules/ngx_http_lightstep_module.so; - http { - # Configure your vendor's tracer. - # For example, - # jaeger_service_name my-nginx-server; - # ... - # or - # zipkin_collector_host localhost; - # ... - # or - # lightstep_access_token ACCESSTOKEN; - # .... + # Load a vendor tracer + opentracing_load_tracer /usr/local/lib/libjaegertracing_plugin.so /etc/jaeger-nginx-config.json; + + # or + # opentracing_load_tracer /usr/local/lib/liblightstep_tracer_plugin.so /path/to/config; + # or + # opentracing_load_tracer /usr/local/lib/libzipkin_opentracing_plugin.so /path/to/config; # Enable tracing for all requests. opentracing on; @@ -66,20 +92,25 @@ http { # Optionally, set additional tags. opentracing_tag http_user_agent $http_user_agent; - location ~ \.php$ { + upstream backend { + server app-service:9001; + } + + location ~ { # The operation name used for spans defaults to the name of the location # block, but you can use this directive to customize it. opentracing_operation_name $uri; - fastcgi_pass 127.0.0.1:1025; + # Propagate the active span context upstream, so that the trace can be + # continued by the backend. + # See http://opentracing.io/documentation/pages/api/cross-process-tracing.html + opentracing_propagate_context; + + proxy_pass http://backend; } } ``` See [Tutorial](doc/Tutorial.md) for a more complete example, -[Reference](doc/Directives.md) for a list of available OpenTracing-related -directives, and [Jaeger](jaeger/doc/Directives.md), -[Zipkin](zipkin/doc/Directives.md), and -[LightStep](lightstep/doc/Directives.md) for a list of vendor tracing +[Reference](doc/Reference.md) for a list of available OpenTracing-related directives. - diff --git a/ci/Dockerfile b/ci/Dockerfile deleted file mode 100644 index a8b78331..00000000 --- a/ci/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG NGINX_LABEL=latest - -FROM nginx:${NGINX_LABEL} - -ARG OPENTRACING_CPP_VERSION=v1.3.0 - -RUN set -x \ - && apt-get update \ - && apt-get install --no-install-recommends --no-install-suggests -y \ - build-essential \ - gettext \ - cmake \ - git \ - curl \ - python3 \ - jq \ - ca-certificates \ - wget \ - && mkdir /src \ -### Build opentracing-cpp - && cd /src \ - && git clone -b $OPENTRACING_CPP_VERSION https://github.com/opentracing/opentracing-cpp.git \ - && cd opentracing-cpp \ - && mkdir .build && cd .build \ - && cmake -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_TESTING=OFF .. \ - && make && make install \ -### Get nginx sources - && cd /src \ - && NGINX_VERSION=`nginx -v 2>&1` && NGINX_VERSION=${NGINX_VERSION#*nginx/} \ - && echo "deb-src http://nginx.org/packages/mainline/debian/ stretch nginx" >> /etc/apt/sources.list \ - && apt-get update \ - && apt-get build-dep -y nginx=${NGINX_VERSION} \ - && wget -O nginx-release-${NGINX_VERSION}.tar.gz https://github.com/nginx/nginx/archive/release-${NGINX_VERSION}.tar.gz \ - && tar zxf nginx-release-${NGINX_VERSION}.tar.gz \ diff --git a/ci/do_ci.sh b/ci/do_ci.sh index 59d76627..cdb7ad33 100755 --- a/ci/do_ci.sh +++ b/ci/do_ci.sh @@ -2,22 +2,12 @@ set -e -NGINX_VERSION=`nginx -v 2>&1` && NGINX_VERSION=${NGINX_VERSION#*nginx/} -NGINX_MODULES_PATH=$(nginx -V 2>&1 | grep -oP "modules-path=\K[^\s]*") -cd /src/nginx-release-${NGINX_VERSION} -auto/configure \ - --with-compat \ - --add-dynamic-module=/src/nginx-opentracing/opentracing -make modules -cp objs/ngx_http_opentracing_module.so $NGINX_MODULES_PATH/ -cd /src/nginx-opentracing -export MOCKTRACER_LIBRARY=/usr/local/lib/libopentracing_mocktracer.so -export NGINX_OPENTRACING_MODULE="$NGINX_MODULES_PATH/ngx_http_opentracing_module.so" -export NGINX_OPENTRACING_TEST_DIR=/nginx-opentracing-test -mkdir $NGINX_OPENTRACING_TEST_DIR -chmod a+rwx $NGINX_OPENTRACING_TEST_DIR -cd test -for i in *; -do - "$i/run.sh" -done +[ -z "${SRC_DIR}" ] && export SRC_DIR="`pwd`" + +if [[ "$1" == "system.testing" ]]; then + docker build -t nginx-opentracing-test/nginx -f Dockerfile-test . + cd test + docker build -t nginx-opentracing-test/backend -f Dockerfile-backend . + python3 nginx_opentracing_test.py + exit 0 +fi diff --git a/ci/run_ci_docker.sh b/ci/run_ci_docker.sh deleted file mode 100755 index fa92f3dd..00000000 --- a/ci/run_ci_docker.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -set -e - -BUILD_IMAGE=nginx-opentracing-ci -docker image inspect "$BUILD_IMAGE" &> /dev/null || { - docker build -t "$BUILD_IMAGE" ci -} - -if [ -n "$1" ]; then - docker run -v "$PWD":/src/nginx-opentracing -w /src/nginx-opentracing -it "$BUILD_IMAGE" /bin/bash -lc "$1" -else - docker run -v "$PWD":/src/nginx-opentracing -w /src/nginx-opentracing --privileged -it "$BUILD_IMAGE" /bin/bash -l -fi diff --git a/doc/Directives.md b/doc/Reference.md similarity index 78% rename from doc/Directives.md rename to doc/Reference.md index ce07d64b..7f6cc5cf 100644 --- a/doc/Directives.md +++ b/doc/Reference.md @@ -1,3 +1,6 @@ +Directives +---------- + ### `opentracing` - **syntax** `opentracing on|off` @@ -13,6 +16,14 @@ Enables or disables OpenTracing for NGINX requests. Dynamicaly loads in a tracer implementing the OpenTracing dynamic loading interface. +### `opentracing_propagate_context` + +- **syntax** `opentracing_propagate_context` +- **context**: `location` + +Propagates the active span context for upstream requests. (See +[cross-process-tracing](http://opentracing.io/documentation/pages/api/cross-process-tracing.html)) + ### `opentracing_trace_locations` - **syntax** `opentracing_trace_locations on|off` @@ -54,3 +65,12 @@ Enables or disables using OpenTracing spans from incoming requests as parent for Sets a [tag](https://github.com/opentracing/specification/blob/master/specification.md#set-a-span-tag) with the given `key` and `value` for an NGINX span. + +Variables +--------- + +### `opentracing_context_`*name* + +Expands to the a value of the active span context; the last part of the variable +name is the span context's header converted to lower case with dashes replaced by +underscores. diff --git a/doc/Tutorial.md b/doc/Tutorial.md index 8d0864c5..cbce53bf 100644 --- a/doc/Tutorial.md +++ b/doc/Tutorial.md @@ -42,11 +42,17 @@ http { # The backend returns a table of thumbnail profile pictures for every # animal in the zoo. proxy_pass http://backend; + + # Propagate the context of the active span. + # + # See http://opentracing.io/documentation/pages/api/cross-process-tracing.html + opentracing_propagate_context; } # Display the profile for a specific animal in the zoo. location = /animal { proxy_pass http://backend; + opentracing_propagate_context; } # Admit a new animal. @@ -57,6 +63,8 @@ http { # thumbnail sized profile picture, and transform the full-sized profile # picture, if necessary, to be in the jpeg format. proxy_pass http://backend; + + opentracing_propagate_context; # Redirect to the spash page if the animal was successfully admitted. proxy_intercept_errors on; @@ -83,7 +91,7 @@ We can tell NGINX to trace every request by adding these two lines to `nginx.conf`: ``` http { - lightstep_access_token `your-access-token`; + opentracing_load_tracer /path/to/tracer/plugin /path/to/tracer/config; opentracing on; ... ``` @@ -95,13 +103,10 @@ By default NGINX creates spans for both the request and the location blocks. It uses the name of the first location as the name for the top-level span. We can change this behavior by using the directives `opentracing_operation_name` and `opentracing_location_operation_name` to set the names of the request and -location block spans respectively. We can also use the directive -`lightstep_component_name` to set a group name for related traces. For +location block spans respectively. For example, by adding ``` http { -... - lightstep_component_name zoo; ... location = /upload/animal { opentracing_location_operation_name upload; diff --git a/example/browser/Dockerfile b/example/browser/Dockerfile deleted file mode 100644 index 8e719236..00000000 --- a/example/browser/Dockerfile +++ /dev/null @@ -1,26 +0,0 @@ -FROM rnburn/nginx-opentracing - -WORKDIR /app -ADD . /app -RUN set -x \ -## install nodejs and node dependencies - && apt-get update \ - && apt-get install --no-install-recommends --no-install-suggests -y \ - python \ - curl \ - gnupg2 \ - && mkdir /node-latest-install \ - && cd /node-latest-install \ - && curl -sL https://deb.nodesource.com/setup_7.x -o nodesource_setup.sh \ - && bash nodesource_setup.sh \ - && apt-get install --no-install-recommends --no-install-suggests -y \ - nodejs \ - && cd /app \ - && npm install . \ - && npm run browserify \ - && mv index.html /usr/share/nginx/html \ - && mv bundle.js /usr/share/nginx/html - - -EXPOSE 80 -CMD ["/app/start.sh"] diff --git a/example/browser/README.md b/example/browser/README.md deleted file mode 100644 index 2c346ec3..00000000 --- a/example/browser/README.md +++ /dev/null @@ -1,9 +0,0 @@ -A port of the [zipkin-js-example/web example](https://github.com/openzipkin/zipkin-js-example/tree/master/web) to use nginx. -It features Nginx as a reverse-proxy in front node services and client-side tracing in the browser. -Use these commands to run: -```bash -docker build -t nginx-example-browser . -docker run -d -p 9411:9411 --name zipkin openzipkin/zipkin -docker run -d -p 8080:80 --link zipkin:zipkin nginx-example-browser -``` -Visit http://localhost:8080 in your browser and http://localhost:9411 to view the traces in Zipkin. diff --git a/example/browser/index.html b/example/browser/index.html deleted file mode 100644 index 928f7f7d..00000000 --- a/example/browser/index.html +++ /dev/null @@ -1,6 +0,0 @@ - - -
Calling frontend service...
- - - diff --git a/example/browser/nginx.conf b/example/browser/nginx.conf deleted file mode 100644 index 9659bbfe..00000000 --- a/example/browser/nginx.conf +++ /dev/null @@ -1,37 +0,0 @@ -load_module modules/ngx_http_opentracing_module.so; -load_module modules/ngx_http_zipkin_module.so; - -events {} - -http { - zipkin_collector_host $ZIPKIN_PORT_9411_TCP_ADDR; - zipkin_collector_port $ZIPKIN_PORT_9411_TCP_PORT; - zipkin_service_name nginx; - opentracing on; - - upstream frontend { - server localhost:8081; - } - - upstream backend { - server localhost:9000; - } - - server { - listen 80; - server_name localhost; - - location / { - root /usr/share/nginx/html; - index index.html index.htm; - } - - location /time { - proxy_pass http://frontend; - } - - location /api { - proxy_pass http://backend; - } - } -} diff --git a/example/browser/node/backend.js b/example/browser/node/backend.js deleted file mode 100644 index 15034656..00000000 --- a/example/browser/node/backend.js +++ /dev/null @@ -1,22 +0,0 @@ -/* eslint-disable import/newline-after-import */ -// initialize tracer -const express = require('express'); -const CLSContext = require('zipkin-context-cls'); -const {Tracer} = require('zipkin'); -const {recorder} = require('./recorder'); - -const ctxImpl = new CLSContext('zipkin'); -const localServiceName = 'backend'; -const tracer = new Tracer({ctxImpl, recorder, localServiceName}); - -const app = express(); - -// instrument the server -const zipkinMiddleware = require('zipkin-instrumentation-express').expressMiddleware; -app.use(zipkinMiddleware({tracer})); - -app.get('/api', (req, res) => res.send(new Date().toString())); - -app.listen(9000, () => { - console.log('Backend listening on port 9000!'); -}); diff --git a/example/browser/node/browser.js b/example/browser/node/browser.js deleted file mode 100644 index bd5763df..00000000 --- a/example/browser/node/browser.js +++ /dev/null @@ -1,37 +0,0 @@ -/* eslint-env browser */ -/* eslint-disable import/newline-after-import */ -// use higher-precision time than milliseconds -process.hrtime = require('browser-process-hrtime'); - -// setup tracer -const { - BatchRecorder, - jsonEncoder: {JSON_V2} -} = require('zipkin'); -const {HttpLogger} = require('zipkin-transport-http'); - -const recorder = new BatchRecorder({ - logger: new HttpLogger({ - endpoint: 'http://localhost:9411/api/v2/spans', - jsonEncoder: JSON_V2 - }) -}); - -const {Tracer, ExplicitContext} = require('zipkin'); - -const ctxImpl = new ExplicitContext(); -const localServiceName = 'browser'; -const tracer = new Tracer({ctxImpl, recorder, localServiceName}); - -// instrument fetch -const wrapFetch = require('zipkin-instrumentation-fetch'); -const zipkinFetch = wrapFetch(fetch, {tracer}); - -const logEl = document.getElementById('log'); -const log = text => logEl.innerHTML = `${logEl.innerHTML}\n${text}`; - -// wrap fetch call so that it is traced -zipkinFetch('http://localhost:8080/time') - .then(response => (response.text())) - .then(text => log(text)) - .catch(err => log(`Failed:\n${err.stack}`)); diff --git a/example/browser/node/frontend.js b/example/browser/node/frontend.js deleted file mode 100644 index 75a2a389..00000000 --- a/example/browser/node/frontend.js +++ /dev/null @@ -1,43 +0,0 @@ -/* eslint-disable import/newline-after-import */ -// initialize tracer -const rest = require('rest'); -const express = require('express'); -const CLSContext = require('zipkin-context-cls'); -const {Tracer} = require('zipkin'); -const {recorder} = require('./recorder'); - -const ctxImpl = new CLSContext('zipkin'); -const localServiceName = 'frontend'; -const tracer = new Tracer({ctxImpl, recorder, localServiceName}); - -const app = express(); - -// instrument the server -const zipkinMiddleware = require('zipkin-instrumentation-express').expressMiddleware; -app.use(zipkinMiddleware({tracer})); - -// instrument the client -const {restInterceptor} = require('zipkin-instrumentation-cujojs-rest'); -const zipkinRest = rest.wrap(restInterceptor, {tracer}); - -// Allow cross-origin, traced requests. See http://enable-cors.org/server_expressjs.html -app.use((req, res, next) => { - res.header('Access-Control-Allow-Origin', '*'); - res.header('Access-Control-Allow-Headers', [ - 'Origin', 'Accept', 'X-Requested-With', 'X-B3-TraceId', - 'X-B3-ParentSpanId', 'X-B3-SpanId', 'X-B3-Sampled' - ].join(', ')); - next(); -}); - -app.get('/time', (req, res) => { - tracer.local('pay-me', () => - zipkinRest('http://localhost/api') - .then(response => res.send(response.entity)) - .catch(err => console.error('Error', err.stack)) - ); -}); - -app.listen(8081, () => { - console.log('Frontend listening on port 8081!'); -}); diff --git a/example/browser/node/recorder.js b/example/browser/node/recorder.js deleted file mode 100644 index 5db304ed..00000000 --- a/example/browser/node/recorder.js +++ /dev/null @@ -1,22 +0,0 @@ -/* eslint-env browser */ -const { - BatchRecorder, - jsonEncoder: {JSON_V2} -} = require('zipkin'); -const {HttpLogger} = require('zipkin-transport-http'); - -// Send spans to Zipkin asynchronously over HTTP -if (!process.env.ZIPKIN_HOST || !process.env.ZIPKIN_PORT) { - console.log("ZIPKIN_HOST and ZIPKIN_PORT must be set!"); - process.exit(1); -} -const zipkinBaseUrl = 'http://' + process.env.ZIPKIN_HOST + ':' + - process.env.ZIPKIN_PORT -const recorder = new BatchRecorder({ - logger: new HttpLogger({ - endpoint: `${zipkinBaseUrl}/api/v2/spans`, - jsonEncoder: JSON_V2 - }) -}); - -module.exports.recorder = recorder; diff --git a/example/browser/node/servers.js b/example/browser/node/servers.js deleted file mode 100644 index 9c1afe62..00000000 --- a/example/browser/node/servers.js +++ /dev/null @@ -1,5 +0,0 @@ -const path = require('path'); -const childProc = require('child_process'); - -childProc.fork(path.join(__dirname, 'frontend.js')); -childProc.fork(path.join(__dirname, 'backend.js')); diff --git a/example/browser/package.json b/example/browser/package.json deleted file mode 100644 index b5d73811..00000000 --- a/example/browser/package.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "zipkin-nginx-js-example", - "version": "0.0.1", - "description": "Example project that shows how to use zipkin with javascript", - "repository": "https://github.com/openzipkin/zipkin-js", - "scripts": { - "lint": "eslint .", - "start": "node node/servers.js", - "browserify": "browserify node/browser.js -o bundle.js" - }, - "dependencies": { - "browser-process-hrtime": "^0.1.2", - "express": "^4.14.0", - "rest": "^1.3.2", - "zipkin": "^0.11.1", - "zipkin-context-cls": "^0.11.0", - "zipkin-instrumentation-cujojs-rest": "^0.11.1", - "zipkin-instrumentation-express": "^0.11.1", - "zipkin-instrumentation-fetch": "^0.11.1", - "zipkin-transport-http": "^0.11.1" - }, - "devDependencies": { - "browserify": "^14.1.0", - "eslint": "^3.4.0", - "eslint-config-airbnb": "^14.1.0", - "eslint-plugin-import": "^2.2.0", - "eslint-plugin-jsx-a11y": "^4.0.0", - "eslint-plugin-react": "^6.2.0" - } -} diff --git a/example/browser/start.sh b/example/browser/start.sh deleted file mode 100755 index a9eec50e..00000000 --- a/example/browser/start.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -export ZIPKIN_HOST=$ZIPKIN_PORT_9411_TCP_ADDR -export ZIPKIN_PORT=$ZIPKIN_PORT_9411_TCP_PORT -envsubst '\$ZIPKIN_PORT_9411_TCP_ADDR \$ZIPKIN_PORT_9411_TCP_PORT' < /app/nginx.conf > /etc/nginx/nginx.conf - -npm start & -nginx -while /bin/true; do - sleep 50 -done diff --git a/example/hotrod/Dockerfile b/example/hotrod/Dockerfile deleted file mode 100644 index aa7ebe78..00000000 --- a/example/hotrod/Dockerfile +++ /dev/null @@ -1,52 +0,0 @@ -FROM rnburn/nginx-opentracing - -WORKDIR /app -ADD . /app -RUN set -x \ -# new directory for storing sources and .deb files - && tempDir="$(mktemp -d)" \ - && chmod 777 "$tempDir" \ - \ -# save list of currently-installed packages so build dependencies can be cleanly removed later - && savedAptMark="$(apt-mark showmanual)" \ - \ -# set up go - && apt-get update \ - && apt-get install --no-install-recommends --no-install-suggests -y \ - git \ - ca-certificates \ - software-properties-common \ - wget \ - curl \ - && wget https://storage.googleapis.com/golang/go1.8.3.linux-amd64.tar.gz \ - && tar -xvf go1.8.3.linux-amd64.tar.gz \ - && mv go $tempDir \ - && export GOROOT=$tempDir/go \ - && export GOPATH=$tempDir/gopath \ - && mkdir -p $GOPATH/bin \ - && export PATH=$GOPATH/bin:$GOROOT/bin:$PATH \ -# install glide - && curl https://glide.sh/get | sh \ -# build the hotrod demo - && mkdir -p $GOPATH/src/github.com/rnburn/ \ - && mv /app/hotrod $GOPATH/src/github.com/rnburn/hotrod-docker \ - && cd $GOPATH/src/github.com/rnburn/hotrod-docker \ - && glide install \ - && go build -o hotrod \ - && mv hotrod /app/hotrod \ - && mkdir -p /app/services/frontend/web_assets \ - && cp services/frontend/web_assets/index.html /app/services/frontend/web_assets/index.html \ -# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies -# (which is done after we install the built packages so we don't have to redownload any overlapping dependencies) - && apt-mark showmanual | xargs apt-mark auto > /dev/null \ - && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \ - \ -# purge leftovers from building them (including extra, unnecessary build deps) - && apt-get purge -y --auto-remove \ - && rm -rf "$tempDir" - -EXPOSE 8080 - -STOPSIGNAL SIGTERM - -CMD ["/app/start.sh"] diff --git a/example/hotrod/README.md b/example/hotrod/README.md deleted file mode 100644 index 2e108f61..00000000 --- a/example/hotrod/README.md +++ /dev/null @@ -1,14 +0,0 @@ -An OpenTracing example demonstrating usage of the nginx-opentracing docker -image with jaeger. It builds on Jaeger's HotROD example (see blog post -[Take OpenTracing for a HotROD ride](https://medium.com/opentracing/take-opentracing-for-a-hotrod-ride-f6e3141f7941)), -but inserts nginx as a load balancer for the HotROD services. - -Use these commands to run: -```bash -docker build -t nginx-example-hotrod . -docker run -d -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 -p5775:5775/udp -p6831:6831/udp -p6832:6832/udp \ - -p5778:5778 -p16686:16686 -p14268:14268 -p9411:9411 --name jaeger jaegertracing/all-in-one:latest -docker run -d -p 8080:80 --link jaeger:jaeger nginx-example-hotrod -``` -Visit http://localhost:8080 to interact with the demo and http://localhost:16686 -to view the traces in Jaeger. diff --git a/example/hotrod/hotrod/cmd/all.go b/example/hotrod/hotrod/cmd/all.go deleted file mode 100644 index 5016fd27..00000000 --- a/example/hotrod/hotrod/cmd/all.go +++ /dev/null @@ -1,35 +0,0 @@ -// 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 cmd - -import "github.com/spf13/cobra" - -// allCmd represents the all command -var allCmd = &cobra.Command{ - Use: "all", - Short: "Starts all services", - Long: `Starts all services.`, - Run: func(cmd *cobra.Command, args []string) { - logger.Info("Starting all services") - go customerCmd.RunE(customerCmd, args) - go driverCmd.RunE(driverCmd, args) - go routeCmd.RunE(routeCmd, args) - frontendCmd.RunE(frontendCmd, args) - }, -} - -func init() { - RootCmd.AddCommand(allCmd) -} diff --git a/example/hotrod/hotrod/cmd/customer.go b/example/hotrod/hotrod/cmd/customer.go deleted file mode 100644 index 7ddf6d4f..00000000 --- a/example/hotrod/hotrod/cmd/customer.go +++ /dev/null @@ -1,58 +0,0 @@ -// 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 cmd - -import ( - "net" - "strconv" - - "github.com/spf13/cobra" - "go.uber.org/zap" - - "github.com/rnburn/hotrod-docker/pkg/log" - "github.com/rnburn/hotrod-docker/pkg/tracing" - "github.com/rnburn/hotrod-docker/services/customer" -) - -// customerCmd represents the customer command -var customerCmd = &cobra.Command{ - Use: "customer", - Short: "Starts Customer service", - Long: `Starts Customer service.`, - RunE: func(cmd *cobra.Command, args []string) error { - logger := log.NewFactory(logger.With(zap.String("service", "customer"))) - server := customer.NewServer( - net.JoinHostPort(customerOptions.serverInterface, strconv.Itoa(customerOptions.serverPort)), - tracing.Init("customer", metricsFactory.Namespace("customer", nil), logger), - metricsFactory, - logger, - ) - return server.Run() - }, -} - -var ( - customerOptions struct { - serverInterface string - serverPort int - } -) - -func init() { - RootCmd.AddCommand(customerCmd) - - customerCmd.Flags().StringVarP(&customerOptions.serverInterface, "bind", "", "127.0.0.1", "interface to which the Customer server will bind") - customerCmd.Flags().IntVarP(&customerOptions.serverPort, "port", "p", 8081, "port on which the Customer server will listen") -} diff --git a/example/hotrod/hotrod/cmd/driver.go b/example/hotrod/hotrod/cmd/driver.go deleted file mode 100644 index 0c401095..00000000 --- a/example/hotrod/hotrod/cmd/driver.go +++ /dev/null @@ -1,58 +0,0 @@ -// 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 cmd - -import ( - "net" - "strconv" - - "github.com/spf13/cobra" - "go.uber.org/zap" - - "github.com/rnburn/hotrod-docker/pkg/log" - "github.com/rnburn/hotrod-docker/pkg/tracing" - "github.com/rnburn/hotrod-docker/services/driver" -) - -// driverCmd represents the driver command -var driverCmd = &cobra.Command{ - Use: "driver", - Short: "Starts Driver service", - Long: `Starts Driver service.`, - RunE: func(cmd *cobra.Command, args []string) error { - logger := log.NewFactory(logger.With(zap.String("service", "driver"))) - server := driver.NewServer( - net.JoinHostPort(driverOptions.serverInterface, strconv.Itoa(driverOptions.serverPort)), - tracing.Init("driver", metricsFactory.Namespace("driver", nil), logger), - metricsFactory, - logger, - ) - return server.Run() - }, -} - -var ( - driverOptions struct { - serverInterface string - serverPort int - } -) - -func init() { - RootCmd.AddCommand(driverCmd) - - driverCmd.Flags().StringVarP(&driverOptions.serverInterface, "bind", "", "127.0.0.1", "interface to which the driver server will bind") - driverCmd.Flags().IntVarP(&driverOptions.serverPort, "port", "p", 8082, "port on which the driver server will listen") -} diff --git a/example/hotrod/hotrod/cmd/frontend.go b/example/hotrod/hotrod/cmd/frontend.go deleted file mode 100644 index 6841edec..00000000 --- a/example/hotrod/hotrod/cmd/frontend.go +++ /dev/null @@ -1,57 +0,0 @@ -// 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 cmd - -import ( - "net" - "strconv" - - "github.com/spf13/cobra" - "go.uber.org/zap" - - "github.com/rnburn/hotrod-docker/pkg/log" - "github.com/rnburn/hotrod-docker/pkg/tracing" - "github.com/rnburn/hotrod-docker/services/frontend" -) - -// frontendCmd represents the frontend command -var frontendCmd = &cobra.Command{ - Use: "frontend", - Short: "Starts Frontend service", - Long: `Starts Frontend service.`, - RunE: func(cmd *cobra.Command, args []string) error { - logger := log.NewFactory(logger.With(zap.String("service", "frontend"))) - server := frontend.NewServer( - net.JoinHostPort(frontendOptions.serverInterface, strconv.Itoa(frontendOptions.serverPort)), - tracing.Init("frontend", metricsFactory.Namespace("frontend", nil), logger), - logger, - ) - return server.Run() - }, -} - -var ( - frontendOptions struct { - serverInterface string - serverPort int - } -) - -func init() { - RootCmd.AddCommand(frontendCmd) - - frontendCmd.Flags().StringVarP(&frontendOptions.serverInterface, "bind", "", "", "interface to which the frontend server will bind") - frontendCmd.Flags().IntVarP(&frontendOptions.serverPort, "port", "p", 8080, "port on which the frontend server will listen") -} diff --git a/example/hotrod/hotrod/cmd/root.go b/example/hotrod/hotrod/cmd/root.go deleted file mode 100644 index 0b16e7c9..00000000 --- a/example/hotrod/hotrod/cmd/root.go +++ /dev/null @@ -1,75 +0,0 @@ -// 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 cmd - -import ( - "fmt" - "math/rand" - "os" - "time" - - "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/uber/jaeger-lib/metrics" - "github.com/uber/jaeger-lib/metrics/go-kit" - "github.com/uber/jaeger-lib/metrics/go-kit/expvar" - "go.uber.org/zap" -) - -var ( - cfgFile string - logger *zap.Logger - metricsFactory metrics.Factory -) - -// RootCmd represents the base command when called without any subcommands -var RootCmd = &cobra.Command{ - Use: "jaeger-demo", - Short: "HotR.O.D. - A tracing demo application", - Long: `HotR.O.D. - A tracing demo application.`, -} - -// Execute adds all child commands to the root command sets flags appropriately. -// This is called by main.main(). It only needs to happen once to the rootCmd. -func Execute() { - if err := RootCmd.Execute(); err != nil { - logger.Fatal("We bowled a googly", zap.Error(err)) - os.Exit(-1) - } -} - -func init() { - cobra.OnInitialize(initConfig) - logger, _ = zap.NewDevelopment() - metricsFactory = xkit.Wrap("", expvar.NewFactory(10)) // 10 buckets for histograms -} - -// initConfig reads in config file and ENV variables if set. -func initConfig() { - if cfgFile != "" { // enable ability to specify config file via flag - viper.SetConfigFile(cfgFile) - } - - viper.SetConfigName(".jaeger-demo") // name of config file (without extension) - viper.AddConfigPath("$HOME") // adding home directory as first search path - viper.AutomaticEnv() // read in environment variables that match - - // If a config file is found, read it in. - if err := viper.ReadInConfig(); err == nil { - fmt.Println("Using config file:", viper.ConfigFileUsed()) - } - - rand.Seed(int64(time.Now().Nanosecond())) -} diff --git a/example/hotrod/hotrod/cmd/route.go b/example/hotrod/hotrod/cmd/route.go deleted file mode 100644 index c4d5a51c..00000000 --- a/example/hotrod/hotrod/cmd/route.go +++ /dev/null @@ -1,57 +0,0 @@ -// 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 cmd - -import ( - "net" - "strconv" - - "github.com/spf13/cobra" - "go.uber.org/zap" - - "github.com/rnburn/hotrod-docker/pkg/log" - "github.com/rnburn/hotrod-docker/pkg/tracing" - "github.com/rnburn/hotrod-docker/services/route" -) - -// routeCmd represents the route command -var routeCmd = &cobra.Command{ - Use: "route", - Short: "Starts Route service", - Long: `Starts Route service.`, - RunE: func(cmd *cobra.Command, args []string) error { - logger := log.NewFactory(logger.With(zap.String("service", "route"))) - server := route.NewServer( - net.JoinHostPort(routeOptions.serverInterface, strconv.Itoa(routeOptions.serverPort)), - tracing.Init("route", metricsFactory.Namespace("route", nil), logger), - logger, - ) - return server.Run() - }, -} - -var ( - routeOptions struct { - serverInterface string - serverPort int - } -) - -func init() { - RootCmd.AddCommand(routeCmd) - - routeCmd.Flags().StringVarP(&routeOptions.serverInterface, "bind", "", "127.0.0.1", "interface to which the Route server will bind") - routeCmd.Flags().IntVarP(&routeOptions.serverPort, "port", "p", 8083, "port on which the Route server will listen") -} diff --git a/example/hotrod/hotrod/glide.lock b/example/hotrod/hotrod/glide.lock deleted file mode 100644 index b829ce60..00000000 --- a/example/hotrod/hotrod/glide.lock +++ /dev/null @@ -1,126 +0,0 @@ -hash: 5afb055333607d4ef6fdd4023a87611599776f05195058dd4bf7a8381dad0bec -updated: 2017-10-20T13:24:51.186338625-07:00 -imports: -- name: github.com/apache/thrift - version: b2a4d4ae21c789b689dd162deb819665567f481c - subpackages: - - lib/go/thrift -- name: github.com/codahale/hdrhistogram - version: 3a0bb77429bd3a61596f5e8a3172445844342120 -- name: github.com/fsnotify/fsnotify - version: 4da3e2cfbabc9f751898f250b49f2439785783a1 -- name: github.com/go-kit/kit - version: a9ca6725cbbea455e61c6bc8a1ed28e81eb3493b - subpackages: - - metrics - - metrics/expvar - - metrics/generic - - metrics/internal/lv -- name: github.com/hashicorp/hcl - version: 23c074d0eceb2b8a5bfdbb271ab780cde70f05a8 - subpackages: - - hcl/ast - - hcl/parser - - hcl/scanner - - hcl/strconv - - hcl/token - - json/parser - - json/scanner - - json/token -- name: github.com/inconshreveable/mousetrap - version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 -- name: github.com/magiconair/properties - version: 8d7837e64d3c1ee4e54a880c5a920ab4316fc90a -- name: github.com/mitchellh/mapstructure - version: 06020f85339e21b2478f756a78e295255ffa4d6a -- name: github.com/opentracing-contrib/go-stdlib - version: 48e4d763b2fbcd10e666e6a1742acdf8cc2286ef - subpackages: - - nethttp -- name: github.com/opentracing/opentracing-go - version: 1949ddbfd147afd4d964a9f00b24eb291e0e7c38 - subpackages: - - ext - - log -- name: github.com/pelletier/go-toml - version: 2009e44b6f182e34d8ce081ac2767622937ea3d4 -- name: github.com/spf13/afero - version: e67d870304c4bca21331b02f414f970df13aa694 - subpackages: - - mem -- name: github.com/spf13/cast - version: acbeb36b902d72a7a4c18e8f3241075e7ab763e4 -- name: github.com/spf13/cobra - version: 7b2c5ac9fc04fc5efafb60700713d4fa609b777b -- name: github.com/spf13/jwalterweatherman - version: 12bd96e66386c1960ab0f74ced1362f66f552f7b -- name: github.com/spf13/pflag - version: 97afa5e7ca8a08a383cb259e06636b5e2cc7897f -- name: github.com/spf13/viper - version: 25b30aa063fc18e48662b86996252eabdcf2f0c7 -- name: github.com/uber-go/atomic - version: 4e336646b2ef9fc6e47be8e21594178f98e5ebcf -- name: github.com/uber/jaeger-client-go - version: 3e3870040def0ebdaf65a003863fa64f5cb26139 - subpackages: - - config - - internal/spanlog - - log - - rpcmetrics - - thrift-gen/agent - - thrift-gen/jaeger - - thrift-gen/sampling - - thrift-gen/zipkincore - - utils -- name: github.com/uber/jaeger-lib - version: 3b2a9ad2a045881ab7a0f81d465be54c8292ee4f - subpackages: - - metrics - - metrics/go-kit - - metrics/go-kit/expvar -- name: github.com/uber/tchannel-go - version: a7ad9ecb640b5f10a0395b38d6319175172b3ab2 - subpackages: - - internal/argreader - - relay - - thrift - - thrift/gen-go/meta - - tnet - - tos - - trand - - typed -- name: github.com/VividCortex/gohistogram - version: 51564d9861991fb0ad0f531c99ef602d0f9866e6 -- name: go.uber.org/atomic - version: 4e336646b2ef9fc6e47be8e21594178f98e5ebcf -- name: go.uber.org/multierr - version: 3c4937480c32f4c13a875a1829af76c98ca3d40a -- name: go.uber.org/zap - version: 35aad584952c3e7020db7b839f6b102de6271f89 - subpackages: - - buffer - - internal/bufferpool - - internal/color - - internal/exit - - zapcore -- name: golang.org/x/net - version: aabf50738bcdd9b207582cbe796b59ed65d56680 - subpackages: - - bpf - - context - - internal/iana - - internal/socket - - ipv4 - - ipv6 -- name: golang.org/x/sys - version: 8dbc5d05d6edcc104950cc299a1ce6641235bc86 - subpackages: - - unix -- name: golang.org/x/text - version: c01e4764d870b77f8abe5096ee19ad20d80e8075 - subpackages: - - transform - - unicode/norm -- name: gopkg.in/yaml.v2 - version: eb3733d160e74a9c7e442f435eb3bea458e1d19f -testImports: [] diff --git a/example/hotrod/hotrod/glide.yaml b/example/hotrod/hotrod/glide.yaml deleted file mode 100644 index 519d0f67..00000000 --- a/example/hotrod/hotrod/glide.yaml +++ /dev/null @@ -1,36 +0,0 @@ -package: github.com/rnburn/hotrod-docker -import: -- package: github.com/apache/thrift - version: ~0.10.0 - subpackages: - - lib/go/thrift -- package: github.com/opentracing-contrib/go-stdlib - subpackages: - - nethttp -- package: github.com/opentracing/opentracing-go - version: ~1.0.2 - subpackages: - - ext - - log -- package: github.com/spf13/cobra -- package: github.com/spf13/viper - version: ~1.0.0 -- package: github.com/uber/jaeger-client-go - version: ~2.9.0 - subpackages: - - config - - rpcmetrics -- package: github.com/uber/jaeger-lib - version: ~1.1.0 - subpackages: - - metrics - - metrics/go-kit - - metrics/go-kit/expvar -- package: github.com/uber/tchannel-go - version: ~1.7.0 - subpackages: - - thrift -- package: go.uber.org/zap - version: ~1.7.1 - subpackages: - - zapcore diff --git a/example/hotrod/hotrod/main.go b/example/hotrod/hotrod/main.go deleted file mode 100644 index b352952c..00000000 --- a/example/hotrod/hotrod/main.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) 2017 Uber Technologies, Inc. - -package main - -import ( - "runtime" - - "github.com/rnburn/hotrod-docker/cmd" -) - -func main() { - runtime.GOMAXPROCS(runtime.NumCPU()) - cmd.Execute() -} diff --git a/example/hotrod/hotrod/pkg/delay/delay.go b/example/hotrod/hotrod/pkg/delay/delay.go deleted file mode 100644 index d644eed5..00000000 --- a/example/hotrod/hotrod/pkg/delay/delay.go +++ /dev/null @@ -1,30 +0,0 @@ -// 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 delay - -import ( - "math" - "math/rand" - "time" -) - -// Sleep generates a normally distributed random delay with given mean and stdDev -// and blocks for that duration. -func Sleep(mean time.Duration, stdDev time.Duration) { - fMean := float64(mean) - fStdDev := float64(stdDev) - delay := time.Duration(math.Max(1, rand.NormFloat64()*fStdDev+fMean)) - time.Sleep(delay) -} diff --git a/example/hotrod/hotrod/pkg/httperr/httperr.go b/example/hotrod/hotrod/pkg/httperr/httperr.go deleted file mode 100644 index 9351eea7..00000000 --- a/example/hotrod/hotrod/pkg/httperr/httperr.go +++ /dev/null @@ -1,27 +0,0 @@ -// 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 httperr - -import "net/http" - -// HandleError checks if the error is not nil, writes it to the output -// with the specified status code, and returns true. If error is nil it returns false. -func HandleError(w http.ResponseWriter, err error, statusCode int) bool { - if err == nil { - return false - } - http.Error(w, string(err.Error()), statusCode) - return true -} diff --git a/example/hotrod/hotrod/pkg/httpexpvar/handler.go b/example/hotrod/hotrod/pkg/httpexpvar/handler.go deleted file mode 100644 index 13046eeb..00000000 --- a/example/hotrod/hotrod/pkg/httpexpvar/handler.go +++ /dev/null @@ -1,37 +0,0 @@ -// 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 httpexpvar - -import ( - "expvar" - "fmt" - "net/http" -) - -// Handler is a copy of expvar.expvarHandler private method. -// TODO this won't be needed in Go 1.8 -func Handler(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json; charset=utf-8") - fmt.Fprintf(w, "{\n") - first := true - expvar.Do(func(kv expvar.KeyValue) { - if !first { - fmt.Fprintf(w, ",\n") - } - first = false - fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value) - }) - fmt.Fprintf(w, "\n}\n") -} diff --git a/example/hotrod/hotrod/pkg/log/factory.go b/example/hotrod/hotrod/pkg/log/factory.go deleted file mode 100644 index 88cc4bb2..00000000 --- a/example/hotrod/hotrod/pkg/log/factory.go +++ /dev/null @@ -1,55 +0,0 @@ -// 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 ( - "context" - - "github.com/opentracing/opentracing-go" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" -) - -// Factory is the default logging wrapper that can create -// logger instances either for a given Context or context-less. -type Factory struct { - logger *zap.Logger -} - -// NewFactory creates a new Factory. -func NewFactory(logger *zap.Logger) Factory { - return Factory{logger: logger} -} - -// Bg creates a context-unaware logger. -func (b Factory) Bg() Logger { - return logger{logger: b.logger} -} - -// For returns a context-aware Logger. If the context -// contains an OpenTracing span, all logging calls are also -// echo-ed into the span. -func (b Factory) For(ctx context.Context) Logger { - if span := opentracing.SpanFromContext(ctx); span != nil { - // TODO for Jaeger span extract trace/span IDs as fields - return spanLogger{span: span, logger: b.logger} - } - return b.Bg() -} - -// With creates a child logger, and optionally adds some context fields to that logger. -func (b Factory) With(fields ...zapcore.Field) Factory { - return Factory{logger: b.logger.With(fields...)} -} diff --git a/example/hotrod/hotrod/pkg/log/logger.go b/example/hotrod/hotrod/pkg/log/logger.go deleted file mode 100644 index a2e1b594..00000000 --- a/example/hotrod/hotrod/pkg/log/logger.go +++ /dev/null @@ -1,53 +0,0 @@ -// 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 ( - "go.uber.org/zap" - "go.uber.org/zap/zapcore" -) - -// Logger is a simplified abstraction of the zap.Logger -type Logger interface { - Info(msg string, fields ...zapcore.Field) - Error(msg string, fields ...zapcore.Field) - Fatal(msg string, fields ...zapcore.Field) - With(fields ...zapcore.Field) Logger -} - -// logger delegates all calls to the underlying zap.Logger -type logger struct { - logger *zap.Logger -} - -// Info logs an info msg with fields -func (l logger) Info(msg string, fields ...zapcore.Field) { - l.logger.Info(msg, fields...) -} - -// Error logs an error msg with fields -func (l logger) Error(msg string, fields ...zapcore.Field) { - l.logger.Error(msg, fields...) -} - -// Fatal logs a fatal error msg with fields -func (l logger) Fatal(msg string, fields ...zapcore.Field) { - l.logger.Fatal(msg, fields...) -} - -// With creates a child logger, and optionally adds some context fields to that logger. -func (l logger) With(fields ...zapcore.Field) Logger { - return logger{logger: l.logger.With(fields...)} -} diff --git a/example/hotrod/hotrod/pkg/log/spanlogger.go b/example/hotrod/hotrod/pkg/log/spanlogger.go deleted file mode 100644 index 2d0580ac..00000000 --- a/example/hotrod/hotrod/pkg/log/spanlogger.go +++ /dev/null @@ -1,148 +0,0 @@ -// 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 ( - "time" - - "github.com/opentracing/opentracing-go" - tag "github.com/opentracing/opentracing-go/ext" - "github.com/opentracing/opentracing-go/log" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" -) - -type spanLogger struct { - logger *zap.Logger - span opentracing.Span -} - -func (sl spanLogger) Info(msg string, fields ...zapcore.Field) { - sl.logToSpan("info", msg, fields...) - sl.logger.Info(msg, fields...) -} - -func (sl spanLogger) Error(msg string, fields ...zapcore.Field) { - sl.logToSpan("error", msg, fields...) - sl.logger.Error(msg, fields...) -} - -func (sl spanLogger) Fatal(msg string, fields ...zapcore.Field) { - sl.logToSpan("fatal", msg, fields...) - tag.Error.Set(sl.span, true) - sl.logger.Fatal(msg, fields...) -} - -// With creates a child logger, and optionally adds some context fields to that logger. -func (sl spanLogger) With(fields ...zapcore.Field) Logger { - return spanLogger{logger: sl.logger.With(fields...), span: sl.span} -} - -func (sl spanLogger) logToSpan(level string, msg string, fields ...zapcore.Field) { - // TODO rather than always converting the fields, we could wrap them into a lazy logger - fa := fieldAdapter(make([]log.Field, 0, 2+len(fields))) - fa = append(fa, log.String("event", msg)) - fa = append(fa, log.String("level", level)) - for _, field := range fields { - field.AddTo(&fa) - } - sl.span.LogFields(fa...) -} - -type fieldAdapter []log.Field - -func (fa *fieldAdapter) AddBool(key string, value bool) { - *fa = append(*fa, log.Bool(key, value)) -} - -func (fa *fieldAdapter) AddFloat64(key string, value float64) { - *fa = append(*fa, log.Float64(key, value)) -} - -func (fa *fieldAdapter) AddFloat32(key string, value float32) { - *fa = append(*fa, log.Float64(key, float64(value))) -} - -func (fa *fieldAdapter) AddInt(key string, value int) { - *fa = append(*fa, log.Int(key, value)) -} - -func (fa *fieldAdapter) AddInt64(key string, value int64) { - *fa = append(*fa, log.Int64(key, value)) -} - -func (fa *fieldAdapter) AddInt32(key string, value int32) { - *fa = append(*fa, log.Int64(key, int64(value))) -} - -func (fa *fieldAdapter) AddInt16(key string, value int16) { - *fa = append(*fa, log.Int64(key, int64(value))) -} - -func (fa *fieldAdapter) AddInt8(key string, value int8) { - *fa = append(*fa, log.Int64(key, int64(value))) -} - -func (fa *fieldAdapter) AddUint(key string, value uint) { - *fa = append(*fa, log.Uint64(key, uint64(value))) -} - -func (fa *fieldAdapter) AddUint64(key string, value uint64) { - *fa = append(*fa, log.Uint64(key, value)) -} - -func (fa *fieldAdapter) AddUint32(key string, value uint32) { - *fa = append(*fa, log.Uint64(key, uint64(value))) -} - -func (fa *fieldAdapter) AddUint16(key string, value uint16) { - *fa = append(*fa, log.Uint64(key, uint64(value))) -} - -func (fa *fieldAdapter) AddUint8(key string, value uint8) { - *fa = append(*fa, log.Uint64(key, uint64(value))) -} - -func (fa *fieldAdapter) AddUintptr(key string, value uintptr) {} -func (fa *fieldAdapter) AddArray(key string, marshaler zapcore.ArrayMarshaler) error { return nil } -func (fa *fieldAdapter) AddComplex128(key string, value complex128) {} -func (fa *fieldAdapter) AddComplex64(key string, value complex64) {} -func (fa *fieldAdapter) AddObject(key string, value zapcore.ObjectMarshaler) error { return nil } -func (fa *fieldAdapter) AddReflected(key string, value interface{}) error { return nil } -func (fa *fieldAdapter) OpenNamespace(key string) {} - -func (fa *fieldAdapter) AddDuration(key string, value time.Duration) { - // TODO inefficient - *fa = append(*fa, log.String(key, value.String())) -} - -func (fa *fieldAdapter) AddTime(key string, value time.Time) { - // TODO inefficient - *fa = append(*fa, log.String(key, value.String())) -} - -func (fa *fieldAdapter) AddBinary(key string, value []byte) { - *fa = append(*fa, log.Object(key, value)) -} - -func (fa *fieldAdapter) AddByteString(key string, value []byte) { - *fa = append(*fa, log.Object(key, value)) -} - -func (fa *fieldAdapter) AddString(key, value string) { - if key != "" && value != "" { - *fa = append(*fa, log.String(key, value)) - } -} diff --git a/example/hotrod/hotrod/pkg/pool/pool.go b/example/hotrod/hotrod/pkg/pool/pool.go deleted file mode 100644 index 172064e7..00000000 --- a/example/hotrod/hotrod/pkg/pool/pool.go +++ /dev/null @@ -1,53 +0,0 @@ -// 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 pool - -// Pool is a simple worker pool -type Pool struct { - jobs chan func() - stop chan struct{} -} - -// New creates a new pool with the given number of workers -func New(workers int) *Pool { - jobs := make(chan func()) - stop := make(chan struct{}) - for i := 0; i < workers; i++ { - go func() { - for { - select { - case job := <-jobs: - job() - case <-stop: - return - } - } - }() - } - return &Pool{ - jobs: jobs, - stop: stop, - } -} - -// Execute enqueues the job to be executed by one of the workers in the pool -func (p *Pool) Execute(job func()) { - p.jobs <- job -} - -// Stop halts all the workers -func (p *Pool) Stop() { - p.stop <- struct{}{} -} diff --git a/example/hotrod/hotrod/pkg/tracing/http.go b/example/hotrod/hotrod/pkg/tracing/http.go deleted file mode 100644 index 6707602b..00000000 --- a/example/hotrod/hotrod/pkg/tracing/http.go +++ /dev/null @@ -1,61 +0,0 @@ -// 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 tracing - -import ( - "context" - "encoding/json" - "errors" - "io/ioutil" - "net/http" - - "github.com/opentracing-contrib/go-stdlib/nethttp" - "github.com/opentracing/opentracing-go" -) - -// HTTPClient wraps an http.Client with tracing instrumentation. -type HTTPClient struct { - Tracer opentracing.Tracer - Client *http.Client -} - -// GetJSON executes HTTP GET against specified url and tried to parse -// the response into out object. -func (c *HTTPClient) GetJSON(ctx context.Context, endpoint string, url string, out interface{}) error { - req, err := http.NewRequest("GET", url, nil) - if err != nil { - return err - } - req = req.WithContext(ctx) - req, ht := nethttp.TraceRequest(c.Tracer, req, nethttp.OperationName("HTTP GET: "+endpoint)) - defer ht.Finish() - - res, err := c.Client.Do(req) - if err != nil { - return err - } - - defer res.Body.Close() - - if res.StatusCode >= 400 { - body, err := ioutil.ReadAll(res.Body) - if err != nil { - return err - } - return errors.New(string(body)) - } - decoder := json.NewDecoder(res.Body) - return decoder.Decode(out) -} diff --git a/example/hotrod/hotrod/pkg/tracing/init.go b/example/hotrod/hotrod/pkg/tracing/init.go deleted file mode 100644 index 482b320e..00000000 --- a/example/hotrod/hotrod/pkg/tracing/init.go +++ /dev/null @@ -1,73 +0,0 @@ -// 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 tracing - -import ( - "fmt" - "time" - "os" - - "github.com/opentracing/opentracing-go" - "github.com/uber/jaeger-client-go/config" - "github.com/uber/jaeger-client-go/rpcmetrics" - "github.com/uber/jaeger-lib/metrics" - "go.uber.org/zap" - - "github.com/rnburn/hotrod-docker/pkg/log" -) - -// Init creates a new instance of Jaeger tracer. -func Init(serviceName string, metricsFactory metrics.Factory, logger log.Factory) opentracing.Tracer { - localAgentHost := os.Getenv("JAEGER_AGENT_HOST") - if localAgentHost == "" { - localAgentHost = "localhost" - } - localAgentPort := os.Getenv("JAEGER_AGENT_PORT") - if localAgentPort == "" { - localAgentPort = "6831" - } - cfg := config.Configuration{ - Sampler: &config.SamplerConfig{ - Type: "const", - Param: 1, - }, - Reporter: &config.ReporterConfig{ - LogSpans: false, - BufferFlushInterval: 1 * time.Second, - LocalAgentHostPort: fmt.Sprintf("%s:%s", localAgentHost, localAgentPort), - }, - } - tracer, _, err := cfg.New( - serviceName, - config.Logger(jaegerLoggerAdapter{logger.Bg()}), - config.Observer(rpcmetrics.NewObserver(metricsFactory, rpcmetrics.DefaultNameNormalizer)), - ) - if err != nil { - logger.Bg().Fatal("cannot initialize Jaeger Tracer", zap.Error(err)) - } - return tracer -} - -type jaegerLoggerAdapter struct { - logger log.Logger -} - -func (l jaegerLoggerAdapter) Error(msg string) { - l.logger.Error(msg) -} - -func (l jaegerLoggerAdapter) Infof(msg string, args ...interface{}) { - l.logger.Info(fmt.Sprintf(msg, args...)) -} diff --git a/example/hotrod/hotrod/pkg/tracing/mutex.go b/example/hotrod/hotrod/pkg/tracing/mutex.go deleted file mode 100644 index b7da3fdc..00000000 --- a/example/hotrod/hotrod/pkg/tracing/mutex.go +++ /dev/null @@ -1,82 +0,0 @@ -// 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 tracing - -import ( - "context" - "fmt" - "sync" - - "github.com/opentracing/opentracing-go" - "github.com/opentracing/opentracing-go/log" -) - -// Mutex is just like the standard sync.Mutex, except that it is aware of the Context -// and logs some diagnostic information into the current span. -type Mutex struct { - SessionBaggageKey string - - realLock sync.Mutex - holder string - - waiters []string - waitersLock sync.Mutex -} - -// Lock acquires an exclusive lock. -func (sm *Mutex) Lock(ctx context.Context) { - var session string - activeSpan := opentracing.SpanFromContext(ctx) - if activeSpan != nil { - session = activeSpan.BaggageItem(sm.SessionBaggageKey) - activeSpan.SetTag(sm.SessionBaggageKey, session) - } - - sm.waitersLock.Lock() - if waiting := len(sm.waiters); waiting > 0 && activeSpan != nil { - activeSpan.LogFields( - log.String("event", fmt.Sprintf("Waiting for lock behind %d transactions", waiting)), - log.String("blockers", fmt.Sprintf("%v", sm.waiters))) // avoid deferred slice.String() - fmt.Printf("%s Waiting for lock behind %d transactions: %v\n", session, waiting, sm.waiters) - } - sm.waiters = append(sm.waiters, session) - sm.waitersLock.Unlock() - - sm.realLock.Lock() - sm.holder = session - - sm.waitersLock.Lock() - behindLen := len(sm.waiters) - 1 - sm.waitersLock.Unlock() - - if activeSpan != nil { - activeSpan.LogEvent( - fmt.Sprintf("Acquired lock with %d transactions waiting behind", behindLen)) - } -} - -// Unlock releases the lock. -func (sm *Mutex) Unlock() { - sm.waitersLock.Lock() - for i, v := range sm.waiters { - if v == sm.holder { - sm.waiters = append(sm.waiters[0:i], sm.waiters[i+1:]...) - break - } - } - sm.waitersLock.Unlock() - - sm.realLock.Unlock() -} diff --git a/example/hotrod/hotrod/pkg/tracing/mux.go b/example/hotrod/hotrod/pkg/tracing/mux.go deleted file mode 100644 index ac309f0d..00000000 --- a/example/hotrod/hotrod/pkg/tracing/mux.go +++ /dev/null @@ -1,52 +0,0 @@ -// 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 tracing - -import ( - "net/http" - - "github.com/opentracing-contrib/go-stdlib/nethttp" - "github.com/opentracing/opentracing-go" -) - -// NewServeMux creates a new TracedServeMux. -func NewServeMux(tracer opentracing.Tracer) *TracedServeMux { - return &TracedServeMux{ - mux: http.NewServeMux(), - tracer: tracer, - } -} - -// TracedServeMux is a wrapper around http.ServeMux that instruments handlers for tracing. -type TracedServeMux struct { - mux *http.ServeMux - tracer opentracing.Tracer -} - -// Handle implements http.ServeMux#Handle -func (tm *TracedServeMux) Handle(pattern string, handler http.Handler) { - middleware := nethttp.Middleware( - tm.tracer, - handler, - nethttp.OperationNameFunc(func(r *http.Request) string { - return "HTTP " + r.Method + " " + pattern - })) - tm.mux.Handle(pattern, middleware) -} - -// ServeHTTP implements http.ServeMux#ServeHTTP -func (tm *TracedServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { - tm.mux.ServeHTTP(w, r) -} diff --git a/example/hotrod/hotrod/services/config/config.go b/example/hotrod/hotrod/services/config/config.go deleted file mode 100644 index 28cccd74..00000000 --- a/example/hotrod/hotrod/services/config/config.go +++ /dev/null @@ -1,60 +0,0 @@ -// 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 ( - "time" -) - -var ( - // 'frontend' service - - // WorkerPoolSize is the size of goroutine pool used to query for routes - WorkerPoolSize = 3 - - // 'customer' service - - // MySQLGetDelay is how long retrieving a customer record takes. - // Using large value mostly because I cannot click the button fast enough to cause a queue. - MySQLGetDelay = 300 * time.Millisecond - - // MySQLGetDelayStdDev is standard deviation - MySQLGetDelayStdDev = MySQLGetDelay / 10 - - // RouteWorkerPoolSize is the size of the worker pool used to query `route` service - RouteWorkerPoolSize = 3 - - // 'driver' service - - // RedisFindDelay is how long finding closest drivers takes - RedisFindDelay = 20 * time.Millisecond - - // RedisFindDelayStdDev is standard deviation - RedisFindDelayStdDev = RedisFindDelay / 4 - - // RedisGetDelay is how long retrieving a driver record takes - RedisGetDelay = 10 * time.Millisecond - - // RedisGetDelayStdDev is standard deviation - RedisGetDelayStdDev = RedisGetDelay / 4 - - // 'route' service - - // RouteCalcDelay is how long a route calculation takes - RouteCalcDelay = 50 * time.Millisecond - - // RouteCalcDelayStdDev is standard deviation - RouteCalcDelayStdDev = RouteCalcDelay / 4 -) diff --git a/example/hotrod/hotrod/services/customer/client.go b/example/hotrod/hotrod/services/customer/client.go deleted file mode 100644 index 95316f33..00000000 --- a/example/hotrod/hotrod/services/customer/client.go +++ /dev/null @@ -1,59 +0,0 @@ -// 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 customer - -import ( - "context" - "fmt" - "net/http" - - "github.com/opentracing-contrib/go-stdlib/nethttp" - "github.com/opentracing/opentracing-go" - "go.uber.org/zap" - - "github.com/rnburn/hotrod-docker/pkg/log" - "github.com/rnburn/hotrod-docker/pkg/tracing" -) - -// Client is a remote client that implements customer.Interface -type Client struct { - tracer opentracing.Tracer - logger log.Factory - client *tracing.HTTPClient -} - -// NewClient creates a new customer.Client -func NewClient(tracer opentracing.Tracer, logger log.Factory) *Client { - return &Client{ - tracer: tracer, - logger: logger, - client: &tracing.HTTPClient{ - Client: &http.Client{Transport: &nethttp.Transport{}}, - Tracer: tracer, - }, - } -} - -// Get implements customer.Interface#Get as an RPC -func (c *Client) Get(ctx context.Context, customerID string) (*Customer, error) { - c.logger.For(ctx).Info("Getting customer", zap.String("customer_id", customerID)) - - url := fmt.Sprintf("http://127.0.0.1/customer?customer=%s", customerID) - var customer Customer - if err := c.client.GetJSON(ctx, "/customer", url, &customer); err != nil { - return nil, err - } - return &customer, nil -} diff --git a/example/hotrod/hotrod/services/customer/database.go b/example/hotrod/hotrod/services/customer/database.go deleted file mode 100644 index 41078bbf..00000000 --- a/example/hotrod/hotrod/services/customer/database.go +++ /dev/null @@ -1,96 +0,0 @@ -// 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 customer - -import ( - "context" - "errors" - - "go.uber.org/zap" - - "github.com/opentracing/opentracing-go" - tags "github.com/opentracing/opentracing-go/ext" - - "github.com/rnburn/hotrod-docker/pkg/delay" - "github.com/rnburn/hotrod-docker/pkg/log" - "github.com/rnburn/hotrod-docker/pkg/tracing" - "github.com/rnburn/hotrod-docker/services/config" -) - -// database simulates Customer repository implemented on top of an SQL database -type database struct { - tracer opentracing.Tracer - logger log.Factory - customers map[string]*Customer - lock *tracing.Mutex -} - -func newDatabase(tracer opentracing.Tracer, logger log.Factory) *database { - return &database{ - tracer: tracer, - logger: logger, - lock: &tracing.Mutex{ - SessionBaggageKey: "request", - }, - customers: map[string]*Customer{ - "123": { - ID: "123", - Name: "Rachel's Floral Designs", - Location: "115,277", - }, - "567": { - ID: "567", - Name: "Amazing Coffee Roasters", - Location: "211,653", - }, - "392": { - ID: "392", - Name: "Trom Chocolatier", - Location: "577,322", - }, - "731": { - ID: "731", - Name: "Japanese Deserts", - Location: "728,326", - }, - }, - } -} - -func (d *database) Get(ctx context.Context, customerID string) (*Customer, error) { - d.logger.For(ctx).Info("Loading customer", zap.String("customer_id", customerID)) - - // simulate opentracing instrumentation of an SQL query - if span := opentracing.SpanFromContext(ctx); span != nil { - span := d.tracer.StartSpan("SQL SELECT", opentracing.ChildOf(span.Context())) - tags.SpanKindRPCClient.Set(span) - tags.PeerService.Set(span, "mysql") - span.SetTag("sql.query", "SELECT * FROM customer WHERE customer_id="+customerID) - defer span.Finish() - ctx = opentracing.ContextWithSpan(ctx, span) - } - - // simulate misconfigured connection pool that only gives one connection at a time - d.lock.Lock(ctx) - defer d.lock.Unlock() - - // simulate RPC delay - delay.Sleep(config.MySQLGetDelay, config.MySQLGetDelayStdDev) - - if customer, ok := d.customers[customerID]; ok { - return customer, nil - } - return nil, errors.New("invalid customer ID") -} diff --git a/example/hotrod/hotrod/services/customer/interface.go b/example/hotrod/hotrod/services/customer/interface.go deleted file mode 100644 index 976adc9a..00000000 --- a/example/hotrod/hotrod/services/customer/interface.go +++ /dev/null @@ -1,29 +0,0 @@ -// 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 customer - -import "context" - -// Customer contains data about a customer. -type Customer struct { - ID string - Name string - Location string -} - -// Interface exposed by the Customer service. -type Interface interface { - Get(ctx context.Context, customerID string) (*Customer, error) -} diff --git a/example/hotrod/hotrod/services/customer/server.go b/example/hotrod/hotrod/services/customer/server.go deleted file mode 100644 index c3f44a2a..00000000 --- a/example/hotrod/hotrod/services/customer/server.go +++ /dev/null @@ -1,92 +0,0 @@ -// 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 customer - -import ( - "encoding/json" - "net/http" - - "github.com/opentracing/opentracing-go" - "github.com/uber/jaeger-lib/metrics" - "go.uber.org/zap" - - "github.com/rnburn/hotrod-docker/pkg/httperr" - "github.com/rnburn/hotrod-docker/pkg/log" - "github.com/rnburn/hotrod-docker/pkg/tracing" -) - -// Server implements Customer service -type Server struct { - hostPort string - tracer opentracing.Tracer - logger log.Factory - database *database -} - -// NewServer creates a new customer.Server -func NewServer(hostPort string, tracer opentracing.Tracer, metricsFactory metrics.Factory, logger log.Factory) *Server { - return &Server{ - hostPort: hostPort, - tracer: tracer, - logger: logger, - database: newDatabase( - tracing.Init("mysql", metricsFactory.Namespace("mysql", nil), logger), - logger.With(zap.String("component", "mysql")), - ), - } -} - -// Run starts the Customer server -func (s *Server) Run() error { - mux := s.createServeMux() - s.logger.Bg().Info("Starting", zap.String("address", "http://"+s.hostPort)) - return http.ListenAndServe(s.hostPort, mux) -} - -func (s *Server) createServeMux() http.Handler { - mux := tracing.NewServeMux(s.tracer) - mux.Handle("/customer", http.HandlerFunc(s.customer)) - return mux -} - -func (s *Server) customer(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - s.logger.For(ctx).Info("HTTP request received", zap.String("method", r.Method), zap.Stringer("url", r.URL)) - if err := r.ParseForm(); httperr.HandleError(w, err, http.StatusBadRequest) { - s.logger.For(ctx).Error("bad request", zap.Error(err)) - return - } - - customerID := r.Form.Get("customer") - if customerID == "" { - http.Error(w, "Missing required 'customer' parameter", http.StatusBadRequest) - return - } - - response, err := s.database.Get(ctx, customerID) - if httperr.HandleError(w, err, http.StatusInternalServerError) { - s.logger.For(ctx).Error("request failed", zap.Error(err)) - return - } - - data, err := json.Marshal(response) - if httperr.HandleError(w, err, http.StatusInternalServerError) { - s.logger.For(ctx).Error("cannot marshal response", zap.Error(err)) - return - } - - w.Header().Set("Content-Type", "application/json") - w.Write(data) -} diff --git a/example/hotrod/hotrod/services/driver/client.go b/example/hotrod/hotrod/services/driver/client.go deleted file mode 100644 index 9a8d3f75..00000000 --- a/example/hotrod/hotrod/services/driver/client.go +++ /dev/null @@ -1,84 +0,0 @@ -// 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 driver - -import ( - "context" - "time" - - "github.com/opentracing/opentracing-go" - "github.com/uber/tchannel-go" - "github.com/uber/tchannel-go/thrift" - "go.uber.org/zap" - - "github.com/rnburn/hotrod-docker/pkg/log" - "github.com/rnburn/hotrod-docker/services/driver/thrift-gen/driver" -) - -// Client is a remote client that implements driver.Interface -type Client struct { - tracer opentracing.Tracer - logger log.Factory - ch *tchannel.Channel - client driver.TChanDriver -} - -// NewClient creates a new driver.Client -func NewClient(tracer opentracing.Tracer, logger log.Factory) *Client { - channelOpts := &tchannel.ChannelOptions{ - //Logger: logger, - //StatsReporter: statsReporter, - Tracer: tracer, - } - ch, err := tchannel.NewChannel("driver-client", channelOpts) - if err != nil { - logger.Bg().Fatal("Cannot create TChannel", zap.Error(err)) - } - clientOpts := &thrift.ClientOptions{ - HostPort: "127.0.0.1:8082", - } - thriftClient := thrift.NewClient(ch, "driver", clientOpts) - client := driver.NewTChanDriverClient(thriftClient) - - return &Client{ - tracer: tracer, - logger: logger, - ch: ch, - client: client, - } -} - -// FindNearest implements driver.Interface#FindNearest as an RPC -func (c *Client) FindNearest(ctx context.Context, location string) ([]Driver, error) { - c.logger.For(ctx).Info("Finding nearest drivers", zap.String("location", location)) - ctx, cancel := context.WithTimeout(ctx, 1*time.Second) - defer cancel() - results, err := c.client.FindNearest(thrift.Wrap(ctx), location) - if err != nil { - return nil, err - } - return fromThrift(results), nil -} - -func fromThrift(results []*driver.DriverLocation) []Driver { - retMe := make([]Driver, len(results)) - for i, result := range results { - retMe[i] = Driver{ - DriverID: result.DriverID, - Location: result.Location, - } - } - return retMe -} diff --git a/example/hotrod/hotrod/services/driver/driver.thrift b/example/hotrod/hotrod/services/driver/driver.thrift deleted file mode 100644 index 0b6778f3..00000000 --- a/example/hotrod/hotrod/services/driver/driver.thrift +++ /dev/null @@ -1,9 +0,0 @@ - -struct DriverLocation { - 1: required string driver_id - 2: required string location -} - -service Driver { - list findNearest(1: string location) -} \ No newline at end of file diff --git a/example/hotrod/hotrod/services/driver/interface.go b/example/hotrod/hotrod/services/driver/interface.go deleted file mode 100644 index d20941f4..00000000 --- a/example/hotrod/hotrod/services/driver/interface.go +++ /dev/null @@ -1,28 +0,0 @@ -// 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 driver - -import "context" - -// Driver describes a driver and the currentl car location. -type Driver struct { - DriverID string - Location string -} - -// Interface exposed by the Driver service. -type Interface interface { - FindNearest(ctx context.Context, location string) ([]Driver, error) -} diff --git a/example/hotrod/hotrod/services/driver/redis.go b/example/hotrod/hotrod/services/driver/redis.go deleted file mode 100644 index 3e1edf6f..00000000 --- a/example/hotrod/hotrod/services/driver/redis.go +++ /dev/null @@ -1,111 +0,0 @@ -// 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 driver - -import ( - "context" - "errors" - "fmt" - "math/rand" - "sync" - - "github.com/opentracing/opentracing-go" - "github.com/opentracing/opentracing-go/ext" - "github.com/uber/jaeger-lib/metrics" - "go.uber.org/zap" - - "github.com/rnburn/hotrod-docker/pkg/delay" - "github.com/rnburn/hotrod-docker/pkg/log" - "github.com/rnburn/hotrod-docker/pkg/tracing" - "github.com/rnburn/hotrod-docker/services/config" -) - -// Redis is a simulator of remote Redis cache -type Redis struct { - tracer opentracing.Tracer // simulate redis as a separate process - logger log.Factory - errorSimulator -} - -func newRedis(metricsFactory metrics.Factory, logger log.Factory) *Redis { - return &Redis{ - tracer: tracing.Init("redis", metricsFactory.Namespace("redis", nil), logger), - logger: logger, - } -} - -// FindDriverIDs finds IDs of drivers who are near the location. -func (r *Redis) FindDriverIDs(ctx context.Context, location string) []string { - if span := opentracing.SpanFromContext(ctx); span != nil { - span := r.tracer.StartSpan("FindDriverIDs", opentracing.ChildOf(span.Context())) - span.SetTag("param.location", location) - ext.SpanKindRPCClient.Set(span) - defer span.Finish() - ctx = opentracing.ContextWithSpan(ctx, span) - } - // simulate RPC delay - delay.Sleep(config.RedisFindDelay, config.RedisFindDelayStdDev) - - drivers := make([]string, 10) - for i := range drivers { - drivers[i] = fmt.Sprintf("T7%05dC", rand.Int()%100000) - } - return drivers -} - -// GetDriver returns driver and the current car location -func (r *Redis) GetDriver(ctx context.Context, driverID string) (Driver, error) { - if span := opentracing.SpanFromContext(ctx); span != nil { - span := r.tracer.StartSpan("GetDriver", opentracing.ChildOf(span.Context())) - span.SetTag("param.driverID", driverID) - ext.SpanKindRPCClient.Set(span) - defer span.Finish() - ctx = opentracing.ContextWithSpan(ctx, span) - } - // simulate RPC delay - delay.Sleep(config.RedisGetDelay, config.RedisGetDelayStdDev) - if err := r.checkError(); err != nil { - if span := opentracing.SpanFromContext(ctx); span != nil { - ext.Error.Set(span, true) - } - r.logger.For(ctx).Error("redis timeout", zap.String("driver_id", driverID), zap.Error(err)) - return Driver{}, err - } - - return Driver{ - DriverID: driverID, - Location: fmt.Sprintf("%d,%d", rand.Int()%1000, rand.Int()%1000), - }, nil -} - -var errTimeout = errors.New("redis timeout") - -type errorSimulator struct { - sync.Mutex - countTillError int -} - -func (es *errorSimulator) checkError() error { - es.Lock() - es.countTillError-- - if es.countTillError > 0 { - es.Unlock() - return nil - } - es.countTillError = 5 - es.Unlock() - delay.Sleep(2*config.RedisGetDelay, 0) // add more delay for "timeout" - return errTimeout -} diff --git a/example/hotrod/hotrod/services/driver/server.go b/example/hotrod/hotrod/services/driver/server.go deleted file mode 100644 index a514cac2..00000000 --- a/example/hotrod/hotrod/services/driver/server.go +++ /dev/null @@ -1,102 +0,0 @@ -// 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 driver - -import ( - "github.com/opentracing/opentracing-go" - "github.com/uber/jaeger-lib/metrics" - "github.com/uber/tchannel-go" - "github.com/uber/tchannel-go/thrift" - "go.uber.org/zap" - - "github.com/rnburn/hotrod-docker/pkg/log" - "github.com/rnburn/hotrod-docker/services/driver/thrift-gen/driver" -) - -// Server implements jaeger-demo-frontend service -type Server struct { - hostPort string - tracer opentracing.Tracer - logger log.Factory - ch *tchannel.Channel - server *thrift.Server - redis *Redis -} - -// NewServer creates a new driver.Server -func NewServer(hostPort string, tracer opentracing.Tracer, metricsFactory metrics.Factory, logger log.Factory) *Server { - channelOpts := &tchannel.ChannelOptions{ - Tracer: tracer, - } - ch, err := tchannel.NewChannel("driver", channelOpts) - if err != nil { - logger.Bg().Fatal("Cannot create TChannel", zap.Error(err)) - } - server := thrift.NewServer(ch) - - return &Server{ - hostPort: hostPort, - tracer: tracer, - logger: logger, - ch: ch, - server: server, - redis: newRedis(metricsFactory, logger), - } -} - -// Run starts the Driver server -func (s *Server) Run() error { - - s.server.Register(driver.NewTChanDriverServer(s)) - - if err := s.ch.ListenAndServe(s.hostPort); err != nil { - s.logger.Bg().Fatal("Unable to start tchannel server", zap.Error(err)) - } - - peerInfo := s.ch.PeerInfo() - s.logger.Bg().Info("TChannel listening", zap.String("hostPort", peerInfo.HostPort)) - - // Run must block, but TChannel's ListenAndServe runs in the background, so block indefinitely - select {} -} - -// FindNearest implements Thrift interface TChanDriver -func (s *Server) FindNearest(ctx thrift.Context, location string) ([]*driver.DriverLocation, error) { - s.logger.For(ctx).Info("Searching for nearby drivers", zap.String("location", location)) - driverIDs := s.redis.FindDriverIDs(ctx, location) - - retMe := make([]*driver.DriverLocation, len(driverIDs)) - for i, driverID := range driverIDs { - var drv Driver - var err error - for i := 0; i < 3; i++ { - drv, err = s.redis.GetDriver(ctx, driverID) - if err == nil { - break - } - s.logger.For(ctx).Error("Retrying GetDriver after error", zap.Int("retry_no", i+1), zap.Error(err)) - } - if err != nil { - s.logger.For(ctx).Error("Failed to get driver after 3 attempts", zap.Error(err)) - return nil, err - } - retMe[i] = &driver.DriverLocation{ - DriverID: drv.DriverID, - Location: drv.Location, - } - } - s.logger.For(ctx).Info("Search successful", zap.Int("num_drivers", len(retMe))) - return retMe, nil -} diff --git a/example/hotrod/hotrod/services/driver/thrift-gen/driver/constants.go b/example/hotrod/hotrod/services/driver/thrift-gen/driver/constants.go deleted file mode 100644 index 09d65670..00000000 --- a/example/hotrod/hotrod/services/driver/thrift-gen/driver/constants.go +++ /dev/null @@ -1,18 +0,0 @@ -// Autogenerated by Thrift Compiler (0.9.3) -// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING - -package driver - -import ( - "bytes" - "fmt" - "github.com/apache/thrift/lib/go/thrift" -) - -// (needed to ensure safety because of naive import list construction.) -var _ = thrift.ZERO -var _ = fmt.Printf -var _ = bytes.Equal - -func init() { -} diff --git a/example/hotrod/hotrod/services/driver/thrift-gen/driver/driver.go b/example/hotrod/hotrod/services/driver/thrift-gen/driver/driver.go deleted file mode 100644 index c8b20275..00000000 --- a/example/hotrod/hotrod/services/driver/thrift-gen/driver/driver.go +++ /dev/null @@ -1,427 +0,0 @@ -// Autogenerated by Thrift Compiler (0.9.3) -// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING - -package driver - -import ( - "bytes" - "fmt" - "github.com/apache/thrift/lib/go/thrift" -) - -// (needed to ensure safety because of naive import list construction.) -var _ = thrift.ZERO -var _ = fmt.Printf -var _ = bytes.Equal - -type Driver interface { - // Parameters: - // - Location - FindNearest(location string) (r []*DriverLocation, err error) -} - -type DriverClient struct { - Transport thrift.TTransport - ProtocolFactory thrift.TProtocolFactory - InputProtocol thrift.TProtocol - OutputProtocol thrift.TProtocol - SeqId int32 -} - -func NewDriverClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *DriverClient { - return &DriverClient{Transport: t, - ProtocolFactory: f, - InputProtocol: f.GetProtocol(t), - OutputProtocol: f.GetProtocol(t), - SeqId: 0, - } -} - -func NewDriverClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *DriverClient { - return &DriverClient{Transport: t, - ProtocolFactory: nil, - InputProtocol: iprot, - OutputProtocol: oprot, - SeqId: 0, - } -} - -// Parameters: -// - Location -func (p *DriverClient) FindNearest(location string) (r []*DriverLocation, err error) { - if err = p.sendFindNearest(location); err != nil { - return - } - return p.recvFindNearest() -} - -func (p *DriverClient) sendFindNearest(location string) (err error) { - oprot := p.OutputProtocol - if oprot == nil { - oprot = p.ProtocolFactory.GetProtocol(p.Transport) - p.OutputProtocol = oprot - } - p.SeqId++ - if err = oprot.WriteMessageBegin("findNearest", thrift.CALL, p.SeqId); err != nil { - return - } - args := DriverFindNearestArgs{ - Location: location, - } - if err = args.Write(oprot); err != nil { - return - } - if err = oprot.WriteMessageEnd(); err != nil { - return - } - return oprot.Flush() -} - -func (p *DriverClient) recvFindNearest() (value []*DriverLocation, 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 != "findNearest" { - err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "findNearest failed: wrong method name") - return - } - if p.SeqId != seqId { - err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "findNearest 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, "findNearest failed: invalid message type") - return - } - result := DriverFindNearestResult{} - if err = result.Read(iprot); err != nil { - return - } - if err = iprot.ReadMessageEnd(); err != nil { - return - } - value = result.GetSuccess() - return -} - -type DriverProcessor struct { - processorMap map[string]thrift.TProcessorFunction - handler Driver -} - -func (p *DriverProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { - p.processorMap[key] = processor -} - -func (p *DriverProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { - processor, ok = p.processorMap[key] - return processor, ok -} - -func (p *DriverProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { - return p.processorMap -} - -func NewDriverProcessor(handler Driver) *DriverProcessor { - - self2 := &DriverProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} - self2.processorMap["findNearest"] = &driverProcessorFindNearest{handler: handler} - return self2 -} - -func (p *DriverProcessor) 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 driverProcessorFindNearest struct { - handler Driver -} - -func (p *driverProcessorFindNearest) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { - args := DriverFindNearestArgs{} - if err = args.Read(iprot); err != nil { - iprot.ReadMessageEnd() - x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) - oprot.WriteMessageBegin("findNearest", thrift.EXCEPTION, seqId) - x.Write(oprot) - oprot.WriteMessageEnd() - oprot.Flush() - return false, err - } - - iprot.ReadMessageEnd() - result := DriverFindNearestResult{} - var retval []*DriverLocation - var err2 error - if retval, err2 = p.handler.FindNearest(args.Location); err2 != nil { - x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing findNearest: "+err2.Error()) - oprot.WriteMessageBegin("findNearest", thrift.EXCEPTION, seqId) - x.Write(oprot) - oprot.WriteMessageEnd() - oprot.Flush() - return true, err2 - } else { - result.Success = retval - } - if err2 = oprot.WriteMessageBegin("findNearest", 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: -// - Location -type DriverFindNearestArgs struct { - Location string `thrift:"location,1" json:"location"` -} - -func NewDriverFindNearestArgs() *DriverFindNearestArgs { - return &DriverFindNearestArgs{} -} - -func (p *DriverFindNearestArgs) GetLocation() string { - return p.Location -} -func (p *DriverFindNearestArgs) 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 *DriverFindNearestArgs) readField1(iprot thrift.TProtocol) error { - if v, err := iprot.ReadString(); err != nil { - return thrift.PrependError("error reading field 1: ", err) - } else { - p.Location = v - } - return nil -} - -func (p *DriverFindNearestArgs) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("findNearest_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 *DriverFindNearestArgs) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("location", thrift.STRING, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:location: ", p), err) - } - if err := oprot.WriteString(string(p.Location)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.location (1) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:location: ", p), err) - } - return err -} - -func (p *DriverFindNearestArgs) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("DriverFindNearestArgs(%+v)", *p) -} - -// Attributes: -// - Success -type DriverFindNearestResult struct { - Success []*DriverLocation `thrift:"success,0" json:"success,omitempty"` -} - -func NewDriverFindNearestResult() *DriverFindNearestResult { - return &DriverFindNearestResult{} -} - -var DriverFindNearestResult_Success_DEFAULT []*DriverLocation - -func (p *DriverFindNearestResult) GetSuccess() []*DriverLocation { - return p.Success -} -func (p *DriverFindNearestResult) IsSetSuccess() bool { - return p.Success != nil -} - -func (p *DriverFindNearestResult) 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 *DriverFindNearestResult) readField0(iprot thrift.TProtocol) error { - _, size, err := iprot.ReadListBegin() - if err != nil { - return thrift.PrependError("error reading list begin: ", err) - } - tSlice := make([]*DriverLocation, 0, size) - p.Success = tSlice - for i := 0; i < size; i++ { - _elem4 := &DriverLocation{} - 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 *DriverFindNearestResult) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("findNearest_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 *DriverFindNearestResult) 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 *DriverFindNearestResult) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("DriverFindNearestResult(%+v)", *p) -} diff --git a/example/hotrod/hotrod/services/driver/thrift-gen/driver/tchan-driver.go b/example/hotrod/hotrod/services/driver/thrift-gen/driver/tchan-driver.go deleted file mode 100644 index d69dfd23..00000000 --- a/example/hotrod/hotrod/services/driver/thrift-gen/driver/tchan-driver.go +++ /dev/null @@ -1,101 +0,0 @@ -// @generated Code generated by thrift-gen. Do not modify. - -// Package driver is generated code used to make or handle TChannel calls using Thrift. -package driver - -import ( - "fmt" - - athrift "github.com/apache/thrift/lib/go/thrift" - "github.com/uber/tchannel-go/thrift" -) - -// Interfaces for the service and client for the services defined in the IDL. - -// TChanDriver is the interface that defines the server handler and client interface. -type TChanDriver interface { - FindNearest(ctx thrift.Context, location string) ([]*DriverLocation, error) -} - -// Implementation of a client and service handler. - -type tchanDriverClient struct { - thriftService string - client thrift.TChanClient -} - -func NewTChanDriverInheritedClient(thriftService string, client thrift.TChanClient) *tchanDriverClient { - return &tchanDriverClient{ - thriftService, - client, - } -} - -// NewTChanDriverClient creates a client that can be used to make remote calls. -func NewTChanDriverClient(client thrift.TChanClient) TChanDriver { - return NewTChanDriverInheritedClient("Driver", client) -} - -func (c *tchanDriverClient) FindNearest(ctx thrift.Context, location string) ([]*DriverLocation, error) { - var resp DriverFindNearestResult - args := DriverFindNearestArgs{ - Location: location, - } - success, err := c.client.Call(ctx, c.thriftService, "findNearest", &args, &resp) - if err == nil && !success { - } - - return resp.GetSuccess(), err -} - -type tchanDriverServer struct { - handler TChanDriver -} - -// NewTChanDriverServer wraps a handler for TChanDriver so it can be -// registered with a thrift.Server. -func NewTChanDriverServer(handler TChanDriver) thrift.TChanServer { - return &tchanDriverServer{ - handler, - } -} - -func (s *tchanDriverServer) Service() string { - return "Driver" -} - -func (s *tchanDriverServer) Methods() []string { - return []string{ - "findNearest", - } -} - -func (s *tchanDriverServer) Handle(ctx thrift.Context, methodName string, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { - switch methodName { - case "findNearest": - return s.handleFindNearest(ctx, protocol) - - default: - return false, nil, fmt.Errorf("method %v not found in service %v", methodName, s.Service()) - } -} - -func (s *tchanDriverServer) handleFindNearest(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { - var req DriverFindNearestArgs - var res DriverFindNearestResult - - if err := req.Read(protocol); err != nil { - return false, nil, err - } - - r, err := - s.handler.FindNearest(ctx, req.Location) - - if err != nil { - return false, nil, err - } else { - res.Success = r - } - - return err == nil, &res, nil -} diff --git a/example/hotrod/hotrod/services/driver/thrift-gen/driver/ttypes.go b/example/hotrod/hotrod/services/driver/thrift-gen/driver/ttypes.go deleted file mode 100644 index 2f5093b1..00000000 --- a/example/hotrod/hotrod/services/driver/thrift-gen/driver/ttypes.go +++ /dev/null @@ -1,154 +0,0 @@ -// Autogenerated by Thrift Compiler (0.9.3) -// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING - -package driver - -import ( - "bytes" - "fmt" - "github.com/apache/thrift/lib/go/thrift" -) - -// (needed to ensure safety because of naive import list construction.) -var _ = thrift.ZERO -var _ = fmt.Printf -var _ = bytes.Equal - -var GoUnusedProtection__ int - -// Attributes: -// - DriverID -// - Location -type DriverLocation struct { - DriverID string `thrift:"driver_id,1,required" json:"driver_id"` - Location string `thrift:"location,2,required" json:"location"` -} - -func NewDriverLocation() *DriverLocation { - return &DriverLocation{} -} - -func (p *DriverLocation) GetDriverID() string { - return p.DriverID -} - -func (p *DriverLocation) GetLocation() string { - return p.Location -} -func (p *DriverLocation) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - var issetDriverID bool = false - var issetLocation 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 - } - issetDriverID = true - case 2: - if err := p.readField2(iprot); err != nil { - return err - } - issetLocation = 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 !issetDriverID { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field DriverID is not set")) - } - if !issetLocation { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Location is not set")) - } - return nil -} - -func (p *DriverLocation) readField1(iprot thrift.TProtocol) error { - if v, err := iprot.ReadString(); err != nil { - return thrift.PrependError("error reading field 1: ", err) - } else { - p.DriverID = v - } - return nil -} - -func (p *DriverLocation) readField2(iprot thrift.TProtocol) error { - if v, err := iprot.ReadString(); err != nil { - return thrift.PrependError("error reading field 2: ", err) - } else { - p.Location = v - } - return nil -} - -func (p *DriverLocation) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("DriverLocation"); 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 *DriverLocation) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("driver_id", thrift.STRING, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:driver_id: ", p), err) - } - if err := oprot.WriteString(string(p.DriverID)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.driver_id (1) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:driver_id: ", p), err) - } - return err -} - -func (p *DriverLocation) writeField2(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("location", thrift.STRING, 2); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:location: ", p), err) - } - if err := oprot.WriteString(string(p.Location)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.location (2) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 2:location: ", p), err) - } - return err -} - -func (p *DriverLocation) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("DriverLocation(%+v)", *p) -} diff --git a/example/hotrod/hotrod/services/frontend/best_eta.go b/example/hotrod/hotrod/services/frontend/best_eta.go deleted file mode 100644 index a1094fc7..00000000 --- a/example/hotrod/hotrod/services/frontend/best_eta.go +++ /dev/null @@ -1,135 +0,0 @@ -// 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 frontend - -import ( - "context" - "errors" - "math" - "sync" - "time" - - "github.com/opentracing/opentracing-go" - "go.uber.org/zap" - - "github.com/rnburn/hotrod-docker/pkg/log" - "github.com/rnburn/hotrod-docker/pkg/pool" - "github.com/rnburn/hotrod-docker/services/config" - "github.com/rnburn/hotrod-docker/services/customer" - "github.com/rnburn/hotrod-docker/services/driver" - "github.com/rnburn/hotrod-docker/services/route" -) - -type bestETA struct { - customer customer.Interface - driver driver.Interface - route route.Interface - pool *pool.Pool - logger log.Factory -} - -// Response contains ETA for a trip. -type Response struct { - Driver string - ETA time.Duration -} - -func newBestETA(tracer opentracing.Tracer, logger log.Factory) *bestETA { - return &bestETA{ - customer: customer.NewClient( - tracer, - logger.With(zap.String("component", "customer_client")), - ), - driver: driver.NewClient( - tracer, - logger.With(zap.String("component", "driver_client")), - ), - route: route.NewClient( - tracer, - logger.With(zap.String("component", "route_client")), - ), - pool: pool.New(config.RouteWorkerPoolSize), - logger: logger, - } -} - -func (eta *bestETA) Get(ctx context.Context, customerID string) (*Response, error) { - customer, err := eta.customer.Get(ctx, customerID) - if err != nil { - return nil, err - } - eta.logger.For(ctx).Info("Found customer", zap.Any("customer", customer)) - - if span := opentracing.SpanFromContext(ctx); span != nil { - span.SetBaggageItem("customer", customer.Name) - } - - drivers, err := eta.driver.FindNearest(ctx, customer.Location) - if err != nil { - return nil, err - } - eta.logger.For(ctx).Info("Found drivers", zap.Any("drivers", drivers)) - - results := eta.getRoutes(ctx, customer, drivers) - eta.logger.For(ctx).Info("Found routes", zap.Any("routes", results)) - - resp := &Response{ETA: math.MaxInt64} - for _, result := range results { - if result.err != nil { - return nil, err - } - if result.route.ETA < resp.ETA { - resp.ETA = result.route.ETA - resp.Driver = result.driver - } - } - if resp.Driver == "" { - return nil, errors.New("No routes found") - } - - eta.logger.For(ctx).Info("Dispatch successful", zap.String("driver", resp.Driver), zap.String("eta", resp.ETA.String())) - return resp, nil -} - -type routeResult struct { - driver string - route *route.Route - err error -} - -// getRoutes calls Route service for each (customer, driver) pair -func (eta *bestETA) getRoutes(ctx context.Context, customer *customer.Customer, drivers []driver.Driver) []routeResult { - results := make([]routeResult, 0, len(drivers)) - wg := sync.WaitGroup{} - routesLock := sync.Mutex{} - for _, dd := range drivers { - wg.Add(1) - driver := dd // capture loop var - // Use worker pool to (potentially) execute requests in parallel - eta.pool.Execute(func() { - route, err := eta.route.FindRoute(ctx, driver.Location, customer.Location) - routesLock.Lock() - results = append(results, routeResult{ - driver: driver.DriverID, - route: route, - err: err, - }) - routesLock.Unlock() - wg.Done() - }) - } - wg.Wait() - return results -} diff --git a/example/hotrod/hotrod/services/frontend/server.go b/example/hotrod/hotrod/services/frontend/server.go deleted file mode 100644 index 2a338e23..00000000 --- a/example/hotrod/hotrod/services/frontend/server.go +++ /dev/null @@ -1,95 +0,0 @@ -// 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 frontend - -import ( - "encoding/json" - "net/http" - - "github.com/opentracing/opentracing-go" - "go.uber.org/zap" - - "github.com/rnburn/hotrod-docker/pkg/httperr" - "github.com/rnburn/hotrod-docker/pkg/log" - "github.com/rnburn/hotrod-docker/pkg/tracing" -) - -// Server implements jaeger-demo-frontend service -type Server struct { - hostPort string - tracer opentracing.Tracer - logger log.Factory - bestETA *bestETA -} - -// NewServer creates a new frontend.Server -func NewServer(hostPort string, tracer opentracing.Tracer, logger log.Factory) *Server { - return &Server{ - hostPort: hostPort, - tracer: tracer, - logger: logger, - bestETA: newBestETA(tracer, logger), - } -} - -// Run starts the frontend server -func (s *Server) Run() error { - mux := s.createServeMux() - s.logger.Bg().Info("Starting", zap.String("address", "http://"+s.hostPort)) - return http.ListenAndServe(s.hostPort, mux) -} - -func (s *Server) createServeMux() http.Handler { - mux := tracing.NewServeMux(s.tracer) - mux.Handle("/", http.HandlerFunc(s.home)) - mux.Handle("/dispatch", http.HandlerFunc(s.dispatch)) - return mux -} - -func (s *Server) home(w http.ResponseWriter, r *http.Request) { - s.logger.For(r.Context()).Info("HTTP", zap.String("method", r.Method), zap.Stringer("url", r.URL)) - http.ServeFile(w, r, "services/frontend/web_assets/index.html") -} - -func (s *Server) dispatch(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - s.logger.For(ctx).Info("HTTP request received", zap.String("method", r.Method), zap.Stringer("url", r.URL)) - if err := r.ParseForm(); httperr.HandleError(w, err, http.StatusBadRequest) { - s.logger.For(ctx).Error("bad request", zap.Error(err)) - return - } - - customerID := r.Form.Get("customer") - if customerID == "" { - http.Error(w, "Missing required 'customer' parameter", http.StatusBadRequest) - return - } - - // TODO distinguish between user errors (such as invalid customer ID) and server failures - response, err := s.bestETA.Get(ctx, customerID) - if httperr.HandleError(w, err, http.StatusInternalServerError) { - s.logger.For(ctx).Error("request failed", zap.Error(err)) - return - } - - data, err := json.Marshal(response) - if httperr.HandleError(w, err, http.StatusInternalServerError) { - s.logger.For(ctx).Error("cannot marshal response", zap.Error(err)) - return - } - - w.Header().Set("Content-Type", "application/json") - w.Write(data) -} diff --git a/example/hotrod/hotrod/services/frontend/web_assets/index.html b/example/hotrod/hotrod/services/frontend/web_assets/index.html deleted file mode 100644 index 5db64bad..00000000 --- a/example/hotrod/hotrod/services/frontend/web_assets/index.html +++ /dev/null @@ -1,90 +0,0 @@ - - - - - HotROD - Rides On Demand - - - - - - - - -
-
-
-

Hot R.O.D.

-

Rides On Demand

-
-
- Rachel's Floral Designs -
-
- Trom Chocolatier -
-
- Japanese Deserts -
-
- Amazing Coffee Roasters -
-
-
Click on customer name above to order a car.
-
-
-
- - - - - diff --git a/example/hotrod/hotrod/services/route/client.go b/example/hotrod/hotrod/services/route/client.go deleted file mode 100644 index 3de40a47..00000000 --- a/example/hotrod/hotrod/services/route/client.go +++ /dev/null @@ -1,64 +0,0 @@ -// 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 route - -import ( - "context" - "net/http" - "net/url" - - "github.com/opentracing-contrib/go-stdlib/nethttp" - "github.com/opentracing/opentracing-go" - "go.uber.org/zap" - - "github.com/rnburn/hotrod-docker/pkg/log" - "github.com/rnburn/hotrod-docker/pkg/tracing" -) - -// Client is a remote client that implements route.Interface -type Client struct { - tracer opentracing.Tracer - logger log.Factory - client *tracing.HTTPClient -} - -// NewClient creates a new route.Client -func NewClient(tracer opentracing.Tracer, logger log.Factory) *Client { - return &Client{ - tracer: tracer, - logger: logger, - client: &tracing.HTTPClient{ - Client: &http.Client{Transport: &nethttp.Transport{}}, - Tracer: tracer, - }, - } -} - -// FindRoute implements route.Interface#FindRoute as an RPC -func (c *Client) FindRoute(ctx context.Context, pickup, dropoff string) (*Route, error) { - c.logger.For(ctx).Info("Finding route", zap.String("pickup", pickup), zap.String("dropoff", dropoff)) - - v := url.Values{} - v.Set("pickup", pickup) - v.Set("dropoff", dropoff) - url := "http://127.0.0.1/route?" + v.Encode() - - var route Route - if err := c.client.GetJSON(ctx, "/route", url, &route); err != nil { - c.logger.For(ctx).Error("Error getting route", zap.Error(err)) - return nil, err - } - return &route, nil -} diff --git a/example/hotrod/hotrod/services/route/interface.go b/example/hotrod/hotrod/services/route/interface.go deleted file mode 100644 index f65addce..00000000 --- a/example/hotrod/hotrod/services/route/interface.go +++ /dev/null @@ -1,30 +0,0 @@ -// 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 route - -import "context" -import "time" - -// Route describes a route between Pickup and Dropoff locations and expected time to arrival. -type Route struct { - Pickup string - Dropoff string - ETA time.Duration -} - -// Interface exposed by the Driver service. -type Interface interface { - FindRoute(ctx context.Context, pickup, dropoff string) (*Route, error) -} diff --git a/example/hotrod/hotrod/services/route/server.go b/example/hotrod/hotrod/services/route/server.go deleted file mode 100644 index e4be024a..00000000 --- a/example/hotrod/hotrod/services/route/server.go +++ /dev/null @@ -1,113 +0,0 @@ -// 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 route - -import ( - "context" - "encoding/json" - "math" - "math/rand" - "net/http" - "time" - - "github.com/opentracing/opentracing-go" - "go.uber.org/zap" - - "github.com/rnburn/hotrod-docker/pkg/delay" - "github.com/rnburn/hotrod-docker/pkg/httperr" - "github.com/rnburn/hotrod-docker/pkg/httpexpvar" - "github.com/rnburn/hotrod-docker/pkg/log" - "github.com/rnburn/hotrod-docker/pkg/tracing" - "github.com/rnburn/hotrod-docker/services/config" -) - -// Server implements Route service -type Server struct { - hostPort string - tracer opentracing.Tracer - logger log.Factory -} - -// NewServer creates a new route.Server -func NewServer(hostPort string, tracer opentracing.Tracer, logger log.Factory) *Server { - return &Server{ - hostPort: hostPort, - tracer: tracer, - logger: logger, - } -} - -// Run starts the Route server -func (s *Server) Run() error { - mux := s.createServeMux() - s.logger.Bg().Info("Starting", zap.String("address", "http://"+s.hostPort)) - return http.ListenAndServe(s.hostPort, mux) -} - -func (s *Server) createServeMux() http.Handler { - mux := tracing.NewServeMux(s.tracer) - mux.Handle("/route", http.HandlerFunc(s.route)) - mux.Handle("/debug/vars", http.HandlerFunc(httpexpvar.Handler)) - return mux -} - -func (s *Server) route(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - s.logger.For(ctx).Info("HTTP request received", zap.String("method", r.Method), zap.Stringer("url", r.URL)) - if err := r.ParseForm(); httperr.HandleError(w, err, http.StatusBadRequest) { - s.logger.For(ctx).Error("bad request", zap.Error(err)) - return - } - - pickup := r.Form.Get("pickup") - if pickup == "" { - http.Error(w, "Missing required 'pickup' parameter", http.StatusBadRequest) - return - } - - dropoff := r.Form.Get("dropoff") - if dropoff == "" { - http.Error(w, "Missing required 'dropoff' parameter", http.StatusBadRequest) - return - } - - response := computeRoute(ctx, pickup, dropoff) - - data, err := json.Marshal(response) - if httperr.HandleError(w, err, http.StatusInternalServerError) { - s.logger.For(ctx).Error("cannot marshal response", zap.Error(err)) - return - } - - w.Header().Set("Content-Type", "application/json") - w.Write(data) -} - -func computeRoute(ctx context.Context, pickup, dropoff string) *Route { - start := time.Now() - defer func() { - updateCalcStats(ctx, time.Since(start)) - }() - - // Simulate expensive calculation - delay.Sleep(config.RouteCalcDelay, config.RouteCalcDelayStdDev) - - eta := math.Max(2, rand.NormFloat64()*3+5) - return &Route{ - Pickup: pickup, - Dropoff: dropoff, - ETA: time.Duration(eta) * time.Minute, - } -} diff --git a/example/hotrod/hotrod/services/route/stats.go b/example/hotrod/hotrod/services/route/stats.go deleted file mode 100644 index 22a2037d..00000000 --- a/example/hotrod/hotrod/services/route/stats.go +++ /dev/null @@ -1,48 +0,0 @@ -// 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 route - -import ( - "context" - "expvar" - "time" - - "github.com/opentracing/opentracing-go" -) - -var routeCalcByCustomer = expvar.NewMap("route.calc.by.customer.sec") -var routeCalcBySession = expvar.NewMap("route.calc.by.session.sec") - -var stats = []struct { - expvar *expvar.Map - baggage string -}{ - {routeCalcByCustomer, "customer"}, - {routeCalcBySession, "session"}, -} - -func updateCalcStats(ctx context.Context, delay time.Duration) { - span := opentracing.SpanFromContext(ctx) - if span == nil { - return - } - delaySec := float64(delay/time.Millisecond) / 1000.0 - for _, s := range stats { - key := span.BaggageItem(s.baggage) - if key != "" { - s.expvar.AddFloat(key, delaySec) - } - } -} diff --git a/example/hotrod/nginx.conf b/example/hotrod/nginx.conf deleted file mode 100644 index 1554687c..00000000 --- a/example/hotrod/nginx.conf +++ /dev/null @@ -1,41 +0,0 @@ -load_module modules/ngx_http_opentracing_module.so; -load_module modules/ngx_http_jaeger_module.so; - -events {} - -http { - jaeger_reporter_local_agent_host_port $JAEGER_AGENT_HOST:$JAEGER_AGENT_PORT; - jaeger_service_name nginx; - jaeger_sampler_type const; - jaeger_sampler_param 1; - opentracing on; - - upstream frontend { - server localhost:8080; - } - - upstream customer { - server localhost:8081; - } - - upstream route { - server localhost:8083; - } - - server { - listen 80; - server_name localhost; - - location / { - proxy_pass http://frontend; - } - - location /customer { - proxy_pass http://customer; - } - - location /route { - proxy_pass http://route; - } - } -} diff --git a/example/hotrod/start.sh b/example/hotrod/start.sh deleted file mode 100755 index 301516a1..00000000 --- a/example/hotrod/start.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -export JAEGER_AGENT_HOST=$JAEGER_PORT_6831_UDP_ADDR -export JAEGER_AGENT_PORT=$JAEGER_PORT_6831_UDP_PORT -/app/hotrod all & -envsubst '\$JAEGER_AGENT_HOST \$JAEGER_AGENT_PORT' < /app/nginx.conf > /etc/nginx/nginx.conf -nginx -while /bin/true; do - sleep 50 -done diff --git a/example/trivial/README.md b/example/trivial/README.md deleted file mode 100644 index 13e4fd27..00000000 --- a/example/trivial/README.md +++ /dev/null @@ -1,19 +0,0 @@ -A minimal OpenTracing example demonstrating usage of the nginx-opentracing -docker image with zipkin. It features Nginx as a reverse-proxy in front a Go -server. Use these commands to run: -```bash -docker build -t nginx-example-trivial . -docker run -d -p 9411:9411 --name zipkin openzipkin/zipkin -docker run -d -p 8080:80 --link zipkin:zipkin nginx-example-trivial -curl localhost:8080 -``` -Visit http://localhost:9411 to view the traces in Zipkin. - -Additionaly, the example can be made to work with Jaeger. Run: -```bash -docker build -t nginx-example-trivial . -docker run -d -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 -p5775:5775/udp -p6831:6831/udp -p6832:6832/udp -p5778:5778 -p16686:16686 -p14268:14268 -p9411:9411 --name jaeger jaegertracing/all-in-one:latest -docker run -d -p 8080:80 --link jaeger:zipkin nginx-example-trivial -curl localhost:8080 -``` -And you can view the traces at http://localhost:16686. diff --git a/example/trivial/jaeger/Dockerfile b/example/trivial/jaeger/Dockerfile new file mode 100644 index 00000000..8d7ef8b9 --- /dev/null +++ b/example/trivial/jaeger/Dockerfile @@ -0,0 +1,21 @@ +FROM ubuntu:18.04 + +WORKDIR /app +ADD . /app +ENV GOPATH="/app/go" +RUN set -x \ + && apt-get update \ + && apt-get install --no-install-recommends --no-install-suggests -y \ + golang \ + ca-certificates \ + git \ + curl \ + && export PATH=$PATH:$GOPATH/bin \ + && curl https://glide.sh/get | sh \ + && cd go/src/hello-backend \ + && glide up \ + && glide install \ + && go build -o /app/server server.go + +EXPOSE 9001 +CMD ["/app/server", "-collector_host", "jaeger", "-collector_port", "6831"] diff --git a/example/trivial/jaeger/README.md b/example/trivial/jaeger/README.md new file mode 100644 index 00000000..c8029c38 --- /dev/null +++ b/example/trivial/jaeger/README.md @@ -0,0 +1,8 @@ +A minimal OpenTracing example demonstrating usage of the nginx-opentracing +docker image with Jaeger. It features Nginx as a reverse-proxy in front a Go +server. Use these commands to run: +```bash +docker-compose up +curl localhost:8080 +``` +Visit http://localhost:16686 to view the traces in Jaeger. diff --git a/example/trivial/jaeger/docker-compose.yaml b/example/trivial/jaeger/docker-compose.yaml new file mode 100644 index 00000000..7840c54c --- /dev/null +++ b/example/trivial/jaeger/docker-compose.yaml @@ -0,0 +1,51 @@ +version: '2' +services: + + nginx: + image: opentracing/nginx-opentracing + networks: + trivial_example: + aliases: + - nginx + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf + - ./jaeger-config.json:/etc/jaeger-config.json + expose: + - "8080" + ports: + - "8080:8080" + + app-service: + build: + context: . + dockerfile: ./Dockerfile + networks: + trivial_example: + aliases: + - app-service + expose: + - "9001" + ports: + - "9001:9001" + command: + - /app/server + - -collector_host + - jaeger + + jaeger: + image: jaegertracing/all-in-one + environment: + - COLLECTOR_ZIPKIN_HTTP_PORT=9411 + networks: + trivial_example: + aliases: + - jaeger + expose: + - "9411" + - "16686" + ports: + - "9411:9411" + - "16686:16686" + +networks: + trivial_example: {} diff --git a/example/trivial/jaeger/go/src/hello-backend/glide.yaml b/example/trivial/jaeger/go/src/hello-backend/glide.yaml new file mode 100644 index 00000000..df73e09b --- /dev/null +++ b/example/trivial/jaeger/go/src/hello-backend/glide.yaml @@ -0,0 +1,8 @@ +package: . +import: +- package: github.com/opentracing/opentracing-go + version: ~1.0.2 +- package: github.com/uber/jaeger-client-go + version: ~2.14.0 + subpackages: + - config diff --git a/example/trivial/jaeger/go/src/hello-backend/server.go b/example/trivial/jaeger/go/src/hello-backend/server.go new file mode 100644 index 00000000..f5b3c6c5 --- /dev/null +++ b/example/trivial/jaeger/go/src/hello-backend/server.go @@ -0,0 +1,66 @@ +package main + +import ( + "flag" + "fmt" + "time" + "log" + opentracing "github.com/opentracing/opentracing-go" + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/config" + "net/http" +) + +const ( + serviceName = "hello-server" + hostPort = "0.0.0.0:0" + debug = false + sameSpan = false + traceID128Bit = true +) + +var collectorHost = flag.String("collector_host", "localhost", "Host for Zipkin Collector") +var collectorPort = flag.String("collector_port", "6831", "Port for Zipkin Collector") + +func handler(w http.ResponseWriter, r *http.Request) { + for k, v := range r.Header { + fmt.Fprintf(w, "Header field %q, Value %q\n", k, v) + } + wireContext, _ := opentracing.GlobalTracer().Extract( + opentracing.HTTPHeaders, + opentracing.HTTPHeadersCarrier(r.Header)) + span := opentracing.StartSpan( + "/", + opentracing.ChildOf(wireContext)) + defer span.Finish() + fmt.Fprintf(w, "Hello, World!") +} + +func main() { + flag.Parse() + cfg := config.Configuration{ + Sampler: &config.SamplerConfig{ + Type: "const", + Param: 1, + }, + Reporter: &config.ReporterConfig{ + LocalAgentHostPort: *collectorHost + ":" + *collectorPort, + LogSpans: true, + BufferFlushInterval: 1 * time.Second, + }, + } + closer, err := cfg.InitGlobalTracer( + "backend", + config.Logger(jaeger.StdLogger), + ) + + if err != nil { + log.Printf("Could not initialize jaeger tracer: %s", err.Error()) + return + } + + defer closer.Close() + + http.HandleFunc("/", handler) + http.ListenAndServe(":9001", nil) +} diff --git a/example/trivial/jaeger/jaeger-config.json b/example/trivial/jaeger/jaeger-config.json new file mode 100644 index 00000000..f4ec56fa --- /dev/null +++ b/example/trivial/jaeger/jaeger-config.json @@ -0,0 +1,12 @@ +{ + "service_name": "nginx", + "diabled": false, + "reporter": { + "logSpans": true, + "localAgentHostPort": "jaeger:6831" + }, + "sampler": { + "type": "const", + "param": "1" + } +} diff --git a/example/trivial/nginx.conf b/example/trivial/jaeger/nginx.conf similarity index 53% rename from example/trivial/nginx.conf rename to example/trivial/jaeger/nginx.conf index d7133a54..059e8eeb 100644 --- a/example/trivial/nginx.conf +++ b/example/trivial/jaeger/nginx.conf @@ -1,25 +1,24 @@ load_module modules/ngx_http_opentracing_module.so; -load_module modules/ngx_http_zipkin_module.so; events {} http { - zipkin_collector_host $ZIPKIN_PORT_9411_TCP_ADDR; - zipkin_collector_port $ZIPKIN_PORT_9411_TCP_PORT; - zipkin_service_name nginx; opentracing on; + opentracing_load_tracer /usr/local/lib/libjaegertracing_plugin.so /etc/jaeger-config.json; upstream backend { - server localhost:9001; + server app-service:9001; } server { - listen 80; + error_log /var/log/nginx/debug.log debug; + listen 8080; server_name localhost; location = / { - proxy_pass http://backend; opentracing_trace_locations off; + proxy_pass http://backend; + opentracing_propagate_context; } } } diff --git a/example/trivial/start.sh b/example/trivial/start.sh deleted file mode 100755 index 5ec715ef..00000000 --- a/example/trivial/start.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -./server -collector_host $ZIPKIN_PORT_9411_TCP_ADDR -collector_port $ZIPKIN_PORT_9411_TCP_PORT & -envsubst '\$ZIPKIN_PORT_9411_TCP_ADDR \$ZIPKIN_PORT_9411_TCP_PORT' < /app/nginx.conf > /etc/nginx/nginx.conf -nginx -while /bin/true; do - sleep 50 -done diff --git a/example/trivial/Dockerfile b/example/trivial/zipkin/Dockerfile similarity index 72% rename from example/trivial/Dockerfile rename to example/trivial/zipkin/Dockerfile index 4fa71c86..0aa55322 100644 --- a/example/trivial/Dockerfile +++ b/example/trivial/zipkin/Dockerfile @@ -1,4 +1,4 @@ -FROM rnburn/nginx-opentracing +FROM ubuntu:17.10 WORKDIR /app ADD . /app @@ -7,11 +7,12 @@ RUN set -x \ && apt-get update \ && apt-get install --no-install-recommends --no-install-suggests -y \ golang \ + ca-certificates \ git \ && mkdir /go \ && go get github.com/opentracing/opentracing-go \ && go get github.com/openzipkin/zipkin-go-opentracing \ && go build -o /app/server go/server.go -EXPOSE 80 -CMD ["/app/start.sh"] +EXPOSE 9001 +CMD ["/app/server", "-collector_host", "zipkin", "-collector_port", "9411"] diff --git a/example/trivial/zipkin/README.md b/example/trivial/zipkin/README.md new file mode 100644 index 00000000..f1da1ac4 --- /dev/null +++ b/example/trivial/zipkin/README.md @@ -0,0 +1,8 @@ +A minimal OpenTracing example demonstrating usage of the nginx-opentracing +docker image with Zipkin. It features Nginx as a reverse-proxy in front a Go +server. Use these commands to run: +```bash +docker-compose up +curl localhost:8080 +``` +Visit http://localhost:9411 to view the traces in Zipkin. diff --git a/example/trivial/zipkin/docker-compose.yaml b/example/trivial/zipkin/docker-compose.yaml new file mode 100644 index 00000000..11590a4d --- /dev/null +++ b/example/trivial/zipkin/docker-compose.yaml @@ -0,0 +1,47 @@ +version: '2' +services: + + nginx: + image: opentracing/nginx-opentracing + networks: + trivial_example: + aliases: + - nginx + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf + - ./zipkin-config.json:/etc/zipkin-config.json + expose: + - "8080" + ports: + - "8080:8080" + + app-service: + build: + context: . + dockerfile: ./Dockerfile + networks: + trivial_example: + aliases: + - app-service + expose: + - "9001" + ports: + - "9001:9001" + command: + - /app/server + - -collector_host + - zipkin + + zipkin: + image: openzipkin/zipkin + networks: + trivial_example: + aliases: + - zipkin + expose: + - "9411" + ports: + - "9411:9411" + +networks: + trivial_example: {} diff --git a/example/trivial/go/server.go b/example/trivial/zipkin/go/server.go similarity index 94% rename from example/trivial/go/server.go rename to example/trivial/zipkin/go/server.go index 26c8ca63..7bdef0d7 100644 --- a/example/trivial/go/server.go +++ b/example/trivial/zipkin/go/server.go @@ -21,6 +21,9 @@ var collectorHost = flag.String("collector_host", "localhost", "Host for Zipkin var collectorPort = flag.String("collector_port", "9411", "Port for Zipkin Collector") func handler(w http.ResponseWriter, r *http.Request) { + for k, v := range r.Header { + fmt.Fprintf(w, "Header field %q, Value %q\n", k, v) + } wireContext, _ := opentracing.GlobalTracer().Extract( opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header)) diff --git a/example/trivial/zipkin/nginx.conf b/example/trivial/zipkin/nginx.conf new file mode 100644 index 00000000..11e377d2 --- /dev/null +++ b/example/trivial/zipkin/nginx.conf @@ -0,0 +1,24 @@ +load_module modules/ngx_http_opentracing_module.so; + +events {} + +http { + opentracing on; + + opentracing_load_tracer /usr/local/lib/libzipkin_opentracing_plugin.so /etc/zipkin-config.json; + upstream backend { + server app-service:9001; + } + + server { + error_log /var/log/nginx/debug.log debug; + listen 8080; + server_name localhost; + + location = / { + opentracing_trace_locations off; + proxy_pass http://backend; + opentracing_propagate_context; + } + } +} diff --git a/example/trivial/zipkin/zipkin-config.json b/example/trivial/zipkin/zipkin-config.json new file mode 100644 index 00000000..ffe14cc4 --- /dev/null +++ b/example/trivial/zipkin/zipkin-config.json @@ -0,0 +1,4 @@ +{ + "service_name": "nginx", + "collector_host": "zipkin" +} diff --git a/example/zoo/Dockerfile b/example/zoo/Dockerfile index 0eae320b..4508bf11 100644 --- a/example/zoo/Dockerfile +++ b/example/zoo/Dockerfile @@ -1,4 +1,4 @@ -FROM rnburn/nginx-opentracing +FROM ubuntu:17.10 WORKDIR /app ADD . /app @@ -8,10 +8,12 @@ RUN set -x \ && apt-get install --no-install-recommends --no-install-suggests -y \ python \ curl \ + ca-certificates \ gnupg2 \ + build-essential \ && mkdir /node-latest-install \ && cd /node-latest-install \ - && curl -sL https://deb.nodesource.com/setup_7.x -o nodesource_setup.sh \ + && curl -sL https://deb.nodesource.com/setup_8.x -o nodesource_setup.sh \ && bash nodesource_setup.sh \ && apt-get install --no-install-recommends --no-install-suggests -y \ nodejs \ @@ -25,4 +27,4 @@ RUN set -x \ EXPOSE 80 -CMD ["/app/start.sh"] +CMD ["/app/start-backend.sh"] diff --git a/example/zoo/docker-compose.yaml b/example/zoo/docker-compose.yaml new file mode 100644 index 00000000..7914b10f --- /dev/null +++ b/example/zoo/docker-compose.yaml @@ -0,0 +1,53 @@ +version: '2' +services: + + nginx: + image: opentracing/nginx-opentracing + networks: + zoonet: + aliases: + - nginx + volumes: + - ./start-nginx.sh:/start-nginx.sh + - ./nginx.conf:/etc/nginx/nginx.conf + - ./lightstep-config.json.in:/tmp/lightstep-config.json.in + - ./www:/app/www + - image-volume:/app/data/images + - tmp-volume:/tmp + environment: + - LIGHTSTEP_ACCESS_TOKEN=${LIGHTSTEP_ACCESS_TOKEN} + expose: + - "8080" + ports: + - "8080:8080" + entrypoint: ./start-nginx.sh + + backend: + build: + context: . + dockerfile: ./Dockerfile + environment: + - LIGHTSTEP_ACCESS_TOKEN=${LIGHTSTEP_ACCESS_TOKEN} + networks: + zoonet: + aliases: + - backend + volumes: + - image-volume:/app/data/images + - tmp-volume:/tmp + expose: + - "3001" + - "3002" + - "3003" + ports: + - "3001:3001" + - "3002:3002" + - "3003:3003" + entrypoint: ./start-backend.sh + +volumes: + image-volume: + tmp-volume: + +networks: + zoonet: {} diff --git a/example/zoo/lightstep-config.json.in b/example/zoo/lightstep-config.json.in new file mode 100644 index 00000000..e2af759e --- /dev/null +++ b/example/zoo/lightstep-config.json.in @@ -0,0 +1,4 @@ +{ + "access_token": "$LIGHTSTEP_ACCESS_TOKEN", + "component_name": "nginx" +} diff --git a/example/zoo/nginx.conf b/example/zoo/nginx.conf index 4e347afd..d3acd95b 100644 --- a/example/zoo/nginx.conf +++ b/example/zoo/nginx.conf @@ -1,27 +1,27 @@ load_module modules/ngx_http_opentracing_module.so; -load_module modules/ngx_http_lightstep_module.so; events {} http { - lightstep_access_token $LIGHTSTEP_ACCESS_TOKEN; - lightstep_component_name zoo; - proxy_pass_request_headers on; opentracing on; + opentracing_load_tracer /usr/local/lib/liblightstep_tracer_plugin.so /etc/lightstep-config.json; + upstream backend { - server localhost:3001; - server localhost:3002; - server localhost:3003; + server backend:3001; + server backend:3002; + server backend:3003; } server { - listen 80; + error_log /var/log/nginx/debug.log debug; + listen 8080; server_name localhost; location = / { proxy_pass http://backend; opentracing_tag nginx.upstream_addr $upstream_addr; + opentracing_propagate_context; } location = /animal { @@ -29,6 +29,7 @@ http { proxy_pass http://backend; opentracing_tag nginx.upstream_addr $upstream_addr; + opentracing_propagate_context; } location = /upload/animal { @@ -47,13 +48,13 @@ http { client_body_buffer_size 128K; client_max_body_size 1000M; - proxy_pass_request_headers on; proxy_set_header admit-profile-pic $request_body_file; proxy_set_body off; proxy_redirect off; proxy_pass http://backend; proxy_intercept_errors on; + opentracing_propagate_context; error_page 301 302 303 =200 /; } diff --git a/example/zoo/start.sh b/example/zoo/start-backend.sh similarity index 73% rename from example/zoo/start.sh rename to example/zoo/start-backend.sh index 8a986752..a3c1c4d8 100755 --- a/example/zoo/start.sh +++ b/example/zoo/start-backend.sh @@ -2,8 +2,6 @@ DATA_ROOT=/app/data IMG_ROOT=$DATA_ROOT/images -mkdir -p $IMG_ROOT - node node/setup.js --data_root $DATA_ROOT for i in {1..3}; do @@ -11,8 +9,6 @@ for i in {1..3}; do node node/server.js --port $port --data_root $DATA_ROOT --access_token $LIGHTSTEP_ACCESS_TOKEN& done -envsubst '\$LIGHTSTEP_ACCESS_TOKEN' < /app/nginx.conf > /etc/nginx/nginx.conf -nginx while /bin/true; do sleep 50 done diff --git a/example/zoo/start-nginx.sh b/example/zoo/start-nginx.sh new file mode 100755 index 00000000..ccb97ced --- /dev/null +++ b/example/zoo/start-nginx.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +envsubst '\$LIGHTSTEP_ACCESS_TOKEN' < /tmp/lightstep-config.json.in > /etc/lightstep-config.json +nginx-debug -g "daemon off;" diff --git a/jaeger/config b/jaeger/config deleted file mode 100644 index f447698e..00000000 --- a/jaeger/config +++ /dev/null @@ -1,17 +0,0 @@ -ngx_addon_name=ngx_http_jaeger_module -ngx_module_type=HTTP -ngx_module_name=$ngx_addon_name -ngx_module_incs= -ngx_module_deps= -ngx_module_srcs=" \ - $ngx_addon_dir/src/ngx_http_jaeger_module.cpp \ -" -ngx_module_libs=" \ - -lthrift \ - -lopentracing \ - -ljaegertracing \ -" - -. auto/module - -JAEGER_NGX_SRCS="$ngx_module_srcs" diff --git a/jaeger/config.make b/jaeger/config.make deleted file mode 100644 index 38909ab1..00000000 --- a/jaeger/config.make +++ /dev/null @@ -1,7 +0,0 @@ -# Since nginx build system doesn't normally do C++, there is no CXXFLAGS for us -# to touch, and compilers are understandably unhappy with --std=c++11 on C -# files. Hence, we hack the makefile to add it for just our sources. -for src_file in $JAEGER_NGX_SRCS; do - obj_file="$NGX_OBJS/addon/src/`basename $src_file .cpp`.o" - echo "$obj_file : CFLAGS += --std=c++11" >> $NGX_MAKEFILE -done diff --git a/jaeger/doc/Directives.md b/jaeger/doc/Directives.md deleted file mode 100644 index 4ec97a88..00000000 --- a/jaeger/doc/Directives.md +++ /dev/null @@ -1,139 +0,0 @@ -### `jaeger_service_name` - -- **syntax** `jaeger_service_name ` -- **context**: `http` - -Specifies the service name to use for traces. - -### `jaeger_disabled` - -- **syntax** `jaeger_disabled on|off` -- **default**: `off` -- **context**: `http` - -Specifies whether or not to disable tracing. - -### `jaeger_sampler_type` - -- **syntax** `jaeger_sampler_type ` -- **default**: `remote` -- **context**: `http` - -Specifies the sampler to be used when sampling traces. The available samplers -are: `const`, `probabilistic`, `ratelimiting`, `remote`. `const` sampling always -sample or always ignore. `probabilistic` sampling will use a random number to -check whether or not to sample the trace. `ratelimiting` sampling caps the -number of traces at a given number per second. `remote` sampling allows the -Jaeger backend to customize the sampling strategy based on throughput. The -recommended default is `remote`. - -### `jaeger_sampler_param` - -- **syntax** `jaeger_sampler_param ` -- **default**: `0.001` -- **context**: `http` - -Specifies the argument to be passed to the sampler constructor. Must be a -number. For `const` this should be `0` to never sample and `1` to always sample. -For `probabilistic`, this should be a number representing the percent to sample -(i.e. 0.001 = 0.1%). For `ratelimiting` this should be the maximum number of -traces to sample per second. For `remote` this represents the upfront -probabilistic sampling rate to use for traces before determining throughput on -the backend (i.e. 0.001 = 0.1%, as above). - -### `jaeger_sampling_server_url` - -- **syntax** `jaeger_sampling_server_url ` -- **default**: `http://127.0.0.1:5778` -- **context**: `http` - -Specifies the URL to use to request sampling strategy from Jaeger agent. - -### `jaeger_sampler_max_operations` - -- **syntax** `jaeger_sampler_max_operations ` -- **default**: `2000` -- **context**: `http` - -Specifies the maximum number of operations that the sampler will keep track of. - -### `jaeger_sampling_refresh_interval_seconds` - -- **syntax** `jaeger_sampling_refresh_interval_seconds ` -- **default**: `60` -- **context**: `http` - -Specifies the interval the remote sampler should wait before polling the Jaeger -agent again for sampling strategy information. - -### `jaeger_reporter_queue_size` - -- **syntax** `jaeger_reporter_queue_size ` -- **default**: `100` -- **context**: `http` - -Specifies the maximum number of spans to maintain in memory awaiting submission -to Jaeger agent before dropping new spans. - -### `jaeger_reporter_buffer_flush_interval_seconds` - -- **syntax** `jaeger_reporter_buffer_flush_interval_seconds ` -- **default**: `10` -- **context**: `http` - -Specifies the maximum number of seconds to wait before forcing a flush of spans -to Jaeger agent, even if size does not warrant flush. - -### `jaeger_reporter_log_spans` - -- **syntax** `jaeger_reporter_log_spans ` -- **default**: `false` -- **context**: `http` - -Specifies whether or not the reporter should log the spans it reports. - -### `jaeger_reporter_local_agent_host_port` - -- **syntax** `jaeger_reporter_local_agent_host_port ` -- **default**: `127.0.0.1:6831` -- **context**: `http` - -Specifies the host and port of Jaeger agent where reporter should submit span -data. - -### `jaeger_debug_header` - -- **syntax** `jaeger_debug_header ` -- **default**: `jaeger-debug-id` -- **context**: `http` - -Specifies 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. - -### `jaeger_baggage_header` - -- **syntax** `jaeger_baggage_header ` -- **default**: `jaeger-baggage` -- **context**: `http` - -Specifies the name of the HTTP header that is used to submit baggage. -It differs from `jaeger_trace_baggage_header_prefix` in that it can be used only -in cases where a root span does not exist. - -### `jaeger_trace_context_header_name` - -- **syntax** `jaeger_trace_context_header_name ` -- **default**: `uber-trace-id` -- **context**: `http` - -Specifies the HTTP header name used to propagate tracing context. This must be -lowercase to avoid mismatches when decoding incoming headers. - -### `jaeger_trace_baggage_header_prefix` - -- **syntax** `jaeger_trace_baggage_header_prefix ` -- **default**: `uberctx-` -- **context**: `http` - -Specifies the prefix for HTTP headers used to propagate baggage. This must be -lowercase to avoid mismatches when decoding incoming headers. diff --git a/jaeger/src/ngx_http_jaeger_module.cpp b/jaeger/src/ngx_http_jaeger_module.cpp deleted file mode 100644 index 1bc885c6..00000000 --- a/jaeger/src/ngx_http_jaeger_module.cpp +++ /dev/null @@ -1,208 +0,0 @@ -extern "C" { - -#include -#include -#include -#include - -} // extern "C" - -#include - -extern ngx_module_t ngx_http_jaeger_module; - -namespace { - -struct Config { - ngx_str_t serviceName; - ngx_flag_t disabled = NGX_CONF_UNSET; - // samplers::Config - ngx_str_t samplerType; - ngx_str_t samplerParam; - ngx_str_t samplingServerURL; - ngx_uint_t samplerMaxOperations = NGX_CONF_UNSET_UINT; - ngx_uint_t samplingRefreshIntervalSeconds = NGX_CONF_UNSET_UINT; - // reporters::Config - ngx_uint_t reporterQueueSize = NGX_CONF_UNSET_UINT; - ngx_uint_t reporterBufferFlushIntervalSeconds = NGX_CONF_UNSET_UINT; - ngx_flag_t reporterLogSpans = NGX_CONF_UNSET; - ngx_str_t reporterLocalAgentHostPort; - // propagation::HeadersConfig - ngx_str_t jaegerDebugHeader; - ngx_str_t jaegerBaggageHeader; - ngx_str_t traceContextHeaderName; - ngx_str_t traceBaggageHeaderPrefix; -}; - -inline std::string makeStr(const ngx_str_t& str) { - return std::string(str.data, str.data + str.len); -} - -inline double makeDouble(const ngx_str_t& str) { - std::istringstream iss(makeStr(str)); - double result = 0; - if (iss >> result) { - return result; - } - return 0; -} - -ngx_int_t moduleInit(ngx_conf_t* cf) { - auto* rawConfig = static_cast( - ngx_http_conf_get_module_main_conf(cf, ngx_http_jaeger_module)); - if (rawConfig->serviceName.len <= 0) { - ngx_log_error(NGX_LOG_ERR, cf->log, 0, - "`jaeger_service_name` must be specified"); - return NGX_ERROR; - } - return NGX_OK; -} - -ngx_int_t initWorker(ngx_cycle_t* cycle) { - auto rawConfig = static_cast( - ngx_http_cycle_get_module_main_conf(cycle, ngx_http_jaeger_module)); - try { - // Apply defaults. - if (rawConfig->disabled == NGX_CONF_UNSET) rawConfig->disabled = false; - // samplers::Config - if (!rawConfig->samplerType.data) - rawConfig->samplerType = ngx_string("remote"); - if (!rawConfig->samplerParam.data) - rawConfig->samplerParam = ngx_string("0.001"); - if (!rawConfig->samplingServerURL.data) - rawConfig->samplingServerURL = ngx_string("http://127.0.0.1:5778"); - if (rawConfig->samplerMaxOperations == NGX_CONF_UNSET_UINT) - rawConfig->samplerMaxOperations = 2000; - if (rawConfig->samplingRefreshIntervalSeconds == NGX_CONF_UNSET_UINT) - rawConfig->samplingRefreshIntervalSeconds = 60; - // reporters::Config - if (rawConfig->reporterQueueSize == NGX_CONF_UNSET_UINT) - rawConfig->reporterQueueSize = 100; - if (rawConfig->reporterBufferFlushIntervalSeconds == NGX_CONF_UNSET_UINT) - rawConfig->reporterBufferFlushIntervalSeconds = 10; - if (rawConfig->reporterLogSpans == NGX_CONF_UNSET) - rawConfig->reporterLogSpans = false; - if (!rawConfig->reporterLocalAgentHostPort.data) - rawConfig->reporterLocalAgentHostPort = ngx_string("127.0.0.1:6831"); - // propagation::HeadersConfig - if (!rawConfig->jaegerDebugHeader.data) - rawConfig->jaegerDebugHeader = ngx_string("jaeger-debug-id"); - if (!rawConfig->jaegerBaggageHeader.data) - rawConfig->jaegerBaggageHeader = ngx_string("jaeger-baggage"); - if (!rawConfig->traceContextHeaderName.data) - rawConfig->traceContextHeaderName = ngx_string("uber-trace-id"); - if (!rawConfig->traceBaggageHeaderPrefix.data) - rawConfig->traceBaggageHeaderPrefix = ngx_string("uberctx-"); - - const jaegertracing::Config config( - rawConfig->disabled, - jaegertracing::samplers::Config( - makeStr(rawConfig->samplerType), - makeDouble(rawConfig->samplerParam), - makeStr(rawConfig->samplingServerURL), - rawConfig->samplerMaxOperations, - std::chrono::seconds(rawConfig->samplingRefreshIntervalSeconds)), - jaegertracing::reporters::Config( - rawConfig->reporterQueueSize, - std::chrono::seconds(rawConfig->reporterBufferFlushIntervalSeconds), - rawConfig->reporterLogSpans, - makeStr(rawConfig->reporterLocalAgentHostPort)), - jaegertracing::propagation::HeadersConfig( - makeStr(rawConfig->jaegerDebugHeader), - makeStr(rawConfig->jaegerBaggageHeader), - makeStr(rawConfig->traceContextHeaderName), - makeStr(rawConfig->traceBaggageHeaderPrefix)), - jaegertracing::baggage::RestrictionsConfig()); - auto tracer = - jaegertracing::Tracer::make(makeStr(rawConfig->serviceName), config); - opentracing::Tracer::InitGlobal(tracer); - } catch (const std::exception& e) { - ngx_log_error(NGX_LOG_ERR, cycle->log, 0, - "Failed to create Jaeger tracer: %s", e.what()); - } - return NGX_OK; -} - -void* createJaegerMainConfig(ngx_conf_t* conf) { - auto* mainConfig = - static_cast(ngx_pcalloc(conf->pool, sizeof(Config))); - if (!mainConfig) { - return nullptr; - } - // Default initialize members. - *mainConfig = Config(); - return mainConfig; -} - -ngx_http_module_t moduleCtx = { - nullptr, // preconfiguration - &moduleInit, // postconfiguration - &createJaegerMainConfig, // create main configuration - nullptr, // init main configuration - nullptr, // create server configuration - nullptr, // merge server configuration - nullptr, // create location configuration - nullptr // merge location configuration -}; - -struct JaegerCommands { - JaegerCommands() { -#define DEFINE_COMMAND(commandName, memberName, ngxType) \ - { \ - ngx_command_t command( \ - {ngx_string(#commandName), NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, \ - ngx_conf_set_##ngxType##_slot, NGX_HTTP_MAIN_CONF_OFFSET, \ - offsetof(Config, memberName), nullptr}); \ - _commands.emplace_back(std::move(command)); \ - } - - DEFINE_COMMAND(jaeger_service_name, serviceName, str); - DEFINE_COMMAND(jaeger_disabled, disabled, flag); - // samplers::Config - DEFINE_COMMAND(jaeger_sampler_type, samplerType, str); - DEFINE_COMMAND(jaeger_sampler_param, samplerParam, str); - DEFINE_COMMAND(jaeger_sampling_server_url, samplingServerURL, str); - DEFINE_COMMAND(jaeger_sampler_max_operations, samplerMaxOperations, num); - DEFINE_COMMAND(jaeger_sampling_refresh_interval_seconds, - samplingRefreshIntervalSeconds, num); - // reporters::Config - DEFINE_COMMAND(jaeger_reporter_queue_size, reporterQueueSize, num); - DEFINE_COMMAND(jaeger_reporter_buffer_flush_interval_seconds, - reporterBufferFlushIntervalSeconds, num) - DEFINE_COMMAND(jaeger_reporter_log_spans, reporterLogSpans, flag); - DEFINE_COMMAND(jaeger_reporter_local_agent_host_port, - reporterLocalAgentHostPort, str); - // propagation::HeadersConfig - DEFINE_COMMAND(jaeger_debug_header, jaegerDebugHeader, str); - DEFINE_COMMAND(jaeger_baggage_header, jaegerBaggageHeader, str); - DEFINE_COMMAND(jaeger_trace_context_header_name, traceContextHeaderName, - str); - DEFINE_COMMAND(jaeger_trace_baggage_header_prefix, traceBaggageHeaderPrefix, - str); - _commands.push_back(ngx_null_command); - -#undef DEFINE_COMMAND - } - - std::vector _commands; -}; - -ngx_command_t* jaegerCommands() { - static JaegerCommands commands; - return &commands._commands[0]; -} - -} // anonymous namespace - -ngx_module_t ngx_http_jaeger_module = {NGX_MODULE_V1, - &moduleCtx, // module context - jaegerCommands(), // module directives - NGX_HTTP_MODULE, // module type - nullptr, // init master - nullptr, // init module - &initWorker, // init process - nullptr, // init thread - nullptr, // exit thread - nullptr, // exit process - nullptr, // exit master - NGX_MODULE_V1_PADDING}; diff --git a/lightstep/config b/lightstep/config deleted file mode 100644 index 4774a6b7..00000000 --- a/lightstep/config +++ /dev/null @@ -1,19 +0,0 @@ -ngx_addon_name=ngx_http_lightstep_module -ngx_module_type=HTTP -ngx_module_name=$ngx_addon_name -ngx_module_incs= -ngx_module_deps= -ngx_module_srcs=" \ - $ngx_addon_dir/src/ngx_http_lightstep_module.cpp \ -" -ngx_module_libs=" \ - -lgrpc++ \ - -lgrpc \ - -lprotobuf \ - -lopentracing \ - -llightstep_tracer \ -" - -. auto/module - -LIGHTSTEP_NGX_SRCS="$ngx_module_srcs" diff --git a/lightstep/config.make b/lightstep/config.make deleted file mode 100644 index 625fe712..00000000 --- a/lightstep/config.make +++ /dev/null @@ -1,7 +0,0 @@ -# Since nginx build system doesn't normally do C++, there is no CXXFLAGS for us -# to touch, and compilers are understandably unhappy with --std=c++11 on C -# files. Hence, we hack the makefile to add it for just our sources. -for src_file in $LIGHTSTEP_NGX_SRCS; do - obj_file="$NGX_OBJS/addon/src/`basename $src_file .cpp`.o" - echo "$obj_file : CFLAGS += --std=c++11" >> $NGX_MAKEFILE -done diff --git a/lightstep/doc/Directives.md b/lightstep/doc/Directives.md deleted file mode 100644 index 9393b601..00000000 --- a/lightstep/doc/Directives.md +++ /dev/null @@ -1,38 +0,0 @@ -### `lightstep_access_token` - -- **syntax** `lightstep_access_token ` -- **context**: `http` - -Specifies the access token to use when uploading traces. - -### `lightstep_component_name` - -- **syntax** `lightstep_component_name ` -- **default**: `nginx` -- **context**: `http` - -Specifies the component name to use for any traces created. - -### `lightstep_collector_host` - -- **syntax** `lightstep_collector_host ` -- **default**: `collector-grpc.lightstep.com` -- **context**: `http` - -Specifies the host to use when uploading traces. - -### `lightstep_collector_plaintext` - -- **syntax** `lightstep_collector_plaintext on|off` -- **default**: `off` -- **context**: `http` - -Specifies whether to encrypt when uploading traces. - -### `lightstep_collector_port` - -- **syntax** `lightstep_collector_port ` -- **default**: `443` -- **context**: `http` - -Specifies the port to use when uploading traces. diff --git a/lightstep/src/ngx_http_lightstep_module.cpp b/lightstep/src/ngx_http_lightstep_module.cpp deleted file mode 100644 index 2d26b1a9..00000000 --- a/lightstep/src/ngx_http_lightstep_module.cpp +++ /dev/null @@ -1,147 +0,0 @@ -#include -#include -#include - -extern "C" { -#include -#include -#include -#include - -extern ngx_module_t ngx_http_lightstep_module; -} - -//------------------------------------------------------------------------------ -// to_string -//------------------------------------------------------------------------------ -static inline std::string to_string(const ngx_str_t &ngx_str) { - return {reinterpret_cast(ngx_str.data), ngx_str.len}; -} - -//------------------------------------------------------------------------------ -// lightstep_main_conf_t -//------------------------------------------------------------------------------ -struct lightstep_main_conf_t { - ngx_str_t access_token; - ngx_str_t component_name; - ngx_str_t collector_host; - ngx_flag_t collector_plaintext = NGX_CONF_UNSET; - ngx_str_t collector_port; -}; - -//------------------------------------------------------------------------------ -// lightstep_module_init -//------------------------------------------------------------------------------ -static ngx_int_t lightstep_module_init(ngx_conf_t *cf) { - auto main_conf = static_cast( - ngx_http_conf_get_module_main_conf(cf, ngx_http_lightstep_module)); - // Validate the configuration - if (!main_conf->access_token.data) { - ngx_log_error(NGX_LOG_ERR, cf->log, 0, - "`lighstep_access_token` must be specified"); - return NGX_ERROR; - } - return NGX_OK; -} - -//------------------------------------------------------------------------------ -// lightstep_init_worker -//------------------------------------------------------------------------------ -static ngx_int_t lightstep_init_worker(ngx_cycle_t *cycle) { - auto main_conf = static_cast( - ngx_http_cycle_get_module_main_conf(cycle, ngx_http_lightstep_module)); - lightstep::LightStepTracerOptions tracer_options; - if (!main_conf->access_token.data) { - ngx_log_error(NGX_LOG_ERR, cycle->log, 0, - "`lighstep_access_token` must be specified"); - return NGX_ERROR; - } - tracer_options.access_token = to_string(main_conf->access_token); - if (main_conf->collector_host.data) - tracer_options.collector_host = to_string(main_conf->collector_host); - if (main_conf->collector_port.data) - // TODO: Check for errors here? - tracer_options.collector_port = - std::stoi(to_string(main_conf->collector_port)); - if (main_conf->collector_plaintext != NGX_CONF_UNSET) - tracer_options.collector_plaintext = main_conf->collector_plaintext; - if (main_conf->component_name.data) - tracer_options.component_name = to_string(main_conf->component_name); - else - tracer_options.component_name = "nginx"; - auto tracer = lightstep::MakeLightStepTracer(std::move(tracer_options)); - if (!tracer) { - ngx_log_error(NGX_LOG_ERR, cycle->log, 0, - "Failed to create LightStep tracer"); - return NGX_OK; - } - opentracing::Tracer::InitGlobal(std::move(tracer)); - return NGX_OK; -} - -//------------------------------------------------------------------------------ -// create_lightstep_main_conf -//------------------------------------------------------------------------------ -static void *create_lightstep_main_conf(ngx_conf_t *conf) { - auto main_conf = static_cast( - ngx_pcalloc(conf->pool, sizeof(lightstep_main_conf_t))); - // Default initialize members. - *main_conf = lightstep_main_conf_t(); - if (!main_conf) return nullptr; - return main_conf; -} - -//------------------------------------------------------------------------------ -// lightstep_module_ctx -//------------------------------------------------------------------------------ -static ngx_http_module_t lightstep_module_ctx = { - nullptr, /* preconfiguration */ - lightstep_module_init, /* postconfiguration */ - create_lightstep_main_conf, /* create main configuration */ - nullptr, /* init main configuration */ - nullptr, /* create server configuration */ - nullptr, /* merge server configuration */ - nullptr, /* create location configuration */ - nullptr /* merge location configuration */ -}; - -//------------------------------------------------------------------------------ -// lightstep_commands -//------------------------------------------------------------------------------ -static ngx_command_t lightstep_commands[] = { - {ngx_string("lightstep_access_token"), NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, - ngx_conf_set_str_slot, NGX_HTTP_MAIN_CONF_OFFSET, - offsetof(lightstep_main_conf_t, access_token), nullptr}, - {ngx_string("lightstep_component_name"), - NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, ngx_conf_set_str_slot, - NGX_HTTP_MAIN_CONF_OFFSET, offsetof(lightstep_main_conf_t, component_name), - nullptr}, - {ngx_string("lightstep_collector_host"), - NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, ngx_conf_set_str_slot, - NGX_HTTP_MAIN_CONF_OFFSET, offsetof(lightstep_main_conf_t, collector_host), - nullptr}, - {ngx_string("lightstep_collector_plaintext"), - NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, ngx_conf_set_flag_slot, - NGX_HTTP_MAIN_CONF_OFFSET, - offsetof(lightstep_main_conf_t, collector_plaintext), nullptr}, - {ngx_string("lightstep_collector_port"), - NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, ngx_conf_set_str_slot, - NGX_HTTP_MAIN_CONF_OFFSET, offsetof(lightstep_main_conf_t, collector_port), - nullptr}}; - -//------------------------------------------------------------------------------ -// ngx_http_lightstep_module -//------------------------------------------------------------------------------ -ngx_module_t ngx_http_lightstep_module = { - NGX_MODULE_V1, - &lightstep_module_ctx, /* module context */ - lightstep_commands, /* module directives */ - NGX_HTTP_MODULE, /* module type */ - nullptr, /* init master */ - nullptr, /* init module */ - lightstep_init_worker, /* init process */ - nullptr, /* init thread */ - nullptr, /* exit thread */ - nullptr, /* exit process */ - nullptr, /* exit master */ - NGX_MODULE_V1_PADDING}; diff --git a/opentracing/config b/opentracing/config index cebd9b99..d161bcd1 100644 --- a/opentracing/config +++ b/opentracing/config @@ -4,18 +4,28 @@ ngx_module_name=$ngx_addon_name ngx_module_incs= ngx_module_deps=" \ $ngx_addon_dir/src/opentracing_conf.h \ + $ngx_addon_dir/src/opentracing_directive.h \ + $ngx_addon_dir/src/opentracing_variable.h \ $ngx_addon_dir/src/opentracing_handler.h \ - $ngx_addon_dir/src/opentracing_request_instrumentor.h \ + $ngx_addon_dir/src/load_tracer.h \ + $ngx_addon_dir/src/opentracing_context.h \ + $ngx_addon_dir/src/opentracing_conf_handler.h \ $ngx_addon_dir/src/ngx_script.h \ + $ngx_addon_dir/src/span_context_querier.h \ $ngx_addon_dir/src/utility.h \ " ngx_module_srcs=" \ $ngx_addon_dir/src/ngx_http_opentracing_module.cpp \ $ngx_addon_dir/src/ngx_script.cpp \ $ngx_addon_dir/src/extract_span_context.cpp \ - $ngx_addon_dir/src/inject_span_context.cpp \ + $ngx_addon_dir/src/discover_span_context_keys.cpp \ + $ngx_addon_dir/src/load_tracer.cpp \ + $ngx_addon_dir/src/opentracing_directive.cpp \ + $ngx_addon_dir/src/opentracing_variable.cpp \ $ngx_addon_dir/src/opentracing_handler.cpp \ - $ngx_addon_dir/src/opentracing_request_instrumentor.cpp \ + $ngx_addon_dir/src/opentracing_conf_handler.cpp \ + $ngx_addon_dir/src/opentracing_context.cpp \ + $ngx_addon_dir/src/span_context_querier.cpp \ $ngx_addon_dir/src/utility.cpp \ " ngx_module_libs="-lstdc++ -lopentracing" diff --git a/opentracing/src/discover_span_context_keys.cpp b/opentracing/src/discover_span_context_keys.cpp new file mode 100644 index 00000000..f207c721 --- /dev/null +++ b/opentracing/src/discover_span_context_keys.cpp @@ -0,0 +1,85 @@ +#include "discover_span_context_keys.h" +#include "load_tracer.h" + +#include +#include +#include +#include +#include + +namespace ngx_opentracing { +//------------------------------------------------------------------------------ +// HeaderKeyWriter +//------------------------------------------------------------------------------ +namespace { +class HeaderKeyWriter : public opentracing::HTTPHeadersWriter { + public: + HeaderKeyWriter(ngx_pool_t* pool, std::vector& keys) + : pool_{pool}, keys_{keys} {} + + opentracing::expected Set( + opentracing::string_view key, + opentracing::string_view value) const override { + auto data = static_cast(ngx_palloc(pool_, key.size())); + if (data == nullptr) { + throw std::bad_alloc{}; + } + std::copy_n(key.data(), key.size(), data); + + keys_.emplace_back(data, key.size()); + + return {}; + } + + private: + ngx_pool_t* pool_; + std::vector& keys_; +}; +} // namespace + +//------------------------------------------------------------------------------ +// discover_span_context_keys +//------------------------------------------------------------------------------ +// Loads the vendor tracing library and creates a dummy span so as to obtain +// a list of the keys used for span context propagation. This is necessary to +// make context propagation work for requests proxied upstream. +// +// See propagate_opentracing_context, set_tracer. +// +// Note: Any keys that a tracer might use for propagation that aren't discovered +// discovered here will get dropped during propagation. +ngx_array_t* discover_span_context_keys(ngx_pool_t* pool, ngx_log_t* log, + const char* tracing_library, + const char* tracer_config_file) { + opentracing::DynamicTracingLibraryHandle handle; + std::shared_ptr tracer; + auto rcode = + load_tracer(log, tracing_library, tracer_config_file, handle, tracer); + if (rcode != NGX_OK) { + return nullptr; + } + auto span = tracer->StartSpan("dummySpan"); + std::vector keys; + HeaderKeyWriter carrier_writer{pool, keys}; + auto was_successful = tracer->Inject(span->context(), carrier_writer); + span->SetTag(opentracing::ext::sampling_priority, 0); + if (!was_successful) { + ngx_log_error(NGX_LOG_ERR, log, 0, + "failed to discover span context tags: %s", + was_successful.error().message().c_str()); + return nullptr; + } + ngx_array_t* result = + ngx_array_create(pool, keys.size(), sizeof(opentracing::string_view)); + if (result == nullptr) { + throw std::bad_alloc{}; + } + + for (auto key : keys) { + auto element = + static_cast(ngx_array_push(result)); + *element = key; + } + return result; +} +} // namespace ngx_opentracing diff --git a/opentracing/src/discover_span_context_keys.h b/opentracing/src/discover_span_context_keys.h new file mode 100644 index 00000000..35bb8c15 --- /dev/null +++ b/opentracing/src/discover_span_context_keys.h @@ -0,0 +1,17 @@ +#pragma once + +extern "C" { +#include +#include +#include +#include +} + +namespace ngx_opentracing { +//------------------------------------------------------------------------------ +// discover_span_context_keys +//------------------------------------------------------------------------------ +ngx_array_t* discover_span_context_keys(ngx_pool_t* pool, ngx_log_t* log, + const char* tracing_library, + const char* tracer_config_file); +} // namespace ngx_opentracing diff --git a/opentracing/src/inject_span_context.cpp b/opentracing/src/inject_span_context.cpp deleted file mode 100644 index 8c6136ca..00000000 --- a/opentracing/src/inject_span_context.cpp +++ /dev/null @@ -1,139 +0,0 @@ -#include -#include -#include -#include "utility.h" -using opentracing::expected; -using opentracing::make_unexpected; -using opentracing::string_view; - -extern "C" { -#include -#include -#include -#include -} - -namespace ngx_opentracing { -//------------------------------------------------------------------------------ -// insert_header -//------------------------------------------------------------------------------ -static expected insert_header(ngx_http_request_t *request, ngx_str_t key, - ngx_str_t value) { - auto header = static_cast( - ngx_list_push(&request->headers_in.headers)); - if (!header) - return make_unexpected(std::make_error_code(std::errc::not_enough_memory)); - header->hash = 1; - header->key = key; - header->lowcase_key = key.data; - header->value = value; - return {}; -} - -//------------------------------------------------------------------------------ -// set_headers -//------------------------------------------------------------------------------ -static expected set_headers( - ngx_http_request_t *request, - std::vector> &headers) { - if (headers.empty()) return {}; - - // If header keys are already in the request, overwrite the values instead of - // inserting a new header. - // - // It may be possible in some cases to use nginx's hashes to look up the - // entries faster, but then we'd have to handle the special case of when a - // header element isn't hashed yet. Iterating over the header entries all the - // time keeps things simple. - for_each( - request->headers_in.headers, [&](ngx_table_elt_t &header) { - auto i = std::find_if( - headers.begin(), headers.end(), - [&](const std::pair &key_value) { - const auto &key = key_value.first; - return header.key.len == key.len && - ngx_strncmp(reinterpret_cast(header.lowcase_key), - reinterpret_cast(key.data), - key.len) == 0; - - }); - if (i == headers.end()) return; - ngx_log_debug4( - NGX_LOG_DEBUG_HTTP, request->connection->log, 0, - "replacing opentracing header \"%V:%V\" with value \"%V\"" - " in request %p", - &header.key, &header.value, &i->second, request); - header.value = i->second; - headers.erase(i); - }); - - // Any header left in `headers` doesn't already have a key in the request, so - // create a new entry for it. - for (const auto &key_value : headers) { - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, request->connection->log, 0, - "adding opentracing header \"%V:%V\" in request %p", - &key_value.first, &key_value.second, request); - auto was_successful = - insert_header(request, key_value.first, key_value.second); - if (!was_successful) { - ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, - "failed to insert header"); - return was_successful; - } - } - return {}; -} - -//------------------------------------------------------------------------------ -// NgxHeaderCarrierWriter -//------------------------------------------------------------------------------ -namespace { -class NgxHeaderCarrierWriter : public opentracing::HTTPHeadersWriter { - public: - NgxHeaderCarrierWriter(ngx_http_request_t *request, - std::vector> &headers) - : request_{request}, headers_(headers) {} - - expected Set(string_view key, string_view value) const override { - auto ngx_key = to_lower_ngx_str(request_->pool, key); - if (!ngx_key.data) { - ngx_log_error(NGX_LOG_ERR, request_->connection->log, 0, - "failed to allocate header key"); - return make_unexpected( - std::make_error_code(std::errc::not_enough_memory)); - } - auto ngx_value = to_ngx_str(request_->pool, value); - if (!ngx_value.data) { - ngx_log_error(NGX_LOG_ERR, request_->connection->log, 0, - "failed to allocate header value"); - return make_unexpected( - std::make_error_code(std::errc::not_enough_memory)); - } - headers_.emplace_back(ngx_key, ngx_value); - return {}; - } - - private: - ngx_http_request_t *request_; - std::vector> &headers_; -}; -} // namespace - -//------------------------------------------------------------------------------ -// inject_span_context -//------------------------------------------------------------------------------ -void inject_span_context(const opentracing::Tracer &tracer, - ngx_http_request_t *request, - const opentracing::SpanContext &span_context) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, request->connection->log, 0, - "injecting opentracing span context from request %p", request); - std::vector> headers; - auto carrier_writer = NgxHeaderCarrierWriter{request, headers}; - auto was_successful = tracer.Inject(span_context, carrier_writer); - if (was_successful) was_successful = set_headers(request, headers); - if (!was_successful) - ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, - "Tracer.inject() failed for request %p: %s", request, - was_successful.error().message().c_str()); -} -} // namespace ngx_opentracing diff --git a/opentracing/src/load_tracer.cpp b/opentracing/src/load_tracer.cpp new file mode 100644 index 00000000..e872e1d6 --- /dev/null +++ b/opentracing/src/load_tracer.cpp @@ -0,0 +1,65 @@ +#include "load_tracer.h" + +#include +#include +#include + +namespace ngx_opentracing { +ngx_int_t load_tracer(ngx_log_t* log, const char* tracer_library, + const char* config_file, + opentracing::DynamicTracingLibraryHandle& handle, + std::shared_ptr& tracer) { + std::string error_message; + + // Open the library handle + auto handle_maybe = + opentracing::DynamicallyLoadTracingLibrary(tracer_library, error_message); + if (!handle_maybe) { + if (!error_message.empty()) { + ngx_log_error(NGX_LOG_ERR, log, 0, + "Failed to load tracing library %s: %s", tracer_library, + error_message.c_str()); + } else { + ngx_log_error(NGX_LOG_ERR, log, 0, + "Failed to load tracing library %s: %s", tracer_library, + handle_maybe.error().message().c_str()); + } + return NGX_ERROR; + } + auto& tracer_factory = handle_maybe->tracer_factory(); + + // Construct a tracer + errno = 0; + std::ifstream in{config_file}; + if (!in.good()) { + ngx_log_error(NGX_LOG_ERR, log, errno, + "Failed to open tracer configuration file %s", config_file); + return NGX_ERROR; + } + std::string tracer_config{std::istreambuf_iterator{in}, + std::istreambuf_iterator{}}; + if (!in.good()) { + ngx_log_error(NGX_LOG_ERR, log, errno, + "Failed to read tracer configuration file %s", &config_file); + return NGX_ERROR; + } + + auto tracer_maybe = + tracer_factory.MakeTracer(tracer_config.c_str(), error_message); + if (!tracer_maybe) { + if (!error_message.empty()) { + ngx_log_error(NGX_LOG_ERR, log, 0, "Failed to construct tracer: %s", + error_message.c_str()); + } else { + ngx_log_error(NGX_LOG_ERR, log, 0, "Failed to construct tracer: %s", + tracer_maybe.error().message().c_str()); + } + return NGX_ERROR; + } + + handle = std::move(*handle_maybe); + tracer = std::move(*tracer_maybe); + + return NGX_OK; +} +} // namespace ngx_opentracing diff --git a/opentracing/src/load_tracer.h b/opentracing/src/load_tracer.h new file mode 100644 index 00000000..72767d7c --- /dev/null +++ b/opentracing/src/load_tracer.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +extern "C" { +#include +#include +#include +#include +} + +namespace ngx_opentracing { +ngx_int_t load_tracer(ngx_log_t* log, const char* tracer_library, + const char* config_file, + opentracing::DynamicTracingLibraryHandle& handle, + std::shared_ptr& tracer); +} // namespace ngx_opentracing diff --git a/opentracing/src/ngx_http_opentracing_module.cpp b/opentracing/src/ngx_http_opentracing_module.cpp index a79a9938..a5f8f3f0 100644 --- a/opentracing/src/ngx_http_opentracing_module.cpp +++ b/opentracing/src/ngx_http_opentracing_module.cpp @@ -1,14 +1,16 @@ +#include "load_tracer.h" +#include "opentracing_conf.h" +#include "opentracing_directive.h" +#include "opentracing_handler.h" +#include "opentracing_variable.h" +#include "utility.h" + #include -#include + #include #include -#include #include -#include #include -#include "opentracing_conf.h" -#include "opentracing_handler.h" -#include "utility.h" extern "C" { #include @@ -19,13 +21,7 @@ extern "C" { extern ngx_module_t ngx_http_opentracing_module; } -using ngx_opentracing::opentracing_main_conf_t; -using ngx_opentracing::opentracing_loc_conf_t; -using ngx_opentracing::opentracing_tag_t; -using ngx_opentracing::on_enter_block; -using ngx_opentracing::on_log_request; -using ngx_opentracing::NgxScript; -using ngx_opentracing::to_string; +using namespace ngx_opentracing; static std::unique_ptr opentracing_library_handle; @@ -33,7 +29,7 @@ static std::unique_ptr //------------------------------------------------------------------------------ // kDefaultOpentracingTags //------------------------------------------------------------------------------ -const std::pair kDefaultOpenTracingTags[] = { +const std::pair default_opentracing_tags[] = { {ngx_string("component"), ngx_string("nginx")}, {ngx_string("nginx.worker_pid"), ngx_string("$pid")}, {ngx_string("peer.address"), ngx_string("$remote_addr:$remote_port")}, @@ -41,53 +37,10 @@ const std::pair kDefaultOpenTracingTags[] = { {ngx_string("http.url"), ngx_string("$scheme://$http_host$request_uri")}, {ngx_string("http.host"), ngx_string("$http_host")}}; -//------------------------------------------------------------------------------ -// add_opentracing_tag -//------------------------------------------------------------------------------ -static char *add_opentracing_tag(ngx_conf_t *cf, ngx_array_t *tags, - ngx_str_t key, ngx_str_t value) { - if (!tags) return static_cast(NGX_CONF_ERROR); - - auto tag = static_cast(ngx_array_push(tags)); - if (!tag) return static_cast(NGX_CONF_ERROR); - - ngx_memzero(tag, sizeof(opentracing_tag_t)); - if (tag->key_script.compile(cf, key) != NGX_OK) - return static_cast(NGX_CONF_ERROR); - if (tag->value_script.compile(cf, value) != NGX_OK) - return static_cast(NGX_CONF_ERROR); - - return static_cast(NGX_CONF_OK); -} - -//------------------------------------------------------------------------------ -// set_opentracing_tag -//------------------------------------------------------------------------------ -static char *set_opentracing_tag(ngx_conf_t *cf, ngx_command_t *command, - void *conf) { - auto loc_conf = static_cast(conf); - if (!loc_conf->tags) - loc_conf->tags = ngx_array_create(cf->pool, 1, sizeof(opentracing_tag_t)); - auto values = static_cast(cf->args->elts); - return add_opentracing_tag(cf, loc_conf->tags, values[1], values[2]); -} - -//------------------------------------------------------------------------------ -// set_tracer -//------------------------------------------------------------------------------ -static char *set_tracer(ngx_conf_t *cf, ngx_command_t *command, void *conf) { - auto main_conf = static_cast( - ngx_http_conf_get_module_main_conf(cf, ngx_http_opentracing_module)); - auto values = static_cast(cf->args->elts); - main_conf->tracer_library = values[1]; - main_conf->tracer_conf_file = values[2]; - return static_cast(NGX_CONF_OK); -} - //------------------------------------------------------------------------------ // opentracing_module_init //------------------------------------------------------------------------------ -static ngx_int_t opentracing_module_init(ngx_conf_t *cf) { +static ngx_int_t opentracing_module_init(ngx_conf_t *cf) noexcept { auto core_main_config = static_cast( ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module)); auto main_conf = static_cast( @@ -106,12 +59,12 @@ static ngx_int_t opentracing_module_init(ngx_conf_t *cf) { // Add default span tags. const auto num_default_tags = - sizeof(kDefaultOpenTracingTags) / sizeof(kDefaultOpenTracingTags[0]); + sizeof(default_opentracing_tags) / sizeof(default_opentracing_tags[0]); if (num_default_tags == 0) return NGX_OK; main_conf->tags = ngx_array_create(cf->pool, num_default_tags, sizeof(opentracing_tag_t)); if (!main_conf->tags) return NGX_ERROR; - for (const auto &tag : kDefaultOpenTracingTags) + for (const auto &tag : default_opentracing_tags) if (add_opentracing_tag(cf, main_conf->tags, tag.first, tag.second) != NGX_CONF_OK) return NGX_ERROR; @@ -121,79 +74,43 @@ static ngx_int_t opentracing_module_init(ngx_conf_t *cf) { //------------------------------------------------------------------------------ // opentracing_init_worker //------------------------------------------------------------------------------ -static ngx_int_t opentracing_init_worker(ngx_cycle_t *cycle) { +static ngx_int_t opentracing_init_worker(ngx_cycle_t *cycle) noexcept try { auto main_conf = static_cast( ngx_http_cycle_get_module_main_conf(cycle, ngx_http_opentracing_module)); if (!main_conf->tracer_library.data) { return NGX_OK; } - // Load a tracer factory from the provided library. - std::string error_message; - auto library_handle_maybe = opentracing::DynamicallyLoadTracingLibrary( - to_string(main_conf->tracer_library).data(), error_message); - if (!library_handle_maybe) { - if (!error_message.empty()) { - ngx_log_error(NGX_LOG_ERR, cycle->log, 0, - "Failed to load tracing library %V: %s", - &main_conf->tracer_library, error_message.c_str()); - } else { - ngx_log_error(NGX_LOG_ERR, cycle->log, 0, - "Failed to load tracing library %V: %s", - &main_conf->tracer_library, - library_handle_maybe.error().message().c_str()); - } - return NGX_ERROR; - } - opentracing_library_handle.reset(new opentracing::DynamicTracingLibraryHandle{ - std::move(*library_handle_maybe)}); - auto &tracer_factory = opentracing_library_handle->tracer_factory(); - - // Construct a tracer and initiqlize the global tracer. - std::ifstream in{to_string(main_conf->tracer_conf_file)}; - if (!in.good()) { - ngx_log_error(NGX_LOG_ERR, cycle->log, errno, - "Failed to open tracer configuration file %V", - &main_conf->tracer_conf_file); - return NGX_ERROR; - } - std::string tracer_configuration{std::istreambuf_iterator{in}, - std::istreambuf_iterator{}}; - if (!in.good()) { - ngx_log_error(NGX_LOG_ERR, cycle->log, errno, - "Failed to read tracer configuration file %V", - &main_conf->tracer_conf_file); - return NGX_ERROR; - } - - error_message.clear(); - auto tracer_maybe = - tracer_factory.MakeTracer(tracer_configuration.c_str(), error_message); - if (!tracer_maybe) { - if (!error_message.empty()) { - ngx_log_error(NGX_LOG_ERR, cycle->log, 0, - "Failed to construct tracer: %s", error_message.c_str()); - } else { - ngx_log_error(NGX_LOG_ERR, cycle->log, 0, - "Failed to construct tracer: %s", - tracer_maybe.error().message().c_str()); - } - return NGX_ERROR; + std::unique_ptr handle{ + new opentracing::DynamicTracingLibraryHandle{}}; + std::shared_ptr tracer; + auto result = ngx_opentracing::load_tracer( + cycle->log, to_string(main_conf->tracer_library).data(), + to_string(main_conf->tracer_conf_file).data(), *handle, tracer); + if (result != NGX_OK) { + return result; } - opentracing::Tracer::InitGlobal(std::move(*tracer_maybe)); + opentracing_library_handle = std::move(handle); + opentracing::Tracer::InitGlobal(std::move(tracer)); return NGX_OK; +} catch (const std::exception &e) { + ngx_log_error(NGX_LOG_ERR, cycle->log, 0, "failed to initialize tracer: %s", + e.what()); + return NGX_ERROR; } //------------------------------------------------------------------------------ // opentracing_exit_worker //------------------------------------------------------------------------------ -static void opentracing_exit_worker(ngx_cycle_t *cycle) { +static void opentracing_exit_worker(ngx_cycle_t *cycle) noexcept { // Close the global tracer if it's set and release the reference so as to // ensure that any dynamically loaded tracer is destructed before the library // handle is closed. auto tracer = opentracing::Tracer::InitGlobal(nullptr); if (tracer != nullptr) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cycle->log, 0, + "closing opentracing tracer"); tracer->Close(); tracer.reset(); } @@ -205,7 +122,7 @@ static void opentracing_exit_worker(ngx_cycle_t *cycle) { //------------------------------------------------------------------------------ // create_opentracing_main_conf //------------------------------------------------------------------------------ -static void *create_opentracing_main_conf(ngx_conf_t *conf) { +static void *create_opentracing_main_conf(ngx_conf_t *conf) noexcept { auto main_conf = static_cast( ngx_pcalloc(conf->pool, sizeof(opentracing_main_conf_t))); // Default initialize members. @@ -214,46 +131,10 @@ static void *create_opentracing_main_conf(ngx_conf_t *conf) { return main_conf; } -//------------------------------------------------------------------------------ -// set_script -//------------------------------------------------------------------------------ -static char *set_script(ngx_conf_t *cf, ngx_command_t *command, - NgxScript &script) { - if (script.is_valid()) return const_cast("is duplicate"); - - auto value = static_cast(cf->args->elts); - auto pattern = &value[1]; - - if (script.compile(cf, *pattern) != NGX_OK) - return static_cast(NGX_CONF_ERROR); - - return static_cast(NGX_CONF_OK); -} - -//------------------------------------------------------------------------------ -// set_opentracing_operation_name -//------------------------------------------------------------------------------ -static char *set_opentracing_operation_name(ngx_conf_t *cf, - ngx_command_t *command, - void *conf) { - auto loc_conf = static_cast(conf); - return set_script(cf, command, loc_conf->operation_name_script); -} - -//------------------------------------------------------------------------------ -// set_opentracing_location_operation_name -//------------------------------------------------------------------------------ -static char *set_opentracing_location_operation_name(ngx_conf_t *cf, - ngx_command_t *command, - void *conf) { - auto loc_conf = static_cast(conf); - return set_script(cf, command, loc_conf->loc_operation_name_script); -} - //------------------------------------------------------------------------------ // create_opentracing_loc_conf //------------------------------------------------------------------------------ -static void *create_opentracing_loc_conf(ngx_conf_t *conf) { +static void *create_opentracing_loc_conf(ngx_conf_t *conf) noexcept { auto loc_conf = static_cast( ngx_pcalloc(conf->pool, sizeof(opentracing_loc_conf_t))); if (!loc_conf) return nullptr; @@ -269,7 +150,7 @@ static void *create_opentracing_loc_conf(ngx_conf_t *conf) { // merge_opentracing_loc_conf //------------------------------------------------------------------------------ static char *merge_opentracing_loc_conf(ngx_conf_t *, void *parent, - void *child) { + void *child) noexcept { auto prev = static_cast(parent); auto conf = static_cast(child); @@ -309,7 +190,7 @@ static char *merge_opentracing_loc_conf(ngx_conf_t *, void *parent, // opentracing_module_ctx //------------------------------------------------------------------------------ static ngx_http_module_t opentracing_module_ctx = { - nullptr, /* preconfiguration */ + add_variables, /* preconfiguration */ opentracing_module_init, /* postconfiguration */ create_opentracing_main_conf, /* create main configuration */ nullptr, /* init main configuration */ @@ -333,6 +214,9 @@ static ngx_command_t opentracing_commands[] = { NGX_CONF_TAKE1, ngx_conf_set_flag_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(opentracing_loc_conf_t, enable_locations), nullptr}, + {ngx_string("opentracing_propagate_context"), + NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS, propagate_opentracing_context, + NGX_HTTP_LOC_CONF_OFFSET, 0, nullptr}, {ngx_string("opentracing_operation_name"), NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, diff --git a/opentracing/src/ngx_script.cpp b/opentracing/src/ngx_script.cpp index 6335c2a0..0d13b353 100644 --- a/opentracing/src/ngx_script.cpp +++ b/opentracing/src/ngx_script.cpp @@ -5,13 +5,14 @@ namespace ngx_opentracing { //------------------------------------------------------------------------------ // NgxScript //------------------------------------------------------------------------------ -NgxScript::NgxScript() +NgxScript::NgxScript() noexcept : pattern_{0, nullptr}, lengths_{nullptr}, values_{nullptr} {} //------------------------------------------------------------------------------ // compile //------------------------------------------------------------------------------ -ngx_int_t NgxScript::compile(ngx_conf_t *cf, const ngx_str_t &pattern) { +ngx_int_t NgxScript::compile(ngx_conf_t *cf, + const ngx_str_t &pattern) noexcept { pattern_ = pattern; lengths_ = nullptr; values_ = nullptr; @@ -36,7 +37,7 @@ ngx_int_t NgxScript::compile(ngx_conf_t *cf, const ngx_str_t &pattern) { //------------------------------------------------------------------------------ // run //------------------------------------------------------------------------------ -ngx_str_t NgxScript::run(ngx_http_request_t *request) const { +ngx_str_t NgxScript::run(ngx_http_request_t *request) const noexcept { if (!is_valid()) { ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, "Executing invalid opentracing script"); diff --git a/opentracing/src/ngx_script.h b/opentracing/src/ngx_script.h index 0108e3f0..2285de6b 100644 --- a/opentracing/src/ngx_script.h +++ b/opentracing/src/ngx_script.h @@ -11,12 +11,12 @@ namespace ngx_opentracing { class NgxScript { public: // Note: Constructor is compatible with begin zero initialized. - NgxScript(); + NgxScript() noexcept; - bool is_valid() const { return pattern_.data; } + bool is_valid() const noexcept { return pattern_.data; } - ngx_int_t compile(ngx_conf_t *cf, const ngx_str_t &pattern); - ngx_str_t run(ngx_http_request_t *request) const; + ngx_int_t compile(ngx_conf_t *cf, const ngx_str_t &pattern) noexcept; + ngx_str_t run(ngx_http_request_t *request) const noexcept; private: ngx_str_t pattern_; diff --git a/opentracing/src/opentracing_conf.h b/opentracing/src/opentracing_conf.h index a20990ad..78e64cdc 100644 --- a/opentracing/src/opentracing_conf.h +++ b/opentracing/src/opentracing_conf.h @@ -19,6 +19,7 @@ struct opentracing_main_conf_t { ngx_array_t *tags; ngx_str_t tracer_library; ngx_str_t tracer_conf_file; + ngx_array_t *span_context_keys; }; struct opentracing_loc_conf_t { diff --git a/opentracing/src/opentracing_conf_handler.cpp b/opentracing/src/opentracing_conf_handler.cpp new file mode 100644 index 00000000..c33344b2 --- /dev/null +++ b/opentracing/src/opentracing_conf_handler.cpp @@ -0,0 +1,146 @@ +#include "opentracing_conf_handler.h" + +namespace ngx_opentracing { +/* The eight fixed arguments */ + +static ngx_uint_t argument_number[] = { + NGX_CONF_NOARGS, NGX_CONF_TAKE1, NGX_CONF_TAKE2, NGX_CONF_TAKE3, + NGX_CONF_TAKE4, NGX_CONF_TAKE5, NGX_CONF_TAKE6, NGX_CONF_TAKE7}; + +//------------------------------------------------------------------------------ +// opentracing_conf_handler +//------------------------------------------------------------------------------ +ngx_int_t opentracing_conf_handler(ngx_conf_t *cf, ngx_int_t last) noexcept { + char *rv; + void *conf, **confp; + ngx_uint_t i, found; + ngx_str_t *name; + ngx_command_t *cmd; + + name = static_cast(cf->args->elts); + + found = 0; + + for (i = 0; cf->cycle->modules[i]; i++) { + cmd = cf->cycle->modules[i]->commands; + if (cmd == NULL) { + continue; + } + + for (/* void */; cmd->name.len; cmd++) { + if (name->len != cmd->name.len) { + continue; + } + + if (ngx_strcmp(name->data, cmd->name.data) != 0) { + continue; + } + + found = 1; + + if (cf->cycle->modules[i]->type != NGX_CONF_MODULE && + cf->cycle->modules[i]->type != cf->module_type) { + continue; + } + + /* is the directive's location right ? */ + + if (!(cmd->type & cf->cmd_type)) { + continue; + } + + if (!(cmd->type & NGX_CONF_BLOCK) && last != NGX_OK) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "directive \"%s\" is not terminated by \";\"", + name->data); + return NGX_ERROR; + } + + if ((cmd->type & NGX_CONF_BLOCK) && last != NGX_CONF_BLOCK_START) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "directive \"%s\" has no opening \"{\"", name->data); + return NGX_ERROR; + } + + /* is the directive's argument count right ? */ + + if (!(cmd->type & NGX_CONF_ANY)) { + if (cmd->type & NGX_CONF_FLAG) { + if (cf->args->nelts != 2) { + goto invalid; + } + + } else if (cmd->type & NGX_CONF_1MORE) { + if (cf->args->nelts < 2) { + goto invalid; + } + + } else if (cmd->type & NGX_CONF_2MORE) { + if (cf->args->nelts < 3) { + goto invalid; + } + + } else if (cf->args->nelts > NGX_CONF_MAX_ARGS) { + goto invalid; + + } else if (!(cmd->type & argument_number[cf->args->nelts - 1])) { + goto invalid; + } + } + + /* set up the directive's configuration context */ + + conf = NULL; + + if (cmd->type & NGX_DIRECT_CONF) { + conf = ((void **)cf->ctx)[cf->cycle->modules[i]->index]; + + } else if (cmd->type & NGX_MAIN_CONF) { + conf = &(((void **)cf->ctx)[cf->cycle->modules[i]->index]); + + } else if (cf->ctx) { + confp = static_cast(*(void **)((char *)cf->ctx + cmd->conf)); + + if (confp) { + conf = confp[cf->cycle->modules[i]->ctx_index]; + } + } + + rv = cmd->set(cf, cmd, conf); + + if (rv == NGX_CONF_OK) { + return NGX_OK; + } + + if (rv == NGX_CONF_ERROR) { + return NGX_ERROR; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"%s\" directive %s", + name->data, rv); + + return NGX_ERROR; + } + } + + if (found) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"%s\" directive is not allowed here", name->data); + + return NGX_ERROR; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unknown directive \"%s\"", + name->data); + + return NGX_ERROR; + +invalid: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid number of arguments in \"%s\" directive", + name->data); + + return NGX_ERROR; +} +} // namespace ngx_opentracing diff --git a/opentracing/src/opentracing_conf_handler.h b/opentracing/src/opentracing_conf_handler.h new file mode 100644 index 00000000..62a5bda6 --- /dev/null +++ b/opentracing/src/opentracing_conf_handler.h @@ -0,0 +1,17 @@ +#pragma once + +extern "C" { +#include +#include +#include +#include +} + +namespace ngx_opentracing { +// opentracing_conf_handler is copied from +// https://github.com/nginx/nginx/blob/0ad556fe59ad132dc4d34dea9e80f2ff2c3c1314/src/core/ngx_conf_file.c +// this is necessary for OpenTracing's implementation of context propagation. +// +// See http://mailman.nginx.org/pipermail/nginx-devel/2018-March/011008.html +ngx_int_t opentracing_conf_handler(ngx_conf_t *cf, ngx_int_t last) noexcept; +} // namespace ngx_opentracing diff --git a/opentracing/src/opentracing_request_instrumentor.cpp b/opentracing/src/opentracing_context.cpp similarity index 55% rename from opentracing/src/opentracing_request_instrumentor.cpp rename to opentracing/src/opentracing_context.cpp index a2d3fac2..a6c69cb1 100644 --- a/opentracing/src/opentracing_request_instrumentor.cpp +++ b/opentracing/src/opentracing_context.cpp @@ -1,6 +1,8 @@ -#include "opentracing_request_instrumentor.h" +#include "opentracing_context.h" #include "utility.h" +#include + extern "C" { extern ngx_module_t ngx_http_opentracing_module; } @@ -9,10 +11,6 @@ namespace ngx_opentracing { std::unique_ptr extract_span_context( const opentracing::Tracer &tracer, const ngx_http_request_t *request); -void inject_span_context(const opentracing::Tracer &tracer, - ngx_http_request_t *request, - const opentracing::SpanContext &span_context); - //------------------------------------------------------------------------------ // get_loc_operation_name //------------------------------------------------------------------------------ @@ -70,16 +68,18 @@ static void add_status_tags(const ngx_http_request_t *request, } //------------------------------------------------------------------------------ -// OpenTracingRequestInstrumentor +// OpenTracingContext //------------------------------------------------------------------------------ -OpenTracingRequestInstrumentor::OpenTracingRequestInstrumentor( - ngx_http_request_t *request, ngx_http_core_loc_conf_t *core_loc_conf, - opentracing_loc_conf_t *loc_conf) - : request_{request}, loc_conf_{loc_conf} { - main_conf_ = static_cast( - ngx_http_get_module_main_conf(request_, ngx_http_opentracing_module)); +OpenTracingContext::OpenTracingContext(ngx_http_request_t *request, + ngx_http_core_loc_conf_t *core_loc_conf, + opentracing_loc_conf_t *loc_conf) + : request_{request}, + main_conf_{ + static_cast(ngx_http_get_module_main_conf( + request_, ngx_http_opentracing_module))}, + loc_conf_{loc_conf} { auto tracer = opentracing::Tracer::Global(); - if (!tracer) throw InstrumentationFailure{}; + if (!tracer) throw std::runtime_error{"no global tracer set"}; std::unique_ptr parent_span_context = nullptr; if (loc_conf_->trust_incoming_span) { @@ -93,7 +93,7 @@ OpenTracingRequestInstrumentor::OpenTracingRequestInstrumentor( {opentracing::ChildOf(parent_span_context.get()), opentracing::StartTimestamp{ to_system_timestamp(request->start_sec, request->start_msec)}}); - if (!request_span_) throw InstrumentationFailure{}; + if (!request_span_) throw std::runtime_error{"tracer->StartSpan failed"}; if (loc_conf_->enable_locations) { ngx_log_debug3( @@ -103,19 +103,16 @@ OpenTracingRequestInstrumentor::OpenTracingRequestInstrumentor( span_ = tracer->StartSpan( get_loc_operation_name(request_, core_loc_conf, loc_conf_), {opentracing::ChildOf(&request_span_->context())}); - if (!span_) throw InstrumentationFailure{}; + if (!span_) throw std::runtime_error{"tracer->StartSpan failed"}; } - - set_request_span_context(); } //------------------------------------------------------------------------------ // on_change_block //------------------------------------------------------------------------------ -void OpenTracingRequestInstrumentor::on_change_block( +void OpenTracingContext::on_change_block( ngx_http_core_loc_conf_t *core_loc_conf, opentracing_loc_conf_t *loc_conf) { on_exit_block(); - auto prev_loc_conf = loc_conf_; loc_conf_ = loc_conf; if (loc_conf->enable_locations) { @@ -126,20 +123,25 @@ void OpenTracingRequestInstrumentor::on_change_block( span_ = request_span_->tracer().StartSpan( get_loc_operation_name(request_, core_loc_conf, loc_conf), {opentracing::ChildOf(&request_span_->context())}); - if (!span_) throw InstrumentationFailure{}; + if (!span_) throw std::runtime_error{"tracer->StartSpan failed"}; } +} - // As an optimization, avoid injecting the request span context if neither the - // previous nor current location blocks are traced since the active span - // context will be the same. - if (prev_loc_conf->enable_locations || loc_conf->enable_locations) - set_request_span_context(); +//------------------------------------------------------------------------------ +// active_span +//------------------------------------------------------------------------------ +opentracing::Span &OpenTracingContext::active_span() { + if (loc_conf_->enable_locations) { + return *span_; + } else { + return *request_span_; + } } //------------------------------------------------------------------------------ // on_exit_block //------------------------------------------------------------------------------ -void OpenTracingRequestInstrumentor::on_exit_block( +void OpenTracingContext::on_exit_block( std::chrono::steady_clock::time_point finish_timestamp) { // Set default and custom tags for the block. Many nginx variables won't be // available when a block is first entered, so set tags when the block is @@ -157,21 +159,10 @@ void OpenTracingRequestInstrumentor::on_exit_block( } } -//------------------------------------------------------------------------------ -// set_request_span_context -//------------------------------------------------------------------------------ -void OpenTracingRequestInstrumentor::set_request_span_context() { - if (loc_conf_->enable_locations) - inject_span_context(span_->tracer(), request_, span_->context()); - else - inject_span_context(request_span_->tracer(), request_, - request_span_->context()); -} - //------------------------------------------------------------------------------ // on_log_request //------------------------------------------------------------------------------ -void OpenTracingRequestInstrumentor::on_log_request() { +void OpenTracingContext::on_log_request() { auto finish_timestamp = std::chrono::steady_clock::now(); on_exit_block(finish_timestamp); @@ -192,4 +183,114 @@ void OpenTracingRequestInstrumentor::on_log_request() { request_span_->Finish({opentracing::FinishTimestamp{finish_timestamp}}); } + +//------------------------------------------------------------------------------ +// lookup_span_context_value +//------------------------------------------------------------------------------ +// Expands the active span context into a list of key-value pairs and returns +// the value for `key` if it exists. +// +// Note: there's caching so that if lookup_span_context_value is repeatedly +// called for the same active span context, it will only be expanded once. +// +// See propagate_opentracing_context +ngx_str_t OpenTracingContext::lookup_span_context_value( + opentracing::string_view key) { + return span_context_querier_.lookup_value(request_, active_span(), key); +} + +//------------------------------------------------------------------------------ +// cleanup_opentracing_context +//------------------------------------------------------------------------------ +static void cleanup_opentracing_context(void *data) noexcept { + delete static_cast(data); +} + +//------------------------------------------------------------------------------ +// find_opentracing_cleanup +//------------------------------------------------------------------------------ +static ngx_pool_cleanup_t *find_opentracing_cleanup( + ngx_http_request_t *request) { + for (auto cleanup = request->pool->cleanup; cleanup; + cleanup = cleanup->next) { + if (cleanup->handler == cleanup_opentracing_context) { + return cleanup; + } + } + return nullptr; +} + +//------------------------------------------------------------------------------ +// get_opentracing_context +//------------------------------------------------------------------------------ +OpenTracingContext *get_opentracing_context( + ngx_http_request_t *request) noexcept { + auto context = static_cast( + ngx_http_get_module_ctx(request, ngx_http_opentracing_module)); + if (context != nullptr || !request->internal) { + return context; + } + + // If this is an internal redirect, the OpenTracingContext will have been + // reset, but we can still recover it from the cleanup handler. + // + // See set_opentracing_context below. + auto cleanup = find_opentracing_cleanup(request); + if (cleanup != nullptr) { + context = static_cast(cleanup->data); + } + + // If we found a context, attach with ngx_http_set_ctx so that we don't have + // to loop through the cleanup handlers again. + if (context != nullptr) { + ngx_http_set_ctx(request, static_cast(context), + ngx_http_opentracing_module); + } + + return context; +} + +//------------------------------------------------------------------------------ +// set_opentracing_context +//------------------------------------------------------------------------------ +// Attaches an OpenTracingContext to a request. +// +// Note that internal redirects for nginx will clear any data attached via +// ngx_http_set_ctx. Since OpenTracingContext needs to persist across +// redirection, as a workaround the context is stored in a cleanup handler where +// it can be later recovered. +// +// See the discussion in +// https://forum.nginx.org/read.php?29,272403,272403#msg-272403 +// or the approach taken by the standard nginx realip module. +void set_opentracing_context(ngx_http_request_t *request, + OpenTracingContext *context) { + auto cleanup = ngx_pool_cleanup_add(request->pool, 0); + if (cleanup == nullptr) { + delete context; + throw std::runtime_error{"failed to allocate cleanup handler"}; + } + cleanup->data = static_cast(context); + cleanup->handler = cleanup_opentracing_context; + ngx_http_set_ctx(request, static_cast(context), + ngx_http_opentracing_module); +} + +//------------------------------------------------------------------------------ +// destroy_opentracing_context +//------------------------------------------------------------------------------ +// Supports early destruction of the OpenTracingContext (in case of an +// unrecoverable error). +void destroy_opentracing_context(ngx_http_request_t *request) noexcept { + auto cleanup = find_opentracing_cleanup(request); + if (cleanup == nullptr) { + ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, + "Unable to find OpenTracing cleanup handler for request %p", + request); + return; + } + delete static_cast(cleanup->data); + cleanup->data = nullptr; + ngx_http_set_ctx(request, nullptr, ngx_http_opentracing_module); +} } // namespace ngx_opentracing diff --git a/opentracing/src/opentracing_context.h b/opentracing/src/opentracing_context.h new file mode 100644 index 00000000..08fd06a9 --- /dev/null +++ b/opentracing/src/opentracing_context.h @@ -0,0 +1,64 @@ +#pragma once + +#include "opentracing_conf.h" +#include "span_context_querier.h" + +#include +#include +#include + +extern "C" { +#include +#include +#include +#include +} + +namespace ngx_opentracing { +//------------------------------------------------------------------------------ +// OpenTracingContext +//------------------------------------------------------------------------------ +class OpenTracingContext { + public: + OpenTracingContext(ngx_http_request_t *request, + ngx_http_core_loc_conf_t *core_loc_conf, + opentracing_loc_conf_t *loc_conf); + + void on_change_block(ngx_http_core_loc_conf_t *core_loc_conf, + opentracing_loc_conf_t *loc_conf); + + void on_log_request(); + + ngx_str_t lookup_span_context_value(opentracing::string_view key); + + private: + ngx_http_request_t *request_; + opentracing_main_conf_t *main_conf_; + opentracing_loc_conf_t *loc_conf_; + SpanContextQuerier span_context_querier_; + std::unique_ptr request_span_; + std::unique_ptr span_; + + opentracing::Span &active_span(); + + void on_exit_block(std::chrono::steady_clock::time_point finish_timestamp = + std::chrono::steady_clock::now()); +}; + +//------------------------------------------------------------------------------ +// get_opentracing_context +//------------------------------------------------------------------------------ +OpenTracingContext *get_opentracing_context( + ngx_http_request_t *request) noexcept; + +//------------------------------------------------------------------------------ +// set_opentracing_context +//------------------------------------------------------------------------------ +void set_opentracing_context(ngx_http_request_t *request, + OpenTracingContext *context); + +//------------------------------------------------------------------------------ +// destroy_opentracing_context +//------------------------------------------------------------------------------ +void destroy_opentracing_context(ngx_http_request_t *request) noexcept; +} // namespace ngx_opentracing diff --git a/opentracing/src/opentracing_directive.cpp b/opentracing/src/opentracing_directive.cpp new file mode 100644 index 00000000..d71296ad --- /dev/null +++ b/opentracing/src/opentracing_directive.cpp @@ -0,0 +1,198 @@ +#include "opentracing_directive.h" + +#include "discover_span_context_keys.h" +#include "ngx_script.h" +#include "opentracing_conf.h" +#include "opentracing_conf_handler.h" +#include "opentracing_variable.h" +#include "utility.h" + +#include + +#include +#include + +extern "C" { +extern ngx_module_t ngx_http_opentracing_module; +} + +namespace ngx_opentracing { +//------------------------------------------------------------------------------ +// set_script +//------------------------------------------------------------------------------ +static char *set_script(ngx_conf_t *cf, ngx_command_t *command, + NgxScript &script) noexcept { + if (script.is_valid()) return const_cast("is duplicate"); + + auto value = static_cast(cf->args->elts); + auto pattern = &value[1]; + + if (script.compile(cf, *pattern) != NGX_OK) + return static_cast(NGX_CONF_ERROR); + + return static_cast(NGX_CONF_OK); +} + +//------------------------------------------------------------------------------ +// make_span_context_value_variable +//------------------------------------------------------------------------------ +static ngx_str_t make_span_context_value_variable( + ngx_pool_t *pool, opentracing::string_view key) { + auto size = 1 + opentracing_context_variable_name.size() + key.size(); + auto data = static_cast(ngx_palloc(pool, size)); + if (data == nullptr) throw std::bad_alloc{}; + + int index = 0; + data[index] = '$'; + index += 1; + + std::copy_n(opentracing_context_variable_name.data(), + opentracing_context_variable_name.size(), data + index); + index += opentracing_context_variable_name.size(); + + std::transform(std::begin(key), std::end(key), data + index, + header_transform); + + return {size, reinterpret_cast(data)}; +} + +//------------------------------------------------------------------------------ +// add_opentracing_tag +//------------------------------------------------------------------------------ +char *add_opentracing_tag(ngx_conf_t *cf, ngx_array_t *tags, ngx_str_t key, + ngx_str_t value) noexcept { + if (!tags) return static_cast(NGX_CONF_ERROR); + + auto tag = static_cast(ngx_array_push(tags)); + if (!tag) return static_cast(NGX_CONF_ERROR); + + ngx_memzero(tag, sizeof(opentracing_tag_t)); + if (tag->key_script.compile(cf, key) != NGX_OK) + return static_cast(NGX_CONF_ERROR); + if (tag->value_script.compile(cf, value) != NGX_OK) + return static_cast(NGX_CONF_ERROR); + + return static_cast(NGX_CONF_OK); +} + +//------------------------------------------------------------------------------ +// propagate_opentracing_context +//------------------------------------------------------------------------------ +// Sets up headers to be added so that the active span context is propagated +// upstream when using ngx_http_proxy_module. +// +// The directive gets translated to the directives +// +// proxy_set_header span_context_key0 $opentracing_context_key0 +// proxy_set_header span_context_key1 $opentracing_context_key1 +// ... +// proxy_set_header span_context_keyN $opentracing_context_keyN +// +// where opentracing_context is a prefix variable that expands to the +// corresponding value of the active span context. +// +// The key value of proxy_set_header isn't allowed to be a variable, so the keys +// used for propagation need to be discovered before this directive is called. +// (See set_tracer below). +// +// This approach was dicussed here +// http://mailman.nginx.org/pipermail/nginx-devel/2018-March/011008.html +char *propagate_opentracing_context(ngx_conf_t *cf, ngx_command_t * /*command*/, + void * /*conf*/) noexcept try { + auto main_conf = static_cast( + ngx_http_conf_get_module_main_conf(cf, ngx_http_opentracing_module)); + if (main_conf->span_context_keys == nullptr) { + return static_cast(NGX_CONF_OK); + } + auto keys = static_cast( + main_conf->span_context_keys->elts); + auto num_keys = static_cast(main_conf->span_context_keys->nelts); + + auto old_args = cf->args; + + ngx_str_t args[] = {ngx_string("proxy_set_header"), {}, {}}; + ngx_array_t args_array; + args_array.elts = static_cast(&args); + args_array.nelts = 3; + + cf->args = &args_array; + for (int key_index = 0; key_index < num_keys; ++key_index) { + args[1] = ngx_str_t{keys[key_index].size(), + reinterpret_cast( + const_cast(keys[key_index].data()))}; + args[2] = make_span_context_value_variable(cf->pool, keys[key_index]); + auto rcode = opentracing_conf_handler(cf, 0); + if (rcode != NGX_OK) { + cf->args = old_args; + return static_cast(NGX_CONF_ERROR); + } + } + cf->args = old_args; + return static_cast(NGX_CONF_OK); +} catch (const std::exception &e) { + ngx_log_error(NGX_LOG_ERR, cf->log, 0, + "opentracing_propatate_context failed: %s", e.what()); + return static_cast(NGX_CONF_ERROR); +} + +//------------------------------------------------------------------------------ +// set_opentracing_tag +//------------------------------------------------------------------------------ +char *set_opentracing_tag(ngx_conf_t *cf, ngx_command_t *command, + void *conf) noexcept { + auto loc_conf = static_cast(conf); + if (!loc_conf->tags) + loc_conf->tags = ngx_array_create(cf->pool, 1, sizeof(opentracing_tag_t)); + auto values = static_cast(cf->args->elts); + return add_opentracing_tag(cf, loc_conf->tags, values[1], values[2]); +} + +//------------------------------------------------------------------------------ +// set_opentracing_operation_name +//------------------------------------------------------------------------------ +char *set_opentracing_operation_name(ngx_conf_t *cf, ngx_command_t *command, + void *conf) noexcept { + auto loc_conf = static_cast(conf); + return set_script(cf, command, loc_conf->operation_name_script); +} + +//------------------------------------------------------------------------------ +// set_opentracing_location_operation_name +//------------------------------------------------------------------------------ +char *set_opentracing_location_operation_name(ngx_conf_t *cf, + ngx_command_t *command, + void *conf) noexcept { + auto loc_conf = static_cast(conf); + return set_script(cf, command, loc_conf->loc_operation_name_script); +} + +//------------------------------------------------------------------------------ +// set_tracer +//------------------------------------------------------------------------------ +char *set_tracer(ngx_conf_t *cf, ngx_command_t *command, + void *conf) noexcept try { + auto main_conf = static_cast( + ngx_http_conf_get_module_main_conf(cf, ngx_http_opentracing_module)); + auto values = static_cast(cf->args->elts); + main_conf->tracer_library = values[1]; + main_conf->tracer_conf_file = values[2]; + + // In order for span context propagation to work, the keys used by a tracer + // need to be known ahead of time. OpenTracing-C++ doesn't currently have any + // API for this, so we attempt to do this by creating and injecting a dummy + // span context. + // + // See also propagate_opentracing_context. + main_conf->span_context_keys = discover_span_context_keys( + cf->pool, cf->log, to_string(main_conf->tracer_library).c_str(), + to_string(main_conf->tracer_conf_file).c_str()); + if (main_conf->span_context_keys == nullptr) { + return static_cast(NGX_CONF_ERROR); + } + + return static_cast(NGX_CONF_OK); +} catch (const std::exception &e) { + ngx_log_error(NGX_LOG_ERR, cf->log, 0, "set_tracer failed: %s", e.what()); + return static_cast(NGX_CONF_ERROR); +} +} // namespace ngx_opentracing diff --git a/opentracing/src/opentracing_directive.h b/opentracing/src/opentracing_directive.h new file mode 100644 index 00000000..52576c3e --- /dev/null +++ b/opentracing/src/opentracing_directive.h @@ -0,0 +1,46 @@ +#pragma once + +extern "C" { +#include +#include +#include +#include +} + +namespace ngx_opentracing { +//------------------------------------------------------------------------------ +// propagate_opentracing_context +//------------------------------------------------------------------------------ +char *propagate_opentracing_context(ngx_conf_t *cf, ngx_command_t *command, + void *conf) noexcept; + +//------------------------------------------------------------------------------ +// add_opentracing_tag +//------------------------------------------------------------------------------ +char *add_opentracing_tag(ngx_conf_t *cf, ngx_array_t *tags, ngx_str_t key, + ngx_str_t value) noexcept; + +//------------------------------------------------------------------------------ +// set_opentracing_tag +//------------------------------------------------------------------------------ +char *set_opentracing_tag(ngx_conf_t *cf, ngx_command_t *command, + void *conf) noexcept; + +//------------------------------------------------------------------------------ +// set_opentracing_operation_name +//------------------------------------------------------------------------------ +char *set_opentracing_operation_name(ngx_conf_t *cf, ngx_command_t *command, + void *conf) noexcept; + +//------------------------------------------------------------------------------ +// set_opentracing_location_operation_name +//------------------------------------------------------------------------------ +char *set_opentracing_location_operation_name(ngx_conf_t *cf, + ngx_command_t *command, + void *conf) noexcept; + +//------------------------------------------------------------------------------ +// set_tracer +//------------------------------------------------------------------------------ +char *set_tracer(ngx_conf_t *cf, ngx_command_t *command, void *conf) noexcept; +} // namespace ngx_opentracing diff --git a/opentracing/src/opentracing_handler.cpp b/opentracing/src/opentracing_handler.cpp index ed2e5df9..375d3f04 100644 --- a/opentracing/src/opentracing_handler.cpp +++ b/opentracing/src/opentracing_handler.cpp @@ -1,44 +1,19 @@ #include "opentracing_handler.h" -#include -#include -#include "opentracing_conf.h" -#include "opentracing_request_instrumentor.h" + +#include "opentracing_context.h" extern "C" { extern ngx_module_t ngx_http_opentracing_module; } namespace ngx_opentracing { -//------------------------------------------------------------------------------ -// OpenTracingContext -//------------------------------------------------------------------------------ -namespace { -class OpenTracingContext { - public: - void on_enter_block(ngx_http_request_t *request); - void on_log_request(ngx_http_request_t *request); - - private: - std::unordered_map - active_instrumentors_; -}; -} // namespace - -//------------------------------------------------------------------------------ -// get_handler_context -//------------------------------------------------------------------------------ -static OpenTracingContext &get_handler_context() { - static OpenTracingContext handler_context{}; - return handler_context; -} - //------------------------------------------------------------------------------ // is_opentracing_enabled //------------------------------------------------------------------------------ static bool is_opentracing_enabled( const ngx_http_request_t *request, const ngx_http_core_loc_conf_t *core_loc_conf, - const opentracing_loc_conf_t *loc_conf) { + const opentracing_loc_conf_t *loc_conf) noexcept { // Check if this is a main request instead of a subrequest. if (request == request->main) return loc_conf->enable; @@ -51,58 +26,50 @@ static bool is_opentracing_enabled( //------------------------------------------------------------------------------ // on_enter_block //------------------------------------------------------------------------------ -void OpenTracingContext::on_enter_block(ngx_http_request_t *request) { +ngx_int_t on_enter_block(ngx_http_request_t *request) noexcept try { auto core_loc_conf = static_cast( ngx_http_get_module_loc_conf(request, ngx_http_core_module)); auto loc_conf = static_cast( ngx_http_get_module_loc_conf(request, ngx_http_opentracing_module)); + if (!is_opentracing_enabled(request, core_loc_conf, loc_conf)) + return NGX_DECLINED; - auto instrumentor_iter = active_instrumentors_.find(request); - if (instrumentor_iter == active_instrumentors_.end()) { - if (!is_opentracing_enabled(request, core_loc_conf, loc_conf)) return; - try { - active_instrumentors_.emplace( - request, - OpenTracingRequestInstrumentor{request, core_loc_conf, loc_conf}); - } catch (const InstrumentationFailure &) { - ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, - "OpenTracing instrumentation failed for request %p", - request); - } + auto context = get_opentracing_context(request); + if (context == nullptr) { + context = new OpenTracingContext{request, core_loc_conf, loc_conf}; + set_opentracing_context(request, context); } else { try { - instrumentor_iter->second.on_change_block(core_loc_conf, loc_conf); - } catch (const InstrumentationFailure &) { - active_instrumentors_.erase(instrumentor_iter); - ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, - "OpenTracing instrumentation failed for request %p", - request); + context->on_change_block(core_loc_conf, loc_conf); + } catch (...) { + // The OpenTracingContext may be broken, destroy it so that we don't + // attempt to continue tracing. + destroy_opentracing_context(request); + + throw; } } -} - -ngx_int_t on_enter_block(ngx_http_request_t *request) { - get_handler_context().on_enter_block(request); + return NGX_DECLINED; +} catch (const std::exception &e) { + ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, + "OpenTracing instrumentation failed for request %p: %s", + request, e.what()); return NGX_DECLINED; } //------------------------------------------------------------------------------ // on_log_request //------------------------------------------------------------------------------ -void OpenTracingContext::on_log_request(ngx_http_request_t *request) { - auto instrumentor_iter = active_instrumentors_.find(request); - if (instrumentor_iter == active_instrumentors_.end()) return; +ngx_int_t on_log_request(ngx_http_request_t *request) noexcept { + auto context = get_opentracing_context(request); + if (context == nullptr) return NGX_DECLINED; try { - instrumentor_iter->second.on_log_request(); - } catch (const InstrumentationFailure &) { + context->on_log_request(); + } catch (const std::exception &e) { ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, - "OpenTracing instrumentation failed for request %p", request); + "OpenTracing instrumentation failed for request %p: %s", + request, e.what()); } - active_instrumentors_.erase(instrumentor_iter); -} - -ngx_int_t on_log_request(ngx_http_request_t *request) { - get_handler_context().on_log_request(request); return NGX_DECLINED; } } // namespace ngx_opentracing diff --git a/opentracing/src/opentracing_handler.h b/opentracing/src/opentracing_handler.h index c64a6b99..d021bc84 100644 --- a/opentracing/src/opentracing_handler.h +++ b/opentracing/src/opentracing_handler.h @@ -5,9 +5,18 @@ extern "C" { #include #include #include + +extern ngx_module_t ngx_http_opentracing_module; } namespace ngx_opentracing { -ngx_int_t on_enter_block(ngx_http_request_t *request); -ngx_int_t on_log_request(ngx_http_request_t *request); +//------------------------------------------------------------------------------ +// on_enter_block +//------------------------------------------------------------------------------ +ngx_int_t on_enter_block(ngx_http_request_t *request) noexcept; + +//------------------------------------------------------------------------------ +// on_log_request +//------------------------------------------------------------------------------ +ngx_int_t on_log_request(ngx_http_request_t *request) noexcept; } // namespace ngx_opentracing diff --git a/opentracing/src/opentracing_request_instrumentor.h b/opentracing/src/opentracing_request_instrumentor.h deleted file mode 100644 index eead2408..00000000 --- a/opentracing/src/opentracing_request_instrumentor.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include "opentracing_conf.h" - -extern "C" { -#include -#include -#include -#include -} - -namespace ngx_opentracing { -struct InstrumentationFailure : std::exception { - InstrumentationFailure() = default; - const char *what() const noexcept override { - return "InstrumentationFailure"; - } -}; - -class OpenTracingRequestInstrumentor { - public: - OpenTracingRequestInstrumentor(ngx_http_request_t *request, - ngx_http_core_loc_conf_t *core_loc_conf, - opentracing_loc_conf_t *loc_conf); - - void on_change_block(ngx_http_core_loc_conf_t *core_loc_conf, - opentracing_loc_conf_t *loc_conf); - - void on_log_request(); - - private: - ngx_http_request_t *request_; - opentracing_main_conf_t *main_conf_; - opentracing_loc_conf_t *loc_conf_; - std::unique_ptr request_span_; - std::unique_ptr span_; - - void on_exit_block(std::chrono::steady_clock::time_point finish_timestamp = - std::chrono::steady_clock::now()); - - void set_request_span_context(); -}; -} // namespace ngx_opentracing diff --git a/opentracing/src/opentracing_variable.cpp b/opentracing/src/opentracing_variable.cpp new file mode 100644 index 00000000..ea337bff --- /dev/null +++ b/opentracing/src/opentracing_variable.cpp @@ -0,0 +1,77 @@ +#include "opentracing_variable.h" + +#include "opentracing_context.h" +#include "utility.h" + +#include + +#include +#include +#include +#include +#include +#include + +extern "C" { +extern ngx_module_t ngx_http_opentracing_module; +} + +namespace ngx_opentracing { +//------------------------------------------------------------------------------ +// opentracing_context_variable_name +//------------------------------------------------------------------------------ +const opentracing::string_view opentracing_context_variable_name{ + "opentracing_context_"}; + +//------------------------------------------------------------------------------ +// expand_opentracing_context +//------------------------------------------------------------------------------ +// Extracts the key specified by the variable's suffix and expands to the +// corresponding value of the active span context. +// +// See propagate_opentracing_context +static ngx_int_t expand_opentracing_context_variable( + ngx_http_request_t* request, ngx_http_variable_value_t* variable_value, + uintptr_t data) noexcept try { + auto variable_name = to_string_view(*reinterpret_cast(data)); + auto prefix_length = opentracing_context_variable_name.size(); + + opentracing::string_view key{variable_name.data() + prefix_length, + variable_name.size() - prefix_length}; + + auto context = get_opentracing_context(request); + if (context == nullptr) { + throw std::runtime_error{"no OpenTracingContext attached to request"}; + } + + auto span_context_value = context->lookup_span_context_value(key); + + variable_value->len = span_context_value.len; + variable_value->valid = 1; + variable_value->no_cacheable = 1; + variable_value->not_found = 0; + variable_value->data = span_context_value.data; + + return NGX_OK; +} catch (const std::exception& e) { + ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, + "failed to expand %s" + " for request %p: %s", + opentracing_context_variable_name.data(), request, e.what()); + return NGX_ERROR; +} + +//------------------------------------------------------------------------------ +// add_variables +//------------------------------------------------------------------------------ +ngx_int_t add_variables(ngx_conf_t* cf) noexcept { + auto opentracing_context = to_ngx_str(opentracing_context_variable_name); + auto opentracing_context_var = ngx_http_add_variable( + cf, &opentracing_context, + NGX_HTTP_VAR_NOCACHEABLE | NGX_HTTP_VAR_NOHASH | NGX_HTTP_VAR_PREFIX); + opentracing_context_var->get_handler = expand_opentracing_context_variable; + opentracing_context_var->data = 0; + + return NGX_OK; +} +} // namespace ngx_opentracing diff --git a/opentracing/src/opentracing_variable.h b/opentracing/src/opentracing_variable.h new file mode 100644 index 00000000..8a56b568 --- /dev/null +++ b/opentracing/src/opentracing_variable.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +extern "C" { +#include +#include +#include +#include +} + +#include +#include +#include + +namespace ngx_opentracing { +//------------------------------------------------------------------------------ +// opentracing_context_variable_name +//------------------------------------------------------------------------------ +extern const opentracing::string_view opentracing_context_variable_name; + +//------------------------------------------------------------------------------ +// add_variables +//------------------------------------------------------------------------------ +ngx_int_t add_variables(ngx_conf_t* cf) noexcept; +} // namespace ngx_opentracing diff --git a/opentracing/src/span_context_querier.cpp b/opentracing/src/span_context_querier.cpp new file mode 100644 index 00000000..ce482398 --- /dev/null +++ b/opentracing/src/span_context_querier.cpp @@ -0,0 +1,80 @@ +#include "span_context_querier.h" + +#include "utility.h" + +#include +#include + +#include +#include +#include +#include + +namespace ngx_opentracing { +//------------------------------------------------------------------------------ +// lookup_value +//------------------------------------------------------------------------------ +ngx_str_t SpanContextQuerier::lookup_value(ngx_http_request_t* request, + const opentracing::Span& span, + opentracing::string_view key) { + if (&span != values_span_) { + expand_span_context_values(request, span); + } + + for (auto& key_value : span_context_expansion_) { + if (key_value.first == key) { + return to_ngx_str(key_value.second); + } + } + + auto ngx_key = to_ngx_str(key); + ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, + "no opentracing context value found for span context key %V " + "for request %p", + &ngx_key, request); + return {}; +} + +//------------------------------------------------------------------------------ +// SpanContextValueExpander +//------------------------------------------------------------------------------ +namespace { +class SpanContextValueExpander : public opentracing::HTTPHeadersWriter { + public: + explicit SpanContextValueExpander( + std::vector>& span_context_expansion) + : span_context_expansion_{span_context_expansion} {} + + opentracing::expected Set( + opentracing::string_view key, + opentracing::string_view value) const override { + std::string key_copy; + key_copy.reserve(key.size()); + std::transform(std::begin(key), std::end(key), std::back_inserter(key_copy), + header_transform); + + span_context_expansion_.emplace_back(std::move(key_copy), value); + return {}; + } + + private: + std::vector>& span_context_expansion_; +}; +} // namespace + +//------------------------------------------------------------------------------ +// expand_span_context_values +//------------------------------------------------------------------------------ +void SpanContextQuerier::expand_span_context_values( + ngx_http_request_t* request, const opentracing::Span& span) { + values_span_ = &span; + span_context_expansion_.clear(); + SpanContextValueExpander carrier{span_context_expansion_}; + auto was_successful = span.tracer().Inject(span.context(), carrier); + if (!was_successful) { + ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, + "Tracer.inject() failed for request %p: %s", request, + was_successful.error().message().c_str()); + } +} +} // namespace ngx_opentracing diff --git a/opentracing/src/span_context_querier.h b/opentracing/src/span_context_querier.h new file mode 100644 index 00000000..12399213 --- /dev/null +++ b/opentracing/src/span_context_querier.h @@ -0,0 +1,34 @@ +#pragma once + +#include "opentracing_conf.h" + +#include + +#include +#include + +extern "C" { +#include +#include +#include +#include +} + +namespace ngx_opentracing { +class SpanContextQuerier { + public: + SpanContextQuerier() noexcept = default; + + ngx_str_t lookup_value(ngx_http_request_t* request, + const opentracing::Span& span, + opentracing::string_view key); + + private: + const opentracing::Span* values_span_ = nullptr; + + std::vector> span_context_expansion_; + + void expand_span_context_values(ngx_http_request_t* request, + const opentracing::Span& span); +}; +} // namespace ngx_opentracing diff --git a/opentracing/src/utility.h b/opentracing/src/utility.h index 0e91c374..9adc5931 100644 --- a/opentracing/src/utility.h +++ b/opentracing/src/utility.h @@ -2,6 +2,8 @@ #pragma once +#include + #include #include #include @@ -20,12 +22,26 @@ inline std::string to_string(const ngx_str_t &ngx_str) { return {reinterpret_cast(ngx_str.data), ngx_str.len}; } +//------------------------------------------------------------------------------ +// to_string_view +//------------------------------------------------------------------------------ +inline opentracing::string_view to_string_view(ngx_str_t s) { + return {reinterpret_cast(s.data), s.len}; +} + //------------------------------------------------------------------------------ // to_ngx_str //------------------------------------------------------------------------------ // Convert a std::string to an ngx_str_t ngx_str_t to_ngx_str(ngx_pool_t *pool, const std::string &s); +inline ngx_str_t to_ngx_str(opentracing::string_view s) { + ngx_str_t result; + result.len = s.size(); + result.data = reinterpret_cast(const_cast(s.data())); + return result; +} + //------------------------------------------------------------------------------ // to_lower_ngx_str //------------------------------------------------------------------------------ @@ -66,4 +82,14 @@ void for_each(const ngx_array_t &array, F f) { auto n = array.nelts; for (size_t i = 0; i < n; ++i) f(elements[i]); } + +//------------------------------------------------------------------------------ +// header_transform +//------------------------------------------------------------------------------ +// Performs the transformations on header characters described by +// http://nginx.org/en/docs/http/ngx_http_core_module.html#var_http_ +inline char header_transform(char c) { + if (c == '-') return '_'; + return static_cast(std::tolower(c)); +} } // namespace ngx_opentracing diff --git a/test/.gitignore b/test/.gitignore deleted file mode 100644 index a8f16ca3..00000000 --- a/test/.gitignore +++ /dev/null @@ -1 +0,0 @@ -nginx-test-* diff --git a/test/01 Simple Request/date.py b/test/01 Simple Request/date.py deleted file mode 100644 index 410cd268..00000000 --- a/test/01 Simple Request/date.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -from http.server import HTTPServer -from http.server import BaseHTTPRequestHandler -import datetime - -class DateHandler(BaseHTTPRequestHandler): - - def do_GET(self): - self.send_response(200) - self.send_header('Content-type', 'text/html') - self.end_headers() - self.wfile.write(str(datetime.datetime.now()).encode()) - return - - -def run(server_class=HTTPServer, handler_class=DateHandler): - server_address = ('localhost', 9001) - httpd = server_class(server_address, handler_class) - try: - httpd.serve_forever() - except KeyboardInterrupt: - httpd.socket.close() - - -if __name__ == '__main__': - run() diff --git a/test/01 Simple Request/nginx.conf.in b/test/01 Simple Request/nginx.conf.in deleted file mode 100644 index 04eb590f..00000000 --- a/test/01 Simple Request/nginx.conf.in +++ /dev/null @@ -1,21 +0,0 @@ -load_module "$NGINX_OPENTRACING_MODULE"; - -events {} - -http { - opentracing on; - opentracing_load_tracer "$MOCKTRACER_LIBRARY" "$MOCKTRACER_CONFIG"; - - upstream backend { - server localhost:9001; - } - - server { - listen 8080; - server_name localhost; - - location = / { - proxy_pass http://backend; - } - } -} diff --git a/test/01 Simple Request/run.sh b/test/01 Simple Request/run.sh deleted file mode 100755 index a39ec271..00000000 --- a/test/01 Simple Request/run.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/bin/bash -set -e - -if [ -z $MOCKTRACER_LIBRARY ] -then - >&2 echo "MOCKTRACER_LIBRARY must be set!" - exit -1 -fi - -if [ -z $NGINX_OPENTRACING_MODULE ] -then - >&2 echo "NGINX_OPENTRACING_MODULE must be set!" - exit -1 -fi - -if [ -z $NGINX_OPENTRACING_TEST_DIR ] -then - >&2 echo "NGINX_OPENTRACING_TEST_DIR must be set!" - exit -1 -fi - -TEST_DIR=`dirname "$0"` -cd "$TEST_DIR" -export TMPDIR="$NGINX_OPENTRACING_TEST_DIR" -TEST_WORK_DIR=`mktemp -dt 01-XXXX` -chmod a+rwx "$TEST_WORK_DIR" -ls -ltr "$TEST_WORK_DIR" -cp -r . "$TEST_WORK_DIR" -mkdir "$TEST_WORK_DIR/logs" -echo "$TEST_WORK_DIR" - -export MOCKTRACER_CONFIG="$TEST_WORK_DIR/tracer-configuration.json" -export MOCKTRACER_OUTPUT_FILE="$TEST_WORK_DIR/spans.json" - -envsubst <"$TEST_WORK_DIR/nginx.conf.in" > "$TEST_WORK_DIR/nginx.conf" -envsubst <"$TEST_WORK_DIR/tracer-configuration.json.in" > "$MOCKTRACER_CONFIG" -chmod a+rx "$MOCKTRACER_CONFIG" -ls -ld "$NGINX_OPENTRACING_TEST_DIR" -ls -ld "$TEST_WORK_DIR" -ls -l "$MOCKTRACER_CONFIG" - -echo "Starting date server" -python3 "$TEST_WORK_DIR/date.py" & -DATE_SERVER_PID=$! -echo "Starting nginx" -nginx -p "$TEST_WORK_DIR" -c "$TEST_WORK_DIR/nginx.conf" -sleep 1 -echo "Sending test data to nginx" -curl localhost:8080/ -kill $DATE_SERVER_PID -nginx -p "$TEST_WORK_DIR" -c "$TEST_WORK_DIR/nginx.conf" -s stop -echo "Verifying results" -sleep 2 -SPAN_COUNT=`jq 'length' < "$MOCKTRACER_OUTPUT_FILE"` -rm -rf "$TEST_WORK_DIR" - -EXPECTED_SPAN_COUNT=2 -if [ $SPAN_COUNT != $EXPECTED_SPAN_COUNT ] -then - >&2 echo "expected SPAN_COUNT == EXPECTED_SPAN_COUNT: got $SPAN_COUNT == $EXPECTED_SPAN_COUNT" - exit -1 -fi diff --git a/test/01 Simple Request/server.py b/test/01 Simple Request/server.py deleted file mode 100644 index 0e698993..00000000 --- a/test/01 Simple Request/server.py +++ /dev/null @@ -1,11 +0,0 @@ -import SimpleHTTPServer -import SocketServer - -PORT = 9001 - -Handler = SimpleHTTPServer.SimpleHTTPRequestHandler - -httpd = SocketServer.TCPServer(("", PORT), Handler) - -print "serving at port", PORT -httpd.serve_forever() diff --git a/test/01 Simple Request/tracer-configuration.json.in b/test/01 Simple Request/tracer-configuration.json.in deleted file mode 100644 index af4bce0d..00000000 --- a/test/01 Simple Request/tracer-configuration.json.in +++ /dev/null @@ -1,3 +0,0 @@ -{ - "output_file": "$MOCKTRACER_OUTPUT_FILE" -} diff --git a/test/Dockerfile-backend b/test/Dockerfile-backend new file mode 100644 index 00000000..456f16e6 --- /dev/null +++ b/test/Dockerfile-backend @@ -0,0 +1,8 @@ +FROM ubuntu:latest +RUN apt-get update -y +RUN apt-get install -y python-pip python-dev build-essential +COPY . /app +WORKDIR /app +RUN pip install -r requirements.txt +ENTRYPOINT ["python"] +CMD ["app.py"] diff --git a/test/environment/.gitignore b/test/environment/.gitignore new file mode 100644 index 00000000..587b79d1 --- /dev/null +++ b/test/environment/.gitignore @@ -0,0 +1,2 @@ +logs/* +traces/* diff --git a/test/environment/app.py b/test/environment/app.py new file mode 100644 index 00000000..c190af61 --- /dev/null +++ b/test/environment/app.py @@ -0,0 +1,16 @@ +from flask import Flask, request +from flask.ext.api import status +app = Flask(__name__) + +@app.route('/has-span-context') +def has_span_context(): + assert 'x-ot-span-context' in request.headers + return 'Flask Dockerized' + +@app.route('/has-span-context-redirect') +def has_span_context_redirect(): + assert 'x-ot-span-context' in request.headers + return '', status.HTTP_301_MOVED_PERMANENTLY + +if __name__ == '__main__': + app.run(debug=True,host='0.0.0.0') diff --git a/test/environment/docker-compose.yaml b/test/environment/docker-compose.yaml new file mode 100644 index 00000000..bcfb9ee2 --- /dev/null +++ b/test/environment/docker-compose.yaml @@ -0,0 +1,47 @@ +version: '2' +services: + + nginx: + image: nginx-opentracing-test/nginx + networks: + testnet: + aliases: + - nginx + privileged: true + volumes: + - ./nginx.conf:/usr/local/nginx/conf/nginx.conf + - ./lsan-suppress.txt:/lsan-suppress.txt + - ./traces/nginx.json:/trace.json + - ./tracer-config.json:/tracer-config.json + - ./logs/debug.log:/usr/local/nginx/logs/debug.log + - ./logs/error.log:/usr/local/nginx/logs/error.log + environment: + - LD_LIBRARY_PATH=/usr/local/lib + - ASAN_OPTIONS=detect_leaks=1 + - LSAN_OPTIONS=suppressions=/lsan-suppress.txt + expose: + - "8080" + ports: + - "8080:8080" + command: + - /usr/local/nginx/sbin/nginx + - -g + - daemon off; + + + backend: + image: nginx-opentracing-test/backend + networks: + testnet: + aliases: + - backend + volumes: + - ./app.py:/app/app.py + expose: + - "5000" + ports: + - "5000:5000" + +networks: + testnet: {} + diff --git a/test/environment/logs/debug.log b/test/environment/logs/debug.log new file mode 100755 index 00000000..8b137891 --- /dev/null +++ b/test/environment/logs/debug.log @@ -0,0 +1 @@ + diff --git a/test/environment/logs/error.log b/test/environment/logs/error.log new file mode 100755 index 00000000..8b137891 --- /dev/null +++ b/test/environment/logs/error.log @@ -0,0 +1 @@ + diff --git a/test/environment/lsan-suppress.txt b/test/environment/lsan-suppress.txt new file mode 100644 index 00000000..29b25aea --- /dev/null +++ b/test/environment/lsan-suppress.txt @@ -0,0 +1 @@ +leak:src/core/nginx.c diff --git a/test/environment/nginx.conf b/test/environment/nginx.conf new file mode 100644 index 00000000..2de0c2d0 --- /dev/null +++ b/test/environment/nginx.conf @@ -0,0 +1,51 @@ +load_module modules/ngx_http_opentracing_module.so; + +events {} + +http { + opentracing on; + opentracing_load_tracer /usr/local/lib/libopentracing_mocktracer.so /tracer-config.json; + + upstream backend { + server backend:5000; + } + + server { + error_log logs/debug.log debug; + listen 8080; + server_name localhost; + + location = / { + opentracing_propagate_context; + proxy_pass http://backend/has-span-context; + } + + location = /manual-propagation { + proxy_set_header x-ot-span-context $opentracing_context_x_ot_span_context; + + # An unused context value should get ignored + proxy_set_header no-such-key $opentracing_context_no_such_key; + + proxy_pass http://backend/has-span-context; + } + + location = /no-trace-locations { + opentracing_trace_locations off; + opentracing_propagate_context; + proxy_pass http://backend/has-span-context; + } + + location = /internal-redirect { + opentracing_propagate_context; + proxy_pass http://backend/has-span-context-redirect; + proxy_intercept_errors on; + error_page 301 =200 /; + } + + location = /custom-tag { + opentracing_propagate_context; + opentracing_tag abc 123; + proxy_pass http://backend/has-span-context; + } + } +} diff --git a/test/environment/tracer-config.json b/test/environment/tracer-config.json new file mode 100644 index 00000000..20ad1b54 --- /dev/null +++ b/test/environment/tracer-config.json @@ -0,0 +1,3 @@ +{ + "output_file":"/trace.json" +} diff --git a/test/environment/traces/nginx.json b/test/environment/traces/nginx.json new file mode 100644 index 00000000..e69de29b diff --git a/test/nginx_opentracing_test.py b/test/nginx_opentracing_test.py new file mode 100644 index 00000000..c005e8cf --- /dev/null +++ b/test/nginx_opentracing_test.py @@ -0,0 +1,134 @@ +import unittest +import shutil +import os +import stat +import tempfile +import subprocess +import time +import docker +import json +import http.client + +class NginxOpenTracingTest(unittest.TestCase): + def setUp(self): + self.testdir = os.getcwd() + self.workdir = os.path.join(tempfile.mkdtemp(), "environment") + shutil.copytree(os.path.join(os.getcwd(), "environment"), + self.workdir) + os.chdir(self.workdir) + + # Make sure trace output is writable + os.chmod(os.path.join(self.workdir, "traces", "nginx.json"), + stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + + self.environment_handle = subprocess.Popen(["docker-compose", "up"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + self.client = docker.from_env() + timeout = time.time() + 5 + while len(self.client.containers.list()) != 2: + if time.time() > timeout: + raise TimeoutError() + time.sleep(0.001) + + # Wait so that backend can come up. + # TODO: replace with something better + time.sleep(2) + + self.conn = http.client.HTTPConnection('localhost', 8080, timeout=5) + self.running = True + + def _logEnvironment(self): + logdir = os.environ["LOG_DIR"] + test_log = os.path.join(logdir, self._testMethodName) + if os.path.exists(test_log): + shutil.rmtree(test_log) + os.mkdir(test_log) + + with open(os.path.join(test_log, "docker_compose_stdout"), "w") as out: + out.write(str(self.environment_stdout)) + with open(os.path.join(test_log, "docker_compose_stderr"), "w") as out: + out.write(str(self.environment_stderr)) + + shutil.copyfile(os.path.join(self.workdir, "traces", "nginx.json"), + os.path.join(test_log, "nginx-trace.json")) + + shutil.copyfile(os.path.join(self.workdir, "logs", "debug.log"), + os.path.join(test_log, "nginx-debug.log")) + shutil.copyfile(os.path.join(self.workdir, "logs", "error.log"), + os.path.join(test_log, "nginx-error.log")) + + def tearDown(self): + self._stopDocker() + logdir = None + + if "LOG_DIR" in os.environ: + self._logEnvironment() + + os.chdir(self.testdir) + self.client.close() + self.conn.close() + + def _stopDocker(self): + if not self.running: + return + self.running = False + subprocess.check_call(["docker-compose", "down"]) + stdout, stderr = self.environment_handle.communicate() + self.environment_stdout = stdout + self.environment_stderr = stderr + self.environment_returncode = self.environment_handle.returncode + + def _stopEnvironment(self): + if not self.running: + return + self._stopDocker() + self.assertEqual(self.environment_returncode, 0) + with open(os.path.join(self.workdir, "traces", "nginx.json")) as f: + self.nginx_traces = json.load(f) + + def testTrivialRequest(self): + self.conn.request("GET", "/") + response = self.conn.getresponse() + self.assertEqual(response.status, 200) + self._stopEnvironment() + + self.assertEqual(len(self.nginx_traces), 2) + + def testManualPropagation(self): + self.conn.request("GET", "/manual-propagation") + response = self.conn.getresponse() + self.assertEqual(response.status, 200) + self._stopEnvironment() + + self.assertEqual(len(self.nginx_traces), 2) + + def testNoTraceLocations(self): + self.conn.request("GET", "/no-trace-locations") + response = self.conn.getresponse() + self.assertEqual(response.status, 200) + self._stopEnvironment() + + self.assertEqual(len(self.nginx_traces), 1) + + def testInternalRediect(self): + self.conn.request("GET", "/internal-redirect") + response = self.conn.getresponse() + self.assertEqual(response.status, 200) + self._stopEnvironment() + + self.assertEqual(len(self.nginx_traces), 3) + + def testCustomTag(self): + self.conn.request("GET", "/custom-tag") + response = self.conn.getresponse() + self.assertEqual(response.status, 200) + self._stopEnvironment() + + self.assertEqual(len(self.nginx_traces), 2) + + location_span = self.nginx_traces[0] + self.assertEqual(location_span["tags"]["abc"], "123") + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/test/requirements.txt b/test/requirements.txt new file mode 100644 index 00000000..3858a50c --- /dev/null +++ b/test/requirements.txt @@ -0,0 +1,2 @@ +Flask==0.10.1 +Flask-API==0.7 diff --git a/zipkin/config b/zipkin/config deleted file mode 100644 index fc80ba86..00000000 --- a/zipkin/config +++ /dev/null @@ -1,17 +0,0 @@ -ngx_addon_name=ngx_http_zipkin_module -ngx_module_type=HTTP -ngx_module_name=$ngx_addon_name -ngx_module_incs= -ngx_module_deps= -ngx_module_srcs=" \ - $ngx_addon_dir/src/ngx_http_zipkin_module.cpp \ -" -ngx_module_libs=" \ - -lzipkin_opentracing \ - -lzipkin \ - -lcurl \ -" - -. auto/module - -ZIPKIN_NGX_SRCS="$ngx_module_srcs" diff --git a/zipkin/config.make b/zipkin/config.make deleted file mode 100644 index 16921b0f..00000000 --- a/zipkin/config.make +++ /dev/null @@ -1,7 +0,0 @@ -# Since nginx build system doesn't normally do C++, there is no CXXFLAGS for us -# to touch, and compilers are understandably unhappy with --std=c++11 on C -# files. Hence, we hack the makefile to add it for just our sources. -for src_file in $ZIPKIN_NGX_SRCS; do - obj_file="$NGX_OBJS/addon/src/`basename $src_file .cpp`.o" - echo "$obj_file : CFLAGS += --std=c++11" >> $NGX_MAKEFILE -done diff --git a/zipkin/doc/Directives.md b/zipkin/doc/Directives.md deleted file mode 100644 index 47392b11..00000000 --- a/zipkin/doc/Directives.md +++ /dev/null @@ -1,31 +0,0 @@ -### `zipkin_service_name` - -- **syntax** `zipkin_service_name ` -- **default**: `nginx` -- **context**: `http` - -Specifies the service name to use for any traces created. - -### `zipkin_collector_host` - -- **syntax** `zipkin_collector_host ` -- **default**: `NA` -- **context**: `http` - -Specifies the host to use when uploading traces. - -### `zipkin_collector_port` - -- **syntax** `zipkin_collector_port ` -- **default**: `9411` -- **context**: `http` - -Specifies the port to use when uploading traces. - -### `zipkin_sample_rate` - -- **syntax** `zipkin_sample_rate ` -- **default**: `1.0` -- **context**: `http` - -Specifies the probability of a span being sampled and reported. diff --git a/zipkin/src/ngx_http_zipkin_module.cpp b/zipkin/src/ngx_http_zipkin_module.cpp deleted file mode 100644 index e1ecdac4..00000000 --- a/zipkin/src/ngx_http_zipkin_module.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include -#include -#include - -extern "C" { -#include -#include -#include -#include - -extern ngx_module_t ngx_http_zipkin_module; -} - -//------------------------------------------------------------------------------ -// to_string -//------------------------------------------------------------------------------ -static inline std::string to_string(const ngx_str_t &ngx_str) { - return {reinterpret_cast(ngx_str.data), ngx_str.len}; -} - -//------------------------------------------------------------------------------ -// zipkin_main_conf_t -//------------------------------------------------------------------------------ -struct zipkin_main_conf_t { - ngx_str_t collector_host; - ngx_str_t collector_port; - ngx_str_t service_name; - ngx_str_t sample_rate; -}; - -//------------------------------------------------------------------------------ -// zipkin_init_worker -//------------------------------------------------------------------------------ -static ngx_int_t zipkin_init_worker(ngx_cycle_t *cycle) { - auto main_conf = static_cast( - ngx_http_cycle_get_module_main_conf(cycle, ngx_http_zipkin_module)); - zipkin::ZipkinOtTracerOptions tracer_options; - if (main_conf->collector_host.data) - tracer_options.collector_host = to_string(main_conf->collector_host); - if (main_conf->collector_port.data) - // TODO: Check for errors here? - tracer_options.collector_port = - std::stoi(to_string(main_conf->collector_port)); - if (main_conf->service_name.data) - tracer_options.service_name = to_string(main_conf->service_name); - else - tracer_options.service_name = "nginx"; - if (main_conf->sample_rate.data) - tracer_options.sample_rate = std::stod(to_string(main_conf->sample_rate)); - else - tracer_options.sample_rate = 1.0; - - auto tracer = makeZipkinOtTracer(tracer_options); - if (!tracer) { - ngx_log_error(NGX_LOG_ERR, cycle->log, 0, "Failed to create Zipkin tracer"); - return NGX_OK; - } - opentracing::Tracer::InitGlobal(std::move(tracer)); - return NGX_OK; -} - -//------------------------------------------------------------------------------ -// create_zipkin_main_conf -//------------------------------------------------------------------------------ -static void *create_zipkin_main_conf(ngx_conf_t *conf) { - auto main_conf = static_cast( - ngx_pcalloc(conf->pool, sizeof(zipkin_main_conf_t))); - // Default initialize members. - *main_conf = zipkin_main_conf_t(); - if (!main_conf) return nullptr; - return main_conf; -} - -//------------------------------------------------------------------------------ -// zipkin_module_ctx -//------------------------------------------------------------------------------ -static ngx_http_module_t zipkin_module_ctx = { - nullptr, /* preconfiguration */ - nullptr, /* postconfiguration */ - create_zipkin_main_conf, /* create main configuration */ - nullptr, /* init main configuration */ - nullptr, /* create server configuration */ - nullptr, /* merge server configuration */ - nullptr, /* create location configuration */ - nullptr /* merge location configuration */ -}; - -//------------------------------------------------------------------------------ -// zipkin_commands -//------------------------------------------------------------------------------ -static ngx_command_t zipkin_commands[] = { - {ngx_string("zipkin_service_name"), NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, - ngx_conf_set_str_slot, NGX_HTTP_MAIN_CONF_OFFSET, - offsetof(zipkin_main_conf_t, service_name), nullptr}, - {ngx_string("zipkin_collector_host"), NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, - ngx_conf_set_str_slot, NGX_HTTP_MAIN_CONF_OFFSET, - offsetof(zipkin_main_conf_t, collector_host), nullptr}, - {ngx_string("zipkin_collector_port"), NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, - ngx_conf_set_str_slot, NGX_HTTP_MAIN_CONF_OFFSET, - offsetof(zipkin_main_conf_t, collector_port), nullptr}, - {ngx_string("zipkin_sample_rate"), NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, - ngx_conf_set_str_slot, NGX_HTTP_MAIN_CONF_OFFSET, - offsetof(zipkin_main_conf_t, sample_rate), nullptr}}; - -//------------------------------------------------------------------------------ -// ngx_http_zipkin_module -//------------------------------------------------------------------------------ -ngx_module_t ngx_http_zipkin_module = {NGX_MODULE_V1, - &zipkin_module_ctx, /* module context */ - zipkin_commands, /* module directives */ - NGX_HTTP_MODULE, /* module type */ - nullptr, /* init master */ - nullptr, /* init module */ - zipkin_init_worker, /* init process */ - nullptr, /* init thread */ - nullptr, /* exit thread */ - nullptr, /* exit process */ - nullptr, /* exit master */ - NGX_MODULE_V1_PADDING};