Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use simplified zipkin v2 span format #12

Merged
merged 1 commit into from
Apr 16, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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