gruf 1.2.7 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/README.md +98 -119
  4. data/bin/gruf +9 -3
  5. data/lib/gruf.rb +4 -4
  6. data/lib/gruf/configuration.rb +11 -20
  7. data/lib/gruf/controllers/base.rb +82 -0
  8. data/lib/gruf/controllers/request.rb +96 -0
  9. data/lib/gruf/controllers/service_binder.rb +86 -0
  10. data/lib/gruf/error.rb +9 -0
  11. data/lib/gruf/errors/helpers.rb +40 -0
  12. data/lib/gruf/{hooks → interceptors}/active_record/connection_reset.rb +4 -10
  13. data/lib/gruf/interceptors/authentication/basic.rb +80 -0
  14. data/lib/gruf/interceptors/base.rb +51 -0
  15. data/lib/gruf/{instrumentation/output_metadata_timer.rb → interceptors/context.rb} +25 -15
  16. data/lib/gruf/interceptors/instrumentation/output_metadata_timer.rb +59 -0
  17. data/lib/gruf/{instrumentation → interceptors/instrumentation}/request_logging/formatters/base.rb +15 -13
  18. data/lib/gruf/{instrumentation → interceptors/instrumentation}/request_logging/formatters/logstash.rb +15 -13
  19. data/lib/gruf/{instrumentation → interceptors/instrumentation}/request_logging/formatters/plain.rb +21 -19
  20. data/lib/gruf/interceptors/instrumentation/request_logging/interceptor.rb +191 -0
  21. data/lib/gruf/interceptors/instrumentation/statsd.rb +80 -0
  22. data/lib/gruf/interceptors/registry.rb +131 -0
  23. data/lib/gruf/{authentication/none.rb → interceptors/server_interceptor.rb} +8 -7
  24. data/lib/gruf/interceptors/timer.rb +79 -0
  25. data/lib/gruf/response.rb +1 -2
  26. data/lib/gruf/server.rb +40 -25
  27. data/lib/gruf/version.rb +1 -1
  28. metadata +19 -20
  29. data/lib/gruf/authentication.rb +0 -65
  30. data/lib/gruf/authentication/base.rb +0 -65
  31. data/lib/gruf/authentication/basic.rb +0 -74
  32. data/lib/gruf/authentication/strategies.rb +0 -107
  33. data/lib/gruf/hooks/base.rb +0 -66
  34. data/lib/gruf/hooks/registry.rb +0 -110
  35. data/lib/gruf/instrumentation/base.rb +0 -114
  36. data/lib/gruf/instrumentation/registry.rb +0 -104
  37. data/lib/gruf/instrumentation/request_context.rb +0 -82
  38. data/lib/gruf/instrumentation/request_logging/hook.rb +0 -185
  39. data/lib/gruf/instrumentation/statsd.rb +0 -80
  40. data/lib/gruf/service.rb +0 -333
@@ -0,0 +1,82 @@
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_relative 'request'
18
+ require_relative 'service_binder'
19
+
20
+ module Gruf
21
+ module Controllers
22
+ ##
23
+ # Base controller object for Gruf gRPC requests
24
+ #
25
+ class Base
26
+ include Gruf::Errors::Helpers
27
+
28
+ # @var [Gruf::Controller::Request] request
29
+ attr_reader :request
30
+ # @var [Gruf::Error] error
31
+ attr_reader :error
32
+
33
+ ##
34
+ # Initialize the controller within the given request context
35
+ #
36
+ # @param [Symbol] method_key
37
+ # @param [GRPC::GenericService] service
38
+ # @param [GRPC::RpcDesc] rpc_desc
39
+ # @param [GRPC::ActiveCall] active_call
40
+ # @param [Google::Protobuf::MessageExts] message
41
+ #
42
+ def initialize(method_key:, service:, rpc_desc:, active_call:, message:)
43
+ @request = Request.new(
44
+ method_key: method_key,
45
+ service: service,
46
+ rpc_desc: rpc_desc,
47
+ active_call: active_call,
48
+ message: message
49
+ )
50
+ @error = Gruf::Error.new
51
+ @interceptors = Gruf.interceptors.prepare(@request, @error)
52
+ end
53
+
54
+ ##
55
+ # Bind the controller to the given service and add it to the service registry
56
+ #
57
+ # @param [GRPC::GenericService] service
58
+ #
59
+ def self.bind(service)
60
+ Gruf.services << service.name.constantize
61
+ ServiceBinder.new(service).bind!(self)
62
+ end
63
+
64
+ ##
65
+ # Call a method on this controller
66
+ #
67
+ # @param [Symbol] method_key
68
+ #
69
+ def call(method_key, &block)
70
+ Interceptors::Context.new(@interceptors).intercept! do
71
+ send(method_key, &block)
72
+ end
73
+ rescue GRPC::BadStatus
74
+ raise # passthrough
75
+ rescue => e
76
+ set_debug_info(e.message, e.backtrace) if Gruf.backtrace_on_error
77
+ error_message = Gruf.use_exception_message ? e.message : Gruf.internal_error_message
78
+ fail!(:internal, :unknown, error_message)
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,96 @@
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 Controllers
19
+ ##
20
+ # Encapsulates a request for a controller
21
+ #
22
+ class Request
23
+ # @var [Object] message
24
+ attr_reader :message
25
+ # @var [GRPC::ActiveCall] active_call
26
+ attr_reader :active_call
27
+ # @var [Symbol] method_key
28
+ attr_reader :method_key
29
+ # @var [Gruf::Controllers::Request::Type] type
30
+ attr_reader :type
31
+
32
+ delegate :metadata, to: :active_call
33
+ delegate :messages, :client_streamer?, :server_streamer?, :bidi_streamer?, :request_response?, to: :type
34
+
35
+ class Type
36
+ delegate :client_streamer?, :server_streamer?, :bidi_streamer?, :request_response?, to: :@rpc_desc
37
+
38
+ ##
39
+ # @param [GRPC::RpcDesc] rpc_desc
40
+ #
41
+ def initialize(rpc_desc)
42
+ @rpc_desc = rpc_desc
43
+ end
44
+ end
45
+
46
+ ##
47
+ # @param [Symbol] method_key The method symbol of the RPC method being executed
48
+ # @param [Class] service The class of the service being executed against
49
+ # @param [GRPC::RpcDesc] rpc_desc The RPC descriptor of the call
50
+ # @param [GRPC::ActiveCall] active_call The restricted view of the call
51
+ # @param [Object] message The protobuf message (or messages) of the request
52
+ #
53
+ def initialize(method_key:, service:, rpc_desc:, active_call:, message:)
54
+ @method_key = method_key
55
+ @service = service
56
+ @active_call = active_call
57
+ @message = message
58
+ @type = Type.new(rpc_desc)
59
+ end
60
+
61
+ ##
62
+ # Returns the service name as a translated name separated by periods. Strips
63
+ # the superfluous "Service" suffix from the name
64
+ #
65
+ # @return [String]
66
+ #
67
+ def service_key
68
+ @service.name.underscore.tr('/', '.').gsub('.service', '')
69
+ end
70
+
71
+ ##
72
+ # Parse the method signature into a service.method name format
73
+ #
74
+ # @return [String] The parsed service method name
75
+ #
76
+ def method_name
77
+ "#{service_key}.#{method_key}"
78
+ end
79
+
80
+ ##
81
+ # Return all messages for this request, properly handling different request types
82
+ #
83
+ # @return Enumerable<Object>
84
+ #
85
+ def messages
86
+ if client_streamer?
87
+ message.call { |msg| yield msg }
88
+ elsif bidi_streamer?
89
+ message
90
+ else
91
+ [message]
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,86 @@
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 Controllers
19
+ ##
20
+ # Binds gRPC services to a gruf controller
21
+ #
22
+ class ServiceBinder
23
+ ##
24
+ # Represents a bound RPC descriptor for future-proofing internal helpers
25
+ #
26
+ class BoundDesc < SimpleDelegator; end
27
+
28
+ ##
29
+ # @param [GRPC::GenericService] service
30
+ #
31
+ def initialize(service)
32
+ @service = service
33
+ end
34
+
35
+ ##
36
+ # @param [Gruf::Controllers::Base]
37
+ #
38
+ def bind!(controller)
39
+ rpc_methods.each { |name, desc| bind_method(controller, name, desc) }
40
+ end
41
+
42
+ private
43
+
44
+ ##
45
+ # @param [Gruf::Controllers::Base] controller
46
+ # @param [Symbol] method_name
47
+ # @param [BoundDesc] desc
48
+ #
49
+ def bind_method(controller, method_name, desc)
50
+ method_key = method_name.to_s.underscore.to_sym
51
+ service_ref = @service
52
+
53
+ @service.class_eval do
54
+ if desc.request_response?
55
+ define_method(method_key) do |message, active_call|
56
+ c = controller.new(method_key: method_key, service: service_ref, message: message, active_call: active_call, rpc_desc: desc)
57
+ c.call(method_key)
58
+ end
59
+ elsif desc.client_streamer?
60
+ define_method(method_key) do |active_call|
61
+ c = controller.new(method_key: method_key, service: service_ref, message: proc { |&block| active_call.each_remote_read(&block) }, active_call: active_call, rpc_desc: desc)
62
+ c.call(method_key)
63
+ end
64
+ elsif desc.server_streamer?
65
+ define_method(method_key) do |message, active_call, &block|
66
+ c = controller.new(method_key: method_key, service: service_ref, message: message, active_call: active_call, rpc_desc: desc)
67
+ c.call(method_key, &block)
68
+ end
69
+ else # bidi
70
+ define_method(method_key) do |messages, active_call, &block|
71
+ c = controller.new(method_key: method_key, service: service_ref, message: messages, active_call: active_call, rpc_desc: desc)
72
+ c.call(method_key, &block)
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ ##
79
+ # @return Array<Gruf::Controllers::ServiceBinder::BoundDesc>
80
+ #
81
+ def rpc_methods
82
+ @service.rpc_descs.map { |rd| BoundDesc.new(rd) }
83
+ end
84
+ end
85
+ end
86
+ end
@@ -90,6 +90,15 @@ module Gruf
90
90
  @field_errors << Errors::Field.new(field_name, error_code, message)
91
91
  end
92
92
 
93
+ ##
94
+ # Return true if there are any present field errors
95
+ #
96
+ # @return [Boolean] True if the service has any field errors
97
+ #
98
+ def has_field_errors?
99
+ @field_errors.any?
100
+ end
101
+
93
102
  ##
94
103
  # Set the debugging information for the error message
95
104
  #
@@ -0,0 +1,40 @@
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 Errors
19
+ module Helpers
20
+ delegate :add_field_error, :set_debug_info, :has_field_errors?, to: :error
21
+
22
+ ##
23
+ # Will issue a GRPC BadStatus exception, with a code based on the code passed.
24
+ #
25
+ # @param [Symbol] error_code The network error code that maps to gRPC status codes
26
+ # @param [Symbol] app_code The application-specific code for the error
27
+ # @param [String] message (Optional) A detail message about the error
28
+ # @param [Hash] metadata (Optional) Any metadata to inject into the trailing metadata for the response
29
+ #
30
+ def fail!(error_code, app_code = nil, message = '', metadata = {})
31
+ e = error
32
+ e.code = error_code.to_sym
33
+ e.app_code = app_code ? app_code.to_sym : e.code
34
+ e.message = message.to_s
35
+ e.metadata = metadata
36
+ e.fail!(request.active_call)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -15,25 +15,19 @@
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 Hooks
18
+ module Interceptors
19
19
  module ActiveRecord
20
20
  ##
21
21
  # Resets the ActiveRecord connection to maintain accurate connected state in the thread pool
22
22
  #
23
- class ConnectionReset < Gruf::Hooks::Base
23
+ class ConnectionReset < ::Gruf::Interceptors::ServerInterceptor
24
24
  ##
25
25
  # Reset any ActiveRecord connections after a gRPC service is called. Because of the way gRPC manages its
26
26
  # connection pool, we need to ensure that this is done to properly
27
27
  #
28
- # @param [Boolean] _success Whether or not the call was successful
29
- # @param [Object] _response The protobuf response object
30
- # @param [Symbol] _call_signature The gRPC method on the service that was called
31
- # @param [Object] _request The protobuf request object
32
- # @param [GRPC::ActiveCall] _call the gRPC core active call object, which represents marshalled data for
33
- # the call itself
34
- #
35
- def after(_success, _response, _call_signature, _request, _call)
28
+ def call
36
29
  ::ActiveRecord::Base.clear_active_connections! if enabled?
30
+ yield
37
31
  end
38
32
 
39
33
  private
@@ -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
+ require 'base64'
18
+
19
+ module Gruf
20
+ module Interceptors
21
+ module Authentication
22
+ ##
23
+ # Handles basic authentication for gRPC requests
24
+ #
25
+ class Basic < Gruf::Interceptors::ServerInterceptor
26
+
27
+ def call
28
+ fail!(:unauthenticated, :unauthenticated) unless bypass? || valid?
29
+ yield
30
+ end
31
+
32
+ private
33
+
34
+ ##
35
+ # @return [Boolean] If this method is in the excluded list, bypass
36
+ #
37
+ def bypass?
38
+ options.fetch(:excluded_methods, []).include?(request.method_name)
39
+ end
40
+
41
+ ##
42
+ # @return [Boolean] True if the basic authentication was valid
43
+ #
44
+ def valid?
45
+ server_credentials.any? do |cred|
46
+ username = cred.fetch(:username, '').to_s
47
+ password = cred.fetch(:password, '').to_s
48
+ if username.empty?
49
+ request_password == password
50
+ else
51
+ request_credentials == "#{username}:#{password}"
52
+ end
53
+ end
54
+ end
55
+
56
+ ##
57
+ # @return [Array<Hash>] An array of valid server credentials for this service
58
+ #
59
+ def server_credentials
60
+ options.fetch(:credentials, [])
61
+ end
62
+
63
+ ##
64
+ # @return [String] The decoded request credentials
65
+ #
66
+ def request_credentials
67
+ credentials = request.active_call.respond_to?(:metadata) ? request.active_call.metadata['authorization'].to_s : ''
68
+ Base64.decode64(credentials.to_s.gsub('Basic ', '').strip)
69
+ end
70
+
71
+ ##
72
+ # @return [String] The decoded request password
73
+ #
74
+ def request_password
75
+ @request_password ||= request_credentials.split(':').last
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,51 @@
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
+ ##
20
+ # Base class for interception requests
21
+ #
22
+ class Base
23
+ # @var [Gruf::Controllers::Request] request
24
+ attr_reader :request
25
+ # @var [Gruf::Error] error
26
+ attr_reader :error
27
+ # @var [Hash] options
28
+ attr_reader :options
29
+
30
+ ##
31
+ # @param [Gruf::Controllers::Request] request
32
+ # @param [Gruf::Error] error
33
+ # @param [Hash] options
34
+ #
35
+ def initialize(request, error, options = {})
36
+ @request = request
37
+ @error = error
38
+ @options = options || {}
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ require_relative 'server_interceptor'
45
+ require_relative 'context'
46
+ require_relative 'timer'
47
+ require_relative 'active_record/connection_reset'
48
+ require_relative 'authentication/basic'
49
+ require_relative 'instrumentation/statsd'
50
+ require_relative 'instrumentation/output_metadata_timer'
51
+ require_relative 'instrumentation/request_logging/interceptor'