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



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

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
    )
    yield(c) if block_given?
  end
end

.do_enhanced_metrics?Boolean

Check the DD_ENHANCED_METRICS environment variable enhanced metrics

Returns:

  • (Boolean)


132
133
134
135
136
137
# File 'lib/datadog/lambda.rb', line 132

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

Parameters:

Returns:

  • (hash)

    a hash of the enhanced metrics tags



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/datadog/lambda.rb', line 96

def self.gen_enhanced_tags(context)
  arn_parts = context.invoked_function_arn.split(':')
  {
    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}"
  }
rescue StandardError => e
  Datadog::Utils.logger.error 'Unable to parse Lambda context' \
  "#{context}: #{e}"
  {}
end

.initialize_listenerObject



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/datadog/lambda.rb', line 139

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



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

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
  tag_list = ['dd_lambda_layer:datadog-ruby25']
  tags.each do |tag|
    tag_list.push("#{tag[0]}:#{tag[1]}")
  end
  time_ms = time.to_f.to_i
  metric = { e: time_ms, m: name, t: tag_list, v: value }.to_json
  puts metric
end

.record_enhanced(metric_name, context) ⇒ boolean

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)



121
122
123
124
125
126
127
# File 'lib/datadog/lambda.rb', line 121

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



68
69
70
71
72
# File 'lib/datadog/lambda.rb', line 68

def self.trace_context
  context = Hash[Datadog::Trace.trace_context]
  context.delete(:source)
  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.



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

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