gruf 1.2.7 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|