datadog-lambda 2.19.0 → 2.21.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 14af71dd28cbd28bb7245d9a265c1ef5aae17cc7fca08cdfeec920a0df15863f
4
- data.tar.gz: ddaef0b0f205fd9fabdcd1ee2d6663e3f47f1b80e5a7640bb8b0906600807a9d
3
+ metadata.gz: 6c867a4e0858e0ba773829993e4c60b2d84b26f8afe4e69a766ac75f6ef454c5
4
+ data.tar.gz: feaa85e2d5d998b32602b74958792a656f692eab4950fdc7da0cccb7d12542bd
5
5
  SHA512:
6
- metadata.gz: 7e91c96b8bd3c20e420b94621df6eb260e3baaa310fc131600e6343663a6b8f613091a5f7b7beba460cce0c3c4c62b2a932e18c83f47ce2848c4a89b8c135053
7
- data.tar.gz: 9dbf1189862c063bdf8eaa361fcebcbca5ee857bb8196b8d420d5a0f9151e7468f971d3628a68baf8182247a57227366ca6387f98523ccb6a86b2ad2b748536d
6
+ metadata.gz: cd5062d00ae5f88a8c5021f6c76c3b87fa9236d035508e04dbd33113cf51a5b7bec006060ab6dc12ea27da46feaf78ae3eb98d9bd4c907b5a972c7176ca40ee7
7
+ data.tar.gz: 823cc2efc44cfefd3e0829ca038e966f6b999fd5c2648131a91a2b8b805dbd8ae5aeb5cdba1c14d3c8579aafffb719679b8e1bdd60306f37c80c3bc176f154d2
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Unless explicitly stated otherwise all files in this repository are licensed
5
+ # under the Apache License Version 2.0.
6
+ #
7
+ # This product includes software developed at Datadog (https://www.datadoghq.com/).
8
+ # Copyright 2023 Datadog, Inc.
9
+ #
10
+
11
+ require 'datadog/statsd'
12
+ require 'time'
13
+
14
+ module Datadog
15
+ # Metrics module contains the singleton class Client to send custom
16
+ # metrics to Datadog.
17
+ module Metrics
18
+ URL = 'localhost'
19
+ PORT = '8125'
20
+ # Client is a singleton class that instantiates a Datadog::Statsd
21
+ # client to send metrics to the Datadog Extension if present.
22
+ class Client
23
+ private_class_method :new
24
+
25
+ def self.instance
26
+ @instance ||= new
27
+ end
28
+
29
+ def distribution(name, value, time: nil, **tags)
30
+ tag_list = get_tags(**tags)
31
+
32
+ if Datadog::Utils.extension_running?
33
+ begin
34
+ @statsd.distribution(name, value, tags: tag_list)
35
+ rescue StandardError => e
36
+ Datadog::Utils.logger.warning "error sending metric to the extension: #{e}"
37
+ end
38
+ else
39
+ time ||= Time.now
40
+ time_ms = time.to_i
41
+
42
+ metric = { e: time_ms, m: name, t: tag_list, v: value }
43
+ puts metric.to_json
44
+ end
45
+ end
46
+
47
+ def close
48
+ @statsd&.close
49
+ end
50
+
51
+ def get_tags(**tags)
52
+ tag_list = ["dd_lambda_layer:datadog-ruby#{Datadog::Lambda.dd_lambda_layer_tag}"]
53
+ tags.each do |tag|
54
+ tag_list << "#{tag[0]}:#{tag[1]}"
55
+ end
56
+
57
+ tag_list
58
+ end
59
+
60
+ private_class_method def initialize
61
+ if Datadog::Utils.extension_running?
62
+ Datadog::Utils.logger.debug 'sending metrics through extension'
63
+ @statsd = Datadog::Statsd.new(URL, PORT, single_thread: true)
64
+ else
65
+ Datadog::Utils.logger.debug 'metrics are going to be handled by the forwarder'
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -20,6 +20,7 @@ module Datadog
20
20
  DD_XRAY_SUBSEGMENT_NAME = 'datadog-metadata'
21
21
  DD_XRAY_SUBSEGMENT_KEY = 'trace'
22
22
  DD_XRAY_SUBSEGMENT_NAMESPACE = 'datadog'
23
+ DD_TRACE_MANAGED_SERVICES = 'DD_TRACE_MANAGED_SERVICES'
23
24
  SOURCE_XRAY = 'XRAY'
24
25
  SOURCE_EVENT = 'EVENT'
25
26
  XRAY_ENV_VAR = '_X_AMZN_TRACE_ID'
@@ -16,6 +16,7 @@ module Datadog
16
16
  module Trace
17
17
  # TraceListener tracks tracing context information
18
18
  class Listener
19
+ @trace = nil
19
20
  def initialize(handler_name:, function_name:, patch_http:,
20
21
  merge_xray_traces:)
21
22
  @handler_name = handler_name
@@ -25,18 +26,11 @@ module Datadog
25
26
  Datadog::Trace.patch_http if patch_http
26
27
  end
27
28
 
28
- def on_start(event:)
29
- trace_context = Datadog::Trace.extract_trace_context(event,
30
- @merge_xray_traces)
29
+ # rubocop:disable Metrics/AbcSize
30
+ def on_start(event:, request_context:, cold_start:)
31
+ trace_context = Datadog::Trace.extract_trace_context(event, @merge_xray_traces)
31
32
  Datadog::Trace.trace_context = trace_context
32
33
  Datadog::Utils.logger.debug "extracted trace context #{trace_context}"
33
- rescue StandardError => e
34
- Datadog::Utils.logger.error "couldn't read tracing context #{e}"
35
- end
36
-
37
- def on_end; end
38
-
39
- def on_wrap(request_context:, cold_start:, &block)
40
34
  options = get_option_tags(
41
35
  request_context: request_context,
42
36
  cold_start: cold_start
@@ -48,9 +42,14 @@ module Datadog
48
42
  options[:service] = 'aws.lambda'
49
43
  options[:span_type] = 'serverless'
50
44
  Datadog::Trace.apply_datadog_trace_context(Datadog::Trace.trace_context)
51
- Datadog::Trace.wrap_datadog(options) do
52
- block.call
53
- end
45
+ @trace = Datadog::Tracing.trace('aws.lambda', **options)
46
+ Datadog::Utils.send_start_invocation_request(event: event)
47
+ end
48
+ # rubocop:enable Metrics/AbcSize
49
+
50
+ def on_end(response:)
51
+ Datadog::Utils.send_end_invocation_request(response: response)
52
+ @trace&.finish
54
53
  end
55
54
 
56
55
  private
@@ -12,22 +12,60 @@ require 'net/http'
12
12
  module Datadog
13
13
  # Utils contains utility functions shared between modules
14
14
  module Utils
15
- AGENT_URL = 'http://127.0.0.1:8124'
16
- HELLO_PATH = '/lambda/hello'
17
- EXTENSION_CHECK_URI = URI(AGENT_URL + HELLO_PATH).freeze
18
15
  EXTENSION_PATH = '/opt/extensions/datadog-agent'
16
+ EXTENSION_BASE_URL = 'http://127.0.0.1:8124'
19
17
 
20
- def self.extension_running
21
- return false unless File.exist?(EXTENSION_PATH)
18
+ START_INVOCATION_PATH = '/lambda/start-invocation'
19
+ END_INVOCATION_PATH = '/lambda/end-invocation'
22
20
 
23
- begin
24
- Net::HTTP.get(EXTENSION_CHECK_URI)
25
- rescue StandardError => e
26
- Datadog::Utils.logger.debug "extension is not running, returned with error #{e}"
27
- return false
21
+ START_INVOCATION_URI = URI(EXTENSION_BASE_URL + START_INVOCATION_PATH).freeze
22
+ END_INVOCATION_URI = URI(EXTENSION_BASE_URL + END_INVOCATION_PATH).freeze
23
+
24
+ def self.extension_running?
25
+ return @is_extension_running unless @is_extension_running.nil?
26
+
27
+ @is_extension_running = check_extension_running
28
+ end
29
+
30
+ def self.check_extension_running
31
+ File.exist?(EXTENSION_PATH)
32
+ end
33
+
34
+ def self.send_start_invocation_request(event:)
35
+ return unless extension_running?
36
+
37
+ response = Net::HTTP.post(START_INVOCATION_URI, event.to_json, request_headers)
38
+
39
+ trace_digest = Tracing::Propagation::HTTP.extract(response)
40
+ # Only continue trace from a new one if it exist, or else,
41
+ # it will create a new trace, which is not ideal here.
42
+ Tracing.continue_trace!(trace_digest) if trace_digest
43
+ rescue StandardError => e
44
+ Datadog::Utils.logger.debug "failed on start invocation request to extension: #{e}"
45
+ end
46
+
47
+ def self.send_end_invocation_request(response:)
48
+ return unless extension_running?
49
+
50
+ request = Net::HTTP::Post.new(END_INVOCATION_URI)
51
+ request.body = response.to_json
52
+ request[Datadog::Core::Transport::Ext::HTTP::HEADER_DD_INTERNAL_UNTRACED_REQUEST] = 1
53
+
54
+ trace = Datadog::Tracing.active_trace
55
+ Tracing::Propagation::HTTP.inject!(trace, request)
56
+ Net::HTTP.start(END_INVOCATION_URI.host, END_INVOCATION_URI.port) do |http|
57
+ http.request(request)
28
58
  end
59
+ rescue StandardError => e
60
+ Datadog::Utils.logger.debug "failed on end invocation request to extension: #{e}"
61
+ end
29
62
 
30
- true
63
+ def self.request_headers
64
+ {
65
+ # Header used to avoid tracing requests that are internal to
66
+ # Datadog products.
67
+ Datadog::Core::Transport::Ext::HTTP::HEADER_DD_INTERNAL_UNTRACED_REQUEST.to_sym => 'true'
68
+ }
31
69
  end
32
70
  end
33
71
  end
@@ -12,7 +12,7 @@ module Datadog
12
12
  module Lambda
13
13
  module VERSION
14
14
  MAJOR = 2
15
- MINOR = 19
15
+ MINOR = 21
16
16
  PATCH = 0
17
17
  PRE = nil
18
18
 
@@ -9,39 +9,44 @@
9
9
  #
10
10
  # rubocop:disable Metrics/ModuleLength
11
11
 
12
+ require 'datadog/lambda/metrics'
12
13
  require 'datadog/lambda/trace/listener'
13
14
  require 'datadog/lambda/utils/logger'
14
15
  require 'datadog/lambda/utils/extension'
15
16
  require 'datadog/lambda/trace/patch_http'
16
17
  require 'json'
17
- require 'time'
18
18
  require 'datadog/lambda/version'
19
19
 
20
20
  module Datadog
21
21
  # Instruments AWS Lambda functions with Datadog distributed tracing and
22
22
  # custom metrics
23
23
  module Lambda
24
+ @response = nil
24
25
  @is_cold_start = true
25
26
  @patch_http = true
27
+ @metrics_client = Metrics::Client.instance
26
28
 
27
29
  # Configures Datadog's APM tracer with lambda specific defaults.
28
30
  # Same options can be given as Datadog.configure in tracer
29
31
  # See https://github.com/DataDog/dd-trace-rb/blob/master/docs/GettingStarted.md#quickstart-for-ruby-applications
30
32
  def self.configure_apm
31
33
  require 'datadog/tracing'
32
- require 'ddtrace/transport/io'
34
+ require 'datadog/tracing/transport/io'
33
35
 
34
36
  @patch_http = false
35
37
  # Needed to keep trace flushes on a single line
36
38
  $stdout.sync = true
37
39
 
38
40
  Datadog.configure do |c|
39
- unless Datadog::Utils.extension_running
41
+ unless Datadog::Utils.extension_running?
40
42
  c.tracing.writer = Datadog::Tracing::SyncWriter.new(
41
- transport: Datadog::Transport::IO.default
43
+ transport: Datadog::Tracing::Transport::IO.default
42
44
  )
43
45
  end
44
46
  c.tags = { "_dd.origin": 'lambda' }
47
+ # Enable AWS SDK instrumentation
48
+ c.tracing.instrument :aws if trace_managed_services?
49
+
45
50
  yield(c) if block_given?
46
51
  end
47
52
  end
@@ -53,27 +58,25 @@ module Datadog
53
58
  def self.wrap(event, context, &block)
54
59
  Datadog::Utils.update_log_level
55
60
  @listener ||= initialize_listener
56
- @listener.on_start(event: event)
57
61
  record_enhanced('invocations', context)
58
62
  begin
59
63
  cold = @is_cold_start
60
- res = @listener.on_wrap(request_context: context, cold_start: cold) do
61
- block.call
62
- end
64
+ @listener&.on_start(event: event, request_context: context, cold_start: cold)
65
+ @response = block.call
63
66
  rescue StandardError => e
64
67
  record_enhanced('errors', context)
65
68
  raise e
66
69
  ensure
67
- @listener.on_end
70
+ @listener&.on_end(response: @response)
68
71
  @is_cold_start = false
72
+ @metrics_client.close
69
73
  end
70
- res
74
+ @response
71
75
  end
72
76
 
73
77
  # Gets the current tracing context
74
78
  def self.trace_context
75
- context = Hash[Datadog::Trace.trace_context]
76
- context
79
+ Hash[Datadog::Trace.trace_context]
77
80
  end
78
81
 
79
82
  # Send a custom distribution metric
@@ -85,15 +88,7 @@ module Datadog
85
88
  raise 'name must be a string' unless name.is_a?(String)
86
89
  raise 'value must be a number' unless value.is_a?(Numeric)
87
90
 
88
- time ||= Time.now
89
- time_ms = time.to_f.to_i
90
-
91
- tag_list = ["dd_lambda_layer:datadog-ruby#{dd_lambda_layer_tag}"]
92
- tags.each do |tag|
93
- tag_list.push("#{tag[0]}:#{tag[1]}")
94
- end
95
- metric = { e: time_ms, m: name, t: tag_list, v: value }
96
- puts metric.to_json
91
+ @metrics_client.distribution(name, value, time: time, **tags)
97
92
  end
98
93
 
99
94
  def self.dd_lambda_layer_tag
@@ -172,6 +167,15 @@ module Datadog
172
167
  dd_enhanced_metrics.downcase == 'true'
173
168
  end
174
169
 
170
+ # Read DD_TRACE_MANAGED_SERVICES environment variable
171
+ # @return [boolean] true if we should trace AWS services
172
+ def self.trace_managed_services?
173
+ dd_trace_managed_services = ENV[Trace::DD_TRACE_MANAGED_SERVICES]
174
+ return true if dd_trace_managed_services.nil?
175
+
176
+ dd_trace_managed_services.downcase == 'true'
177
+ end
178
+
175
179
  def self.initialize_listener
176
180
  handler = ENV['_HANDLER'].nil? ? 'handler' : ENV['_HANDLER']
177
181
  function = ENV['AWS_LAMBDA_FUNCTION_NAME']
@@ -182,10 +186,18 @@ module Datadog
182
186
  Datadog::Utils.logger.debug("Setting merge traces #{merge_xray_traces}")
183
187
  end
184
188
 
185
- Trace::Listener.new(handler_name: handler,
186
- function_name: function,
187
- patch_http: @patch_http,
188
- merge_xray_traces: merge_xray_traces)
189
+ # Only initialize listener if Tracing enabled.
190
+ unless Datadog::Tracing.enabled?
191
+ Datadog::Utils.logger.debug 'dd-trace unavailable'
192
+ return nil
193
+ end
194
+
195
+ Trace::Listener.new(
196
+ handler_name: handler,
197
+ function_name: function,
198
+ patch_http: @patch_http,
199
+ merge_xray_traces: merge_xray_traces
200
+ )
189
201
  end
190
202
  end
191
203
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: datadog-lambda
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.19.0
4
+ version: 2.21.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Datadog, Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-05-10 00:00:00.000000000 Z
11
+ date: 2023-10-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-xray-sdk
@@ -24,20 +24,34 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: 0.11.3
27
+ - !ruby/object:Gem::Dependency
28
+ name: dogstatsd-ruby
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '5.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '5.0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: ddtrace
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
45
  - - "~>"
32
46
  - !ruby/object:Gem::Version
33
- version: 1.11.0
47
+ version: 1.15.0
34
48
  type: :development
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
38
52
  - - "~>"
39
53
  - !ruby/object:Gem::Version
40
- version: 1.11.0
54
+ version: 1.15.0
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: rake
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -119,6 +133,7 @@ extensions: []
119
133
  extra_rdoc_files: []
120
134
  files:
121
135
  - lib/datadog/lambda.rb
136
+ - lib/datadog/lambda/metrics.rb
122
137
  - lib/datadog/lambda/trace/constants.rb
123
138
  - lib/datadog/lambda/trace/context.rb
124
139
  - lib/datadog/lambda/trace/ddtrace.rb