datadog-lambda 2.19.0 → 2.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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