gruf 1.2.7 → 2.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +98 -119
- data/bin/gruf +9 -3
- data/lib/gruf.rb +4 -4
- data/lib/gruf/configuration.rb +11 -20
- data/lib/gruf/controllers/base.rb +82 -0
- data/lib/gruf/controllers/request.rb +96 -0
- data/lib/gruf/controllers/service_binder.rb +86 -0
- data/lib/gruf/error.rb +9 -0
- data/lib/gruf/errors/helpers.rb +40 -0
- data/lib/gruf/{hooks → interceptors}/active_record/connection_reset.rb +4 -10
- data/lib/gruf/interceptors/authentication/basic.rb +80 -0
- data/lib/gruf/interceptors/base.rb +51 -0
- data/lib/gruf/{instrumentation/output_metadata_timer.rb → interceptors/context.rb} +25 -15
- data/lib/gruf/interceptors/instrumentation/output_metadata_timer.rb +59 -0
- data/lib/gruf/{instrumentation → interceptors/instrumentation}/request_logging/formatters/base.rb +15 -13
- data/lib/gruf/{instrumentation → interceptors/instrumentation}/request_logging/formatters/logstash.rb +15 -13
- data/lib/gruf/{instrumentation → interceptors/instrumentation}/request_logging/formatters/plain.rb +21 -19
- data/lib/gruf/interceptors/instrumentation/request_logging/interceptor.rb +191 -0
- data/lib/gruf/interceptors/instrumentation/statsd.rb +80 -0
- data/lib/gruf/interceptors/registry.rb +131 -0
- data/lib/gruf/{authentication/none.rb → interceptors/server_interceptor.rb} +8 -7
- data/lib/gruf/interceptors/timer.rb +79 -0
- data/lib/gruf/response.rb +1 -2
- data/lib/gruf/server.rb +40 -25
- data/lib/gruf/version.rb +1 -1
- metadata +19 -20
- data/lib/gruf/authentication.rb +0 -65
- data/lib/gruf/authentication/base.rb +0 -65
- data/lib/gruf/authentication/basic.rb +0 -74
- data/lib/gruf/authentication/strategies.rb +0 -107
- data/lib/gruf/hooks/base.rb +0 -66
- data/lib/gruf/hooks/registry.rb +0 -110
- data/lib/gruf/instrumentation/base.rb +0 -114
- data/lib/gruf/instrumentation/registry.rb +0 -104
- data/lib/gruf/instrumentation/request_context.rb +0 -82
- data/lib/gruf/instrumentation/request_logging/hook.rb +0 -185
- data/lib/gruf/instrumentation/statsd.rb +0 -80
- data/lib/gruf/service.rb +0 -333
@@ -15,30 +15,40 @@
|
|
15
15
|
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
16
16
|
#
|
17
17
|
module Gruf
|
18
|
-
module
|
18
|
+
module Interceptors
|
19
19
|
##
|
20
|
-
#
|
20
|
+
# Runs interceptors in a given request context
|
21
21
|
#
|
22
|
-
class
|
22
|
+
class Context
|
23
|
+
include Gruf::Loggable
|
24
|
+
|
23
25
|
##
|
24
|
-
#
|
26
|
+
# Initialize the interception context
|
25
27
|
#
|
26
|
-
# @param [Gruf::
|
27
|
-
# @return [Hash] The resulting output metadata with the timer attached
|
28
|
+
# @param [Array<Gruf::Interceptors::ServerInterceptor>] interceptors
|
28
29
|
#
|
29
|
-
def
|
30
|
-
|
31
|
-
|
32
|
-
rc.active_call.output_metadata.update(metadata_key => rc.execution_time.to_s)
|
30
|
+
def initialize(interceptors = [])
|
31
|
+
@interceptors = interceptors
|
33
32
|
end
|
34
33
|
|
35
|
-
private
|
36
|
-
|
37
34
|
##
|
38
|
-
#
|
35
|
+
# Intercept the given request and run interceptors in a FIFO execution order
|
39
36
|
#
|
40
|
-
def
|
41
|
-
|
37
|
+
def intercept!
|
38
|
+
return yield if @interceptors.none?
|
39
|
+
|
40
|
+
i = @interceptors.pop
|
41
|
+
return yield unless i
|
42
|
+
|
43
|
+
logger.debug "Intercepting request with interceptor: #{i.class}"
|
44
|
+
|
45
|
+
i.call do
|
46
|
+
if @interceptors.any?
|
47
|
+
intercept! { yield }
|
48
|
+
else
|
49
|
+
yield
|
50
|
+
end
|
51
|
+
end
|
42
52
|
end
|
43
53
|
end
|
44
54
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
5
|
+
# documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
|
6
|
+
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
|
7
|
+
# persons to whom the Software is furnished to do so, subject to the following conditions:
|
8
|
+
#
|
9
|
+
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
|
10
|
+
# Software.
|
11
|
+
#
|
12
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
13
|
+
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
14
|
+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
15
|
+
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
16
|
+
#
|
17
|
+
module Gruf
|
18
|
+
module Interceptors
|
19
|
+
module Instrumentation
|
20
|
+
##
|
21
|
+
# Appends the timer metadata to the active call output metadata
|
22
|
+
#
|
23
|
+
class OutputMetadataTimer < ::Gruf::Interceptors::ServerInterceptor
|
24
|
+
delegate :active_call, to: :request
|
25
|
+
|
26
|
+
##
|
27
|
+
# Handle the instrumented response. Note: this will only instrument timings of _successful_ responses.
|
28
|
+
#
|
29
|
+
def call
|
30
|
+
return unless active_call.respond_to?(:output_metadata)
|
31
|
+
|
32
|
+
result = Gruf::Interceptors::Timer.time do
|
33
|
+
yield
|
34
|
+
end
|
35
|
+
output_metadata.update(metadata_key => result.elapsed.to_s)
|
36
|
+
|
37
|
+
raise result.message unless result.successful?
|
38
|
+
result.message
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
##
|
44
|
+
# @return [Symbol] The metadata key that the time result should be set to
|
45
|
+
#
|
46
|
+
def metadata_key
|
47
|
+
options.fetch(:metadata_key, :timer).to_sym
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# @return [Hash]
|
52
|
+
#
|
53
|
+
def output_metadata
|
54
|
+
active_call.output_metadata
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/gruf/{instrumentation → interceptors/instrumentation}/request_logging/formatters/base.rb
RENAMED
@@ -15,21 +15,23 @@
|
|
15
15
|
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
16
16
|
#
|
17
17
|
module Gruf
|
18
|
-
module
|
19
|
-
module
|
20
|
-
module
|
21
|
-
|
22
|
-
# Base class for request log formatting
|
23
|
-
#
|
24
|
-
class Base
|
18
|
+
module Interceptors
|
19
|
+
module Instrumentation
|
20
|
+
module RequestLogging
|
21
|
+
module Formatters
|
25
22
|
##
|
26
|
-
#
|
23
|
+
# Base class for request log formatting
|
27
24
|
#
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
25
|
+
class Base
|
26
|
+
##
|
27
|
+
# Format the parameters into a loggable string. Must be implemented in every derivative class
|
28
|
+
#
|
29
|
+
# @param [Hash] _payload The incoming request payload
|
30
|
+
# @return [String] The formatted string
|
31
|
+
#
|
32
|
+
def format(_payload)
|
33
|
+
raise NotImplementedError
|
34
|
+
end
|
33
35
|
end
|
34
36
|
end
|
35
37
|
end
|
@@ -17,21 +17,23 @@
|
|
17
17
|
require 'json'
|
18
18
|
|
19
19
|
module Gruf
|
20
|
-
module
|
21
|
-
module
|
22
|
-
module
|
23
|
-
|
24
|
-
# Formats logging for gruf services into a Logstash-friendly JSON format
|
25
|
-
#
|
26
|
-
class Logstash < Base
|
20
|
+
module Interceptors
|
21
|
+
module Instrumentation
|
22
|
+
module RequestLogging
|
23
|
+
module Formatters
|
27
24
|
##
|
28
|
-
#
|
25
|
+
# Formats logging for gruf services into a Logstash-friendly JSON format
|
29
26
|
#
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
payload
|
27
|
+
class Logstash < Base
|
28
|
+
##
|
29
|
+
# Format the request into a JSON-friendly payload
|
30
|
+
#
|
31
|
+
# @param [Hash] payload The incoming request payload
|
32
|
+
# @return [String] The JSON representation of the payload
|
33
|
+
#
|
34
|
+
def format(payload)
|
35
|
+
payload.merge(format: 'json').to_json
|
36
|
+
end
|
35
37
|
end
|
36
38
|
end
|
37
39
|
end
|
data/lib/gruf/{instrumentation → interceptors/instrumentation}/request_logging/formatters/plain.rb
RENAMED
@@ -15,28 +15,30 @@
|
|
15
15
|
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
16
16
|
#
|
17
17
|
module Gruf
|
18
|
-
module
|
19
|
-
module
|
20
|
-
module
|
21
|
-
|
22
|
-
# Formats the request into plaintext logging
|
23
|
-
#
|
24
|
-
class Plain < Base
|
18
|
+
module Interceptors
|
19
|
+
module Instrumentation
|
20
|
+
module RequestLogging
|
21
|
+
module Formatters
|
25
22
|
##
|
26
|
-
#
|
23
|
+
# Formats the request into plaintext logging
|
27
24
|
#
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
25
|
+
class Plain < Base
|
26
|
+
##
|
27
|
+
# Format the request by only outputting the message body and params (if set to log params)
|
28
|
+
#
|
29
|
+
# @param [Hash] payload The incoming request payload
|
30
|
+
# @return [String] The formatted string
|
31
|
+
#
|
32
|
+
def format(payload)
|
33
|
+
time = payload.fetch(:duration, 0)
|
34
|
+
grpc_status = payload.fetch(:grpc_status, 'GRPC::Ok')
|
35
|
+
route_key = payload.fetch(:method, 'unknown')
|
36
|
+
body = payload.fetch(:message, '')
|
36
37
|
|
37
|
-
|
38
|
-
|
39
|
-
|
38
|
+
msg = "[#{grpc_status}] (#{route_key}) [#{time}ms] #{body}".strip
|
39
|
+
msg += " Parameters: #{payload[:params].to_h}" if payload.key?(:params)
|
40
|
+
msg.strip
|
41
|
+
end
|
40
42
|
end
|
41
43
|
end
|
42
44
|
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
5
|
+
# documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
|
6
|
+
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
|
7
|
+
# persons to whom the Software is furnished to do so, subject to the following conditions:
|
8
|
+
#
|
9
|
+
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
|
10
|
+
# Software.
|
11
|
+
#
|
12
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
13
|
+
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
14
|
+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
15
|
+
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
16
|
+
#
|
17
|
+
require 'socket'
|
18
|
+
require_relative 'formatters/base'
|
19
|
+
require_relative 'formatters/logstash'
|
20
|
+
require_relative 'formatters/plain'
|
21
|
+
|
22
|
+
module Gruf
|
23
|
+
module Interceptors
|
24
|
+
module Instrumentation
|
25
|
+
module RequestLogging
|
26
|
+
##
|
27
|
+
# Represents an error if the formatter does not extend the base formatter
|
28
|
+
#
|
29
|
+
class InvalidFormatterError < StandardError; end
|
30
|
+
|
31
|
+
##
|
32
|
+
# Handles Rails-style request logging for gruf services.
|
33
|
+
#
|
34
|
+
# This is added by default to gruf servers; if you have `Gruf.use_default_hooks = false`, you can add it back
|
35
|
+
# manually by doing:
|
36
|
+
#
|
37
|
+
# Gruf::Instrumentation::Registry.add(:request_logging, Gruf::Instrumentation::RequestLogging::Hook)
|
38
|
+
#
|
39
|
+
class Interceptor < ::Gruf::Interceptors::ServerInterceptor
|
40
|
+
|
41
|
+
###
|
42
|
+
# Log the request, sending it to the appropriate formatter
|
43
|
+
#
|
44
|
+
# @return [String]
|
45
|
+
#
|
46
|
+
def call
|
47
|
+
result = Gruf::Interceptors::Timer.time do
|
48
|
+
yield
|
49
|
+
end
|
50
|
+
|
51
|
+
if result.successful?
|
52
|
+
type = options.fetch(:success_log_level, :info).to_sym
|
53
|
+
status_name = 'GRPC::Ok'
|
54
|
+
else
|
55
|
+
type = options.fetch(:failure_log_level, :error).to_sym
|
56
|
+
status_name = result.message_class_name
|
57
|
+
end
|
58
|
+
|
59
|
+
payload = {}
|
60
|
+
if !request.client_streamer?
|
61
|
+
payload[:params] = sanitize(request.message.to_h) if options.fetch(:log_parameters, false)
|
62
|
+
payload[:message] = message(request, result)
|
63
|
+
payload[:status] = status(result.message, result.successful?)
|
64
|
+
else
|
65
|
+
payload[:params] = {}
|
66
|
+
payload[:message] = ''
|
67
|
+
payload[:status] = GRPC::Core::StatusCodes::OK
|
68
|
+
end
|
69
|
+
payload[:service] = request.service_key
|
70
|
+
payload[:method] = request.method_key
|
71
|
+
payload[:action] = request.method_key
|
72
|
+
payload[:grpc_status] = status_name
|
73
|
+
payload[:duration] = result.elapsed_rounded
|
74
|
+
payload[:thread_id] = Thread.current.object_id
|
75
|
+
payload[:time] = Time.now.to_s
|
76
|
+
payload[:host] = Socket.gethostname
|
77
|
+
|
78
|
+
::Gruf.logger.send(type, formatter.format(payload))
|
79
|
+
|
80
|
+
raise result.message unless result.successful?
|
81
|
+
result.message
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
##
|
87
|
+
# Return an appropriate log message dependent on the status
|
88
|
+
#
|
89
|
+
# @param [Gruf::Controllers::Request] request
|
90
|
+
# @param [Gruf::Interceptors::Timer::Result] result
|
91
|
+
# @return [String] The appropriate message body
|
92
|
+
#
|
93
|
+
def message(request, result)
|
94
|
+
if result.successful?
|
95
|
+
"[GRPC::Ok] (#{request.method_name})"
|
96
|
+
else
|
97
|
+
"[#{result.message_class_name}] (#{request.method_name}) #{result.message.message}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
##
|
102
|
+
# Return the proper status code for the response
|
103
|
+
#
|
104
|
+
# @param [Object] response The response object
|
105
|
+
# @param [Boolean] successful If the response was successful
|
106
|
+
# @return [Boolean] The proper status code
|
107
|
+
#
|
108
|
+
def status(response, successful)
|
109
|
+
successful ? GRPC::Core::StatusCodes::OK : response.code
|
110
|
+
end
|
111
|
+
|
112
|
+
##
|
113
|
+
# Determine the appropriate formatter for the request logging
|
114
|
+
#
|
115
|
+
# @return [Gruf::Instrumentation::RequestLogging::Formatters::Base]
|
116
|
+
#
|
117
|
+
def formatter
|
118
|
+
unless @formatter
|
119
|
+
fmt = options.fetch(:formatter, :plain)
|
120
|
+
@formatter = case fmt
|
121
|
+
when Symbol
|
122
|
+
klass = "Gruf::Interceptors::Instrumentation::RequestLogging::Formatters::#{fmt.to_s.capitalize}"
|
123
|
+
fmt = klass.constantize.new
|
124
|
+
when Class
|
125
|
+
fmt = fmt.new
|
126
|
+
else
|
127
|
+
fmt
|
128
|
+
end
|
129
|
+
raise InvalidFormatterError unless fmt.is_a?(Formatters::Base)
|
130
|
+
end
|
131
|
+
@formatter
|
132
|
+
end
|
133
|
+
|
134
|
+
##
|
135
|
+
# Redact any blacklisted params and return an updated hash
|
136
|
+
#
|
137
|
+
# @param [Hash] params The hash of parameters to sanitize
|
138
|
+
# @return [Hash] The sanitized params in hash form
|
139
|
+
#
|
140
|
+
def sanitize(params = {})
|
141
|
+
blacklists = options.fetch(:blacklist, []).map(&:to_s)
|
142
|
+
redacted_string = options.fetch(:redacted_string, 'REDACTED')
|
143
|
+
blacklists.each do |blacklist|
|
144
|
+
parts = blacklist.split('.').map(&:to_sym)
|
145
|
+
redact!(parts, 0, params, redacted_string)
|
146
|
+
end
|
147
|
+
params
|
148
|
+
end
|
149
|
+
|
150
|
+
##
|
151
|
+
# Helper method to recursively redact based on the black list
|
152
|
+
#
|
153
|
+
# @param [Array] parts The blacklist. ex. 'data.schema' -> [:data, :schema]
|
154
|
+
# @param [Integer] idx The current index of the blacklist
|
155
|
+
# @param [Hash] params The hash of parameters to sanitize
|
156
|
+
# @param [String] redacted_string The custom redact string
|
157
|
+
#
|
158
|
+
def redact!(parts = [], idx = 0, params = {}, redacted_string = 'REDACTED')
|
159
|
+
return unless parts.is_a?(Array) && params.is_a?(Hash)
|
160
|
+
return if idx >= parts.size || !params.key?(parts[idx])
|
161
|
+
if idx == parts.size - 1
|
162
|
+
if params[parts[idx]].is_a? Hash
|
163
|
+
hash_deep_redact!(params[parts[idx]], redacted_string)
|
164
|
+
else
|
165
|
+
params[parts[idx]] = redacted_string
|
166
|
+
end
|
167
|
+
return
|
168
|
+
end
|
169
|
+
redact!(parts, idx + 1, params[parts[idx]], redacted_string)
|
170
|
+
end
|
171
|
+
|
172
|
+
##
|
173
|
+
# Helper method to recursively redact the value of all hash keys
|
174
|
+
#
|
175
|
+
# @param [Hash] hash Part of the hash of parameters to sanitize
|
176
|
+
# @param [String] redacted_string The custom redact string
|
177
|
+
#
|
178
|
+
def hash_deep_redact!(hash, redacted_string)
|
179
|
+
hash.keys.each do |key|
|
180
|
+
if hash[key].is_a? Hash
|
181
|
+
hash_deep_redact!(hash[key], redacted_string)
|
182
|
+
else
|
183
|
+
hash[key] = redacted_string
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
5
|
+
# documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
|
6
|
+
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
|
7
|
+
# persons to whom the Software is furnished to do so, subject to the following conditions:
|
8
|
+
#
|
9
|
+
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
|
10
|
+
# Software.
|
11
|
+
#
|
12
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
13
|
+
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
14
|
+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
15
|
+
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
16
|
+
#
|
17
|
+
module Gruf
|
18
|
+
module Interceptors
|
19
|
+
module Instrumentation
|
20
|
+
##
|
21
|
+
# Adds increment and timing stats to gRPC routes, pushing data to StatsD
|
22
|
+
#
|
23
|
+
class Statsd < Gruf::Interceptors::ServerInterceptor
|
24
|
+
##
|
25
|
+
# Push data to StatsD, only doing so if a client is set
|
26
|
+
#
|
27
|
+
def call
|
28
|
+
unless client
|
29
|
+
Gruf.logger.error 'Statsd module loaded, but no client configured!'
|
30
|
+
return yield
|
31
|
+
end
|
32
|
+
|
33
|
+
client.increment(route_key)
|
34
|
+
|
35
|
+
result = Gruf::Interceptors::Timer.time do
|
36
|
+
yield
|
37
|
+
end
|
38
|
+
|
39
|
+
client.increment("#{route_key}.#{postfix(result.successful?)}")
|
40
|
+
client.timing(route_key, result.elapsed)
|
41
|
+
|
42
|
+
raise result.message unless result.successful?
|
43
|
+
result.message
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
##
|
49
|
+
# @param [Boolean] successful Whether or not the request was successful
|
50
|
+
# @return [String] The appropriate postfix for the key dependent on response status
|
51
|
+
#
|
52
|
+
def postfix(successful)
|
53
|
+
successful ? 'success' : 'failure'
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# @return [String] Return a composed route key that is used in the statsd metric
|
58
|
+
#
|
59
|
+
def route_key
|
60
|
+
"#{key_prefix}#{request.method_name}"
|
61
|
+
end
|
62
|
+
|
63
|
+
##
|
64
|
+
# @return [String] Return the sanitized key prefix for the statsd metric key
|
65
|
+
#
|
66
|
+
def key_prefix
|
67
|
+
prefix = options.fetch(:prefix, '').to_s
|
68
|
+
prefix.empty? ? '' : "#{prefix}."
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# @return [::Statsd] Return the given StatsD client
|
73
|
+
#
|
74
|
+
def client
|
75
|
+
@client ||= options.fetch(:client, nil)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|