protobuffy 3.1.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.
Files changed (192) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/.travis.yml +12 -0
  4. data/.yardopts +5 -0
  5. data/CHANGES.md +261 -0
  6. data/CONTRIBUTING.md +16 -0
  7. data/Gemfile +3 -0
  8. data/LICENSE.txt +14 -0
  9. data/README.md +58 -0
  10. data/Rakefile +61 -0
  11. data/bin/protoc-gen-ruby +17 -0
  12. data/bin/rpc_server +4 -0
  13. data/examples/bin/reverse-client-http +4 -0
  14. data/examples/bin/reverse-client-socket +4 -0
  15. data/examples/bin/reverse-client-zmq +4 -0
  16. data/examples/config.ru +6 -0
  17. data/examples/definitions/example/reverse.proto +12 -0
  18. data/examples/lib/example/reverse-client.rb +23 -0
  19. data/examples/lib/example/reverse-service.rb +9 -0
  20. data/examples/lib/example/reverse.pb.rb +36 -0
  21. data/lib/protobuf.rb +106 -0
  22. data/lib/protobuf/cli.rb +249 -0
  23. data/lib/protobuf/code_generator.rb +41 -0
  24. data/lib/protobuf/decoder.rb +74 -0
  25. data/lib/protobuf/deprecator.rb +42 -0
  26. data/lib/protobuf/descriptors.rb +3 -0
  27. data/lib/protobuf/descriptors/google/protobuf/compiler/plugin.pb.rb +52 -0
  28. data/lib/protobuf/descriptors/google/protobuf/descriptor.pb.rb +249 -0
  29. data/lib/protobuf/encoder.rb +62 -0
  30. data/lib/protobuf/enum.rb +319 -0
  31. data/lib/protobuf/exceptions.rb +9 -0
  32. data/lib/protobuf/field.rb +74 -0
  33. data/lib/protobuf/field/base_field.rb +280 -0
  34. data/lib/protobuf/field/bool_field.rb +53 -0
  35. data/lib/protobuf/field/bytes_field.rb +81 -0
  36. data/lib/protobuf/field/double_field.rb +26 -0
  37. data/lib/protobuf/field/enum_field.rb +57 -0
  38. data/lib/protobuf/field/field_array.rb +86 -0
  39. data/lib/protobuf/field/fixed32_field.rb +25 -0
  40. data/lib/protobuf/field/fixed64_field.rb +29 -0
  41. data/lib/protobuf/field/float_field.rb +38 -0
  42. data/lib/protobuf/field/int32_field.rb +22 -0
  43. data/lib/protobuf/field/int64_field.rb +22 -0
  44. data/lib/protobuf/field/integer_field.rb +24 -0
  45. data/lib/protobuf/field/message_field.rb +66 -0
  46. data/lib/protobuf/field/sfixed32_field.rb +28 -0
  47. data/lib/protobuf/field/sfixed64_field.rb +29 -0
  48. data/lib/protobuf/field/signed_integer_field.rb +30 -0
  49. data/lib/protobuf/field/sint32_field.rb +22 -0
  50. data/lib/protobuf/field/sint64_field.rb +22 -0
  51. data/lib/protobuf/field/string_field.rb +35 -0
  52. data/lib/protobuf/field/uint32_field.rb +22 -0
  53. data/lib/protobuf/field/uint64_field.rb +22 -0
  54. data/lib/protobuf/field/varint_field.rb +68 -0
  55. data/lib/protobuf/generators/base.rb +71 -0
  56. data/lib/protobuf/generators/enum_generator.rb +42 -0
  57. data/lib/protobuf/generators/extension_generator.rb +28 -0
  58. data/lib/protobuf/generators/field_generator.rb +132 -0
  59. data/lib/protobuf/generators/file_generator.rb +140 -0
  60. data/lib/protobuf/generators/group_generator.rb +113 -0
  61. data/lib/protobuf/generators/message_generator.rb +99 -0
  62. data/lib/protobuf/generators/printable.rb +161 -0
  63. data/lib/protobuf/generators/service_generator.rb +27 -0
  64. data/lib/protobuf/http.rb +20 -0
  65. data/lib/protobuf/lifecycle.rb +46 -0
  66. data/lib/protobuf/logger.rb +86 -0
  67. data/lib/protobuf/message.rb +182 -0
  68. data/lib/protobuf/message/fields.rb +122 -0
  69. data/lib/protobuf/message/serialization.rb +84 -0
  70. data/lib/protobuf/optionable.rb +23 -0
  71. data/lib/protobuf/rpc/buffer.rb +79 -0
  72. data/lib/protobuf/rpc/client.rb +168 -0
  73. data/lib/protobuf/rpc/connector.rb +21 -0
  74. data/lib/protobuf/rpc/connectors/base.rb +54 -0
  75. data/lib/protobuf/rpc/connectors/common.rb +172 -0
  76. data/lib/protobuf/rpc/connectors/http.rb +90 -0
  77. data/lib/protobuf/rpc/connectors/socket.rb +73 -0
  78. data/lib/protobuf/rpc/connectors/zmq.rb +205 -0
  79. data/lib/protobuf/rpc/dynamic_discovery.pb.rb +47 -0
  80. data/lib/protobuf/rpc/env.rb +58 -0
  81. data/lib/protobuf/rpc/error.rb +28 -0
  82. data/lib/protobuf/rpc/error/client_error.rb +31 -0
  83. data/lib/protobuf/rpc/error/server_error.rb +43 -0
  84. data/lib/protobuf/rpc/middleware.rb +25 -0
  85. data/lib/protobuf/rpc/middleware/exception_handler.rb +36 -0
  86. data/lib/protobuf/rpc/middleware/logger.rb +91 -0
  87. data/lib/protobuf/rpc/middleware/request_decoder.rb +83 -0
  88. data/lib/protobuf/rpc/middleware/response_encoder.rb +88 -0
  89. data/lib/protobuf/rpc/middleware/runner.rb +18 -0
  90. data/lib/protobuf/rpc/rpc.pb.rb +53 -0
  91. data/lib/protobuf/rpc/server.rb +39 -0
  92. data/lib/protobuf/rpc/servers/http/server.rb +101 -0
  93. data/lib/protobuf/rpc/servers/http_runner.rb +34 -0
  94. data/lib/protobuf/rpc/servers/socket/server.rb +113 -0
  95. data/lib/protobuf/rpc/servers/socket/worker.rb +56 -0
  96. data/lib/protobuf/rpc/servers/socket_runner.rb +34 -0
  97. data/lib/protobuf/rpc/servers/zmq/broker.rb +155 -0
  98. data/lib/protobuf/rpc/servers/zmq/server.rb +313 -0
  99. data/lib/protobuf/rpc/servers/zmq/util.rb +47 -0
  100. data/lib/protobuf/rpc/servers/zmq/worker.rb +105 -0
  101. data/lib/protobuf/rpc/servers/zmq_runner.rb +51 -0
  102. data/lib/protobuf/rpc/service.rb +179 -0
  103. data/lib/protobuf/rpc/service_directory.rb +245 -0
  104. data/lib/protobuf/rpc/service_dispatcher.rb +46 -0
  105. data/lib/protobuf/rpc/service_filters.rb +273 -0
  106. data/lib/protobuf/rpc/stat.rb +148 -0
  107. data/lib/protobuf/socket.rb +22 -0
  108. data/lib/protobuf/tasks.rb +1 -0
  109. data/lib/protobuf/tasks/compile.rake +61 -0
  110. data/lib/protobuf/version.rb +3 -0
  111. data/lib/protobuf/wire_type.rb +10 -0
  112. data/lib/protobuf/zmq.rb +21 -0
  113. data/proto/dynamic_discovery.proto +44 -0
  114. data/proto/google/protobuf/compiler/plugin.proto +147 -0
  115. data/proto/google/protobuf/descriptor.proto +620 -0
  116. data/proto/rpc.proto +62 -0
  117. data/protobuffy.gemspec +37 -0
  118. data/spec/benchmark/tasks.rb +113 -0
  119. data/spec/bin/protoc-gen-ruby_spec.rb +18 -0
  120. data/spec/data/data.bin +3 -0
  121. data/spec/data/types.bin +0 -0
  122. data/spec/encoding/all_types_spec.rb +91 -0
  123. data/spec/encoding/extreme_values_spec.rb +0 -0
  124. data/spec/functional/socket_server_spec.rb +59 -0
  125. data/spec/functional/zmq_server_spec.rb +103 -0
  126. data/spec/lib/protobuf/cli_spec.rb +267 -0
  127. data/spec/lib/protobuf/code_generator_spec.rb +60 -0
  128. data/spec/lib/protobuf/enum_spec.rb +239 -0
  129. data/spec/lib/protobuf/field/int32_field_spec.rb +7 -0
  130. data/spec/lib/protobuf/field/string_field_spec.rb +46 -0
  131. data/spec/lib/protobuf/field_spec.rb +194 -0
  132. data/spec/lib/protobuf/generators/base_spec.rb +87 -0
  133. data/spec/lib/protobuf/generators/enum_generator_spec.rb +68 -0
  134. data/spec/lib/protobuf/generators/extension_generator_spec.rb +43 -0
  135. data/spec/lib/protobuf/generators/field_generator_spec.rb +99 -0
  136. data/spec/lib/protobuf/generators/file_generator_spec.rb +29 -0
  137. data/spec/lib/protobuf/generators/message_generator_spec.rb +0 -0
  138. data/spec/lib/protobuf/generators/service_generator_spec.rb +43 -0
  139. data/spec/lib/protobuf/lifecycle_spec.rb +89 -0
  140. data/spec/lib/protobuf/logger_spec.rb +136 -0
  141. data/spec/lib/protobuf/message_spec.rb +368 -0
  142. data/spec/lib/protobuf/optionable_spec.rb +46 -0
  143. data/spec/lib/protobuf/rpc/client_spec.rb +66 -0
  144. data/spec/lib/protobuf/rpc/connector_spec.rb +26 -0
  145. data/spec/lib/protobuf/rpc/connectors/base_spec.rb +50 -0
  146. data/spec/lib/protobuf/rpc/connectors/common_spec.rb +170 -0
  147. data/spec/lib/protobuf/rpc/connectors/connector_spec.rb +13 -0
  148. data/spec/lib/protobuf/rpc/connectors/http_spec.rb +61 -0
  149. data/spec/lib/protobuf/rpc/connectors/socket_spec.rb +24 -0
  150. data/spec/lib/protobuf/rpc/connectors/zmq_spec.rb +129 -0
  151. data/spec/lib/protobuf/rpc/middleware/exception_handler_spec.rb +62 -0
  152. data/spec/lib/protobuf/rpc/middleware/logger_spec.rb +49 -0
  153. data/spec/lib/protobuf/rpc/middleware/request_decoder_spec.rb +115 -0
  154. data/spec/lib/protobuf/rpc/middleware/response_encoder_spec.rb +75 -0
  155. data/spec/lib/protobuf/rpc/servers/http/server_spec.rb +104 -0
  156. data/spec/lib/protobuf/rpc/servers/socket_server_spec.rb +38 -0
  157. data/spec/lib/protobuf/rpc/servers/zmq/server_spec.rb +41 -0
  158. data/spec/lib/protobuf/rpc/servers/zmq/util_spec.rb +55 -0
  159. data/spec/lib/protobuf/rpc/servers/zmq/worker_spec.rb +35 -0
  160. data/spec/lib/protobuf/rpc/service_directory_spec.rb +295 -0
  161. data/spec/lib/protobuf/rpc/service_dispatcher_spec.rb +52 -0
  162. data/spec/lib/protobuf/rpc/service_filters_spec.rb +484 -0
  163. data/spec/lib/protobuf/rpc/service_spec.rb +161 -0
  164. data/spec/lib/protobuf/rpc/stat_spec.rb +151 -0
  165. data/spec/lib/protobuf_spec.rb +78 -0
  166. data/spec/spec_helper.rb +57 -0
  167. data/spec/support/all.rb +7 -0
  168. data/spec/support/packed_field.rb +22 -0
  169. data/spec/support/server.rb +94 -0
  170. data/spec/support/test/all_types.data.bin +0 -0
  171. data/spec/support/test/all_types.data.txt +119 -0
  172. data/spec/support/test/defaults.pb.rb +25 -0
  173. data/spec/support/test/defaults.proto +9 -0
  174. data/spec/support/test/enum.pb.rb +59 -0
  175. data/spec/support/test/enum.proto +34 -0
  176. data/spec/support/test/extended.pb.rb +22 -0
  177. data/spec/support/test/extended.proto +10 -0
  178. data/spec/support/test/extreme_values.data.bin +0 -0
  179. data/spec/support/test/google_unittest.pb.rb +543 -0
  180. data/spec/support/test/google_unittest.proto +713 -0
  181. data/spec/support/test/google_unittest_import.pb.rb +37 -0
  182. data/spec/support/test/google_unittest_import.proto +64 -0
  183. data/spec/support/test/google_unittest_import_public.pb.rb +8 -0
  184. data/spec/support/test/google_unittest_import_public.proto +38 -0
  185. data/spec/support/test/multi_field_extensions.pb.rb +56 -0
  186. data/spec/support/test/multi_field_extensions.proto +33 -0
  187. data/spec/support/test/resource.pb.rb +117 -0
  188. data/spec/support/test/resource.proto +94 -0
  189. data/spec/support/test/resource_service.rb +26 -0
  190. data/spec/support/test_app_file.rb +2 -0
  191. data/spec/support/tolerance_matcher.rb +40 -0
  192. metadata +367 -0
@@ -0,0 +1,46 @@
1
+ require 'protobuf/logger'
2
+
3
+ module Protobuf
4
+ module Rpc
5
+ class ServiceDispatcher
6
+ include ::Protobuf::Logger::LogMethods
7
+
8
+ attr_reader :env
9
+
10
+ def initialize(app)
11
+ # End of the line...
12
+ end
13
+
14
+ def call(env)
15
+ @env = env
16
+
17
+ env.response = dispatch_rpc_request
18
+ env
19
+ end
20
+
21
+ def rpc_service
22
+ @rpc_service ||= env.rpc_service.new(env)
23
+ end
24
+
25
+ private
26
+
27
+ # Call the given service method.
28
+ def dispatch_rpc_request
29
+ unless rpc_service.respond_to?(method_name)
30
+ raise MethodNotFound.new("#{service_name}##{method_name} is not a publicly defined method.")
31
+ end
32
+
33
+ rpc_service.callable_rpc_method(method_name).call
34
+ rpc_service.response
35
+ end
36
+
37
+ def method_name
38
+ env.method_name
39
+ end
40
+
41
+ def service_name
42
+ env.service_name
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,273 @@
1
+ module Protobuf
2
+ module Rpc
3
+ module ServiceFilters
4
+
5
+ def self.included(other)
6
+ other.class_eval do
7
+ extend Protobuf::Rpc::ServiceFilters::ClassMethods
8
+ include Protobuf::Rpc::ServiceFilters::InstanceMethods
9
+ end
10
+ end
11
+
12
+ module ClassMethods
13
+
14
+ [:after, :around, :before].each do |type|
15
+ # Setter DSL method for given filter types.
16
+ #
17
+ define_method "#{type}_filter" do |*args|
18
+ set_filters(type, *args)
19
+ end
20
+ alias_method "#{type}_action", "#{type}_filter"
21
+ end
22
+
23
+ # Filters hash keyed based on filter type (e.g. :before, :after, :around),
24
+ # whose values are Sets.
25
+ #
26
+ def filters
27
+ @filters ||= Hash.new { |h,k| h[k] = [] }
28
+ end
29
+
30
+ # Filters hash keyed based on filter type (e.g. :before, :after, :around),
31
+ # whose values are Sets.
32
+ #
33
+ def rescue_filters
34
+ @rescue_filters ||= {}
35
+ end
36
+
37
+ def rescue_from(*ex_klasses, &block)
38
+ options = ex_klasses.last.is_a?(Hash) ? ex_klasses.pop : {}
39
+ callable = options.delete(:with) { block }
40
+ raise ArgumentError, 'Option :with missing from rescue_from options' if callable.nil?
41
+ ex_klasses.each { |ex_klass| rescue_filters[ex_klass] = callable }
42
+ end
43
+
44
+ private
45
+
46
+ def define_filter(type, filter, options = {})
47
+ return if filter_defined?(type, filter)
48
+ filters[type] << options.merge({ :callable => filter })
49
+ remember_filter(type, filter)
50
+ end
51
+
52
+ def defined_filters
53
+ @defined_filters ||= Hash.new { |h,k| h[k] = Set.new }
54
+ end
55
+
56
+ # Check to see if the filter has been defined.
57
+ #
58
+ def filter_defined?(type, filter)
59
+ defined_filters[type].include?(filter)
60
+ end
61
+
62
+ # Remember that we stored the filter.
63
+ #
64
+ def remember_filter(type, filter)
65
+ defined_filters[type] << filter
66
+ end
67
+
68
+ # Takes a list of actually (or potentially) callable objects.
69
+ # TODO add support for if/unless
70
+ # TODO add support for only/except sub-filters
71
+ #
72
+ def set_filters(type, *args)
73
+ options = args.last.is_a?(Hash) ? args.pop : {}
74
+ args.each do |filter|
75
+ define_filter(type, filter, options)
76
+ end
77
+ end
78
+
79
+ end
80
+
81
+ module InstanceMethods
82
+
83
+ private
84
+
85
+ # Get back to class filters.
86
+ #
87
+ def filters
88
+ self.class.filters
89
+ end
90
+
91
+ # Predicate which uses the filter options to determine if the filter
92
+ # should be called. Specifically checks the :if, :unless, :only, and :except
93
+ # options for every filter. Each option check is expected to return false
94
+ # if the filter should not be invoked, true if invocation should occur.
95
+ #
96
+ def invoke_filter?(rpc_method, filter)
97
+ return invoke_via_only?(rpc_method, filter) \
98
+ && invoke_via_except?(rpc_method, filter) \
99
+ && invoke_via_if?(rpc_method, filter) \
100
+ && invoke_via_unless?(rpc_method, filter)
101
+ end
102
+
103
+ # If the target rpc endpoint method is listed under an :except option,
104
+ # return false to indicate that the filter should not be invoked. Any
105
+ # other target rpc endpoint methods not listed should be invoked.
106
+ # This option is the opposite of :only.
107
+ #
108
+ # Value should be a symbol/string or an array of symbols/strings.
109
+ #
110
+ def invoke_via_except?(rpc_method, filter)
111
+ except = [ filter.fetch(:except) { [] } ].flatten
112
+ return except.empty? || ! except.include?(rpc_method)
113
+ end
114
+
115
+ # Invoke the given :if callable (if any) and return its return value.
116
+ # Used by `invoke_filter?` which expects a true/false
117
+ # return value to determine if we should invoke the target filter.
118
+ #
119
+ # Value can either be a symbol/string indicating an instance method to call
120
+ # or an object that responds to `call`.
121
+ #
122
+ def invoke_via_if?(rpc_method, filter)
123
+ if_check = filter.fetch(:if) { lambda { |service| return true } }
124
+ do_invoke = case
125
+ when if_check.nil? then
126
+ true
127
+ else
128
+ call_or_send(if_check)
129
+ end
130
+
131
+ return do_invoke
132
+ end
133
+
134
+ # If the target rpc endpoint method is listed in the :only option,
135
+ # it should be invoked. Any target rpc endpoint methods not listed in this
136
+ # option should not be invoked. This option is the opposite of :except.
137
+ #
138
+ # Value should be a symbol/string or an array of symbols/strings.
139
+ #
140
+ def invoke_via_only?(rpc_method, filter)
141
+ only = [ filter.fetch(:only) { [] } ].flatten
142
+ return only.empty? || only.include?(rpc_method)
143
+ end
144
+
145
+ # Invoke the given :unless callable (if any) and return the opposite
146
+ # of it's return value. Used by `invoke_filter?` which expects a true/false
147
+ # return value to determine if we should invoke the target filter.
148
+ #
149
+ # Value can either be a symbol/string indicating an instance method to call
150
+ # or an object that responds to `call`.
151
+ #
152
+ def invoke_via_unless?(rpc_method, filter)
153
+ unless_check = filter.fetch(:unless) { lambda { |service| return false } }
154
+ skip_invoke = case
155
+ when unless_check.nil? then
156
+ false
157
+ else
158
+ call_or_send(unless_check)
159
+ end
160
+
161
+ return ! skip_invoke
162
+ end
163
+
164
+ def rescue_filters
165
+ self.class.rescue_filters
166
+ end
167
+
168
+ # Loop over the unwrapped filters and invoke them. An unwrapped filter
169
+ # is either a before or after filter, not an around filter.
170
+ #
171
+ def run_unwrapped_filters(unwrapped_filters, rpc_method, stop_on_false_return = false)
172
+ unwrapped_filters.each do |filter|
173
+ if invoke_filter?(rpc_method, filter)
174
+ return_value = call_or_send(filter[:callable])
175
+ return false if stop_on_false_return && return_value == false
176
+ end
177
+ end
178
+
179
+ return true
180
+ end
181
+
182
+ # Reverse build a chain of around filters. To implement an around chain,
183
+ # simply build a method that yields control when it expects the underlying
184
+ # method to be invoked. If the endpoint should not be run (due to some
185
+ # condition), simply do not yield.
186
+ #
187
+ # Around filters are invoked in the order they are defined, outer to inner,
188
+ # with the inner-most method called being the actual rpc endpoint.
189
+ #
190
+ # Let's say you have a class defined with the following filters:
191
+ #
192
+ # class MyService
193
+ # around_filter :filter1, :filter2, :filter3
194
+ #
195
+ # def my_endpoint
196
+ # # do stuff
197
+ # end
198
+ # end
199
+ #
200
+ # When the my_endpoint method is invoked using Service#callable_rpc_method,
201
+ # It is similar to this call chain:
202
+ #
203
+ # filter1 do
204
+ # filter2 do
205
+ # filter3 do
206
+ # my_endpoint
207
+ # end
208
+ # end
209
+ # end
210
+ #
211
+ def run_around_filters(rpc_method)
212
+ final = lambda { __send__(rpc_method) }
213
+ filters[:around].reverse.inject(final) { |previous, filter|
214
+ if invoke_filter?(rpc_method, filter)
215
+ lambda { call_or_send(filter[:callable], &previous) }
216
+ else
217
+ previous
218
+ end
219
+ }.call
220
+ end
221
+
222
+
223
+ # Entry method to call each filter type in the appropriate order. This should
224
+ # be used instead of the other run methods directly.
225
+ #
226
+ def run_filters(rpc_method)
227
+ run_rescue_filters do
228
+ continue = run_unwrapped_filters(filters[:before], rpc_method, true)
229
+ if continue
230
+ run_around_filters(rpc_method)
231
+ run_unwrapped_filters(filters[:after], rpc_method)
232
+ end
233
+ end
234
+ end
235
+
236
+ def run_rescue_filters
237
+ if rescue_filters.keys.empty?
238
+ yield
239
+ else
240
+ begin
241
+ yield
242
+ rescue *rescue_filters.keys => ex
243
+ callable = rescue_filters.fetch(ex.class) {
244
+ mapped_klass = rescue_filters.keys.detect { |child_klass| ex.class < child_klass }
245
+ rescue_filters[mapped_klass]
246
+ }
247
+
248
+ call_or_send(callable, ex)
249
+ end
250
+ end
251
+ end
252
+
253
+ # Call the object if it is callable, otherwise invoke the method using
254
+ # __send__ assuming that we respond_to it. Return the call's return value.
255
+ #
256
+ def call_or_send(callable, *args, &block)
257
+ return_value = case
258
+ when callable.respond_to?(:call) then
259
+ callable.call(self, *args, &block)
260
+ when respond_to?(callable, true) then
261
+ __send__(callable, *args, &block)
262
+ else
263
+ raise "Object #{callable} is not callable"
264
+ end
265
+
266
+ return return_value
267
+ end
268
+
269
+ end
270
+
271
+ end
272
+ end
273
+ end
@@ -0,0 +1,148 @@
1
+ require 'date'
2
+ require 'time'
3
+ require 'protobuf/logger'
4
+
5
+ module Protobuf
6
+ module Rpc
7
+ class Stat
8
+ attr_accessor :mode, :start_time, :end_time, :request_size, :dispatcher
9
+ attr_accessor :response_size, :client, :server, :service, :method_name
10
+
11
+ MODES = [:SERVER, :CLIENT].freeze
12
+
13
+ # Set the StatsD Client to send stats to. The client must match
14
+ # the interface provided by lookout-statsd
15
+ # (https://github.com/lookout/statsd).
16
+ def self.statsd_client=(statsd_client)
17
+ @statsd_client = statsd_client
18
+ end
19
+
20
+ # The StatsD Client configured, if any.
21
+ def self.statsd_client
22
+ @statsd_client
23
+ end
24
+
25
+ def initialize(mode = :SERVER)
26
+ @mode = mode
27
+ @request_size = 0
28
+ @response_size = 0
29
+ @success = false
30
+ @failure_code = nil
31
+ start
32
+ end
33
+
34
+ def client=(client_host)
35
+ @client = client_host
36
+ end
37
+
38
+ def client
39
+ @client || nil
40
+ end
41
+
42
+ def elapsed_time
43
+ (start_time && end_time ? "#{(end_time - start_time).round(4)}s" : nil)
44
+ end
45
+
46
+ def method_name
47
+ @method_name ||= @dispatcher.try(:service).try(:method_name)
48
+ end
49
+
50
+ def server=(peer)
51
+ @server = {:port => peer[0], :ip => peer[1]}
52
+ end
53
+
54
+ def server
55
+ @server ? "#{@server[:ip]}:#{@server[:port]}" : nil
56
+ end
57
+
58
+ def service
59
+ @service ||= @dispatcher.try(:service).class.name
60
+ end
61
+
62
+ def sizes
63
+ if stopped?
64
+ "#{@request_size}B/#{@response_size}B"
65
+ else
66
+ "#{@request_size}B/-"
67
+ end
68
+ end
69
+
70
+ def start
71
+ @start_time ||= ::Time.now
72
+ end
73
+
74
+ def stop
75
+ start unless @start_time
76
+ @end_time ||= ::Time.now
77
+ call_statsd_client
78
+ end
79
+
80
+ def success
81
+ @success = true
82
+ end
83
+
84
+ def failure(code)
85
+ @failure_code = code
86
+ end
87
+
88
+ def stopped?
89
+ ! end_time.nil?
90
+ end
91
+
92
+ def rpc
93
+ service && method_name ? "#{service}##{method_name}" : nil
94
+ end
95
+
96
+ def server?
97
+ @mode == :SERVER
98
+ end
99
+
100
+ def client?
101
+ @mode == :CLIENT
102
+ end
103
+
104
+ def to_s
105
+ [
106
+ server? ? "[SRV]" : "[CLT]",
107
+ server? ? client : server,
108
+ trace_id,
109
+ rpc,
110
+ sizes,
111
+ elapsed_time,
112
+ @end_time.try(:iso8601)
113
+ ].compact.join(' - ')
114
+ end
115
+
116
+ def trace_id
117
+ ::Thread.current.object_id.to_s(16)
118
+ end
119
+
120
+ # Return base path for StatsD metrics
121
+ def statsd_base_path
122
+ "rpc.#{service}.#{method_name}".gsub('::', '.').downcase
123
+ end
124
+
125
+ # If a StatsD Client has been configured, send stats to it upon
126
+ # completion.
127
+ def call_statsd_client
128
+ path = statsd_base_path
129
+ statsd_client = self.class.statsd_client
130
+ return unless statsd_client
131
+
132
+ if @success
133
+ statsd_client.increment("#{path}.success")
134
+ elsif @failure_code
135
+ statsd_client.increment("#{path}.failure.total")
136
+ statsd_client.increment("#{path}.failure.#{@failure_code}")
137
+ end
138
+
139
+ if start_time && end_time
140
+ duration = end_time - start_time
141
+ statsd_client.timing("#{path}.time", duration)
142
+ end
143
+ end
144
+ private :call_statsd_client
145
+ end
146
+ end
147
+ end
148
+