Class: Datadog::ErrorTracking::Component Private

Inherits:
Object
  • Object
show all
Defined in:
lib/datadog/error_tracking/component.rb

Overview

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

Component for Error Tracking.

Only one instance of the Component should ever be active.

The component instance records every handled exceptions from the configured scopes (user, third_party packages, specified files or everything).

Constant Summary collapse

LOCK =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Mutex.new

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(tracer:, handled_errors:, handled_errors_include:) ⇒ Component

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of Component.



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/datadog/error_tracking/component.rb', line 48

def initialize(tracer:, handled_errors:, handled_errors_include:)
  @tracer = tracer

  # Hash containing the paths to the instrumented files
  @instrumented_files = Set.new unless handled_errors_include.empty?
  # Array containing file paths, file names and gems names to instrument.
  # This is coming from the DD_ERROR_TRACKING_HANDLED_ERRORS_INCLUDE env variable
  @handled_errors_include = handled_errors_include

  # Filter function is used to filter out the exception
  # we do not want to report. For instance exception from gems.
  @filter_function = Filters.generate_filter(handled_errors, @instrumented_files)

  # :rescue event was added in Ruby 3.3
  #
  # Before Ruby3.3 the TracePoint listen for :raise events.
  # If an error is not handled, we will delete the according
  # span event in the collector.
  event = (RUBY_VERSION >= '3.3') ? :rescue : :raise

  # This TracePoint is in charge of capturing the handled exceptions
  # and of adding the corresponding span events to the collector
  @handled_exc_tracker = create_exc_tracker_trace_point(event)

  if @instrumented_files
    # The only thing we know about the handled errors is the path of the file
    # in which the error was rescued. Therefore, we need to retrieve the path
    # of the files the user want to instrument. This TracePoint is used for that
    # purpose
    @include_path_getter = create_script_compiled_trace_point
  end
end

Class Method Details

.build(settings, tracer, logger) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/datadog/error_tracking/component.rb', line 19

def build(settings, tracer, logger)
  return if !settings.respond_to?(:error_tracking) || (settings.error_tracking.handled_errors.nil? &&
    settings.error_tracking.handled_errors_include.empty?)

  return unless environment_supported?(logger)

  new(
    tracer: tracer,
    handled_errors: settings.error_tracking.handled_errors,
    handled_errors_include: settings.error_tracking.handled_errors_include,
  ).tap(&:start)
end

.environment_supported?(logger) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/datadog/error_tracking/component.rb', line 32

def environment_supported?(logger)
  if RUBY_ENGINE != 'ruby'
    logger.warn("error tracking: cannot enable error tracking: MRI is required, but running on #{RUBY_ENGINE}")
    false
  elsif RUBY_VERSION < '2.7'
    logger.warn(
      "error tracking: cannot enable error tracking: Ruby 2.7+ is required, but running
      on #{RUBY_VERSION}"
    )
    false
  else
    true
  end
end

Instance Method Details

#create_exc_tracker_trace_point(event) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/datadog/error_tracking/component.rb', line 81

def create_exc_tracker_trace_point(event)
  TracePoint.new(event) do |tp|
    active_span = @tracer.active_span
    if active_span
      raised_exception = tp.raised_exception
      # Note that in 3.2, this will give the path of where the error was raised
      # which may cause the handled_error_include env variable to malfunction.
      rescue_file_path = tp.path
      if @filter_function.call(rescue_file_path)
        span_event = generate_span_event(raised_exception)
        LOCK.synchronize do
          collector = active_span.get_collector_or_initialize { Collector.new }
          collector.add_span_event(active_span, span_event, raised_exception)
        end
      end
    end
  end
end

#create_script_compiled_trace_pointObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/datadog/error_tracking/component.rb', line 100

def create_script_compiled_trace_point
  TracePoint.new(:script_compiled) do |tp|
    next if tp.eval_script

    path = tp.instruction_sequence.path
    next if path.nil?

    @handled_errors_include.each do |file_to_instr|
      # The user can provide either
      # - absolute_path starting with '/'. In that case the path of the file
      #   should begin with file_to_instr
      # - a relative_path starting with './'. In that case, we extend the path
      #   and it is the same as above
      # - otherwise we just check if the name provided is in the path and is
      #   either the name of a folder or of a ruby file.
      regex =
        if file_to_instr.start_with?('/')
          %r{\A#{Regexp.escape(file_to_instr)}(?:/|\.rb\z|\z)}
        elsif file_to_instr.start_with?('./')
          abs_path = File.expand_path(file_to_instr)
          %r{\A#{Regexp.escape(abs_path)}(?:/|\.rb\z|\z)}
        else
          %r{/#{Regexp.escape(file_to_instr)}(?:/|\.rb\z|\z)}
        end

      add_instrumented_file(path) if path.match?(regex)
    end
  end
end

#shutdown!Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Shuts down error tracker.

Disables the TracePoints.



141
142
143
144
# File 'lib/datadog/error_tracking/component.rb', line 141

def shutdown!
  @handled_exc_tracker.disable
  @include_path_getter&.disable
end

#startObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Starts the TracePoints.

Enables the script_compiled TracePoint if handled_errors_include is not empty.



133
134
135
136
# File 'lib/datadog/error_tracking/component.rb', line 133

def start
  @handled_exc_tracker.enable
  @include_path_getter&.enable
end