ddtrace 0.13.0 → 0.14.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 (58) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +88 -6
  3. data/.rubocop.yml +10 -0
  4. data/Appraisals +24 -0
  5. data/CHANGELOG.md +108 -2
  6. data/Gemfile +1 -0
  7. data/Rakefile +32 -2
  8. data/benchmarks/postgres_database.yml +9 -0
  9. data/benchmarks/sidekiq_test.rb +154 -0
  10. data/ddtrace.gemspec +3 -0
  11. data/docs/GettingStarted.md +127 -6
  12. data/lib/ddtrace/configuration.rb +19 -7
  13. data/lib/ddtrace/context.rb +8 -0
  14. data/lib/ddtrace/contrib/active_record/configuration/resolver.rb +46 -0
  15. data/lib/ddtrace/contrib/active_record/configuration/settings.rb +28 -0
  16. data/lib/ddtrace/contrib/active_record/events/sql.rb +11 -7
  17. data/lib/ddtrace/contrib/active_record/integration.rb +44 -0
  18. data/lib/ddtrace/contrib/active_record/patcher.rb +4 -28
  19. data/lib/ddtrace/contrib/active_record/utils.rb +5 -11
  20. data/lib/ddtrace/contrib/active_support/notifications/subscription.rb +2 -1
  21. data/lib/ddtrace/contrib/base.rb +4 -3
  22. data/lib/ddtrace/contrib/configurable.rb +55 -0
  23. data/lib/ddtrace/contrib/configuration/option.rb +33 -0
  24. data/lib/ddtrace/contrib/configuration/option_definition.rb +29 -0
  25. data/lib/ddtrace/contrib/configuration/option_definition_set.rb +20 -0
  26. data/lib/ddtrace/contrib/configuration/option_set.rb +8 -0
  27. data/lib/ddtrace/contrib/configuration/options.rb +95 -0
  28. data/lib/ddtrace/contrib/configuration/resolver.rb +12 -0
  29. data/lib/ddtrace/contrib/configuration/settings.rb +35 -0
  30. data/lib/ddtrace/contrib/delayed_job/patcher.rb +41 -0
  31. data/lib/ddtrace/contrib/delayed_job/plugin.rb +43 -0
  32. data/lib/ddtrace/contrib/integration.rb +16 -0
  33. data/lib/ddtrace/contrib/patchable.rb +38 -0
  34. data/lib/ddtrace/contrib/patcher.rb +28 -0
  35. data/lib/ddtrace/contrib/rack/middlewares.rb +34 -15
  36. data/lib/ddtrace/contrib/rake/instrumentation.rb +15 -3
  37. data/lib/ddtrace/contrib/registerable.rb +33 -0
  38. data/lib/ddtrace/contrib/resque/resque_job.rb +2 -10
  39. data/lib/ddtrace/contrib/rest_client/configuration/settings.rb +22 -0
  40. data/lib/ddtrace/contrib/rest_client/integration.rb +27 -0
  41. data/lib/ddtrace/contrib/rest_client/patcher.rb +25 -0
  42. data/lib/ddtrace/contrib/rest_client/request_patch.rb +90 -0
  43. data/lib/ddtrace/contrib/sequel/configuration/settings.rb +13 -0
  44. data/lib/ddtrace/contrib/sequel/database.rb +1 -1
  45. data/lib/ddtrace/contrib/sequel/integration.rb +38 -0
  46. data/lib/ddtrace/contrib/sequel/patcher.rb +4 -20
  47. data/lib/ddtrace/monkey.rb +0 -7
  48. data/lib/ddtrace/patcher.rb +5 -0
  49. data/lib/ddtrace/pin.rb +1 -1
  50. data/lib/ddtrace/propagation/http_propagator.rb +6 -0
  51. data/lib/ddtrace/span.rb +0 -1
  52. data/lib/ddtrace/tracer.rb +22 -5
  53. data/lib/ddtrace/vendor/active_record/connection_specification.rb +301 -0
  54. data/lib/ddtrace/version.rb +1 -1
  55. data/lib/ddtrace/workers.rb +6 -4
  56. data/lib/ddtrace/writer.rb +4 -5
  57. data/lib/ddtrace.rb +7 -15
  58. metadata +57 -3
@@ -1,6 +1,4 @@
1
- require 'ddtrace/ext/sql'
2
- require 'ddtrace/ext/app_types'
3
- require 'ddtrace/contrib/active_record/utils'
1
+ require 'ddtrace/contrib/patcher'
4
2
  require 'ddtrace/contrib/active_record/events'
5
3
 
6
4
  module Datadog
@@ -8,44 +6,22 @@ module Datadog
8
6
  module ActiveRecord
9
7
  # Patcher enables patching of 'active_record' module.
10
8
  module Patcher
11
- include Base
12
-
13
- register_as :active_record, auto_patch: false
14
- option :service_name, depends_on: [:tracer] do |value|
15
- (value || Utils.adapter_name).tap do |v|
16
- get_option(:tracer).set_service_info(v, 'active_record', Ext::AppTypes::DB)
17
- end
18
- end
19
- option :orm_service_name
20
- option :tracer, default: Datadog.tracer do |value|
21
- (value || Datadog.tracer).tap do |v|
22
- # Make sure to update tracers of all subscriptions
23
- Events.subscriptions.each do |subscription|
24
- subscription.tracer = v
25
- end
26
- end
27
- end
28
-
29
- @patched = false
9
+ include Contrib::Patcher
30
10
 
31
11
  module_function
32
12
 
33
- # patched? tells whether patch has been successfully applied
34
13
  def patched?
35
- @patched
14
+ done?(:active_record)
36
15
  end
37
16
 
38
17
  def patch
39
- if !@patched && defined?(::ActiveRecord)
18
+ do_once(:active_record) do
40
19
  begin
41
20
  Events.subscribe!
42
- @patched = true
43
21
  rescue StandardError => e
44
22
  Datadog::Tracer.log.error("Unable to apply Active Record integration: #{e}")
45
23
  end
46
24
  end
47
-
48
- @patched
49
25
  end
50
26
  end
51
27
  end
@@ -4,29 +4,23 @@ module Datadog
4
4
  # Common utilities for Rails
5
5
  module Utils
6
6
  def self.adapter_name
7
- connection_config[:adapter_name]
7
+ Datadog::Utils::Database.normalize_vendor(connection_config[:adapter])
8
8
  end
9
9
 
10
10
  def self.database_name
11
- connection_config[:database_name]
11
+ connection_config[:database]
12
12
  end
13
13
 
14
14
  def self.adapter_host
15
- connection_config[:adapter_host]
15
+ connection_config[:host]
16
16
  end
17
17
 
18
18
  def self.adapter_port
19
- connection_config[:adapter_port]
19
+ connection_config[:port]
20
20
  end
21
21
 
22
22
  def self.connection_config(object_id = nil)
23
- config = object_id.nil? ? default_connection_config : connection_config_by_id(object_id)
24
- {
25
- adapter_name: Datadog::Utils::Database.normalize_vendor(config[:adapter]),
26
- adapter_host: config[:host],
27
- adapter_port: config[:port],
28
- database_name: config[:database]
29
- }
23
+ object_id.nil? ? default_connection_config : connection_config_by_id(object_id)
30
24
  end
31
25
 
32
26
  # Attempt to retrieve the connection from an object ID.
@@ -75,11 +75,12 @@ module Datadog
75
75
  tracer.trace(@span_name, @options).tap do |span|
76
76
  # Assign start time if provided
77
77
  span.start_time = start unless start.nil?
78
+ payload[:datadog_span] = span
78
79
  end
79
80
  end
80
81
 
81
82
  def finish_span(name, id, payload, finish = nil)
82
- tracer.active_span.tap do |span|
83
+ payload[:datadog_span].tap do |span|
83
84
  # If no active span, return.
84
85
  return nil if span.nil?
85
86
 
@@ -1,5 +1,6 @@
1
- require 'ddtrace/registry'
2
1
  require 'ddtrace/configurable'
2
+ require 'ddtrace/patcher'
3
+ require 'ddtrace/registry/registerable'
3
4
 
4
5
  module Datadog
5
6
  module Contrib
@@ -7,8 +8,8 @@ module Datadog
7
8
  module Base
8
9
  def self.included(base)
9
10
  base.send(:include, Registry::Registerable)
10
- base.send(:include, Configurable)
11
- base.send(:include, Patcher)
11
+ base.send(:include, Datadog::Configurable)
12
+ base.send(:include, Datadog::Patcher)
12
13
  end
13
14
  end
14
15
  end
@@ -0,0 +1,55 @@
1
+ require 'ddtrace/contrib/configuration/resolver'
2
+ require 'ddtrace/contrib/configuration/settings'
3
+
4
+ module Datadog
5
+ module Contrib
6
+ # Defines configurable behavior for integrations
7
+ module Configurable
8
+ def self.included(base)
9
+ base.send(:include, InstanceMethods)
10
+ end
11
+
12
+ # Configurable instance behavior for integrations
13
+ module InstanceMethods
14
+ def default_configuration
15
+ Configuration::Settings.new
16
+ end
17
+
18
+ def reset_configuration!
19
+ @configurations = nil
20
+ @resolver = nil
21
+ end
22
+
23
+ def configuration(name = :default)
24
+ name = :default if name.nil?
25
+ name = resolver.resolve(name)
26
+ return nil unless configurations.key?(name)
27
+ configurations[name]
28
+ end
29
+
30
+ def configurations
31
+ @configurations ||= Hash.new { default_configuration }.tap do |configs|
32
+ configs[:default] = default_configuration
33
+ end
34
+ end
35
+
36
+ def configure(name, options = {}, &block)
37
+ name = resolver.resolve(name || :default)
38
+
39
+ configurations[name].tap do |settings|
40
+ settings.configure(options, &block)
41
+ configurations[name] = settings
42
+ end
43
+ end
44
+
45
+ protected
46
+
47
+ attr_writer :resolver
48
+
49
+ def resolver
50
+ @resolver ||= Configuration::Resolver.new
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,33 @@
1
+ module Datadog
2
+ module Contrib
3
+ module Configuration
4
+ # Represents an instance of an integration configuration option
5
+ class Option
6
+ attr_reader \
7
+ :definition
8
+
9
+ def initialize(definition, context)
10
+ @definition = definition
11
+ @context = context
12
+ @value = nil
13
+ @is_set = false
14
+ end
15
+
16
+ def set(value)
17
+ @value = @context.instance_exec(value, &definition.setter).tap do
18
+ @is_set = true
19
+ end
20
+ end
21
+
22
+ def get
23
+ return definition.default_value unless @is_set
24
+ @value
25
+ end
26
+
27
+ def reset
28
+ set(definition.default_value)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,29 @@
1
+ module Datadog
2
+ module Contrib
3
+ module Configuration
4
+ # Represents a definition for an integration configuration option
5
+ class OptionDefinition
6
+ IDENTITY = ->(x) { x }
7
+
8
+ attr_reader \
9
+ :default,
10
+ :depends_on,
11
+ :lazy,
12
+ :name,
13
+ :setter
14
+
15
+ def initialize(name, meta = {}, &block)
16
+ @default = meta[:default]
17
+ @depends_on = meta[:depends_on] || []
18
+ @lazy = meta[:lazy] || false
19
+ @name = name.to_sym
20
+ @setter = meta[:setter] || block || IDENTITY
21
+ end
22
+
23
+ def default_value
24
+ lazy ? @default.call : @default
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,20 @@
1
+ require 'ddtrace/configuration/resolver'
2
+
3
+ module Datadog
4
+ module Contrib
5
+ module Configuration
6
+ # Represents a set of configuration option definitions for an integration
7
+ class OptionDefinitionSet < Hash
8
+ def dependency_order
9
+ Datadog::Configuration::Resolver.new(dependency_graph).call
10
+ end
11
+
12
+ def dependency_graph
13
+ each_with_object({}) do |(name, option), graph|
14
+ graph[name] = option.depends_on
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,8 @@
1
+ module Datadog
2
+ module Contrib
3
+ module Configuration
4
+ class OptionSet < Hash
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,95 @@
1
+ require 'ddtrace/contrib/configuration/option'
2
+ require 'ddtrace/contrib/configuration/option_set'
3
+ require 'ddtrace/contrib/configuration/option_definition'
4
+ require 'ddtrace/contrib/configuration/option_definition_set'
5
+
6
+ module Datadog
7
+ module Contrib
8
+ module Configuration
9
+ # Behavior for a configuration object that has options
10
+ module Options
11
+ def self.included(base)
12
+ base.send(:extend, ClassMethods)
13
+ base.send(:include, InstanceMethods)
14
+ end
15
+
16
+ # Class behavior for a configuration object with options
17
+ module ClassMethods
18
+ def options
19
+ @options ||= begin
20
+ # Allows for class inheritance of option definitions
21
+ superclass <= Options ? superclass.options.dup : OptionDefinitionSet.new
22
+ end
23
+ end
24
+
25
+ protected
26
+
27
+ def option(name, meta = {}, &block)
28
+ options[name] = OptionDefinition.new(name, meta, &block).tap do
29
+ define_option_accessors(name)
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def define_option_accessors(name)
36
+ option_name = name
37
+
38
+ define_method(option_name) do
39
+ get_option(option_name)
40
+ end
41
+
42
+ define_method("#{option_name}=") do |value|
43
+ set_option(option_name, value)
44
+ end
45
+ end
46
+ end
47
+
48
+ # Instance behavior for a configuration object with options
49
+ module InstanceMethods
50
+ def options
51
+ @options ||= OptionSet.new
52
+ end
53
+
54
+ def set_option(name, value)
55
+ add_option(name) unless options.key?(name)
56
+ options[name].set(value)
57
+ end
58
+
59
+ def get_option(name)
60
+ add_option(name) unless options.key?(name)
61
+ options[name].get
62
+ end
63
+
64
+ def to_h
65
+ options.each_with_object({}) do |(key, _), hash|
66
+ hash[key] = get_option(key)
67
+ end
68
+ end
69
+
70
+ def reset_options!
71
+ options.values.each(&:reset)
72
+ end
73
+
74
+ private
75
+
76
+ def add_option(name)
77
+ assert_valid_option!(name)
78
+ definition = self.class.options[name]
79
+ Option.new(definition, self).tap do |option|
80
+ options[name] = option
81
+ end
82
+ end
83
+
84
+ def assert_valid_option!(name)
85
+ unless self.class.options.key?(name)
86
+ raise(InvalidOptionError, "#{self.class.name} doesn't define the option: #{name}")
87
+ end
88
+ end
89
+ end
90
+
91
+ InvalidOptionError = Class.new(StandardError)
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,12 @@
1
+ module Datadog
2
+ module Contrib
3
+ module Configuration
4
+ # Resolves a value to a configuration key
5
+ class Resolver
6
+ def resolve(name)
7
+ name
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,35 @@
1
+ require 'ddtrace/contrib/configuration/options'
2
+
3
+ module Datadog
4
+ module Contrib
5
+ module Configuration
6
+ # Common settings for all integrations
7
+ class Settings
8
+ include Options
9
+
10
+ option :service_name
11
+ option :tracer, default: Datadog.tracer
12
+
13
+ def initialize(options = {})
14
+ configure(options)
15
+ end
16
+
17
+ def configure(options = {})
18
+ self.class.options.dependency_order.each do |name|
19
+ self[name] = options.fetch(name, self[name])
20
+ end
21
+
22
+ yield(self) if block_given?
23
+ end
24
+
25
+ def [](name)
26
+ respond_to?(name) ? send(name) : get_option(name)
27
+ end
28
+
29
+ def []=(name, value)
30
+ respond_to?("#{name}=") ? send("#{name}=", value) : set_option(name, value)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,41 @@
1
+ module Datadog
2
+ module Contrib
3
+ module DelayedJob
4
+ # DelayedJob integration
5
+ module Patcher
6
+ include Base
7
+ register_as :delayed_job
8
+
9
+ option :service_name, default: 'delayed_job'.freeze
10
+ option :tracer, default: Datadog.tracer
11
+
12
+ @patched = false
13
+
14
+ class << self
15
+ def patch
16
+ return @patched if patched? || !defined?(::Delayed)
17
+
18
+ require 'ddtrace/ext/app_types'
19
+ require_relative 'plugin'
20
+
21
+ add_instrumentation(::Delayed::Worker)
22
+ @patched = true
23
+ rescue => e
24
+ Tracer.log.error("Unable to apply DelayedJob integration: #{e}")
25
+ @patched
26
+ end
27
+
28
+ def patched?
29
+ @patched
30
+ end
31
+
32
+ private
33
+
34
+ def add_instrumentation(klass)
35
+ klass.plugins << Plugin
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,43 @@
1
+ require 'delayed/plugin'
2
+
3
+ module Datadog
4
+ module Contrib
5
+ module DelayedJob
6
+ # DelayedJob plugin that instruments invoke_job hook
7
+ class Plugin < Delayed::Plugin
8
+ def self.instrument(job, &block)
9
+ return block.call(job) unless tracer && tracer.enabled
10
+
11
+ tracer.trace('delayed_job'.freeze, service: configuration[:service_name], resource: job.name) do |span|
12
+ span.set_tag('delayed_job.id'.freeze, job.id)
13
+ span.set_tag('delayed_job.queue'.freeze, job.queue) if job.queue
14
+ span.set_tag('delayed_job.priority'.freeze, job.priority)
15
+ span.set_tag('delayed_job.attempts'.freeze, job.attempts)
16
+ span.span_type = Ext::AppTypes::WORKER
17
+
18
+ yield job
19
+ end
20
+ end
21
+
22
+ def self.flush(worker, &block)
23
+ yield worker
24
+
25
+ tracer.shutdown! if tracer && tracer.enabled
26
+ end
27
+
28
+ def self.configuration
29
+ Datadog.configuration[:delayed_job]
30
+ end
31
+
32
+ def self.tracer
33
+ configuration[:tracer]
34
+ end
35
+
36
+ callbacks do |lifecycle|
37
+ lifecycle.around(:invoke_job, &method(:instrument))
38
+ lifecycle.around(:execute, &method(:flush))
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,16 @@
1
+ require 'ddtrace/contrib/configurable'
2
+ require 'ddtrace/contrib/patchable'
3
+ require 'ddtrace/contrib/registerable'
4
+
5
+ module Datadog
6
+ module Contrib
7
+ # Base provides features that are shared across all integrations
8
+ module Integration
9
+ def self.included(base)
10
+ base.send(:include, Configurable)
11
+ base.send(:include, Patchable)
12
+ base.send(:include, Registerable)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,38 @@
1
+ module Datadog
2
+ module Contrib
3
+ # Base provides features that are shared across all integrations
4
+ module Patchable
5
+ def self.included(base)
6
+ base.send(:extend, ClassMethods)
7
+ base.send(:include, InstanceMethods)
8
+ end
9
+
10
+ # Class methods for integrations
11
+ module ClassMethods
12
+ def version
13
+ nil
14
+ end
15
+
16
+ def present?
17
+ !version.nil?
18
+ end
19
+
20
+ def compatible?
21
+ RUBY_VERSION >= '1.9.3' && present?
22
+ end
23
+ end
24
+
25
+ # Instance methods for integrations
26
+ module InstanceMethods
27
+ def patcher
28
+ nil
29
+ end
30
+
31
+ def patch
32
+ return if !self.class.compatible? || patcher.nil?
33
+ patcher.patch
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,28 @@
1
+ require 'ddtrace/patcher'
2
+
3
+ module Datadog
4
+ module Contrib
5
+ # Common behavior for patcher modules
6
+ module Patcher
7
+ def self.included(base)
8
+ base.send(:include, Datadog::Patcher)
9
+ base.send(:extend, InstanceMethods)
10
+ base.send(:include, InstanceMethods)
11
+ end
12
+
13
+ # Class methods for patchers
14
+ module ClassMethods
15
+ def patch
16
+ raise NotImplementedError, '#patch not implemented for Patcher!'
17
+ end
18
+ end
19
+
20
+ # Instance methods for patchers
21
+ module InstanceMethods
22
+ def patch
23
+ raise NotImplementedError, '#patch not implemented for Patcher!'
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -13,6 +13,7 @@ module Datadog
13
13
  # in the Rack environment so that it can be retrieved by the underlying
14
14
  # application. If request tags are not set by the app, they will be set using
15
15
  # information available at the Rack level.
16
+ # rubocop:disable Metrics/ClassLength
16
17
  class TraceMiddleware
17
18
  RACK_REQUEST_SPAN = 'datadog.rack_request_span'.freeze
18
19
 
@@ -58,11 +59,12 @@ module Datadog
58
59
  request_span = tracer.trace('rack.request', trace_options)
59
60
  env[RACK_REQUEST_SPAN] = request_span
60
61
 
61
- # TODO: For backwards compatibility; this attribute is deprecated.
62
- env[:datadog_rack_request_span] = env[RACK_REQUEST_SPAN]
63
-
64
62
  # Add deprecation warnings
65
63
  add_deprecation_warnings(env)
64
+ env.without_datadog_warnings do
65
+ # TODO: For backwards compatibility; this attribute is deprecated.
66
+ env[:datadog_rack_request_span] = env[RACK_REQUEST_SPAN]
67
+ end
66
68
 
67
69
  # Copy the original env, before the rest of the stack executes.
68
70
  # Values may change; we want values before that happens.
@@ -175,24 +177,41 @@ module Datadog
175
177
  :datadog_rack_request_span is considered an internal symbol in the Rack env,
176
178
  and has been been DEPRECATED. Public support for its usage is discontinued.
177
179
  If you need the Rack request span, try using `Datadog.tracer.active_span`.
178
- This key will be removed in version 0.14.0).freeze
180
+ This key will be removed in version 1.0).freeze
179
181
 
180
182
  def add_deprecation_warnings(env)
181
183
  env.instance_eval do
182
- def [](key)
183
- if key == :datadog_rack_request_span && !@request_span_warning_issued
184
- Datadog::Tracer.log.warn(REQUEST_SPAN_DEPRECATION_WARNING)
185
- @request_span_warning_issued = true
184
+ unless instance_variable_defined?(:@patched_with_datadog_warnings)
185
+ @patched_with_datadog_warnings = true
186
+ @datadog_deprecation_warnings = true
187
+ @datadog_span_warning = true
188
+
189
+ def [](key)
190
+ if key == :datadog_rack_request_span \
191
+ && @datadog_span_warning \
192
+ && @datadog_deprecation_warnings
193
+ Datadog::Tracer.log.warn(REQUEST_SPAN_DEPRECATION_WARNING)
194
+ @datadog_span_warning = true
195
+ end
196
+ super
197
+ end
198
+
199
+ def []=(key, value)
200
+ if key == :datadog_rack_request_span \
201
+ && @datadog_span_warning \
202
+ && @datadog_deprecation_warnings
203
+ Datadog::Tracer.log.warn(REQUEST_SPAN_DEPRECATION_WARNING)
204
+ @datadog_span_warning = true
205
+ end
206
+ super
186
207
  end
187
- super
188
- end
189
208
 
190
- def []=(key, value)
191
- if key == :datadog_rack_request_span && !@request_span_warning_issued
192
- Datadog::Tracer.log.warn(REQUEST_SPAN_DEPRECATION_WARNING)
193
- @request_span_warning_issued = true
209
+ def without_datadog_warnings
210
+ @datadog_deprecation_warnings = false
211
+ yield
212
+ ensure
213
+ @datadog_deprecation_warnings = true
194
214
  end
195
- super
196
215
  end
197
216
  end
198
217
  end