199 lines
6.6 KiB
Markdown
199 lines
6.6 KiB
Markdown
|
# OpenTracing API for Ruby
|
||
|
|
||
|
[![Build Status](https://travis-ci.org/ngauthier/opentracing-ruby.svg?branch=master)](https://travis-ci.org/ngauthier/opentracing-ruby) [![Code Climate](https://codeclimate.com/github/ngauthier/opentracing-ruby/badges/gpa.svg)](https://codeclimate.com/github/ngauthier/opentracing-ruby) [![Test Coverage](https://codeclimate.com/github/ngauthier/opentracing-ruby/badges/coverage.svg)](https://codeclimate.com/github/ngauthier/opentracing-ruby/coverage)
|
||
|
|
||
|
This package is a Ruby platform API for OpenTracing.
|
||
|
|
||
|
## Required Reading
|
||
|
|
||
|
In order to understand the Ruby platform API, one must first be familiar with the
|
||
|
[OpenTracing project](https://opentracing.io) and
|
||
|
[terminology](https://opentracing.io/docs/overview/) more specifically.
|
||
|
|
||
|
## Installation
|
||
|
|
||
|
Add this line to your application's Gemfile:
|
||
|
|
||
|
```ruby
|
||
|
gem 'opentracing'
|
||
|
```
|
||
|
|
||
|
And then execute:
|
||
|
|
||
|
$ bundle
|
||
|
|
||
|
Or install it yourself as:
|
||
|
|
||
|
$ gem install opentracing
|
||
|
|
||
|
`opentracing` supports Ruby 2.0+.
|
||
|
|
||
|
## Usage
|
||
|
|
||
|
Everyday consumers of this `opentracing` gem really only need to worry
|
||
|
about a couple of key abstractions: the `start_active_span` and `start_span`
|
||
|
methods, the `Span` and `ScopeManager` interfaces, and binding a `Tracer`
|
||
|
at runtime. Here are code snippets demonstrating some important use cases.
|
||
|
|
||
|
### Singleton initialization
|
||
|
|
||
|
As early as possible, call
|
||
|
|
||
|
```ruby
|
||
|
require 'opentracing'
|
||
|
OpenTracing.global_tracer = MyTracerImplementation.new(...)
|
||
|
```
|
||
|
|
||
|
Where `MyTracerImplementation` is your tracer. For testing, you can use
|
||
|
the provided `OpenTracing::Tracer`
|
||
|
|
||
|
### Non-Singleton initialization
|
||
|
|
||
|
If you prefer direct control to singletons, manage ownership of the
|
||
|
`Tracer` implementation explicitly.
|
||
|
|
||
|
### Scopes and within-process propagation
|
||
|
|
||
|
For any thread, at most one `Span` may be "active". Of course there may be many
|
||
|
other `Spans` involved with the thread which are (a) started, (b) not finished,
|
||
|
and yet (c) not "active": perhaps they are waiting for I/O, blocked on a child
|
||
|
`Span`, or otherwise off of the critical path.
|
||
|
|
||
|
It's inconvenient to pass an active `Span` from function to function manually,
|
||
|
so OpenTracing requires that every `Tracer` contains a `ScopeManager` that
|
||
|
grants access to the active `Span` through a `Scope`. Any `Span` may be
|
||
|
transferred to another callback or thread, but not `Scope`.
|
||
|
|
||
|
#### Accessing the active Span through Scope
|
||
|
|
||
|
```ruby
|
||
|
# Access to the active span is straightforward.
|
||
|
|
||
|
span = OpenTracing.active_span
|
||
|
if span
|
||
|
span.set_tag('...', '...')
|
||
|
end
|
||
|
|
||
|
# or
|
||
|
|
||
|
scope = OpenTracing.scope_manager.active
|
||
|
if scope
|
||
|
scope.span.set_tag('...', '...')
|
||
|
end
|
||
|
```
|
||
|
|
||
|
### Starting a new Span
|
||
|
|
||
|
The common case starts a `Scope` that's automatically registered for
|
||
|
intra-process propagation via `ScopeManager`.
|
||
|
|
||
|
Note that `start_active_span('...')` automatically finishes the span on
|
||
|
`Scope#close` (`start_active_span('...', finish_on_close: false)` does not
|
||
|
finish it, in contrast).
|
||
|
|
||
|
```ruby
|
||
|
# Automatic activation of the Span.
|
||
|
# By default the active span will be finished when the returned scope is closed.
|
||
|
# This can be controlled by passing finish_on_close parameter to
|
||
|
# start_active_span
|
||
|
scope = OpenTracing.start_active_span('operation_name')
|
||
|
# Do things.
|
||
|
|
||
|
# Block form of start_active_span
|
||
|
# start_active_span optionally accepts a block. If a block is passed to
|
||
|
# start_active_span it will yield the newly created scope. The scope will
|
||
|
# be closed and its associated span will be finished unless
|
||
|
# finish_on_close: false is passed to start_active_span.
|
||
|
OpenTracing.start_active_span('operation_name') do |scope|
|
||
|
# Do things.
|
||
|
end
|
||
|
|
||
|
# Manual activation of the Span.
|
||
|
# Spans can be managed manually. This is equivalent to the more concise examples
|
||
|
# above.
|
||
|
span = OpenTracing.start_span('operation_name')
|
||
|
OpenTracing.scope_manager.activate(span)
|
||
|
scope = OpenTracing.scope_manager.active
|
||
|
# Do things.
|
||
|
|
||
|
# If there is an active Scope, it will act as the parent to any newly started
|
||
|
# Span unless ignore_active_scope: true is passed to start_span or
|
||
|
# start_active_span.
|
||
|
|
||
|
# create a root span, ignoring the currently active scope (if it's set)
|
||
|
scope = OpenTracing.start_active_span('operation_name', ignore_active_scope: true)
|
||
|
|
||
|
# or
|
||
|
span = OpenTracing.start_span('operation_name', ignore_active_scope: true)
|
||
|
|
||
|
# It's possible to create a child Span given an existing parent Span by
|
||
|
# using the child_of option.
|
||
|
|
||
|
parent_scope = OpenTracing.start_active_span('parent_operation, ignore_active_scope: true)
|
||
|
child_scope = OpenTracing.start_active_span('child_operation', child_of: parent_scope.span)
|
||
|
|
||
|
# or
|
||
|
parent_span = OpenTracing.start_span('parent_operation', ignore_active_scope: true)
|
||
|
child_span = OpenTracing.start_span('child_operation', child_of: parent_span)
|
||
|
|
||
|
```
|
||
|
|
||
|
### Serializing to the wire
|
||
|
|
||
|
Using `Net::HTTP`:
|
||
|
```ruby
|
||
|
client = Net::HTTP.new("myservice.com")
|
||
|
req = Net::HTTP::Post.new("/")
|
||
|
|
||
|
span = OpenTracing.start_span("my_span")
|
||
|
OpenTracing.inject(span.context, OpenTracing::FORMAT_RACK, req)
|
||
|
res = client.request(req)
|
||
|
#...
|
||
|
```
|
||
|
|
||
|
Using Faraday middleware:
|
||
|
```ruby
|
||
|
class TraceMiddleware < Faraday::Middleware
|
||
|
def call(env)
|
||
|
span = OpenTracing.start_span("my_span")
|
||
|
OpenTracing.inject(span.context, OpenTracing::FORMAT_RACK, env)
|
||
|
@app.call(env).on_complete do
|
||
|
span.finish
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
```
|
||
|
|
||
|
### Deserializing from the wire
|
||
|
|
||
|
The OpenTracing Ruby gem provides a specific Rack header extraction format,
|
||
|
since most Ruby web servers get their HTTP Headers from Rack. Keep in mind that
|
||
|
Rack automatically uppercases all headers and replaces dashes with underscores.
|
||
|
This means that if you use dashes and underscores and case-sensitive baggage,
|
||
|
it will not be possible to discern once Rack has processed it.
|
||
|
|
||
|
```ruby
|
||
|
class MyRackApp
|
||
|
def call(env)
|
||
|
extracted_ctx = @tracer.extract(OpenTracing::FORMAT_RACK, env)
|
||
|
span = @tracer.start_span("my_app", child_of: extracted_ctx)
|
||
|
span.finish
|
||
|
[200, {}, ["hello"]]
|
||
|
end
|
||
|
end
|
||
|
```
|
||
|
|
||
|
## Development
|
||
|
|
||
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
||
|
|
||
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
||
|
|
||
|
## Contributing
|
||
|
|
||
|
Bug reports and pull requests are welcome on GitHub at https://github.com/opentracing/opentracing-ruby. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
||
|
|
||
|
## Licensing
|
||
|
|
||
|
[Apache 2.0 License](./LICENSE).
|