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



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

def self.configure_apm
  require 'ddtrace'
  require 'ddtrace/sync_writer'

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

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

.dd_lambda_layer_tagObject



96
97
98
# File 'lib/datadog/lambda.rb', line 96

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

Returns:

  • (Boolean)


165
166
167
168
169
170
# File 'lib/datadog/lambda.rb', line 165

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

Parameters:

Returns:

  • (hash)

    a hash of the enhanced metrics tags



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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/datadog/lambda.rb', line 104

def self.gen_enhanced_tags(context)
  arn_parts = context.invoked_function_arn.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



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/datadog/lambda.rb', line 172

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

Parameters:

  • name (String)

    name of the metric

  • value (Numeric)

    value of the metric

  • time (Time) (defaults to: nil)

    the time of the metric, should be in the past

  • tags (Hash)

    hash of tags, must be in “my.tag.name”:“value” format



81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/datadog/lambda.rb', line 81

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.

Parameters:

  • metric_name (String)

    basic name of the metric

  • context (Object)

    AWS Ruby Lambda Context

Returns:

  • (boolean)

    false if the metric was not added for some reason, true otherwise (for ease of testing



154
155
156
157
158
159
160
# File 'lib/datadog/lambda.rb', line 154

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



71
72
73
74
# File 'lib/datadog/lambda.rb', line 71

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

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

Wrap the body of a lambda invocation

Parameters:

  • event (Object)

    event sent to lambda

  • context (Object)

    lambda context

  • block (Proc)

    implementation of the handler function.



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

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