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 +4 -4
- data/lib/datadog/lambda/metrics.rb +70 -0
- data/lib/datadog/lambda/trace/constants.rb +1 -0
- data/lib/datadog/lambda/trace/listener.rb +12 -13
- data/lib/datadog/lambda/utils/extension.rb +49 -11
- data/lib/datadog/lambda/version.rb +1 -1
- data/lib/datadog/lambda.rb +37 -25
- metadata +19 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6c867a4e0858e0ba773829993e4c60b2d84b26f8afe4e69a766ac75f6ef454c5
|
4
|
+
data.tar.gz: feaa85e2d5d998b32602b74958792a656f692eab4950fdc7da0cccb7d12542bd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
29
|
-
|
30
|
-
|
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::
|
52
|
-
|
53
|
-
|
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
|
-
|
21
|
-
|
18
|
+
START_INVOCATION_PATH = '/lambda/start-invocation'
|
19
|
+
END_INVOCATION_PATH = '/lambda/end-invocation'
|
22
20
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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
|
-
|
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
|
data/lib/datadog/lambda.rb
CHANGED
@@ -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 '
|
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
|
-
|
61
|
-
|
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
|
70
|
+
@listener&.on_end(response: @response)
|
68
71
|
@is_cold_start = false
|
72
|
+
@metrics_client.close
|
69
73
|
end
|
70
|
-
|
74
|
+
@response
|
71
75
|
end
|
72
76
|
|
73
77
|
# Gets the current tracing context
|
74
78
|
def self.trace_context
|
75
|
-
|
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
|
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
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
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.
|
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-
|
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.
|
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.
|
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
|