datadog-lambda 0.4.0 → 0.9.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.rb +120 -5
- data/lib/datadog/lambda/trace/constants.rb +6 -0
- data/lib/datadog/lambda/trace/context.rb +7 -69
- data/lib/datadog/lambda/trace/ddtrace.rb +45 -0
- data/lib/datadog/lambda/trace/listener.rb +43 -12
- data/lib/datadog/lambda/trace/xray.rb +141 -0
- data/lib/datadog/lambda/version.rb +1 -1
- metadata +22 -8
- data/lib/datadog/lambda/trace/xray_lambda.rb +0 -84
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 15ddc462886c4a35ba66d327ebbc2be17c403b2d47cab973fa06e05d6df3d2d1
|
4
|
+
data.tar.gz: b90918aa8b2372e2149e4b084b5ee97d9a8268ebff7ebb83a0e1a6a3751488c1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 71b82b6b3ec47c17fff39fb4f3bfd40c6d2ef7ba477baa82de83a87bc710fc0114fa7aadc84153908db40de6af8437248e9634b8838a6d60af54e44fefbe8e29
|
7
|
+
data.tar.gz: ca9afd36577caa6b5efef5663add0b37e32bfa953b55ee2909bc2f104a139e0a6478fcddcdde67687537672ae88b2d50f81b3d79c4aefa2967c29cae4faf367b
|
data/lib/datadog/lambda.rb
CHANGED
@@ -7,6 +7,7 @@
|
|
7
7
|
# This product includes software developed at Datadog (https://www.datadoghq.com/).
|
8
8
|
# Copyright 2019 Datadog, Inc.
|
9
9
|
#
|
10
|
+
# rubocop:disable Metrics/ModuleLength
|
10
11
|
|
11
12
|
require 'datadog/lambda/trace/listener'
|
12
13
|
require 'datadog/lambda/utils/logger'
|
@@ -18,26 +19,57 @@ module Datadog
|
|
18
19
|
# Instruments AWS Lambda functions with Datadog distributed tracing and
|
19
20
|
# custom metrics
|
20
21
|
module Lambda
|
22
|
+
@is_cold_start = true
|
23
|
+
@patch_http = true
|
24
|
+
|
25
|
+
# Configures Datadog's APM tracer with lambda specific defaults.
|
26
|
+
# Same options can be given as Datadog.configure in tracer
|
27
|
+
# See https://github.com/DataDog/dd-trace-rb/blob/master/docs/GettingStarted.md#quickstart-for-ruby-applications
|
28
|
+
def self.configure_apm
|
29
|
+
require 'ddtrace'
|
30
|
+
require 'ddtrace/sync_writer'
|
31
|
+
|
32
|
+
@patch_http = false
|
33
|
+
# Needed to keep trace flushes on a single line
|
34
|
+
$stdout.sync = true
|
35
|
+
|
36
|
+
Datadog.configure do |c|
|
37
|
+
c.tracer writer: Datadog::SyncWriter.new(
|
38
|
+
transport: Datadog::Transport::IO.default
|
39
|
+
)
|
40
|
+
yield(c) if block_given?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
21
44
|
# Wrap the body of a lambda invocation
|
22
45
|
# @param event [Object] event sent to lambda
|
23
46
|
# @param context [Object] lambda context
|
24
47
|
# @param block [Proc] implementation of the handler function.
|
25
|
-
def self.wrap(event,
|
48
|
+
def self.wrap(event, context, &block)
|
26
49
|
Datadog::Utils.update_log_level
|
27
|
-
|
28
|
-
@listener ||= Trace::Listener.new
|
50
|
+
@listener ||= initialize_listener
|
29
51
|
@listener.on_start(event: event)
|
52
|
+
record_enhanced('invocations', context)
|
30
53
|
begin
|
31
|
-
|
54
|
+
cold = @is_cold_start
|
55
|
+
res = @listener.on_wrap(request_context: context, cold_start: cold) do
|
56
|
+
block.call
|
57
|
+
end
|
58
|
+
rescue StandardError => e
|
59
|
+
record_enhanced('errors', context)
|
60
|
+
raise e
|
32
61
|
ensure
|
33
62
|
@listener.on_end
|
63
|
+
@is_cold_start = false
|
34
64
|
end
|
35
65
|
res
|
36
66
|
end
|
37
67
|
|
38
68
|
# Gets the current tracing context
|
39
69
|
def self.trace_context
|
40
|
-
Datadog::Trace.trace_context
|
70
|
+
context = Hash[Datadog::Trace.trace_context]
|
71
|
+
context.delete(:source)
|
72
|
+
context
|
41
73
|
end
|
42
74
|
|
43
75
|
# Send a custom distribution metric
|
@@ -58,5 +90,88 @@ module Datadog
|
|
58
90
|
metric = { e: time_ms, m: name, t: tag_list, v: value }.to_json
|
59
91
|
puts metric
|
60
92
|
end
|
93
|
+
|
94
|
+
# Generate tags for enhanced metrics
|
95
|
+
# @param context [Object] https://docs.aws.amazon.com/lambda/latest/dg/ruby-context.html
|
96
|
+
# @return [hash] a hash of the enhanced metrics tags
|
97
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
98
|
+
def self.gen_enhanced_tags(context)
|
99
|
+
arn_parts = context.invoked_function_arn.split(':')
|
100
|
+
# Check if we have an alias or version
|
101
|
+
function_alias = arn_parts[7].nil? ? nil : arn_parts[7]
|
102
|
+
|
103
|
+
tags = {
|
104
|
+
functionname: context.function_name,
|
105
|
+
region: arn_parts[3],
|
106
|
+
account_id: arn_parts[4],
|
107
|
+
memorysize: context.memory_limit_in_mb,
|
108
|
+
cold_start: @is_cold_start,
|
109
|
+
runtime: "Ruby #{RUBY_VERSION}",
|
110
|
+
resource: context.function_name
|
111
|
+
}
|
112
|
+
# If we have an alias...
|
113
|
+
unless function_alias.nil?
|
114
|
+
# If the alis version is $Latest, drop the $ for ddog tag convention.
|
115
|
+
if function_alias.start_with?('$')
|
116
|
+
function_alias[0] = ''
|
117
|
+
# If the alias is not a version number add the executed version tag
|
118
|
+
elsif !/\A\d+\z/.match(function_alias)
|
119
|
+
tags[:executedversion] = context.function_version
|
120
|
+
end
|
121
|
+
# Append the alias to the resource tag
|
122
|
+
tags[:resource] = context.function_name + ':' + function_alias
|
123
|
+
end
|
124
|
+
|
125
|
+
tags
|
126
|
+
rescue StandardError => e
|
127
|
+
Datadog::Utils.logger.error 'Unable to parse Lambda context' \
|
128
|
+
"#{context}: #{e}"
|
129
|
+
{}
|
130
|
+
end
|
131
|
+
|
132
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
133
|
+
# Format and add tags to enhanced metrics
|
134
|
+
# This method wraps the metric method, checking the DD_ENHANCED_METRICS
|
135
|
+
# environment variable, adding 'aws.lambda.enhanced' to the metric name,
|
136
|
+
# and adding the enhanced metric tags to the enhanced metrics.
|
137
|
+
# @param metric_name [String] basic name of the metric
|
138
|
+
# @param context [Object] AWS Ruby Lambda Context
|
139
|
+
# @return [boolean] false if the metric was not added for some reason,
|
140
|
+
# true otherwise (for ease of testing
|
141
|
+
|
142
|
+
def self.record_enhanced(metric_name, context)
|
143
|
+
return false unless do_enhanced_metrics?
|
144
|
+
|
145
|
+
etags = gen_enhanced_tags(context)
|
146
|
+
metric("aws.lambda.enhanced.#{metric_name}", 1, **etags)
|
147
|
+
true
|
148
|
+
end
|
149
|
+
|
150
|
+
# Check the DD_ENHANCED_METRICS environment variable
|
151
|
+
# @reurn [boolean] true if this lambda should have
|
152
|
+
# enhanced metrics
|
153
|
+
def self.do_enhanced_metrics?
|
154
|
+
dd_enhanced_metrics = ENV['DD_ENHANCED_METRICS']
|
155
|
+
return true if dd_enhanced_metrics.nil?
|
156
|
+
|
157
|
+
dd_enhanced_metrics.downcase == 'true'
|
158
|
+
end
|
159
|
+
|
160
|
+
def self.initialize_listener
|
161
|
+
handler = ENV['_HANDLER'].nil? ? 'handler' : ENV['_HANDLER']
|
162
|
+
function = ENV['AWS_LAMBDA_FUNCTION_NAME']
|
163
|
+
merge_xray_traces = false
|
164
|
+
merge_xray_traces_env = ENV['DD_MERGE_DATADOG_XRAY_TRACES']
|
165
|
+
unless merge_xray_traces_env.nil?
|
166
|
+
merge_xray_traces = merge_xray_traces_env.downcase == 'true'
|
167
|
+
Datadog::Utils.logger.debug("Setting merge traces #{merge_xray_traces}")
|
168
|
+
end
|
169
|
+
|
170
|
+
Trace::Listener.new(handler_name: handler,
|
171
|
+
function_name: function,
|
172
|
+
patch_http: @patch_http,
|
173
|
+
merge_xray_traces: merge_xray_traces)
|
174
|
+
end
|
61
175
|
end
|
62
176
|
end
|
177
|
+
# rubocop:enable Metrics/ModuleLength
|
@@ -20,5 +20,11 @@ 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
|
+
SOURCE_XRAY = 'XRAY'
|
24
|
+
SOURCE_EVENT = 'EVENT'
|
25
|
+
XRAY_ENV_VAR = '_X_AMZN_TRACE_ID'
|
26
|
+
XRAY_UDP_PORT = 2000
|
27
|
+
LOCAL_HOST = '127.0.0.1'
|
28
|
+
AWS_XRAY_DAEMON_ADDRESS_ENV_VAR = 'AWS_XRAY_DAEMON_ADDRESS'
|
23
29
|
end
|
24
30
|
end
|
@@ -8,8 +8,8 @@
|
|
8
8
|
# Copyright 2019 Datadog, Inc.
|
9
9
|
#
|
10
10
|
|
11
|
-
require 'aws-xray-sdk'
|
12
11
|
require 'datadog/lambda/trace/constants'
|
12
|
+
require 'datadog/lambda/trace/xray'
|
13
13
|
require 'datadog/lambda/utils/logger'
|
14
14
|
|
15
15
|
module Datadog
|
@@ -17,56 +17,19 @@ module Datadog
|
|
17
17
|
# lambda events/X-Ray
|
18
18
|
module Trace
|
19
19
|
class << self
|
20
|
-
def extract_trace_context(event)
|
20
|
+
def extract_trace_context(event, merge_xray_traces)
|
21
21
|
context = read_trace_context_from_event(event)
|
22
22
|
unless context.nil?
|
23
23
|
begin
|
24
24
|
add_trace_context_to_xray(context)
|
25
25
|
rescue StandardError => e
|
26
|
-
Datadog::Utils.logger.
|
26
|
+
Datadog::Utils.logger.debug("couldn't add metadata to xray #{e}")
|
27
27
|
end
|
28
28
|
return context
|
29
29
|
end
|
30
|
-
|
31
|
-
end
|
32
|
-
|
33
|
-
def add_trace_context_to_xray(context)
|
34
|
-
seg = XRay.recorder.begin_subsegment(
|
35
|
-
DD_XRAY_SUBSEGMENT_NAME,
|
36
|
-
namespace: DD_XRAY_SUBSEGMENT_NAMESPACE
|
37
|
-
)
|
38
|
-
data = {
|
39
|
-
"parent-id": context[:parent_id],
|
40
|
-
"sampling-priority": context[:sample_mode].to_s,
|
41
|
-
"trace-id": context[:trace_id]
|
42
|
-
}
|
43
|
-
seg.metadata(namespace: DD_XRAY_SUBSEGMENT_NAMESPACE)
|
44
|
-
.update("#{DD_XRAY_SUBSEGMENT_KEY}": data)
|
45
|
-
XRay.recorder.end_subsegment
|
46
|
-
end
|
30
|
+
return nil unless merge_xray_traces
|
47
31
|
|
48
|
-
|
49
|
-
entity = XRay.recorder.current_entity
|
50
|
-
parent_id = entity.id
|
51
|
-
{
|
52
|
-
trace_id: trace_context[:trace_id],
|
53
|
-
parent_id: convert_to_apm_parent_id(parent_id),
|
54
|
-
sample_mode: trace_context[:sample_mode]
|
55
|
-
}
|
56
|
-
end
|
57
|
-
|
58
|
-
def read_trace_context_from_xray
|
59
|
-
segment = XRay.recorder.current_entity
|
60
|
-
parent_id = segment.id
|
61
|
-
mode = segment.sampled ? SAMPLE_MODE_USER_KEEP : SAMPLE_MODE_USER_REJECT
|
62
|
-
{
|
63
|
-
trace_id: convert_to_apm_trace_id(segment.trace_id),
|
64
|
-
parent_id: convert_to_apm_parent_id(parent_id),
|
65
|
-
sample_mode: mode
|
66
|
-
}
|
67
|
-
rescue StandardError => e
|
68
|
-
Datadog::Utils.logger.error("couldn't read xray trace header #{e}")
|
69
|
-
nil
|
32
|
+
read_trace_context_from_xray
|
70
33
|
end
|
71
34
|
|
72
35
|
def read_trace_context_from_event(event)
|
@@ -79,7 +42,8 @@ module Datadog
|
|
79
42
|
{
|
80
43
|
trace_id: headers[DD_TRACE_ID_HEADER],
|
81
44
|
parent_id: headers[DD_PARENT_ID_HEADER],
|
82
|
-
sample_mode: headers[DD_SAMPLING_PRIORITY_HEADER].to_i
|
45
|
+
sample_mode: headers[DD_SAMPLING_PRIORITY_HEADER].to_i,
|
46
|
+
source: SOURCE_EVENT
|
83
47
|
}
|
84
48
|
end
|
85
49
|
|
@@ -99,32 +63,6 @@ module Datadog
|
|
99
63
|
end
|
100
64
|
true
|
101
65
|
end
|
102
|
-
|
103
|
-
def convert_to_apm_trace_id(xray_trace_id)
|
104
|
-
parts = xray_trace_id.split('-')
|
105
|
-
return nil if parts.length < 3
|
106
|
-
|
107
|
-
last_part = parts[2]
|
108
|
-
return nil if last_part.length != 24
|
109
|
-
# Make sure every character is hex
|
110
|
-
return nil if last_part.upcase[/\H/]
|
111
|
-
|
112
|
-
hex = last_part.to_i(16)
|
113
|
-
last_63_bits = hex & 0x7fffffffffffffff
|
114
|
-
last_63_bits.to_s(10)
|
115
|
-
end
|
116
|
-
|
117
|
-
def convert_to_apm_parent_id(xray_parent_id)
|
118
|
-
return nil if xray_parent_id.length != 16
|
119
|
-
return nil if xray_parent_id.upcase[/\H/]
|
120
|
-
|
121
|
-
hex = xray_parent_id.to_i(16)
|
122
|
-
hex.to_s(10)
|
123
|
-
end
|
124
|
-
|
125
|
-
def convert_to_sample_mode(xray_sampled)
|
126
|
-
xray_sampled == '1' ? SAMPLE_MODE_USER_KEEP : SAMPLE_MODE_USER_REJECT
|
127
|
-
end
|
128
66
|
end
|
129
67
|
end
|
130
68
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# ddtrace is an optional dependency for the ruby package
|
4
|
+
begin
|
5
|
+
require 'ddtrace'
|
6
|
+
rescue LoadError
|
7
|
+
Datadog::Utils.logger.debug 'dd-trace unavailable'
|
8
|
+
end
|
9
|
+
|
10
|
+
module Datadog
|
11
|
+
# TraceListener tracks tracing context information
|
12
|
+
module Trace
|
13
|
+
class <<self
|
14
|
+
def apply_datadog_trace_context(context)
|
15
|
+
unless context.nil?
|
16
|
+
trace_id = context[:trace_id].to_i
|
17
|
+
span_id = context[:parent_id].to_i
|
18
|
+
sampling_priority = context[:sample_mode]
|
19
|
+
Datadog.tracer.provider.context = Datadog::Context.new(
|
20
|
+
trace_id: trace_id,
|
21
|
+
span_id: span_id,
|
22
|
+
sampling_priority: sampling_priority
|
23
|
+
)
|
24
|
+
end
|
25
|
+
rescue StandardError
|
26
|
+
Datadog::Utils.logger.debug 'dd-trace unavailable'
|
27
|
+
end
|
28
|
+
|
29
|
+
def wrap_datadog(options, &block)
|
30
|
+
tracer_available = false
|
31
|
+
begin
|
32
|
+
Datadog.tracer
|
33
|
+
tracer_available = true
|
34
|
+
rescue StandardError
|
35
|
+
Datadog::Utils.logger.debug 'dd-trace unavailable'
|
36
|
+
end
|
37
|
+
return block.call unless tracer_available
|
38
|
+
|
39
|
+
Datadog.tracer.trace('aws.lambda', options) do |_span|
|
40
|
+
block.call
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -9,27 +9,25 @@
|
|
9
9
|
#
|
10
10
|
|
11
11
|
require 'datadog/lambda/trace/context'
|
12
|
-
require 'datadog/lambda/trace/xray_lambda'
|
13
12
|
require 'datadog/lambda/trace/patch_http'
|
14
|
-
|
15
|
-
require 'aws-xray-sdk'
|
13
|
+
require 'datadog/lambda/trace/ddtrace'
|
16
14
|
|
17
15
|
module Datadog
|
18
16
|
module Trace
|
19
17
|
# TraceListener tracks tracing context information
|
20
18
|
class Listener
|
21
|
-
def initialize
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
Datadog::Trace.patch_http
|
19
|
+
def initialize(handler_name:, function_name:, patch_http:,
|
20
|
+
merge_xray_traces:)
|
21
|
+
@handler_name = handler_name
|
22
|
+
@function_name = function_name
|
23
|
+
@merge_xray_traces = merge_xray_traces
|
24
|
+
|
25
|
+
Datadog::Trace.patch_http if patch_http
|
29
26
|
end
|
30
27
|
|
31
28
|
def on_start(event:)
|
32
|
-
trace_context = Datadog::Trace.extract_trace_context(event
|
29
|
+
trace_context = Datadog::Trace.extract_trace_context(event,
|
30
|
+
@merge_xray_traces)
|
33
31
|
Datadog::Trace.trace_context = trace_context
|
34
32
|
Datadog::Utils.logger.debug "extracted trace context #{trace_context}"
|
35
33
|
rescue StandardError => e
|
@@ -37,6 +35,39 @@ module Datadog
|
|
37
35
|
end
|
38
36
|
|
39
37
|
def on_end; end
|
38
|
+
|
39
|
+
def on_wrap(request_context:, cold_start:, &block)
|
40
|
+
options = get_option_tags(
|
41
|
+
request_context: request_context,
|
42
|
+
cold_start: cold_start
|
43
|
+
)
|
44
|
+
options[:resource] = @handler_name
|
45
|
+
options[:service] = @function_name
|
46
|
+
options[:span_type] = 'serverless'
|
47
|
+
Datadog::Trace.apply_datadog_trace_context(Datadog::Trace.trace_context)
|
48
|
+
Datadog::Trace.wrap_datadog(options) do
|
49
|
+
block.call
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def get_option_tags(request_context:, cold_start:)
|
56
|
+
function_arn = request_context.invoked_function_arn.downcase
|
57
|
+
tk = function_arn.split(':')
|
58
|
+
function_arn = tk.length > 7 ? tk[0, 7].join(':') : function_arn
|
59
|
+
function_version = tk.length > 7 ? tk[7] : '$LATEST'
|
60
|
+
options = {
|
61
|
+
tags: {
|
62
|
+
cold_start: cold_start,
|
63
|
+
function_arn: function_arn,
|
64
|
+
function_version: function_version,
|
65
|
+
request_id: request_context.aws_request_id,
|
66
|
+
resource_names: request_context.function_name
|
67
|
+
}
|
68
|
+
}
|
69
|
+
options
|
70
|
+
end
|
40
71
|
end
|
41
72
|
end
|
42
73
|
end
|
@@ -0,0 +1,141 @@
|
|
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 2019 Datadog, Inc.
|
9
|
+
#
|
10
|
+
require 'datadog/lambda/trace/constants'
|
11
|
+
require 'datadog/lambda/utils/logger'
|
12
|
+
require 'securerandom'
|
13
|
+
require 'json'
|
14
|
+
require 'socket'
|
15
|
+
|
16
|
+
module Datadog
|
17
|
+
# Trace contains utilities related to reading/writing trace context from
|
18
|
+
# lambda events/X-Ray
|
19
|
+
module Trace
|
20
|
+
class << self
|
21
|
+
def read_trace_context_from_xray
|
22
|
+
header = ENV[XRAY_ENV_VAR]
|
23
|
+
segment = parse_xray_trace_context_header(header)
|
24
|
+
trace_id = convert_to_apm_trace_id(segment[:xray_trace_id])
|
25
|
+
parent_id = convert_to_apm_parent_id(segment[:xray_parent_id])
|
26
|
+
sample_mode = convert_to_sample_mode(segment[:xray_sample_mode])
|
27
|
+
|
28
|
+
if trace_id.nil? || parent_id.nil? || sample_mode.nil?
|
29
|
+
Datadog::Utils.logger.error("couldn't read xray header #{header}")
|
30
|
+
return nil
|
31
|
+
end
|
32
|
+
{
|
33
|
+
trace_id: trace_id,
|
34
|
+
parent_id: parent_id,
|
35
|
+
sample_mode: sample_mode,
|
36
|
+
source: SOURCE_XRAY
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def add_trace_context_to_xray(context)
|
41
|
+
data = generate_xray_metadata_subsegment(context)
|
42
|
+
send_xray_daemon_data(data)
|
43
|
+
Datadog::Utils.logger.debug("sent metadata to xray #{data}")
|
44
|
+
end
|
45
|
+
|
46
|
+
def generate_xray_metadata_subsegment(context)
|
47
|
+
header = ENV[XRAY_ENV_VAR]
|
48
|
+
segment = parse_xray_trace_context_header(header)
|
49
|
+
|
50
|
+
{
|
51
|
+
"id": SecureRandom.hex(8),
|
52
|
+
"trace_id": segment[:xray_trace_id],
|
53
|
+
"parent_id": segment[:xray_parent_id],
|
54
|
+
"name": DD_XRAY_SUBSEGMENT_NAME,
|
55
|
+
"start_time": Time.now.to_f,
|
56
|
+
"end_time": Time.now.to_f,
|
57
|
+
"type": 'subsegment',
|
58
|
+
"metadata": {
|
59
|
+
"datadog": {
|
60
|
+
"trace": {
|
61
|
+
"parent-id": context[:parent_id],
|
62
|
+
"sampling-priority": context[:sample_mode].to_s,
|
63
|
+
"trace-id": context[:trace_id]
|
64
|
+
}
|
65
|
+
}
|
66
|
+
}
|
67
|
+
}.to_json
|
68
|
+
end
|
69
|
+
|
70
|
+
def current_trace_context(trace_context)
|
71
|
+
trace_context = Hash[trace_context]
|
72
|
+
begin
|
73
|
+
# This will only succeed if the user has imported xray themselves
|
74
|
+
entity = XRay.recorder.current_entity
|
75
|
+
trace_context[:parent_id] = convert_to_apm_parent_id(entity.id)
|
76
|
+
rescue StandardError
|
77
|
+
Datadog::Utils.logger.debug("couldn't fetch xray entity")
|
78
|
+
end
|
79
|
+
trace_context
|
80
|
+
end
|
81
|
+
|
82
|
+
def send_xray_daemon_data(data)
|
83
|
+
xray_daemon_env = ENV[AWS_XRAY_DAEMON_ADDRESS_ENV_VAR]
|
84
|
+
socket = XRAY_UDP_PORT
|
85
|
+
address = LOCAL_HOST
|
86
|
+
address, socket = xray_daemon_env.split(':') unless xray_daemon_env.nil?
|
87
|
+
|
88
|
+
sock = UDPSocket.open
|
89
|
+
message = "{\"format\": \"json\", \"version\": 1}\n#{data}"
|
90
|
+
sock.send(message, 0, address, socket)
|
91
|
+
end
|
92
|
+
|
93
|
+
def parse_xray_trace_context_header(header)
|
94
|
+
Datadog::Utils.logger.debug("Reading trace context from env #{header}")
|
95
|
+
trace_id, parent_id, sampled = header.split(';')
|
96
|
+
.map { |v| parse_assignment(v) }
|
97
|
+
|
98
|
+
return nil if trace_id.nil? || parent_id.nil? || sampled. nil?
|
99
|
+
|
100
|
+
{
|
101
|
+
xray_trace_id: trace_id,
|
102
|
+
xray_parent_id: parent_id,
|
103
|
+
xray_sample_mode: sampled
|
104
|
+
}
|
105
|
+
end
|
106
|
+
|
107
|
+
def parse_assignment(value)
|
108
|
+
return nil if value.nil?
|
109
|
+
|
110
|
+
_, raw_value, * = value.split('=')
|
111
|
+
raw_value
|
112
|
+
end
|
113
|
+
|
114
|
+
def convert_to_apm_trace_id(xray_trace_id)
|
115
|
+
parts = xray_trace_id.split('-')
|
116
|
+
return nil if parts.length < 3
|
117
|
+
|
118
|
+
last_part = parts[2]
|
119
|
+
return nil if last_part.length != 24
|
120
|
+
# Make sure every character is hex
|
121
|
+
return nil if last_part.upcase[/\H/]
|
122
|
+
|
123
|
+
hex = last_part.to_i(16)
|
124
|
+
last_63_bits = hex & 0x7fffffffffffffff
|
125
|
+
last_63_bits.to_s(10)
|
126
|
+
end
|
127
|
+
|
128
|
+
def convert_to_apm_parent_id(xray_parent_id)
|
129
|
+
return nil if xray_parent_id.length != 16
|
130
|
+
return nil if xray_parent_id.upcase[/\H/]
|
131
|
+
|
132
|
+
hex = xray_parent_id.to_i(16)
|
133
|
+
hex.to_s(10)
|
134
|
+
end
|
135
|
+
|
136
|
+
def convert_to_sample_mode(xray_sampled)
|
137
|
+
xray_sampled == '1' ? SAMPLE_MODE_USER_KEEP : SAMPLE_MODE_USER_REJECT
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
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: 0.
|
4
|
+
version: 0.9.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:
|
11
|
+
date: 2020-07-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-xray-sdk
|
@@ -16,14 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 0.11.3
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 0.11.3
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: ddtrace
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.32'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.32'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: rake
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -93,12 +107,13 @@ files:
|
|
93
107
|
- lib/datadog/lambda.rb
|
94
108
|
- lib/datadog/lambda/trace/constants.rb
|
95
109
|
- lib/datadog/lambda/trace/context.rb
|
110
|
+
- lib/datadog/lambda/trace/ddtrace.rb
|
96
111
|
- lib/datadog/lambda/trace/listener.rb
|
97
112
|
- lib/datadog/lambda/trace/patch_http.rb
|
98
|
-
- lib/datadog/lambda/trace/
|
113
|
+
- lib/datadog/lambda/trace/xray.rb
|
99
114
|
- lib/datadog/lambda/utils/logger.rb
|
100
115
|
- lib/datadog/lambda/version.rb
|
101
|
-
homepage: https://github.com/DataDog/
|
116
|
+
homepage: https://github.com/DataDog/datadog-lambda-layer-rb
|
102
117
|
licenses:
|
103
118
|
- Apache-2.0
|
104
119
|
metadata:
|
@@ -118,8 +133,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
118
133
|
- !ruby/object:Gem::Version
|
119
134
|
version: '0'
|
120
135
|
requirements: []
|
121
|
-
|
122
|
-
rubygems_version: 2.7.6
|
136
|
+
rubygems_version: 3.0.3
|
123
137
|
signing_key:
|
124
138
|
specification_version: 4
|
125
139
|
summary: Instruments your Ruby AWS Lambda functions with Datadog
|
@@ -1,84 +0,0 @@
|
|
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 2019 Datadog, Inc.
|
9
|
-
#
|
10
|
-
|
11
|
-
require 'aws-xray-sdk'
|
12
|
-
|
13
|
-
module Datadog
|
14
|
-
module Trace
|
15
|
-
# Workaround to XRay not being supported in Lambda
|
16
|
-
# https://github.com/aws/aws-xray-sdk-ruby/issues/20
|
17
|
-
class LambdaStreamer < XRay::DefaultStreamer
|
18
|
-
def initialize
|
19
|
-
@stream_threshold = 1 # Stream every subsegment as it is available
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
# LambdaEmitter filters out spans generated from the lambda daemon
|
24
|
-
class LambdaEmitter < XRay::DefaultEmitter
|
25
|
-
def should_send?(entity:)
|
26
|
-
entity.name != '127.0.0.1' # Do not send localhost entities.
|
27
|
-
end
|
28
|
-
|
29
|
-
def send_entity(entity:)
|
30
|
-
return nil unless should_send?(entity: entity)
|
31
|
-
|
32
|
-
super
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
# LambdaContext updated the trace id based on the curren trace header,
|
37
|
-
# using a FacadeSegment
|
38
|
-
class LambdaContext < XRay::DefaultContext
|
39
|
-
def handle_context_missing
|
40
|
-
nil
|
41
|
-
end
|
42
|
-
|
43
|
-
def check_context
|
44
|
-
# Create a new FacadeSegment if the _X_AMZN_TRACE_ID changes.
|
45
|
-
return if ENV['_X_AMZN_TRACE_ID'] == @current_trace_id
|
46
|
-
|
47
|
-
# puts "XRAY: Starting new segment for #{ENV['_X_AMZN_TRACE_ID']}"
|
48
|
-
@current_trace_id = ENV['_X_AMZN_TRACE_ID']
|
49
|
-
trace_header = XRay::TraceHeader.from_header_string(
|
50
|
-
header_str: @current_trace_id
|
51
|
-
)
|
52
|
-
segment = FacadeSegment.new(trace_id: trace_header.root,
|
53
|
-
parent_id: trace_header.parent_id,
|
54
|
-
id: trace_header.parent_id,
|
55
|
-
name: 'lambda_context',
|
56
|
-
sampled: trace_header.sampled)
|
57
|
-
store_entity(entity: segment)
|
58
|
-
end
|
59
|
-
|
60
|
-
def current_entity
|
61
|
-
# ensure the FacadeSegment is current whenever the current_entity is
|
62
|
-
# retrieved
|
63
|
-
check_context
|
64
|
-
super
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
# FacadeSegment is used to create a mock root span, that segments/
|
69
|
-
# subsegments can be attached to. This span will never be submitted to the
|
70
|
-
# X-Ray daemon
|
71
|
-
class FacadeSegment < XRay::Segment
|
72
|
-
def initialize(trace_id: nil, name: nil, parent_id: nil, id: nil,
|
73
|
-
sampled: true)
|
74
|
-
super(trace_id: trace_id, name: name, parent_id: parent_id)
|
75
|
-
@id = id
|
76
|
-
@sampled = sampled
|
77
|
-
end
|
78
|
-
|
79
|
-
def ready_to_send?
|
80
|
-
false # never send this facade.
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|