Module: Datadog::Lambda

Defined in:
lib/datadog/lambda.rb,
lib/datadog/lambda/version.rb

Overview

Instruments AWS Lambda functions with Datadog distributed tracing and custom metrics

Defined Under Namespace

Modules: VERSION

Class Method Summary collapse

Class Method Details

.configure_apmObject

Configures Datadog’s APM tracer with lambda specific defaults. Same options can be given as Datadog.configure in tracer See github.com/DataDog/dd-trace-rb/blob/master/docs/GettingStarted.md#quickstart-for-ruby-applications



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/datadog/lambda.rb', line 30

def self.configure_apm
  require 'datadog/tracing'
  require 'ddtrace/transport/io'

  @patch_http = false
  # Needed to keep trace flushes on a single line
  $stdout.sync = true

  Datadog.configure do |c|
    unless Datadog::Utils.extension_running
      c.tracing.writer = Datadog::Tracing::SyncWriter.new(
        transport: Datadog::Transport::IO.default
      )
    end
    c.tags = { "_dd.origin": 'lambda' }
    yield(c) if block_given?
  end
end

.dd_lambda_layer_tagObject



99
100
101
# File 'lib/datadog/lambda.rb', line 99

def self.dd_lambda_layer_tag
  RUBY_VERSION[0, 3].tr('.', '')
end

.do_enhanced_metrics?boolean

Check the DD_ENHANCED_METRICS environment variable enhanced metrics



168
169
170
171
172
173
# File 'lib/datadog/lambda.rb', line 168

def self.do_enhanced_metrics?
  dd_enhanced_metrics = ENV['DD_ENHANCED_METRICS']
  return true if dd_enhanced_metrics.nil?

  dd_enhanced_metrics.downcase == 'true'
end

.gen_enhanced_tags(context) ⇒ hash

Generate tags for enhanced metrics rubocop:disable Metrics/AbcSize, Metrics/MethodLength



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/datadog/lambda.rb', line 107

def self.gen_enhanced_tags(context)
  arn_parts = context.invoked_function_arn.to_s.split(':')
  # Check if we have an alias or version
  function_alias = arn_parts[7].nil? ? nil : arn_parts[7]

  tags = {
    functionname: context.function_name,
    region: arn_parts[3],
    account_id: arn_parts[4],
    memorysize: context.memory_limit_in_mb,
    cold_start: @is_cold_start,
    runtime: "Ruby #{RUBY_VERSION}",
    resource: context.function_name,
    datadog_lambda: Datadog::Lambda::VERSION::STRING.to_sym
  }
  begin
    tags[:dd_trace] = Gem.loaded_specs['ddtrace'].version
  rescue StandardError
    Datadog::Utils.logger.debug 'dd-trace unavailable'
  end
  # If we have an alias...
  unless function_alias.nil?
    # If the alis version is $Latest, drop the $ for ddog tag convention.
    if function_alias.start_with?('$')
      function_alias[0] = ''
      # If the alias is not a version number add the executed version tag
    elsif !/\A\d+\z/.match(function_alias)
      tags[:executedversion] = context.function_version
    end
    # Append the alias to the resource tag
    tags[:resource] = context.function_name + ':' + function_alias
  end

  tags
rescue StandardError => e
  Datadog::Utils.logger.error 'Unable to parse Lambda context' \
  "#{context}: #{e}"
  {}
end

.initialize_listenerObject



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/datadog/lambda.rb', line 175

def self.initialize_listener
  handler = ENV['_HANDLER'].nil? ? 'handler' : ENV['_HANDLER']
  function = ENV['AWS_LAMBDA_FUNCTION_NAME']
  merge_xray_traces = false
  merge_xray_traces_env = ENV['DD_MERGE_DATADOG_XRAY_TRACES']
  unless merge_xray_traces_env.nil?
    merge_xray_traces = merge_xray_traces_env.downcase == 'true'
    Datadog::Utils.logger.debug("Setting merge traces #{merge_xray_traces}")
  end

  Trace::Listener.new(handler_name: handler,
                      function_name: function,
                      patch_http: @patch_http,
                      merge_xray_traces: merge_xray_traces)
end

.metric(name, value, time: nil, **tags) ⇒ Object

Send a custom distribution metric



84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/datadog/lambda.rb', line 84

def self.metric(name, value, time: nil, **tags)
  raise 'name must be a string' unless name.is_a?(String)
  raise 'value must be a number' unless value.is_a?(Numeric)

  time ||= Time.now
  time_ms = time.to_f.to_i

  tag_list = ["dd_lambda_layer:datadog-ruby#{dd_lambda_layer_tag}"]
  tags.each do |tag|
    tag_list.push("#{tag[0]}:#{tag[1]}")
  end
  metric = { e: time_ms, m: name, t: tag_list, v: value }
  puts metric.to_json
end

.record_enhanced(metric_name, context) ⇒ boolean

rubocop:enable Metrics/AbcSize, Metrics/MethodLength Format and add tags to enhanced metrics This method wraps the metric method, checking the DD_ENHANCED_METRICS environment variable, adding ‘aws.lambda.enhanced’ to the metric name, and adding the enhanced metric tags to the enhanced metrics.



157
158
159
160
161
162
163
# File 'lib/datadog/lambda.rb', line 157

def self.record_enhanced(metric_name, context)
  return false unless do_enhanced_metrics?

  etags = gen_enhanced_tags(context)
  metric("aws.lambda.enhanced.#{metric_name}", 1, **etags)
  true
end

.trace_contextObject

Gets the current tracing context



74
75
76
77
# File 'lib/datadog/lambda.rb', line 74

def self.trace_context
  context = Hash[Datadog::Trace.trace_context]
  context
end

.wrap(event, context, &block) ⇒ Object

Wrap the body of a lambda invocation



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/datadog/lambda.rb', line 53

def self.wrap(event, context, &block)
  Datadog::Utils.update_log_level
  @listener ||= initialize_listener
  @listener.on_start(event: event)
  record_enhanced('invocations', context)
  begin
    cold = @is_cold_start
    res = @listener.on_wrap(request_context: context, cold_start: cold) do
      block.call
    end
  rescue StandardError => e
    record_enhanced('errors', context)
    raise e
  ensure
    @listener.on_end
    @is_cold_start = false
  end
  res
end