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