gruf 2.2.2 → 2.9.1
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 +5 -5
- data/CHANGELOG.md +107 -12
- data/CODE_OF_CONDUCT.md +38 -41
- data/README.md +8 -360
- data/bin/gruf +2 -12
- data/gruf.gemspec +31 -7
- data/lib/gruf/cli/executor.rb +102 -0
- data/lib/gruf/client/error.rb +68 -0
- data/lib/gruf/client/error_factory.rb +105 -0
- data/lib/gruf/client.rb +52 -38
- data/lib/gruf/configuration.rb +25 -5
- data/lib/gruf/controllers/base.rb +35 -12
- data/lib/gruf/controllers/request.rb +21 -10
- data/lib/gruf/controllers/service_binder.rb +38 -6
- data/lib/gruf/error.rb +34 -9
- data/lib/gruf/errors/debug_info.rb +9 -2
- data/lib/gruf/errors/field.rb +2 -0
- data/lib/gruf/errors/helpers.rb +5 -0
- data/lib/gruf/hooks/base.rb +34 -0
- data/lib/gruf/hooks/executor.rb +47 -0
- data/lib/gruf/hooks/registry.rb +159 -0
- data/lib/gruf/instrumentable_grpc_server.rb +64 -0
- data/lib/gruf/integrations/rails/railtie.rb +10 -0
- data/lib/gruf/interceptors/active_record/connection_reset.rb +4 -3
- data/lib/gruf/interceptors/authentication/basic.rb +10 -2
- data/lib/gruf/interceptors/base.rb +3 -0
- data/lib/gruf/interceptors/client_interceptor.rb +117 -0
- data/lib/gruf/interceptors/context.rb +6 -4
- data/lib/gruf/interceptors/instrumentation/output_metadata_timer.rb +5 -4
- data/lib/gruf/interceptors/instrumentation/request_logging/formatters/base.rb +5 -1
- data/lib/gruf/interceptors/instrumentation/request_logging/formatters/logstash.rb +5 -1
- data/lib/gruf/interceptors/instrumentation/request_logging/formatters/plain.rb +5 -1
- data/lib/gruf/interceptors/instrumentation/request_logging/interceptor.rb +60 -29
- data/lib/gruf/interceptors/instrumentation/statsd.rb +5 -4
- data/lib/gruf/interceptors/registry.rb +6 -1
- data/lib/gruf/interceptors/server_interceptor.rb +2 -0
- data/lib/gruf/interceptors/timer.rb +12 -2
- data/lib/gruf/loggable.rb +2 -0
- data/lib/gruf/logging.rb +2 -0
- data/lib/gruf/outbound/request_context.rb +71 -0
- data/lib/gruf/response.rb +5 -2
- data/lib/gruf/serializers/errors/base.rb +2 -0
- data/lib/gruf/serializers/errors/json.rb +2 -0
- data/lib/gruf/server.rb +57 -23
- data/lib/gruf/synchronized_client.rb +97 -0
- data/lib/gruf/timer.rb +9 -6
- data/lib/gruf/version.rb +3 -1
- data/lib/gruf.rb +10 -0
- metadata +254 -17
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
|
2
4
|
#
|
3
5
|
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
@@ -22,7 +24,9 @@ module Gruf
|
|
22
24
|
# Handles basic authentication for gRPC requests
|
23
25
|
#
|
24
26
|
class Basic < Gruf::Interceptors::ServerInterceptor
|
25
|
-
|
27
|
+
##
|
28
|
+
# Validate authentication
|
29
|
+
#
|
26
30
|
def call
|
27
31
|
fail!(:unauthenticated, :unauthenticated) unless bypass? || valid?
|
28
32
|
yield
|
@@ -63,7 +67,11 @@ module Gruf
|
|
63
67
|
# @return [String] The decoded request credentials
|
64
68
|
#
|
65
69
|
def request_credentials
|
66
|
-
credentials = request.active_call.respond_to?(:metadata)
|
70
|
+
credentials = if request.active_call.respond_to?(:metadata)
|
71
|
+
request.active_call.metadata['authorization'].to_s
|
72
|
+
else
|
73
|
+
''
|
74
|
+
end
|
67
75
|
Base64.decode64(credentials.to_s.gsub('Basic ', '').strip)
|
68
76
|
end
|
69
77
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
|
2
4
|
#
|
3
5
|
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
@@ -40,6 +42,7 @@ module Gruf
|
|
40
42
|
end
|
41
43
|
end
|
42
44
|
|
45
|
+
require_relative 'client_interceptor'
|
43
46
|
require_relative 'server_interceptor'
|
44
47
|
require_relative 'context'
|
45
48
|
require_relative 'timer'
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
6
|
+
# documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
|
7
|
+
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
|
8
|
+
# persons to whom the Software is furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
|
11
|
+
# Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
14
|
+
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
15
|
+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
16
|
+
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
17
|
+
#
|
18
|
+
module Gruf
|
19
|
+
module Interceptors
|
20
|
+
##
|
21
|
+
# Intercepts outbound client requests to provide a unified interface and request context
|
22
|
+
#
|
23
|
+
class ClientInterceptor < GRPC::ClientInterceptor
|
24
|
+
include Gruf::Loggable
|
25
|
+
|
26
|
+
##
|
27
|
+
# Handles interception of outbound calls. Implement this in your derivative interceptor implementation.
|
28
|
+
#
|
29
|
+
# @param [Gruf::Outbound::RequestContext] request_context The context of the outbound request
|
30
|
+
# @return [Object] This method must return the response from the yielded block
|
31
|
+
#
|
32
|
+
def call(request_context:)
|
33
|
+
logger.debug "Logging client interceptor for request: #{request_context.method}"
|
34
|
+
yield
|
35
|
+
end
|
36
|
+
|
37
|
+
##
|
38
|
+
# Call the interceptor from the request_response call
|
39
|
+
#
|
40
|
+
# @param [Object] request The request being sent
|
41
|
+
# @param [GRPC::ActiveCall] call The GRPC ActiveCall object
|
42
|
+
# @param [Method] method The method being called
|
43
|
+
# @param [Hash] metadata A hash of outgoing metadata
|
44
|
+
# @return [Object] The response message
|
45
|
+
#
|
46
|
+
def request_response(request: nil, call: nil, method: nil, metadata: nil, &block)
|
47
|
+
rc = Gruf::Outbound::RequestContext.new(
|
48
|
+
type: :request_response,
|
49
|
+
requests: [request],
|
50
|
+
call: call,
|
51
|
+
method: method,
|
52
|
+
metadata: metadata
|
53
|
+
)
|
54
|
+
call(request_context: rc, &block)
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
58
|
+
# Call the interceptor from the client_streamer call
|
59
|
+
#
|
60
|
+
# @param [Enumerable] requests An enumerable of requests being sent
|
61
|
+
# @param [GRPC::ActiveCall] call The GRPC ActiveCall object
|
62
|
+
# @param [Method] method The method being called
|
63
|
+
# @param [Hash] metadata A hash of outgoing metadata
|
64
|
+
# @return [Object] The response message
|
65
|
+
#
|
66
|
+
def client_streamer(requests: nil, call: nil, method: nil, metadata: nil, &block)
|
67
|
+
rc = Gruf::Outbound::RequestContext.new(
|
68
|
+
type: :client_streamer,
|
69
|
+
requests: requests,
|
70
|
+
call: call,
|
71
|
+
method: method,
|
72
|
+
metadata: metadata
|
73
|
+
)
|
74
|
+
call(request_context: rc, &block)
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# Call the interceptor from the server_streamer call
|
79
|
+
#
|
80
|
+
# @param [Object] request The request being sent
|
81
|
+
# @param [GRPC::ActiveCall] call The GRPC ActiveCall object
|
82
|
+
# @param [Method] method The method being called
|
83
|
+
# @param [Hash] metadata A hash of outgoing metadata
|
84
|
+
# @return [Object] The response message
|
85
|
+
#
|
86
|
+
def server_streamer(request: nil, call: nil, method: nil, metadata: nil, &block)
|
87
|
+
rc = Gruf::Outbound::RequestContext.new(
|
88
|
+
type: :server_streamer,
|
89
|
+
requests: [request],
|
90
|
+
call: call,
|
91
|
+
method: method,
|
92
|
+
metadata: metadata
|
93
|
+
)
|
94
|
+
call(request_context: rc, &block)
|
95
|
+
end
|
96
|
+
|
97
|
+
##
|
98
|
+
# Call the interceptor from the bidi_streamer call
|
99
|
+
#
|
100
|
+
# @param [Enumerable] requests An enumerable of requests being sent
|
101
|
+
# @param [GRPC::ActiveCall] call The GRPC ActiveCall object
|
102
|
+
# @param [Method] method The method being called
|
103
|
+
# @param [Hash] metadata A hash of outgoing metadata
|
104
|
+
#
|
105
|
+
def bidi_streamer(requests: nil, call: nil, method: nil, metadata: nil, &block)
|
106
|
+
rc = Gruf::Outbound::RequestContext.new(
|
107
|
+
type: :bidi_streamer,
|
108
|
+
requests: requests,
|
109
|
+
call: call,
|
110
|
+
method: method,
|
111
|
+
metadata: metadata
|
112
|
+
)
|
113
|
+
call(request_context: rc, &block)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
|
2
4
|
#
|
3
5
|
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
@@ -26,14 +28,14 @@ module Gruf
|
|
26
28
|
#
|
27
29
|
# @param [Array<Gruf::Interceptors::ServerInterceptor>] interceptors
|
28
30
|
#
|
29
|
-
def initialize(interceptors =
|
30
|
-
@interceptors = interceptors
|
31
|
+
def initialize(interceptors = nil)
|
32
|
+
@interceptors = interceptors || []
|
31
33
|
end
|
32
34
|
|
33
35
|
##
|
34
36
|
# Intercept the given request and run interceptors in a FIFO execution order
|
35
37
|
#
|
36
|
-
def intercept!
|
38
|
+
def intercept!(&block)
|
37
39
|
return yield if @interceptors.none?
|
38
40
|
|
39
41
|
i = @interceptors.pop
|
@@ -43,7 +45,7 @@ module Gruf
|
|
43
45
|
|
44
46
|
i.call do
|
45
47
|
if @interceptors.any?
|
46
|
-
intercept!
|
48
|
+
intercept!(&block)
|
47
49
|
else
|
48
50
|
yield
|
49
51
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
|
2
4
|
#
|
3
5
|
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
@@ -25,15 +27,14 @@ module Gruf
|
|
25
27
|
##
|
26
28
|
# Handle the instrumented response. Note: this will only instrument timings of _successful_ responses.
|
27
29
|
#
|
28
|
-
def call
|
30
|
+
def call(&block)
|
29
31
|
return unless active_call.respond_to?(:output_metadata)
|
30
32
|
|
31
|
-
result = Gruf::Interceptors::Timer.time
|
32
|
-
yield
|
33
|
-
end
|
33
|
+
result = Gruf::Interceptors::Timer.time(&block)
|
34
34
|
output_metadata.update(metadata_key => result.elapsed.to_s)
|
35
35
|
|
36
36
|
raise result.message unless result.successful?
|
37
|
+
|
37
38
|
result.message
|
38
39
|
end
|
39
40
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
|
2
4
|
#
|
3
5
|
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
@@ -26,9 +28,11 @@ module Gruf
|
|
26
28
|
# Format the parameters into a loggable string. Must be implemented in every derivative class
|
27
29
|
#
|
28
30
|
# @param [Hash] _payload The incoming request payload
|
31
|
+
# @param [Gruf::Controllers::Request] request The current controller request
|
32
|
+
# @param [Gruf::Interceptors::Timer::Result] result The timed result of the response
|
29
33
|
# @return [String] The formatted string
|
30
34
|
#
|
31
|
-
def format(_payload)
|
35
|
+
def format(_payload, request:, result:)
|
32
36
|
raise NotImplementedError
|
33
37
|
end
|
34
38
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
|
2
4
|
#
|
3
5
|
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
@@ -28,9 +30,11 @@ module Gruf
|
|
28
30
|
# Format the request into a JSON-friendly payload
|
29
31
|
#
|
30
32
|
# @param [Hash] payload The incoming request payload
|
33
|
+
# @param [Gruf::Controllers::Request] request The current controller request
|
34
|
+
# @param [Gruf::Interceptors::Timer::Result] result The timed result of the response
|
31
35
|
# @return [String] The JSON representation of the payload
|
32
36
|
#
|
33
|
-
def format(payload)
|
37
|
+
def format(payload, request:, result:)
|
34
38
|
payload.merge(format: 'json').to_json
|
35
39
|
end
|
36
40
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
|
2
4
|
#
|
3
5
|
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
@@ -26,9 +28,11 @@ module Gruf
|
|
26
28
|
# Format the request by only outputting the message body and params (if set to log params)
|
27
29
|
#
|
28
30
|
# @param [Hash] payload The incoming request payload
|
31
|
+
# @param [Gruf::Controllers::Request] request The current controller request
|
32
|
+
# @param [Gruf::Interceptors::Timer::Result] result The timed result of the response
|
29
33
|
# @return [String] The formatted string
|
30
34
|
#
|
31
|
-
def format(payload)
|
35
|
+
def format(payload, request:, result:)
|
32
36
|
time = payload.fetch(:duration, 0)
|
33
37
|
grpc_status = payload.fetch(:grpc_status, 'GRPC::Ok')
|
34
38
|
route_key = payload.fetch(:method, 'unknown')
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
|
2
4
|
#
|
3
5
|
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
@@ -36,29 +38,48 @@ module Gruf
|
|
36
38
|
# Gruf::Instrumentation::Registry.add(:request_logging, Gruf::Instrumentation::RequestLogging::Hook)
|
37
39
|
#
|
38
40
|
class Interceptor < ::Gruf::Interceptors::ServerInterceptor
|
41
|
+
# Default mappings of codes to log levels...
|
42
|
+
LOG_LEVEL_MAP = {
|
43
|
+
'GRPC::Ok' => :debug,
|
44
|
+
'GRPC::InvalidArgument' => :debug,
|
45
|
+
'GRPC::NotFound' => :debug,
|
46
|
+
'GRPC::AlreadyExists' => :debug,
|
47
|
+
'GRPC::OutOfRange' => :debug,
|
48
|
+
'GRPC::Unauthenticated' => :warn,
|
49
|
+
'GRPC::PermissionDenied' => :warn,
|
50
|
+
'GRPC::Unknown' => :error,
|
51
|
+
'GRPC::Internal' => :error,
|
52
|
+
'GRPC::DataLoss' => :error,
|
53
|
+
'GRPC::FailedPrecondition' => :error,
|
54
|
+
'GRPC::Unavailable' => :error,
|
55
|
+
'GRPC::DeadlineExceeded' => :error,
|
56
|
+
'GRPC::Cancelled' => :error
|
57
|
+
}.freeze
|
39
58
|
|
40
59
|
###
|
41
60
|
# Log the request, sending it to the appropriate formatter
|
42
61
|
#
|
43
62
|
# @return [String]
|
44
63
|
#
|
45
|
-
def call
|
46
|
-
return yield if options.fetch(:ignore_methods, [])
|
64
|
+
def call(&block)
|
65
|
+
return yield if options.fetch(:ignore_methods, [])&.include?(request.method_name)
|
47
66
|
|
48
|
-
result = Gruf::Interceptors::Timer.time
|
49
|
-
yield
|
50
|
-
end
|
67
|
+
result = Gruf::Interceptors::Timer.time(&block)
|
51
68
|
|
69
|
+
# Fetch log level options and merge with default...
|
70
|
+
log_level_map = LOG_LEVEL_MAP.merge(options.fetch(:log_levels, {}))
|
71
|
+
|
72
|
+
# A result is either successful, or, some level of feedback handled in the else block...
|
52
73
|
if result.successful?
|
53
|
-
type =
|
74
|
+
type = log_level_map['GRPC::Ok'] || :debug
|
54
75
|
status_name = 'GRPC::Ok'
|
55
76
|
else
|
56
|
-
type =
|
77
|
+
type = log_level_map[result.message_class_name] || :error
|
57
78
|
status_name = result.message_class_name
|
58
79
|
end
|
59
80
|
|
60
81
|
payload = {}
|
61
|
-
if !request.client_streamer?
|
82
|
+
if !request.client_streamer? && !request.bidi_streamer?
|
62
83
|
payload[:params] = sanitize(request.message.to_h) if options.fetch(:log_parameters, false)
|
63
84
|
payload[:message] = message(request, result)
|
64
85
|
payload[:status] = status(result.message, result.successful?)
|
@@ -67,6 +88,7 @@ module Gruf
|
|
67
88
|
payload[:message] = ''
|
68
89
|
payload[:status] = GRPC::Core::StatusCodes::OK
|
69
90
|
end
|
91
|
+
|
70
92
|
payload[:service] = request.service_key
|
71
93
|
payload[:method] = request.method_key
|
72
94
|
payload[:action] = request.method_key
|
@@ -76,14 +98,22 @@ module Gruf
|
|
76
98
|
payload[:time] = Time.now.to_s
|
77
99
|
payload[:host] = Socket.gethostname
|
78
100
|
|
79
|
-
|
101
|
+
logger.send(type, formatter.format(payload, request: request, result: result))
|
80
102
|
|
81
103
|
raise result.message unless result.successful?
|
104
|
+
|
82
105
|
result.message
|
83
106
|
end
|
84
107
|
|
85
108
|
private
|
86
109
|
|
110
|
+
##
|
111
|
+
# @return [::Gruf::Logger]
|
112
|
+
#
|
113
|
+
def logger
|
114
|
+
@logger ||= (options.fetch(:logger, nil) || Gruf.logger)
|
115
|
+
end
|
116
|
+
|
87
117
|
##
|
88
118
|
# Return an appropriate log message dependent on the status
|
89
119
|
#
|
@@ -92,11 +122,9 @@ module Gruf
|
|
92
122
|
# @return [String] The appropriate message body
|
93
123
|
#
|
94
124
|
def message(request, result)
|
95
|
-
if result.successful?
|
96
|
-
|
97
|
-
|
98
|
-
"[#{result.message_class_name}] (#{request.method_name}) #{result.message.message}"
|
99
|
-
end
|
125
|
+
return "[GRPC::Ok] (#{request.method_name})" if result.successful?
|
126
|
+
|
127
|
+
"[#{result.message_class_name}] (#{request.method_name}) #{result.message.message}"
|
100
128
|
end
|
101
129
|
|
102
130
|
##
|
@@ -119,13 +147,14 @@ module Gruf
|
|
119
147
|
unless @formatter
|
120
148
|
fmt = options.fetch(:formatter, :plain)
|
121
149
|
@formatter = case fmt
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
150
|
+
when Symbol
|
151
|
+
prefix = 'Gruf::Interceptors::Instrumentation::RequestLogging::Formatters::'
|
152
|
+
klass = "#{prefix}#{fmt.to_s.capitalize}"
|
153
|
+
fmt = klass.constantize.new
|
154
|
+
when Class
|
155
|
+
fmt = fmt.new
|
156
|
+
else
|
157
|
+
fmt
|
129
158
|
end
|
130
159
|
raise InvalidFormatterError unless fmt.is_a?(Formatters::Base)
|
131
160
|
end
|
@@ -133,32 +162,34 @@ module Gruf
|
|
133
162
|
end
|
134
163
|
|
135
164
|
##
|
136
|
-
# Redact any
|
165
|
+
# Redact any blocklisted params and return an updated hash
|
137
166
|
#
|
138
167
|
# @param [Hash] params The hash of parameters to sanitize
|
139
168
|
# @return [Hash] The sanitized params in hash form
|
140
169
|
#
|
141
170
|
def sanitize(params = {})
|
142
|
-
|
143
|
-
redacted_string = options.fetch(:redacted_string, 'REDACTED'
|
144
|
-
|
145
|
-
parts =
|
171
|
+
blocklists = (options.fetch(:blocklist, []) || []).map(&:to_s)
|
172
|
+
redacted_string = options.fetch(:redacted_string, nil) || 'REDACTED'
|
173
|
+
blocklists.each do |blocklist|
|
174
|
+
parts = blocklist.split('.').map(&:to_sym)
|
146
175
|
redact!(parts, 0, params, redacted_string)
|
147
176
|
end
|
148
177
|
params
|
149
178
|
end
|
150
179
|
|
151
180
|
##
|
152
|
-
# Helper method to recursively redact based on the
|
181
|
+
# Helper method to recursively redact based on the blocklist
|
153
182
|
#
|
154
|
-
# @param [Array] parts The
|
155
|
-
# @param [Integer] idx The current index of the
|
183
|
+
# @param [Array] parts The blocklist. ex. 'data.schema' -> [:data, :schema]
|
184
|
+
# @param [Integer] idx The current index of the blocklist
|
156
185
|
# @param [Hash] params The hash of parameters to sanitize
|
157
186
|
# @param [String] redacted_string The custom redact string
|
158
187
|
#
|
159
188
|
def redact!(parts = [], idx = 0, params = {}, redacted_string = 'REDACTED')
|
160
189
|
return unless parts.is_a?(Array) && params.is_a?(Hash)
|
190
|
+
|
161
191
|
return if idx >= parts.size || !params.key?(parts[idx])
|
192
|
+
|
162
193
|
if idx == parts.size - 1
|
163
194
|
if params[parts[idx]].is_a? Hash
|
164
195
|
hash_deep_redact!(params[parts[idx]], redacted_string)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
|
2
4
|
#
|
3
5
|
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
@@ -23,7 +25,7 @@ module Gruf
|
|
23
25
|
##
|
24
26
|
# Push data to StatsD, only doing so if a client is set
|
25
27
|
#
|
26
|
-
def call
|
28
|
+
def call(&block)
|
27
29
|
unless client
|
28
30
|
Gruf.logger.error 'Statsd module loaded, but no client configured!'
|
29
31
|
return yield
|
@@ -31,14 +33,13 @@ module Gruf
|
|
31
33
|
|
32
34
|
client.increment(route_key)
|
33
35
|
|
34
|
-
result = Gruf::Interceptors::Timer.time
|
35
|
-
yield
|
36
|
-
end
|
36
|
+
result = Gruf::Interceptors::Timer.time(&block)
|
37
37
|
|
38
38
|
client.increment("#{route_key}.#{postfix(result.successful?)}")
|
39
39
|
client.timing(route_key, result.elapsed)
|
40
40
|
|
41
41
|
raise result.message unless result.successful?
|
42
|
+
|
42
43
|
result.message
|
43
44
|
end
|
44
45
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
|
2
4
|
#
|
3
5
|
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
@@ -22,7 +24,7 @@ module Gruf
|
|
22
24
|
class InterceptorNotFoundError < StandardError; end
|
23
25
|
|
24
26
|
def initialize
|
25
|
-
@registry
|
27
|
+
@registry = []
|
26
28
|
end
|
27
29
|
|
28
30
|
##
|
@@ -49,6 +51,7 @@ module Gruf
|
|
49
51
|
def remove(interceptor_class)
|
50
52
|
pos = @registry.find_index { |opts| opts.fetch(:klass, '') == interceptor_class }
|
51
53
|
raise InterceptorNotFoundError if pos.nil?
|
54
|
+
|
52
55
|
@registry.delete_at(pos)
|
53
56
|
end
|
54
57
|
|
@@ -64,6 +67,7 @@ module Gruf
|
|
64
67
|
interceptors_mutex do
|
65
68
|
pos = @registry.find_index { |opts| opts.fetch(:klass, '') == before_class }
|
66
69
|
raise InterceptorNotFoundError if pos.nil?
|
70
|
+
|
67
71
|
@registry.insert(
|
68
72
|
pos,
|
69
73
|
klass: interceptor_class,
|
@@ -84,6 +88,7 @@ module Gruf
|
|
84
88
|
interceptors_mutex do
|
85
89
|
pos = @registry.find_index { |opts| opts.fetch(:klass, '') == after_class }
|
86
90
|
raise InterceptorNotFoundError if pos.nil?
|
91
|
+
|
87
92
|
@registry.insert(
|
88
93
|
(pos + 1),
|
89
94
|
klass: interceptor_class,
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
|
2
4
|
#
|
3
5
|
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
@@ -22,10 +24,18 @@ module Gruf
|
|
22
24
|
##
|
23
25
|
# Represents a timed result for an interceptor
|
24
26
|
#
|
27
|
+
# @property [Object] message The protobuf message
|
28
|
+
# @property [Float] elapsed The elapsed time of the request
|
29
|
+
#
|
25
30
|
class Result
|
26
|
-
attr_reader :message
|
27
|
-
|
31
|
+
attr_reader :message,
|
32
|
+
:elapsed
|
28
33
|
|
34
|
+
##
|
35
|
+
# @param [Object] message The protobuf message
|
36
|
+
# @param [Float] elapsed The elapsed time of the request
|
37
|
+
# @param [Boolean] successful If the request was successful
|
38
|
+
#
|
29
39
|
def initialize(message, elapsed, successful)
|
30
40
|
@message = message
|
31
41
|
@elapsed = elapsed.to_f
|
data/lib/gruf/loggable.rb
CHANGED
data/lib/gruf/logging.rb
CHANGED
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
6
|
+
# documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
|
7
|
+
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
|
8
|
+
# persons to whom the Software is furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
|
11
|
+
# Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
14
|
+
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
15
|
+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
16
|
+
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
17
|
+
#
|
18
|
+
module Gruf
|
19
|
+
module Outbound
|
20
|
+
##
|
21
|
+
# Encapsulates the context of an outbound client request
|
22
|
+
#
|
23
|
+
class RequestContext
|
24
|
+
# @var [Symbol]
|
25
|
+
attr_reader :type
|
26
|
+
# @var [Enumerable] requests
|
27
|
+
attr_reader :requests
|
28
|
+
# @var [GRPC::ActiveCall]
|
29
|
+
attr_reader :call
|
30
|
+
# @var [Method] method
|
31
|
+
attr_reader :method
|
32
|
+
# @var [Hash] metadata
|
33
|
+
attr_reader :metadata
|
34
|
+
|
35
|
+
##
|
36
|
+
# Initialize the new request context
|
37
|
+
#
|
38
|
+
# @param [Symbol] type The type of request
|
39
|
+
# @param [Enumerable] requests An enumerable of requests being sent
|
40
|
+
# @param [GRPC::ActiveCall] call The GRPC ActiveCall object
|
41
|
+
# @param [Method] method The method being called
|
42
|
+
# @param [Hash] metadata A hash of outgoing metadata
|
43
|
+
#
|
44
|
+
def initialize(type:, requests:, call:, method:, metadata:)
|
45
|
+
@type = type
|
46
|
+
@requests = requests
|
47
|
+
@call = call
|
48
|
+
@method = method
|
49
|
+
@metadata = metadata
|
50
|
+
end
|
51
|
+
|
52
|
+
##
|
53
|
+
# Return the name of the method being called, e.g. GetThing
|
54
|
+
#
|
55
|
+
# @return [String]
|
56
|
+
#
|
57
|
+
def method_name
|
58
|
+
@method.to_s.split('/').last
|
59
|
+
end
|
60
|
+
|
61
|
+
##
|
62
|
+
# Return the proper routing key for the request
|
63
|
+
#
|
64
|
+
# @return [String]
|
65
|
+
#
|
66
|
+
def route_key
|
67
|
+
@method[1..-1].underscore.tr('/', '.')
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/gruf/response.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
|
2
4
|
#
|
3
5
|
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
@@ -35,11 +37,12 @@ module Gruf
|
|
35
37
|
# Initialize a response object with the given gRPC operation
|
36
38
|
#
|
37
39
|
# @param [GRPC::ActiveCall::Operation] operation The given operation for the current call
|
40
|
+
# @param [StdClass] message
|
38
41
|
# @param [Float] execution_time The amount of time that the response took to occur
|
39
42
|
#
|
40
|
-
def initialize(operation
|
43
|
+
def initialize(operation:, message:, execution_time: nil)
|
41
44
|
@operation = operation
|
42
|
-
@message =
|
45
|
+
@message = message
|
43
46
|
@metadata = operation.metadata
|
44
47
|
@trailing_metadata = operation.trailing_metadata
|
45
48
|
@deadline = operation.deadline
|