From 565968529ec373ccdb275d4d2de43b672c6da933 Mon Sep 17 00:00:00 2001 From: Don Morrison Date: Wed, 16 Oct 2019 08:55:56 -0700 Subject: [PATCH 1/4] Add 'ex-http' service to docker-compose --- docker-compose.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 8a48f1a0d0..cb872601df 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -24,6 +24,11 @@ services: context: . working_dir: /app + ex-http: + <<: *base + command: ./start_server.sh + working_dir: /app/examples/http + sdk: <<: *base working_dir: /app/sdk From 55ff8c633e7e16a8661d417b61ba6280e1f0330f Mon Sep 17 00:00:00 2001 From: Don Morrison Date: Tue, 5 Nov 2019 04:14:03 -0800 Subject: [PATCH 2/4] Update SDK::TracerFactory to inherit from API --- sdk/lib/opentelemetry/sdk/trace/tracer_factory.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/lib/opentelemetry/sdk/trace/tracer_factory.rb b/sdk/lib/opentelemetry/sdk/trace/tracer_factory.rb index 5f17334bf3..8a021c82b4 100644 --- a/sdk/lib/opentelemetry/sdk/trace/tracer_factory.rb +++ b/sdk/lib/opentelemetry/sdk/trace/tracer_factory.rb @@ -8,7 +8,7 @@ module OpenTelemetry module SDK module Trace # {TracerFactory} is the SDK implementation of {OpenTelemetry::Trace::TracerFactory}. - class TracerFactory + class TracerFactory < OpenTelemetry::Trace::TracerFactory Key = Struct.new(:name, :version) private_constant(:Key) From 18e2dc6ffadf26d941534874281bd3d30f1a593a Mon Sep 17 00:00:00 2001 From: Don Morrison Date: Wed, 23 Oct 2019 00:00:12 -0700 Subject: [PATCH 3/4] Add http example Adds simple client and server examples using a console exporter to demonstrate instrumentation. --- examples/http/Gemfile | 8 ++++ examples/http/Gemfile.lock | 39 +++++++++++++++++++ examples/http/client.rb | 49 +++++++++++++++++++++++ examples/http/server.rb | 73 +++++++++++++++++++++++++++++++++++ examples/http/start_server.sh | 4 ++ 5 files changed, 173 insertions(+) create mode 100644 examples/http/Gemfile create mode 100644 examples/http/Gemfile.lock create mode 100755 examples/http/client.rb create mode 100755 examples/http/server.rb create mode 100755 examples/http/start_server.sh diff --git a/examples/http/Gemfile b/examples/http/Gemfile new file mode 100644 index 0000000000..03ec98ce43 --- /dev/null +++ b/examples/http/Gemfile @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gem "faraday", "~> 0.16.1" +gem "opentelemetry-api", path: "../../api" +gem "opentelemetry-sdk", path: "../../sdk" +gem "sinatra", "~> 2.0" diff --git a/examples/http/Gemfile.lock b/examples/http/Gemfile.lock new file mode 100644 index 0000000000..15518cc564 --- /dev/null +++ b/examples/http/Gemfile.lock @@ -0,0 +1,39 @@ +PATH + remote: ../../api + specs: + opentelemetry-api (0.0.0) + +PATH + remote: ../../sdk + specs: + opentelemetry-sdk (0.0.0) + opentelemetry-api (~> 0.0) + +GEM + remote: https://rubygems.org/ + specs: + faraday (0.16.2) + multipart-post (>= 1.2, < 3) + multipart-post (2.1.1) + mustermann (1.0.3) + rack (2.0.7) + rack-protection (2.0.7) + rack + sinatra (2.0.7) + mustermann (~> 1.0) + rack (~> 2.0) + rack-protection (= 2.0.7) + tilt (~> 2.0) + tilt (2.0.10) + +PLATFORMS + ruby + +DEPENDENCIES + faraday (~> 0.16.1) + opentelemetry-api! + opentelemetry-sdk! + sinatra (~> 2.0) + +BUNDLED WITH + 2.0.2 diff --git a/examples/http/client.rb b/examples/http/client.rb new file mode 100755 index 0000000000..bb83719208 --- /dev/null +++ b/examples/http/client.rb @@ -0,0 +1,49 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Copyright 2019 OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 +require 'rubygems' +require 'bundler/setup' +require 'faraday' +# Require otel-ruby +require 'opentelemetry/sdk' + +# Allow setting the host from the ENV +host = ENV.fetch('HTTP_EXAMPLE_HOST', '0.0.0.0') + +SDK = OpenTelemetry::SDK +OpenTelemetry.tracer_factory = SDK::Trace::TracerFactory.new + +# Configure tracer +exporter = SDK::Trace::Export::ConsoleSpanExporter.new +processor = SDK::Trace::Export::SimpleSpanProcessor.new(exporter) +OpenTelemetry.tracer_factory.add_span_processor(processor) +tracer = OpenTelemetry.tracer_factory.tracer('faraday', 'semver:1.0') +formatter = OpenTelemetry.tracer_factory.http_text_format + +connection = Faraday.new("http://#{host}:4567") +url = '/hello' + +# For attribute naming, see: +# https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-semantic-conventions.md#http-client + +# Span name should be set to URI path value: +tracer.in_span( + url, + attributes: { + 'component' => 'http', + 'http.method' => 'GET', + }, + kind: :client +) do |span| + response = connection.get(url) do |request| + # Inject context into request headers + formatter.inject(span.context, request.headers) + end + + span.set_attribute('http.url', response.env.url.to_s) + span.set_attribute('http.status_code', response.status) + span.set_attribute('http.status_text', response.reason_phrase) +end diff --git a/examples/http/server.rb b/examples/http/server.rb new file mode 100755 index 0000000000..3cccffcdcc --- /dev/null +++ b/examples/http/server.rb @@ -0,0 +1,73 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Copyright 2019 OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'rubygems' +require 'bundler/setup' +require 'sinatra/base' +# Require otel-ruby +require 'opentelemetry/sdk' + +SDK = OpenTelemetry::SDK +OpenTelemetry.tracer_factory = SDK::Trace::TracerFactory.new + +exporter = SDK::Trace::Export::ConsoleSpanExporter.new +processor = SDK::Trace::Export::SimpleSpanProcessor.new(exporter) +OpenTelemetry.tracer_factory.add_span_processor(processor) + +# Rack middleware to extract span context, create child span, and add +# attributes/events to the span +class OpenTelemetryMiddleware + def initialize(app) + @app = app + @formatter = OpenTelemetry.tracer_factory.http_text_format + @tracer = OpenTelemetry.tracer_factory.tracer('sinatra', 'semver:1.0') + end + + def call(env) + # Extract context from request headers + context = @formatter.extract(env) + + status, headers, response_body = 200, {}, '' + + # Span name SHOULD be set to route: + span_name = env['PATH_INFO'] + + # For attribute naming, see + # https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-semantic-conventions.md#http-server + + # Span kind MUST be `:server` for a HTTP server span + @tracer.in_span( + span_name, + attributes: { + 'component' => 'http', + 'http.method' => env['REQUEST_METHOD'], + 'http.route' => env['PATH_INFO'], + 'http.url' => env['REQUEST_URI'], + }, + kind: :server, + with_parent_context: context + ) do |span| + # Run application stack + status, headers, response_body = @app.call(env) + + span.set_attribute('http.status_code', status) + end + + [status, headers, response_body] + end +end + +class App < Sinatra::Base + set :bind, '0.0.0.0' + use OpenTelemetryMiddleware + + get '/hello' do + 'Hello World!' + end + + run! if app_file == $0 +end diff --git a/examples/http/start_server.sh b/examples/http/start_server.sh new file mode 100755 index 0000000000..2e4ef5f746 --- /dev/null +++ b/examples/http/start_server.sh @@ -0,0 +1,4 @@ +#! /bin/bash + +# Start the server, wait a bit, and provide a command line to run the client +./server.rb & (sleep 5; /bin/bash) From 3c409e9a0af5de988d1c3f9928d7539148a496f8 Mon Sep 17 00:00:00 2001 From: Don Morrison Date: Wed, 23 Oct 2019 00:00:22 -0700 Subject: [PATCH 4/4] Add README --- examples/http/README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 examples/http/README.md diff --git a/examples/http/README.md b/examples/http/README.md new file mode 100644 index 0000000000..eef98a2b7c --- /dev/null +++ b/examples/http/README.md @@ -0,0 +1,21 @@ +# OpenTelemetry Ruby Example + +## HTTP + +This is a simple example that demonstrates tracing an HTTP request from client to server. The example shows several aspects of tracing, such as: + +* Using the `TracerFactory` +* Span Attributes +* Using the console exporter + +### Running the example + +The example uses Docker Compose to make it a bit easier to get things up and running. + +1. Follow the `Developer Setup` instructions in [the main README](../../README.md) +1. Run the server using the `ex-http` compose service + * `docker-compose run ex-http` +1. After a few seconds, an interactive shell should appear +1. Run the client + * `./client.rb` +1. You should see console exporter output for both the client and server sessions