Skip to content

Commit

Permalink
Use simplified zipkin v2 span format
Browse files Browse the repository at this point in the history
Libraries are slowly migrating over to a next version span format. The
new format is a lot easier to implement and understand.

More info: openzipkin/zipkin#1499
  • Loading branch information
indrekj committed Apr 13, 2018
1 parent 78815fd commit a9efdd7
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 30 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

OpenTracing Tracer implementation for Zipkin in Ruby

## Requirements

Zipkin version >= 2.0.0

## Installation

Add this line to your application's Gemfile:
Expand Down
27 changes: 9 additions & 18 deletions lib/zipkin/collector.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,37 +18,28 @@ def send_span(span, end_time)
finish_ts = Timestamp.create(end_time)
start_ts = Timestamp.create(span.start_time)
duration = finish_ts - start_ts
is_server = %w[server consumer].include?(span.tags['span.kind'] || 'server')

@buffer << {
traceId: span.context.trace_id,
id: span.context.span_id,
parentId: span.context.parent_id,
name: span.operation_name,
kind: (span.tags['span.kind'] || 'SERVER').upcase,
timestamp: start_ts,
duration: duration,
annotations: LogAnnotations.build(span, @local_endpoint) + [
{
timestamp: start_ts,
value: is_server ? 'sr' : 'cs',
endpoint: @local_endpoint
},
{
timestamp: finish_ts,
value: is_server ? 'ss' : 'cr',
endpoint: @local_endpoint
}
],
binaryAnnotations: build_binary_annotations(span)
debug: false,
shared: false,
localEndpoint: @local_endpoint,
remoteEndpoint: Endpoint.remote_endpoint(span),
annotations: LogAnnotations.build(span),
tags: build_tags(span)
}
end

private

def build_binary_annotations(span)
span.tags.map do |name, value|
{ key: name, value: value.to_s }
end
def build_tags(span)
span.tags.map { |key, value| [key.to_s, value.to_s] }.to_h
end

class Buffer
Expand Down
5 changes: 2 additions & 3 deletions lib/zipkin/collector/log_annotations.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
module Zipkin
class Collector
module LogAnnotations
def self.build(span, endpoint)
def self.build(span)
span.logs.map do |log|
{
timestamp: Timestamp.create(log.fetch(:timestamp)),
value: format_log_value(log),
endpoint: endpoint
value: format_log_value(log)
}
end
end
Expand Down
42 changes: 42 additions & 0 deletions lib/zipkin/endpoint.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,53 @@ class Endpoint
Socket.ip_address_list.reverse.detect(&:ipv4?)
).ip_address

module SpanKind
SERVER = 'server'.freeze
CLIENT = 'client'.freeze
PRODUCER = 'producer'.freeze
CONSUMER = 'consumer'.freeze
end

module PeerInfo
SERVICE = 'peer.service'.freeze
IPV4 = 'peer.ipv4'.freeze
IPV6 = 'peer.ipv6'.freeze
PORT = 'peer.port'.freeze

def self.keys
[SERVICE, IPV4, IPV6, PORT]
end
end

def self.local_endpoint(service_name)
{
serviceName: service_name,
ipv4: LOCAL_IP
}
end

def self.remote_endpoint(span)
tags = span.tags
kind = tags['span.kind'] || SpanKind::SERVER

case kind
when SpanKind::SERVER, SpanKind::CLIENT
return nil if (tags.keys & PeerInfo.keys).empty?

{
serviceName: tags[PeerInfo::SERVICE],
ipv4: tags[PeerInfo::IPV4],
ipv6: tags[PeerInfo::IPV6],
port: tags[PeerInfo::PORT]
}
when SpanKind::PRODUCER, SpanKind::CONSUMER
{
serviceName: 'broker'
}
else
warn "Unkown span kind: #{kind}"
nil
end
end
end
end
2 changes: 1 addition & 1 deletion lib/zipkin/json_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class JsonClient
def initialize(url:, collector:, flush_interval:, logger: Logger.new(STDOUT))
@collector = collector
@flush_interval = flush_interval
@spans_uri = URI.parse("#{url}/api/v1/spans")
@spans_uri = URI.parse("#{url}/api/v2/spans")
@logger = logger
end

Expand Down
4 changes: 3 additions & 1 deletion script/create_trace
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ tracer2 = Zipkin::Tracer.build(url: url, service_name: 'downstream-service')

outer_span = tracer1.start_span(
'receive request',
tags: { 'span.kind' => 'server' }
tags: {
'span.kind' => 'server'
}
)
sleep 1

Expand Down
11 changes: 4 additions & 7 deletions spec/zipkin/collector/log_annotations_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,24 @@

RSpec.describe Zipkin::Collector::LogAnnotations do
let(:span) { Zipkin::Span.new(nil, 'operation_name', nil) }
let(:endpoint) { 'local-endpoint' }

context 'when log includes only event and timestamp' do
it 'uses event as the annotation value' do
message = 'some message'
span.log_kv(event: message)
expect(described_class.build(span, endpoint)).to include(
expect(described_class.build(span)).to include(
timestamp: instance_of(Integer),
value: message,
endpoint: endpoint
value: message
)
end
end

context 'when log includes multiple fields' do
it 'converts fields into string form' do
span.log_kv(foo: 'bar', baz: 'buz')
expect(described_class.build(span, endpoint)).to include(
expect(described_class.build(span)).to include(
timestamp: instance_of(Integer),
value: 'foo=bar baz=buz',
endpoint: endpoint
value: 'foo=bar baz=buz'
)
end
end
Expand Down
81 changes: 81 additions & 0 deletions spec/zipkin/endpoint_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
require 'spec_helper'

RSpec.describe Zipkin::Endpoint do
let(:span) { Zipkin::Span.new(nil, 'operation_name', nil) }

shared_examples 'a rpc endpoint' do
it 'returns nil if no peer info' do
expect(remote_endpoint(span)).to eq(nil)
end

it 'includes service name' do
service_name = 'service-name'
span.set_tag('peer.service', service_name)
expect(remote_endpoint(span)).to include(serviceName: service_name)
end

it 'includes ipv4 address' do
ipv4 = '8.7.6.5'
span.set_tag('peer.ipv4', ipv4)
expect(remote_endpoint(span)).to include(ipv4: ipv4)
end

it 'includes ipv6 address' do
ipv6 = '2001:0db8:85a3:0000:0000:8a2e:0370:7334'
span.set_tag('peer.ipv6', ipv6)
expect(remote_endpoint(span)).to include(ipv6: ipv6)
end

it 'includes port' do
port = 3000
span.set_tag('peer.port', port)
expect(remote_endpoint(span)).to include(port: port)
end
end

describe '.remote_endpoint' do
context 'when span kind is undefined' do
it_behaves_like 'a rpc endpoint'
end

context 'when span kind is server' do
before { span.set_tag('span.kind', 'server') }

it_behaves_like 'a rpc endpoint'
end

context 'when span kind is client' do
before { span.set_tag('span.kind', 'client') }

it_behaves_like 'a rpc endpoint'
end

context 'when span kind is producer' do
before { span.set_tag('span.kind', 'producer') }

it 'returns broker as service name' do
expect(remote_endpoint(span)).to eq(serviceName: 'broker')
end
end

context 'when span kind is consumer' do
before { span.set_tag('span.kind', 'consumer') }

it 'returns broker as service name' do
expect(remote_endpoint(span)).to eq(serviceName: 'broker')
end
end

context 'when unknown span kind' do
before { span.set_tag('span.kind', 'something-else') }

it 'returns nil' do
expect(remote_endpoint(span)).to eq(nil)
end
end
end

def remote_endpoint(span)
described_class.remote_endpoint(span)
end
end

0 comments on commit a9efdd7

Please sign in to comment.