finagle-thrift 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/finagle-thrift.rb +28 -0
- data/lib/finagle-thrift/client.rb +85 -0
- data/lib/finagle-thrift/thrift/tracing_constants.rb +18 -0
- data/lib/finagle-thrift/thrift/tracing_types.rb +210 -0
- data/lib/finagle-thrift/thrift_client.rb +10 -0
- data/lib/finagle-thrift/trace.rb +135 -0
- data/lib/finagle-thrift/tracer.rb +245 -0
- data/lib/finagle-thrift/version.rb +3 -0
- metadata +75 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
require 'thrift'
|
4
|
+
|
5
|
+
require 'finagle-thrift/thrift/tracing_types'
|
6
|
+
# require 'finagle-thrift/thrift/tracing_constants'
|
7
|
+
|
8
|
+
require 'finagle-thrift/client'
|
9
|
+
require 'finagle-thrift/thrift_client'
|
10
|
+
require 'finagle-thrift/trace'
|
11
|
+
require 'finagle-thrift/tracer'
|
12
|
+
|
13
|
+
module FinagleThrift
|
14
|
+
extend self
|
15
|
+
def enable_tracing!(service, client_id = nil, service_name = nil)
|
16
|
+
raise ArgumentError, "client_id must be nil or of type FinagleThrift::ClientId" if client_id && !client_id.is_a?(FinagleThrift::ClientId)
|
17
|
+
class << service
|
18
|
+
include ::FinagleThrift::ThriftClient
|
19
|
+
end
|
20
|
+
|
21
|
+
client_class = service.client_class
|
22
|
+
client_class.class_eval do
|
23
|
+
include ::FinagleThrift::Client
|
24
|
+
end
|
25
|
+
client_class.send(:define_method, :client_id) { client_id }
|
26
|
+
client_class.send(:define_method, :trace_service_name) { service_name }
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module FinagleThrift
|
2
|
+
module Client
|
3
|
+
CanTraceMethodName = "__can__finagle__trace__v3__"
|
4
|
+
|
5
|
+
include ::Thrift::Client
|
6
|
+
|
7
|
+
alias_method :_orig_send_message, :send_message
|
8
|
+
alias_method :_orig_receive_message, :receive_message
|
9
|
+
|
10
|
+
def initialize(iprot, oprot=nil)
|
11
|
+
super
|
12
|
+
@upgraded = false
|
13
|
+
@client_port = 0
|
14
|
+
attempt_upgrade!
|
15
|
+
end
|
16
|
+
|
17
|
+
def send_message(name, args_class, args = {})
|
18
|
+
if @upgraded
|
19
|
+
header = ::FinagleThrift::RequestHeader.new
|
20
|
+
header.trace_id = Trace.id.trace_id.to_i
|
21
|
+
header.parent_span_id = Trace.id.parent_id.to_i
|
22
|
+
header.span_id = Trace.id.span_id.to_i
|
23
|
+
header.sampled = Trace.id.sampled?
|
24
|
+
header.debug = false
|
25
|
+
|
26
|
+
header.client_id = client_id if client_id
|
27
|
+
|
28
|
+
header.write(@oprot)
|
29
|
+
end
|
30
|
+
Trace.record(Trace::Annotation.new(Trace::Annotation::CLIENT_SEND, self.endpoint))
|
31
|
+
|
32
|
+
_orig_send_message(name, args_class, args)
|
33
|
+
end
|
34
|
+
|
35
|
+
def receive_message(klass)
|
36
|
+
if @upgraded
|
37
|
+
response = ::FinagleThrift::ResponseHeader.new
|
38
|
+
response.read(@iprot)
|
39
|
+
end
|
40
|
+
result = _orig_receive_message(klass)
|
41
|
+
Trace.record(Trace::Annotation.new(Trace::Annotation::CLIENT_RECV, self.endpoint))
|
42
|
+
result
|
43
|
+
end
|
44
|
+
|
45
|
+
protected
|
46
|
+
def client_id
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
|
50
|
+
def trace_service_name
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
|
54
|
+
def endpoint
|
55
|
+
@endpoint ||= Trace.default_endpoint.with_port(@client_port).with_service_name(trace_service_name)
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
def attempt_upgrade!
|
60
|
+
_orig_send_message(CanTraceMethodName, ::FinagleThrift::ConnectionOptions)
|
61
|
+
begin
|
62
|
+
extract_client_port
|
63
|
+
_orig_receive_message(::FinagleThrift::UpgradeReply)
|
64
|
+
@upgraded = true
|
65
|
+
rescue ::Thrift::ApplicationException
|
66
|
+
@upgraded = false
|
67
|
+
end
|
68
|
+
rescue
|
69
|
+
@upgraded = false
|
70
|
+
raise
|
71
|
+
end
|
72
|
+
|
73
|
+
def extract_client_port
|
74
|
+
@client_port = begin
|
75
|
+
# im sorry
|
76
|
+
trans = @oprot.instance_variable_get("@trans")
|
77
|
+
transport = trans.instance_variable_get("@transport")
|
78
|
+
handle = transport.instance_variable_get("@handle")
|
79
|
+
Socket.unpack_sockaddr_in(handle.getsockname).first
|
80
|
+
rescue
|
81
|
+
0
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#
|
2
|
+
# Autogenerated by Thrift
|
3
|
+
#
|
4
|
+
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
|
5
|
+
#
|
6
|
+
|
7
|
+
require 'tracing_types'
|
8
|
+
|
9
|
+
module FinagleThrift
|
10
|
+
CLIENT_SEND = %q"cs"
|
11
|
+
|
12
|
+
CLIENT_RECV = %q"cr"
|
13
|
+
|
14
|
+
SERVER_SEND = %q"ss"
|
15
|
+
|
16
|
+
SERVER_RECV = %q"sr"
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,210 @@
|
|
1
|
+
#
|
2
|
+
# Autogenerated by Thrift
|
3
|
+
#
|
4
|
+
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
|
5
|
+
#
|
6
|
+
|
7
|
+
|
8
|
+
module FinagleThrift
|
9
|
+
module AnnotationType
|
10
|
+
BOOL = 0
|
11
|
+
BYTES = 1
|
12
|
+
I16 = 2
|
13
|
+
I32 = 3
|
14
|
+
I64 = 4
|
15
|
+
DOUBLE = 5
|
16
|
+
STRING = 6
|
17
|
+
VALUE_MAP = {0 => "BOOL", 1 => "BYTES", 2 => "I16", 3 => "I32", 4 => "I64", 5 => "DOUBLE", 6 => "STRING"}
|
18
|
+
VALID_VALUES = Set.new([BOOL, BYTES, I16, I32, I64, DOUBLE, STRING]).freeze
|
19
|
+
end
|
20
|
+
|
21
|
+
class Endpoint
|
22
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
23
|
+
IPV4 = 1
|
24
|
+
PORT = 2
|
25
|
+
SERVICE_NAME = 3
|
26
|
+
|
27
|
+
FIELDS = {
|
28
|
+
IPV4 => {:type => ::Thrift::Types::I32, :name => 'ipv4'},
|
29
|
+
PORT => {:type => ::Thrift::Types::I16, :name => 'port'},
|
30
|
+
SERVICE_NAME => {:type => ::Thrift::Types::STRING, :name => 'service_name'}
|
31
|
+
}
|
32
|
+
|
33
|
+
def struct_fields; FIELDS; end
|
34
|
+
|
35
|
+
def validate
|
36
|
+
end
|
37
|
+
|
38
|
+
::Thrift::Struct.generate_accessors self
|
39
|
+
end
|
40
|
+
|
41
|
+
class Annotation
|
42
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
43
|
+
TIMESTAMP = 1
|
44
|
+
VALUE = 2
|
45
|
+
HOST = 3
|
46
|
+
|
47
|
+
FIELDS = {
|
48
|
+
TIMESTAMP => {:type => ::Thrift::Types::I64, :name => 'timestamp'},
|
49
|
+
VALUE => {:type => ::Thrift::Types::STRING, :name => 'value'},
|
50
|
+
HOST => {:type => ::Thrift::Types::STRUCT, :name => 'host', :class => FinagleThrift::Endpoint, :optional => true}
|
51
|
+
}
|
52
|
+
|
53
|
+
def struct_fields; FIELDS; end
|
54
|
+
|
55
|
+
def validate
|
56
|
+
end
|
57
|
+
|
58
|
+
::Thrift::Struct.generate_accessors self
|
59
|
+
end
|
60
|
+
|
61
|
+
class BinaryAnnotation
|
62
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
63
|
+
KEY = 1
|
64
|
+
VALUE = 2
|
65
|
+
ANNOTATION_TYPE = 3
|
66
|
+
HOST = 4
|
67
|
+
|
68
|
+
FIELDS = {
|
69
|
+
KEY => {:type => ::Thrift::Types::STRING, :name => 'key'},
|
70
|
+
VALUE => {:type => ::Thrift::Types::STRING, :name => 'value', :binary => true},
|
71
|
+
ANNOTATION_TYPE => {:type => ::Thrift::Types::I32, :name => 'annotation_type', :enum_class => FinagleThrift::AnnotationType},
|
72
|
+
HOST => {:type => ::Thrift::Types::STRUCT, :name => 'host', :class => FinagleThrift::Endpoint, :optional => true}
|
73
|
+
}
|
74
|
+
|
75
|
+
def struct_fields; FIELDS; end
|
76
|
+
|
77
|
+
def validate
|
78
|
+
unless @annotation_type.nil? || FinagleThrift::AnnotationType::VALID_VALUES.include?(@annotation_type)
|
79
|
+
raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Invalid value of field annotation_type!')
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
::Thrift::Struct.generate_accessors self
|
84
|
+
end
|
85
|
+
|
86
|
+
class Span
|
87
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
88
|
+
TRACE_ID = 1
|
89
|
+
NAME = 3
|
90
|
+
ID = 4
|
91
|
+
PARENT_ID = 5
|
92
|
+
ANNOTATIONS = 6
|
93
|
+
BINARY_ANNOTATIONS = 8
|
94
|
+
|
95
|
+
FIELDS = {
|
96
|
+
TRACE_ID => {:type => ::Thrift::Types::I64, :name => 'trace_id'},
|
97
|
+
NAME => {:type => ::Thrift::Types::STRING, :name => 'name'},
|
98
|
+
ID => {:type => ::Thrift::Types::I64, :name => 'id'},
|
99
|
+
PARENT_ID => {:type => ::Thrift::Types::I64, :name => 'parent_id', :optional => true},
|
100
|
+
ANNOTATIONS => {:type => ::Thrift::Types::LIST, :name => 'annotations', :element => {:type => ::Thrift::Types::STRUCT, :class => FinagleThrift::Annotation}},
|
101
|
+
BINARY_ANNOTATIONS => {:type => ::Thrift::Types::LIST, :name => 'binary_annotations', :element => {:type => ::Thrift::Types::STRUCT, :class => FinagleThrift::BinaryAnnotation}}
|
102
|
+
}
|
103
|
+
|
104
|
+
def struct_fields; FIELDS; end
|
105
|
+
|
106
|
+
def validate
|
107
|
+
end
|
108
|
+
|
109
|
+
::Thrift::Struct.generate_accessors self
|
110
|
+
end
|
111
|
+
|
112
|
+
# At connection time, we can let the server know who we are so
|
113
|
+
# they can book keep and optionally reject unknown clients.
|
114
|
+
class ClientId
|
115
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
116
|
+
NAME = 1
|
117
|
+
|
118
|
+
FIELDS = {
|
119
|
+
NAME => {:type => ::Thrift::Types::STRING, :name => 'name'}
|
120
|
+
}
|
121
|
+
|
122
|
+
def struct_fields; FIELDS; end
|
123
|
+
|
124
|
+
def validate
|
125
|
+
end
|
126
|
+
|
127
|
+
::Thrift::Struct.generate_accessors self
|
128
|
+
end
|
129
|
+
|
130
|
+
# RequestHeader defines headers for the request. These carry the span data, and
|
131
|
+
# a flag indicating whether the request is to be debugged.
|
132
|
+
class RequestHeader
|
133
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
134
|
+
TRACE_ID = 1
|
135
|
+
SPAN_ID = 2
|
136
|
+
PARENT_SPAN_ID = 3
|
137
|
+
DEBUG = 4
|
138
|
+
SAMPLED = 5
|
139
|
+
CLIENT_ID = 6
|
140
|
+
|
141
|
+
FIELDS = {
|
142
|
+
TRACE_ID => {:type => ::Thrift::Types::I64, :name => 'trace_id'},
|
143
|
+
SPAN_ID => {:type => ::Thrift::Types::I64, :name => 'span_id'},
|
144
|
+
PARENT_SPAN_ID => {:type => ::Thrift::Types::I64, :name => 'parent_span_id', :optional => true},
|
145
|
+
DEBUG => {:type => ::Thrift::Types::BOOL, :name => 'debug'},
|
146
|
+
SAMPLED => {:type => ::Thrift::Types::BOOL, :name => 'sampled', :optional => true},
|
147
|
+
CLIENT_ID => {:type => ::Thrift::Types::STRUCT, :name => 'client_id', :class => FinagleThrift::ClientId, :optional => true}
|
148
|
+
}
|
149
|
+
|
150
|
+
def struct_fields; FIELDS; end
|
151
|
+
|
152
|
+
def validate
|
153
|
+
end
|
154
|
+
|
155
|
+
::Thrift::Struct.generate_accessors self
|
156
|
+
end
|
157
|
+
|
158
|
+
# The Response carries a reply header for tracing. These are
|
159
|
+
# empty unless the request is being debugged, in which case a
|
160
|
+
# transcript is copied.
|
161
|
+
class ResponseHeader
|
162
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
163
|
+
SPANS = 1
|
164
|
+
|
165
|
+
FIELDS = {
|
166
|
+
SPANS => {:type => ::Thrift::Types::LIST, :name => 'spans', :element => {:type => ::Thrift::Types::STRUCT, :class => FinagleThrift::Span}}
|
167
|
+
}
|
168
|
+
|
169
|
+
def struct_fields; FIELDS; end
|
170
|
+
|
171
|
+
def validate
|
172
|
+
end
|
173
|
+
|
174
|
+
::Thrift::Struct.generate_accessors self
|
175
|
+
end
|
176
|
+
|
177
|
+
# These are connection-level options negotiated during protocol
|
178
|
+
# upgrade.
|
179
|
+
class ConnectionOptions
|
180
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
181
|
+
|
182
|
+
FIELDS = {
|
183
|
+
|
184
|
+
}
|
185
|
+
|
186
|
+
def struct_fields; FIELDS; end
|
187
|
+
|
188
|
+
def validate
|
189
|
+
end
|
190
|
+
|
191
|
+
::Thrift::Struct.generate_accessors self
|
192
|
+
end
|
193
|
+
|
194
|
+
# This is the struct that a successful upgrade will reply with.
|
195
|
+
class UpgradeReply
|
196
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
197
|
+
|
198
|
+
FIELDS = {
|
199
|
+
|
200
|
+
}
|
201
|
+
|
202
|
+
def struct_fields; FIELDS; end
|
203
|
+
|
204
|
+
def validate
|
205
|
+
end
|
206
|
+
|
207
|
+
::Thrift::Struct.generate_accessors self
|
208
|
+
end
|
209
|
+
|
210
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
module Trace
|
2
|
+
extend self
|
3
|
+
DEFAULT_SAMPLE_RATE = 0.001
|
4
|
+
TRACE_ID_UPPER_BOUND = 2 ** 64
|
5
|
+
|
6
|
+
def id
|
7
|
+
if stack.empty?
|
8
|
+
span_id = generate_id
|
9
|
+
trace_id = TraceId.new(span_id, nil, span_id, should_sample?)
|
10
|
+
stack.push(trace_id)
|
11
|
+
end
|
12
|
+
stack.last
|
13
|
+
end
|
14
|
+
|
15
|
+
def push(trace_id)
|
16
|
+
stack.push(trace_id)
|
17
|
+
if block_given?
|
18
|
+
begin
|
19
|
+
yield
|
20
|
+
ensure
|
21
|
+
pop
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def pop
|
27
|
+
stack.pop
|
28
|
+
end
|
29
|
+
|
30
|
+
def unwind
|
31
|
+
if block_given?
|
32
|
+
begin
|
33
|
+
saved_stack = stack.dup
|
34
|
+
yield
|
35
|
+
ensure
|
36
|
+
@stack = saved
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def record(annotation)
|
42
|
+
tracer.record(id, annotation) unless stack.empty?
|
43
|
+
end
|
44
|
+
|
45
|
+
def set_rpc_name(name)
|
46
|
+
tracer.set_rpc_name(id, name) unless stack.empty?
|
47
|
+
end
|
48
|
+
|
49
|
+
def sample_rate=(sample_rate)
|
50
|
+
if sample_rate > 1 || sample_rate < 0
|
51
|
+
raise ArgumentError.new("sample rate must be [0,1]")
|
52
|
+
end
|
53
|
+
@sample_rate = sample_rate
|
54
|
+
end
|
55
|
+
|
56
|
+
def tracer=(tracer)
|
57
|
+
@tracer = tracer
|
58
|
+
end
|
59
|
+
|
60
|
+
class TraceId
|
61
|
+
attr_reader :trace_id, :parent_id, :span_id, :sampled
|
62
|
+
alias :sampled? :sampled
|
63
|
+
def initialize(trace_id, parent_id, span_id, sampled)
|
64
|
+
@trace_id = SpanId.from_value(trace_id)
|
65
|
+
@parent_id = parent_id.nil? ? nil : SpanId.from_value(parent_id)
|
66
|
+
@span_id = SpanId.from_value(span_id)
|
67
|
+
@sampled = !!sampled
|
68
|
+
end
|
69
|
+
|
70
|
+
def next_id
|
71
|
+
TraceId.new(@trace_id, @span_id, Trace.generate_id, @sampled)
|
72
|
+
end
|
73
|
+
|
74
|
+
def to_s
|
75
|
+
"TraceId(trace_id = #{@trace_id.to_s}, parent_id = #{@parent_id.to_s}, span_id = #{@span_id.to_s}, sampled = #{@sampled.to_s})"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class SpanId
|
80
|
+
HEX_REGEX = /^[a-f0-9]{16}$/i
|
81
|
+
MAX_SIGNED_I64 = 9223372036854775807
|
82
|
+
MASK = (2 ** 64) - 1
|
83
|
+
|
84
|
+
def self.from_value(v)
|
85
|
+
if v.is_a?(String) && v =~ HEX_REGEX
|
86
|
+
new(v.hex)
|
87
|
+
elsif v.is_a?(Numeric)
|
88
|
+
new(v)
|
89
|
+
elsif v.is_a?(SpanId)
|
90
|
+
v
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def initialize(value)
|
95
|
+
@value = value
|
96
|
+
@i64 = if @value > MAX_SIGNED_I64
|
97
|
+
-1 * ((@value ^ MASK) + 1)
|
98
|
+
else
|
99
|
+
@value
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def to_s; "%016x" % @value; end
|
104
|
+
def to_i; @i64; end
|
105
|
+
end
|
106
|
+
|
107
|
+
def generate_id
|
108
|
+
rand(TRACE_ID_UPPER_BOUND)
|
109
|
+
end
|
110
|
+
|
111
|
+
def should_sample?
|
112
|
+
rand < (@sample_rate || DEFAULT_SAMPLE_RATE)
|
113
|
+
end
|
114
|
+
|
115
|
+
def default_endpoint=(endpoint)
|
116
|
+
@default_endpoint = endpoint
|
117
|
+
end
|
118
|
+
|
119
|
+
def default_endpoint
|
120
|
+
@default_endpoint ||= begin
|
121
|
+
Endpoint.new(Endpoint.host_to_i32(Socket.gethostname), 0, "finagle-ruby")
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
private
|
126
|
+
|
127
|
+
def stack
|
128
|
+
@stack ||= []
|
129
|
+
end
|
130
|
+
|
131
|
+
def tracer
|
132
|
+
@tracer ||= NullTracer.new
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
@@ -0,0 +1,245 @@
|
|
1
|
+
module Trace
|
2
|
+
class Tracer
|
3
|
+
def record(id, annotation)
|
4
|
+
raise "not implemented"
|
5
|
+
end
|
6
|
+
|
7
|
+
def set_rpc_name(id, name)
|
8
|
+
raise "not implemented"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class NullTracer < Tracer
|
13
|
+
def record(id, annotation)
|
14
|
+
end
|
15
|
+
|
16
|
+
def set_rpc_name(id, name)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class FanoutTracer < Tracer
|
21
|
+
def initialize(tracers)
|
22
|
+
@tracers = tracers
|
23
|
+
end
|
24
|
+
|
25
|
+
def record(id, annotation)
|
26
|
+
@tracers.each { |tracer| tracer.record(id, annotation) }
|
27
|
+
end
|
28
|
+
|
29
|
+
def set_rpc_name(id, name)
|
30
|
+
@tracers.each { |tracer| tracer.set_rpc_name(id, name) }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class FileTracer < Tracer
|
35
|
+
def initialize
|
36
|
+
@last_trace_id = nil
|
37
|
+
@file = nil
|
38
|
+
end
|
39
|
+
|
40
|
+
def record(id, annotation)
|
41
|
+
return unless id.sampled?
|
42
|
+
file = get_file_for_id(id)
|
43
|
+
file.puts("#{id.to_s}: #{annotation.to_s}")
|
44
|
+
file.flush if (annotation.is_a?(Annotation) && annotation.value == Annotation::SERVER_SEND)
|
45
|
+
end
|
46
|
+
|
47
|
+
def set_rpc_name(id, name)
|
48
|
+
return unless id.sampled?
|
49
|
+
get_file_for_id(id).puts("#{id.to_s}: name = #{name}")
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def get_file_for_id(id)
|
54
|
+
id = id.trace_id.to_i
|
55
|
+
if @last_trace_id != id
|
56
|
+
@last_trace_id = id
|
57
|
+
@file.close if @file
|
58
|
+
@file = File.open("/tmp/traces/#{id}.log", "a")
|
59
|
+
end
|
60
|
+
@file
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class ZipkinTracer < Tracer
|
65
|
+
TRACER_CATEGORY = "zipkin"
|
66
|
+
def initialize(scribe, max_buffer)
|
67
|
+
@scribe = scribe
|
68
|
+
@max_buffer = max_buffer
|
69
|
+
reset
|
70
|
+
end
|
71
|
+
|
72
|
+
def record(id, annotation)
|
73
|
+
return unless id.sampled?
|
74
|
+
span = get_span_for_id(id)
|
75
|
+
|
76
|
+
case annotation
|
77
|
+
when BinaryAnnotation
|
78
|
+
span.binary_annotations << annotation
|
79
|
+
when Annotation
|
80
|
+
span.annotations << annotation
|
81
|
+
end
|
82
|
+
|
83
|
+
@count += 1
|
84
|
+
if @count >= @max_buffer || (annotation.is_a?(Annotation) && annotation.value == Annotation::SERVER_SEND)
|
85
|
+
flush!
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def set_rpc_name(id, name)
|
90
|
+
return unless id.sampled?
|
91
|
+
span = get_span_for_id(id)
|
92
|
+
span.name = name.to_s
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
def get_span_for_id(id)
|
97
|
+
key = id.span_id.to_s
|
98
|
+
@spans[key] ||= begin
|
99
|
+
Span.new("", id)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def reset
|
104
|
+
@count = 0
|
105
|
+
@spans = {}
|
106
|
+
end
|
107
|
+
|
108
|
+
def flush!
|
109
|
+
@scribe.batch do
|
110
|
+
messages = @spans.values.map do |span|
|
111
|
+
buf = ''
|
112
|
+
trans = Thrift::MemoryBufferTransport.new(buf)
|
113
|
+
oprot = Thrift::BinaryProtocol.new(trans)
|
114
|
+
span.to_thrift.write(oprot)
|
115
|
+
binary = Base64.encode64(buf).gsub("\n", "")
|
116
|
+
@scribe.log(binary, TRACER_CATEGORY)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
reset
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
class Span
|
124
|
+
attr_accessor :name, :annotations, :binary_annotations
|
125
|
+
def initialize(name, span_id)
|
126
|
+
@name = name
|
127
|
+
@span_id = span_id
|
128
|
+
@annotations = []
|
129
|
+
@binary_annotations = []
|
130
|
+
end
|
131
|
+
|
132
|
+
def to_thrift
|
133
|
+
FinagleThrift::Span.new(
|
134
|
+
:name => @name,
|
135
|
+
:trace_id => @span_id.trace_id.to_i,
|
136
|
+
:id => @span_id.span_id.to_i,
|
137
|
+
:parent_id => @span_id.parent_id.nil? ? nil : @span_id.parent_id.to_i,
|
138
|
+
:annotations => @annotations.map { |a| a.to_thrift },
|
139
|
+
:binary_annotations => @binary_annotations.map { |a| a.to_thrift }
|
140
|
+
)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
class Annotation
|
145
|
+
CLIENT_SEND = "cs"
|
146
|
+
CLIENT_RECV = "cr"
|
147
|
+
SERVER_SEND = "ss"
|
148
|
+
SERVER_RECV = "sr"
|
149
|
+
|
150
|
+
attr_reader :value, :host, :timestamp
|
151
|
+
def initialize(value, host)
|
152
|
+
@timestamp = (Time.now.to_f * 1000 * 1000).to_i # micros
|
153
|
+
@value = value
|
154
|
+
@host = host
|
155
|
+
end
|
156
|
+
|
157
|
+
def to_thrift
|
158
|
+
thrift_host = host ? host.to_thrift : nil
|
159
|
+
FinagleThrift::Annotation.new(
|
160
|
+
:value => @value,
|
161
|
+
:host => thrift_host,
|
162
|
+
:timestamp => @timestamp
|
163
|
+
)
|
164
|
+
end
|
165
|
+
|
166
|
+
def to_s
|
167
|
+
"#{@value} at #{@timestamp} for (#{@host.to_s})"
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
class BinaryAnnotation
|
172
|
+
module Type
|
173
|
+
BOOL = "BOOL"
|
174
|
+
BYTES = "BYTES"
|
175
|
+
I16 = "I16"
|
176
|
+
I32 = "I32"
|
177
|
+
I64 = "I64"
|
178
|
+
DOUBLE = "DOUBLE"
|
179
|
+
STRING = "STRING"
|
180
|
+
|
181
|
+
def self.to_thrift(v)
|
182
|
+
FinagleThrift::AnnotationType::VALUE_MAP.index(v)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
attr_reader :key, :value, :host
|
187
|
+
def initialize(key, value, annotation_type, host)
|
188
|
+
@key = key
|
189
|
+
@value = value
|
190
|
+
@annotation_type = annotation_type
|
191
|
+
@host = host
|
192
|
+
end
|
193
|
+
|
194
|
+
def to_thrift
|
195
|
+
thrift_host = host ? host.to_thrift : nil
|
196
|
+
FinagleThrift::BinaryAnnotation.new(
|
197
|
+
:key => @key,
|
198
|
+
:value => @value,
|
199
|
+
:annotation_type => Type.to_thrift(@annotation_type),
|
200
|
+
:host => thrift_host
|
201
|
+
)
|
202
|
+
end
|
203
|
+
|
204
|
+
end
|
205
|
+
|
206
|
+
class Endpoint < Struct.new(:ipv4, :port, :service_name)
|
207
|
+
MAX_I32 = ((2 ** 31) - 1)
|
208
|
+
MASK = (2 ** 32) - 1
|
209
|
+
|
210
|
+
def self.host_to_i32(host)
|
211
|
+
unsigned_i32 = Socket.getaddrinfo(host, nil)[0][3].split(".").map do |i|
|
212
|
+
i.to_i
|
213
|
+
end.inject(0) { |a,e| (a << 8) + e }
|
214
|
+
|
215
|
+
signed_i32 = if unsigned_i32 > MAX_I32
|
216
|
+
-1 * ((unsigned_i32 ^ MASK) + 1)
|
217
|
+
else
|
218
|
+
unsigned_i32
|
219
|
+
end
|
220
|
+
|
221
|
+
signed_i32
|
222
|
+
end
|
223
|
+
|
224
|
+
def with_port(port)
|
225
|
+
Endpoint.new(self.ipv4, port, self.service_name)
|
226
|
+
end
|
227
|
+
|
228
|
+
def with_service_name(service_name)
|
229
|
+
Endpoint.new(self.ipv4, self.port, service_name)
|
230
|
+
end
|
231
|
+
|
232
|
+
def to_thrift
|
233
|
+
FinagleThrift::Endpoint.new(
|
234
|
+
:ipv4 => self.ipv4,
|
235
|
+
:port => self.port,
|
236
|
+
:service_name => self.service_name
|
237
|
+
)
|
238
|
+
end
|
239
|
+
|
240
|
+
def to_s
|
241
|
+
"#{service_name}@#{ipv4}:#{port}"
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
end
|
metadata
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: finagle-thrift
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 31
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 2
|
9
|
+
- 0
|
10
|
+
version: 1.2.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Arya Asemanfar
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-05-25 00:00:00 Z
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: A Ruby client library for integrating into finagle's thrift tracing protocol
|
22
|
+
email:
|
23
|
+
- arya@twitter.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files: []
|
29
|
+
|
30
|
+
files:
|
31
|
+
- lib/finagle-thrift/client.rb
|
32
|
+
- lib/finagle-thrift/thrift/tracing_constants.rb
|
33
|
+
- lib/finagle-thrift/thrift/tracing_types.rb
|
34
|
+
- lib/finagle-thrift/thrift_client.rb
|
35
|
+
- lib/finagle-thrift/trace.rb
|
36
|
+
- lib/finagle-thrift/tracer.rb
|
37
|
+
- lib/finagle-thrift/version.rb
|
38
|
+
- lib/finagle-thrift.rb
|
39
|
+
homepage:
|
40
|
+
licenses: []
|
41
|
+
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
44
|
+
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
none: false
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
hash: 3
|
53
|
+
segments:
|
54
|
+
- 0
|
55
|
+
version: "0"
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 17
|
62
|
+
segments:
|
63
|
+
- 1
|
64
|
+
- 3
|
65
|
+
- 5
|
66
|
+
version: 1.3.5
|
67
|
+
requirements: []
|
68
|
+
|
69
|
+
rubyforge_project:
|
70
|
+
rubygems_version: 1.8.15
|
71
|
+
signing_key:
|
72
|
+
specification_version: 3
|
73
|
+
summary: ""
|
74
|
+
test_files: []
|
75
|
+
|