finagle-thrift 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|