opentelemetry-resource-detector-aws 0.1.0 → 0.3.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/README.md +38 -2
- data/lib/opentelemetry/resource/detector/aws/ec2.rb +3 -2
- data/lib/opentelemetry/resource/detector/aws/ecs.rb +173 -0
- data/lib/opentelemetry/resource/detector/aws/lambda.rb +66 -0
- data/lib/opentelemetry/resource/detector/aws/version.rb +1 -1
- data/lib/opentelemetry/resource/detector/aws.rb +28 -5
- data/lib/opentelemetry-resource-detector-aws.rb +1 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5a8ec0b68eb13782494f65cdafeed9f75447bb1762202bbbcde32cf795f4f3bb
|
4
|
+
data.tar.gz: 5039cfbeb483722204a6e7410881a675cbe7f8326c603a543e7ec233244616af
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0b0171c2e36d1c4c7ff95259b49699e5af069dd0fc34b8ec079682fb75b989190af36c5792e25e2f875499f50f4107c5461c45532a057cffadc283ce1c47dec3
|
7
|
+
data.tar.gz: 99ba3b6378c03a344f119329821c576cb78408bfa0e937dccb01f89e9503a79c02cd723e97e371789135f02529dd201720406f3c7f5697d119f904bdad6cc2c2
|
data/README.md
CHANGED
@@ -30,7 +30,13 @@ require 'opentelemetry/sdk'
|
|
30
30
|
require 'opentelemetry/resource/detector'
|
31
31
|
|
32
32
|
OpenTelemetry::SDK.configure do |c|
|
33
|
-
|
33
|
+
# Specify which AWS resource detectors to use
|
34
|
+
c.resource = OpenTelemetry::Resource::Detector::AWS.detect([:ec2, :ecs, :lambda])
|
35
|
+
|
36
|
+
# Or use just one detector
|
37
|
+
c.resource = OpenTelemetry::Resource::Detector::AWS.detect([:ec2])
|
38
|
+
c.resource = OpenTelemetry::Resource::Detector::AWS.detect([:ecs])
|
39
|
+
c.resource = OpenTelemetry::Resource::Detector::AWS.detect([:lambda])
|
34
40
|
end
|
35
41
|
```
|
36
42
|
|
@@ -52,7 +58,37 @@ Populates `cloud` and `host` for processes running on Amazon EC2, including abst
|
|
52
58
|
| `host.name` | Value of hostname from `/latest/meta-data/hostname` request |
|
53
59
|
| `host.type` | Value of `instanceType` from `/latest/dynamic/instance-identity/document` request |
|
54
60
|
|
55
|
-
|
61
|
+
### AWS ECS Detector
|
62
|
+
|
63
|
+
<!-- cspell:ignore launchtype awslogs -->
|
64
|
+
Populates `cloud`, `container`, and AWS ECS-specific attributes for processes running on Amazon ECS.
|
65
|
+
| Resource Attribute | Description |
|
66
|
+
|--------------------|-------------|
|
67
|
+
| `cloud.platform` | The cloud platform. In this context, it's always "aws_ecs" |
|
68
|
+
| `cloud.provider` | The cloud provider. In this context, it's always "aws" |
|
69
|
+
| `container.id` | The container ID from the `/proc/self/cgroup` file |
|
70
|
+
| `container.name` | The hostname of the container |
|
71
|
+
| `aws.ecs.container.arn` | The hostname of the container |
|
72
|
+
| `aws.ecs.cluster.arn` | The ARN of the ECS cluster |
|
73
|
+
| `aws.ecs.launchtype` | The launch type for the ECS task (e.g., "fargate" or "ec2") |
|
74
|
+
| `aws.ecs.task.arn` | The ARN of the ECS task |
|
75
|
+
| `aws.log.group.names` | The CloudWatch log group names (if awslogs driver is used) |
|
76
|
+
| `aws.log.stream.names` | The CloudWatch log stream names (if awslogs driver is used) |
|
77
|
+
| `aws.log.stream.arns` | The CloudWatch log stream ARNs (if awslogs driver is used) |
|
78
|
+
|
79
|
+
### AWS Lambda Detector
|
80
|
+
Populates `cloud` and `faas` (Function as a Service) attributes for processes running on AWS Lambda.
|
81
|
+
| Resource Attribute | Description |
|
82
|
+
|--------------------|-------------|
|
83
|
+
| `cloud.platform` | The cloud platform. In this context, it's always "aws_lambda" |
|
84
|
+
| `cloud.provider` | The cloud provider. In this context, it's always "aws" |
|
85
|
+
| `cloud.region` | The AWS region from the `AWS_REGION` environment variable |
|
86
|
+
| `faas.name` | The Lambda function name from the `AWS_LAMBDA_FUNCTION_NAME` environment variable |
|
87
|
+
| `faas.version` | The Lambda function version from the `AWS_LAMBDA_FUNCTION_VERSION` environment variable |
|
88
|
+
| `faas.instance` | The Lambda function instance ID from the `AWS_LAMBDA_LOG_STREAM_NAME` environment variable |
|
89
|
+
| `faas.max_memory` | The Lambda function memory size in MB from the `AWS_LAMBDA_FUNCTION_MEMORY_SIZE` environment variable |
|
90
|
+
|
91
|
+
Additional AWS platforms (EKS) will be supported in future versions.
|
56
92
|
|
57
93
|
## License
|
58
94
|
|
@@ -7,6 +7,7 @@
|
|
7
7
|
require 'net/http'
|
8
8
|
require 'json'
|
9
9
|
require 'opentelemetry/common'
|
10
|
+
require 'opentelemetry/semantic_conventions/resource'
|
10
11
|
|
11
12
|
module OpenTelemetry
|
12
13
|
module Resource
|
@@ -59,7 +60,7 @@ module OpenTelemetry
|
|
59
60
|
resource_attributes[RESOURCE::HOST_TYPE] = identity['instanceType']
|
60
61
|
resource_attributes[RESOURCE::HOST_NAME] = hostname
|
61
62
|
rescue StandardError => e
|
62
|
-
OpenTelemetry.
|
63
|
+
OpenTelemetry.handle_error(exception: e, message: 'EC2 resource detection failed')
|
63
64
|
return OpenTelemetry::SDK::Resources::Resource.create({})
|
64
65
|
end
|
65
66
|
|
@@ -133,7 +134,7 @@ module OpenTelemetry
|
|
133
134
|
http.request(request)
|
134
135
|
end
|
135
136
|
rescue StandardError => e
|
136
|
-
OpenTelemetry.
|
137
|
+
OpenTelemetry.handle_error(exception: e, message: 'EC2 metadata service request failed')
|
137
138
|
nil
|
138
139
|
end
|
139
140
|
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright The OpenTelemetry Authors
|
4
|
+
#
|
5
|
+
# SPDX-License-Identifier: Apache-2.0
|
6
|
+
|
7
|
+
require 'net/http'
|
8
|
+
require 'json'
|
9
|
+
require 'socket'
|
10
|
+
require 'opentelemetry/common'
|
11
|
+
require 'opentelemetry/semantic_conventions/resource'
|
12
|
+
|
13
|
+
module OpenTelemetry
|
14
|
+
module Resource
|
15
|
+
module Detector
|
16
|
+
module AWS
|
17
|
+
# ECS contains detect class method for determining the ECS resource attributes
|
18
|
+
module ECS
|
19
|
+
extend self
|
20
|
+
|
21
|
+
# Container ID length from cgroup file
|
22
|
+
CONTAINER_ID_LENGTH = 64
|
23
|
+
|
24
|
+
# HTTP request timeout in seconds
|
25
|
+
HTTP_TIMEOUT = 5
|
26
|
+
|
27
|
+
# Create a constant for resource semantic conventions
|
28
|
+
RESOURCE = OpenTelemetry::SemanticConventions::Resource
|
29
|
+
|
30
|
+
def detect
|
31
|
+
# Return empty resource if not running on ECS
|
32
|
+
metadata_uri = ENV.fetch('ECS_CONTAINER_METADATA_URI', nil)
|
33
|
+
metadata_uri_v4 = ENV.fetch('ECS_CONTAINER_METADATA_URI_V4', nil)
|
34
|
+
|
35
|
+
return OpenTelemetry::SDK::Resources::Resource.create({}) if metadata_uri.nil? && metadata_uri_v4.nil?
|
36
|
+
|
37
|
+
resource_attributes = {}
|
38
|
+
container_id = fetch_container_id
|
39
|
+
|
40
|
+
# Base ECS resource attributes
|
41
|
+
resource_attributes[RESOURCE::CLOUD_PROVIDER] = 'aws'
|
42
|
+
resource_attributes[RESOURCE::CLOUD_PLATFORM] = 'aws_ecs'
|
43
|
+
resource_attributes[RESOURCE::CONTAINER_NAME] = Socket.gethostname
|
44
|
+
resource_attributes[RESOURCE::CONTAINER_ID] = container_id unless container_id.empty?
|
45
|
+
|
46
|
+
# If v4 endpoint is not available, return basic resource
|
47
|
+
return OpenTelemetry::SDK::Resources::Resource.create(resource_attributes) if metadata_uri_v4.nil?
|
48
|
+
|
49
|
+
begin
|
50
|
+
# Fetch container and task metadata
|
51
|
+
container_metadata = JSON.parse(http_get(metadata_uri_v4.to_s))
|
52
|
+
task_metadata = JSON.parse(http_get("#{metadata_uri_v4}/task"))
|
53
|
+
|
54
|
+
task_arn = task_metadata['TaskARN']
|
55
|
+
base_arn = task_arn[0..task_arn.rindex(':') - 1]
|
56
|
+
|
57
|
+
cluster = task_metadata['Cluster']
|
58
|
+
cluster_arn = cluster.start_with?('arn:') ? cluster : "#{base_arn}:cluster/#{cluster}"
|
59
|
+
|
60
|
+
# Set ECS-specific attributes
|
61
|
+
resource_attributes[RESOURCE::AWS_ECS_CONTAINER_ARN] = container_metadata['ContainerARN']
|
62
|
+
resource_attributes[RESOURCE::AWS_ECS_CLUSTER_ARN] = cluster_arn
|
63
|
+
resource_attributes[RESOURCE::AWS_ECS_LAUNCHTYPE] = task_metadata['LaunchType'].downcase
|
64
|
+
resource_attributes[RESOURCE::AWS_ECS_TASK_ARN] = task_arn
|
65
|
+
resource_attributes[RESOURCE::AWS_ECS_TASK_FAMILY] = task_metadata['Family']
|
66
|
+
resource_attributes[RESOURCE::AWS_ECS_TASK_REVISION] = task_metadata['Revision']
|
67
|
+
|
68
|
+
# Add logging attributes if awslogs is used
|
69
|
+
logs_attributes = get_logs_resource(container_metadata)
|
70
|
+
resource_attributes.merge!(logs_attributes)
|
71
|
+
rescue StandardError => e
|
72
|
+
OpenTelemetry.handle_error(exception: e, message: 'ECS resource detection failed')
|
73
|
+
return OpenTelemetry::SDK::Resources::Resource.create({})
|
74
|
+
end
|
75
|
+
|
76
|
+
# Filter out nil or empty values
|
77
|
+
resource_attributes.delete_if { |_key, value| value.nil? || value.empty? }
|
78
|
+
OpenTelemetry::SDK::Resources::Resource.create(resource_attributes)
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
# Fetches container ID from /proc/self/cgroup file
|
84
|
+
#
|
85
|
+
# @return [String] The container ID or empty string if not found
|
86
|
+
def fetch_container_id
|
87
|
+
begin
|
88
|
+
File.open('/proc/self/cgroup', 'r') do |file|
|
89
|
+
file.each_line do |line|
|
90
|
+
line = line.strip
|
91
|
+
# Look for container ID (64 chars) at the end of the line
|
92
|
+
return line[-CONTAINER_ID_LENGTH..-1] if line.length > CONTAINER_ID_LENGTH
|
93
|
+
end
|
94
|
+
end
|
95
|
+
rescue Errno::ENOENT => e
|
96
|
+
OpenTelemetry.handle_error(exception: e, message: 'Failed to get container ID on ECS')
|
97
|
+
end
|
98
|
+
|
99
|
+
''
|
100
|
+
end
|
101
|
+
|
102
|
+
# Extracting logging-related resource attributes
|
103
|
+
#
|
104
|
+
# @param container_metadata [Hash] Container metadata from ECS metadata endpoint
|
105
|
+
# @returhn [Hash] Resource attributes for logging configuration
|
106
|
+
def get_logs_resource(container_metadata)
|
107
|
+
log_attributes = {}
|
108
|
+
|
109
|
+
if container_metadata['LogDriver'] == 'awslogs'
|
110
|
+
log_options = container_metadata['LogOptions']
|
111
|
+
|
112
|
+
if log_options
|
113
|
+
logs_region = log_options['awslogs-region']
|
114
|
+
logs_group_name = log_options['awslogs-group']
|
115
|
+
logs_stream_name = log_options['awslogs-stream']
|
116
|
+
|
117
|
+
container_arn = container_metadata['ContainerARN']
|
118
|
+
|
119
|
+
# Parse region from ARN if not specified in log options
|
120
|
+
if logs_region.nil? || logs_region.empty?
|
121
|
+
region_match = container_arn.match(/arn:aws:ecs:([^:]+):.*/)
|
122
|
+
logs_region = region_match[1] if region_match
|
123
|
+
end
|
124
|
+
|
125
|
+
# Parse account ID from ARN
|
126
|
+
account_match = container_arn.match(/arn:aws:ecs:[^:]+:([^:]+):.*/)
|
127
|
+
aws_account = account_match[1] if account_match
|
128
|
+
|
129
|
+
logs_group_arn = nil
|
130
|
+
logs_stream_arn = nil
|
131
|
+
|
132
|
+
if logs_region && aws_account
|
133
|
+
logs_group_arn = "arn:aws:logs:#{logs_region}:#{aws_account}:log-group:#{logs_group_name}" if logs_group_name
|
134
|
+
|
135
|
+
logs_stream_arn = "arn:aws:logs:#{logs_region}:#{aws_account}:log-group:#{logs_group_name}:log-stream:#{logs_stream_name}" if logs_stream_name && logs_group_name
|
136
|
+
end
|
137
|
+
|
138
|
+
log_attributes[RESOURCE::AWS_LOG_GROUP_NAMES] = [logs_group_name].compact
|
139
|
+
log_attributes[RESOURCE::AWS_LOG_GROUP_ARNS] = [logs_group_arn].compact
|
140
|
+
log_attributes[RESOURCE::AWS_LOG_STREAM_NAMES] = [logs_stream_name].compact
|
141
|
+
log_attributes[RESOURCE::AWS_LOG_STREAM_ARNS] = [logs_stream_arn].compact
|
142
|
+
else
|
143
|
+
OpenTelemetry.handle_error(message: 'The metadata endpoint v4 has returned \'awslogs\' as \'LogDriver\', but there is no \'LogOptions\' data')
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
log_attributes
|
148
|
+
end
|
149
|
+
|
150
|
+
# Makes an HTTP GET request to the specified URL
|
151
|
+
#
|
152
|
+
# @param url [String] The URL to request
|
153
|
+
# @return [String] The response body
|
154
|
+
def http_get(url)
|
155
|
+
uri = URI.parse(url)
|
156
|
+
request = Net::HTTP::Get.new(uri)
|
157
|
+
|
158
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
159
|
+
http.open_timeout = HTTP_TIMEOUT
|
160
|
+
http.read_timeout = HTTP_TIMEOUT
|
161
|
+
|
162
|
+
OpenTelemetry::Common::Utilities.untraced do
|
163
|
+
response = http.request(request)
|
164
|
+
raise "HTTP request failed with status #{response.code}" unless response.is_a?(Net::HTTPSuccess)
|
165
|
+
|
166
|
+
response.body
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright The OpenTelemetry Authors
|
4
|
+
#
|
5
|
+
# SPDX-License-Identifier: Apache-2.0
|
6
|
+
|
7
|
+
require 'opentelemetry/semantic_conventions/resource'
|
8
|
+
|
9
|
+
module OpenTelemetry
|
10
|
+
module Resource
|
11
|
+
module Detector
|
12
|
+
module AWS
|
13
|
+
# Lambda contains detect class method for determining Lambda resource attributes
|
14
|
+
module Lambda
|
15
|
+
extend self
|
16
|
+
|
17
|
+
# Create a constant for resource semantic conventions
|
18
|
+
RESOURCE = OpenTelemetry::SemanticConventions::Resource
|
19
|
+
|
20
|
+
def detect
|
21
|
+
# Return empty resource if not running on Lambda
|
22
|
+
return OpenTelemetry::SDK::Resources::Resource.create({}) unless lambda_environment?
|
23
|
+
|
24
|
+
resource_attributes = {}
|
25
|
+
|
26
|
+
begin
|
27
|
+
# Set Lambda-specific attributes from environment variables
|
28
|
+
resource_attributes[RESOURCE::CLOUD_PROVIDER] = 'aws'
|
29
|
+
resource_attributes[RESOURCE::CLOUD_PLATFORM] = 'aws_lambda'
|
30
|
+
resource_attributes[RESOURCE::CLOUD_REGION] = ENV.fetch('AWS_REGION', nil)
|
31
|
+
resource_attributes[RESOURCE::FAAS_NAME] = ENV.fetch('AWS_LAMBDA_FUNCTION_NAME', nil)
|
32
|
+
resource_attributes[RESOURCE::FAAS_VERSION] = ENV.fetch('AWS_LAMBDA_FUNCTION_VERSION', nil)
|
33
|
+
resource_attributes[RESOURCE::FAAS_INSTANCE] = ENV.fetch('AWS_LAMBDA_LOG_STREAM_NAME', nil)
|
34
|
+
|
35
|
+
# Convert memory size to integer
|
36
|
+
resource_attributes[RESOURCE::FAAS_MAX_MEMORY] = ENV['AWS_LAMBDA_FUNCTION_MEMORY_SIZE'].to_i if ENV['AWS_LAMBDA_FUNCTION_MEMORY_SIZE']
|
37
|
+
rescue StandardError => e
|
38
|
+
OpenTelemetry.handle_error(exception: e, message: 'Lambda resource detection failed')
|
39
|
+
return OpenTelemetry::SDK::Resources::Resource.create({})
|
40
|
+
end
|
41
|
+
|
42
|
+
# Filter out nil or empty values
|
43
|
+
# Note: we need to handle integers differently since they don't respond to empty?
|
44
|
+
resource_attributes.delete_if do |_key, value|
|
45
|
+
value.nil? || (value.respond_to?(:empty?) && value.empty?)
|
46
|
+
end
|
47
|
+
|
48
|
+
OpenTelemetry::SDK::Resources::Resource.create(resource_attributes)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
# Determines if the current environment is AWS Lambda
|
54
|
+
#
|
55
|
+
# @return [Boolean] true if running on AWS Lambda
|
56
|
+
def lambda_environment?
|
57
|
+
# Check for Lambda-specific environment variables
|
58
|
+
!ENV['AWS_LAMBDA_FUNCTION_NAME'].nil? &&
|
59
|
+
!ENV['AWS_LAMBDA_FUNCTION_VERSION'].nil? &&
|
60
|
+
!ENV['AWS_LAMBDA_LOG_STREAM_NAME'].nil?
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -5,6 +5,8 @@
|
|
5
5
|
# SPDX-License-Identifier: Apache-2.0
|
6
6
|
|
7
7
|
require 'opentelemetry/resource/detector/aws/ec2'
|
8
|
+
require 'opentelemetry/resource/detector/aws/ecs'
|
9
|
+
require 'opentelemetry/resource/detector/aws/lambda'
|
8
10
|
|
9
11
|
module OpenTelemetry
|
10
12
|
module Resource
|
@@ -13,12 +15,33 @@ module OpenTelemetry
|
|
13
15
|
module AWS
|
14
16
|
extend self
|
15
17
|
|
16
|
-
|
17
|
-
# This will be a composite of all the AWS platform detectors
|
18
|
-
EC2.detect
|
18
|
+
RESOURCE = OpenTelemetry::SDK::Resources::Resource
|
19
19
|
|
20
|
-
|
21
|
-
|
20
|
+
# Get resources from specified AWS resource detectors
|
21
|
+
#
|
22
|
+
# @param detectors [Array<Symbol>] List of detectors to use (e.g., :ec2)
|
23
|
+
# @return [OpenTelemetry::SDK::Resources::Resource] The detected AWS resources
|
24
|
+
def detect(detectors = [])
|
25
|
+
return RESOURCE.create({}) if detectors.empty?
|
26
|
+
|
27
|
+
resources = detectors.map do |detector|
|
28
|
+
case detector
|
29
|
+
when :ec2
|
30
|
+
EC2.detect
|
31
|
+
when :ecs
|
32
|
+
ECS.detect
|
33
|
+
when :lambda
|
34
|
+
Lambda.detect
|
35
|
+
else
|
36
|
+
OpenTelemetry.logger.warn("Unknown AWS resource detector: #{detector}")
|
37
|
+
OpenTelemetry::SDK::Resources::Resource.create({})
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Merge all resources into a single resource
|
42
|
+
resources.reduce(OpenTelemetry::SDK::Resources::Resource.create({})) do |merged, resource|
|
43
|
+
merged.merge(resource)
|
44
|
+
end
|
22
45
|
end
|
23
46
|
end
|
24
47
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: opentelemetry-resource-detector-aws
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- OpenTelemetry Authors
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-05-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: opentelemetry-sdk
|
@@ -37,6 +37,8 @@ files:
|
|
37
37
|
- lib/opentelemetry/resource/detector.rb
|
38
38
|
- lib/opentelemetry/resource/detector/aws.rb
|
39
39
|
- lib/opentelemetry/resource/detector/aws/ec2.rb
|
40
|
+
- lib/opentelemetry/resource/detector/aws/ecs.rb
|
41
|
+
- lib/opentelemetry/resource/detector/aws/lambda.rb
|
40
42
|
- lib/opentelemetry/resource/detector/aws/version.rb
|
41
43
|
homepage: https://github.com/open-telemetry/opentelemetry-ruby-contrib
|
42
44
|
licenses:
|