|
6 | 6 | import json |
7 | 7 | import logging |
8 | 8 | import math |
9 | | -import os |
10 | 9 | import time |
11 | 10 | from abc import ABC, abstractmethod |
12 | 11 | from collections import defaultdict |
13 | 12 | from typing import Any, Dict, List, Optional, Tuple |
14 | 13 |
|
15 | | -from amazon.opentelemetry.distro._aws_resource_attribute_configurator import get_service_attribute |
| 14 | +from amazon.opentelemetry.distro._utils import should_add_application_signals_dimensions |
16 | 15 | from opentelemetry.sdk.metrics import Counter |
17 | 16 | from opentelemetry.sdk.metrics import Histogram as HistogramInstr |
18 | 17 | from opentelemetry.sdk.metrics import ObservableCounter, ObservableGauge, ObservableUpDownCounter, UpDownCounter |
|
30 | 29 | ) |
31 | 30 | from opentelemetry.sdk.metrics.view import ExponentialBucketHistogramAggregation |
32 | 31 | from opentelemetry.sdk.resources import Resource |
| 32 | +from opentelemetry.semconv._incubating.attributes.cloud_attributes import CloudPlatformValues |
33 | 33 | from opentelemetry.semconv.resource import ResourceAttributes |
34 | 34 | from opentelemetry.util.types import Attributes |
35 | 35 |
|
|
39 | 39 | SERVICE_DIMENSION_NAME: str = "Service" |
40 | 40 | ENVIRONMENT_DIMENSION_NAME: str = "Environment" |
41 | 41 |
|
| 42 | +# Resource attribute constant for deployment.environment.name |
| 43 | +# deployment.environment is deprecated in favor of deployment.environment.name |
| 44 | +# but not yet available in current OTel Python version |
| 45 | +# https://github.com/open-telemetry/opentelemetry.io/commit/b04507d7be1e916f6705126c56d66dbe9536503e |
| 46 | +DEPLOYMENT_ENVIRONMENT_NAME: str = "deployment.environment.name" |
| 47 | + |
42 | 48 | # Constants |
43 | | -LAMBDA_DEFAULT: str = "lambda:default" |
44 | 49 | UNKNOWN_SERVICE: str = "UnknownService" |
| 50 | +UNKNOWN_ENVIRONMENT: str = "generic:default" |
| 51 | +EC2_DEFAULT: str = "ec2:default" |
| 52 | +ECS_DEFAULT: str = "ecs:default" |
| 53 | +EKS_DEFAULT: str = "eks:default" |
| 54 | +LAMBDA_DEFAULT: str = "lambda:default" |
45 | 55 |
|
46 | 56 |
|
47 | 57 | class MetricRecord: |
@@ -195,52 +205,58 @@ def _get_dimension_names(self, attributes: Attributes) -> List[str]: |
195 | 205 | # For now, use all attributes as dimensions |
196 | 206 | return list(attributes.keys()) |
197 | 207 |
|
198 | | - def _has_dimension_case_insensitive(self, dimension_names: List[str], dimension_to_check: str) -> bool: |
199 | | - """Check if dimension already exists (case-insensitive match).""" |
200 | | - dimension_lower = dimension_to_check.lower() |
201 | | - return any(dim.lower() == dimension_lower for dim in dimension_names) |
202 | | - |
203 | | - @staticmethod |
204 | | - def _is_application_signals_emf_export_enabled() -> bool: |
205 | | - """Check if Application Signals EMF export is enabled. |
206 | | -
|
207 | | - Returns True only if BOTH: |
208 | | - - OTEL_AWS_APPLICATION_SIGNALS_ENABLED is true |
209 | | - - OTEL_AWS_APPLICATION_SIGNALS_EMF_EXPORT_ENABLED is true |
210 | | - """ |
211 | | - app_signals_enabled = os.environ.get("OTEL_AWS_APPLICATION_SIGNALS_ENABLED", "false").lower() == "true" |
212 | | - emf_export_enabled = ( |
213 | | - os.environ.get("OTEL_AWS_APPLICATION_SIGNALS_EMF_EXPORT_ENABLED", "false").lower() == "true" |
214 | | - ) |
215 | | - return app_signals_enabled and emf_export_enabled |
216 | | - |
217 | 208 | def _add_application_signals_dimensions( |
218 | | - self, dimension_names: List[str], emf_log: Dict, resource: Resource |
| 209 | + self, dimension_names: List[str], emf_log: Dict, resource_attributes: Optional[Attributes] |
219 | 210 | ) -> None: |
220 | | - """Add Service and Environment dimensions if not already present (case-insensitive).""" |
221 | | - if not self._is_application_signals_emf_export_enabled(): |
| 211 | + """Add Service and Environment dimensions if not already present.""" |
| 212 | + if not should_add_application_signals_dimensions(): |
222 | 213 | return |
223 | 214 |
|
224 | | - # Add Service dimension if not already set by user |
225 | 215 | if not self._has_dimension_case_insensitive(dimension_names, SERVICE_DIMENSION_NAME): |
226 | | - if resource: |
227 | | - service_name, _ = get_service_attribute(resource) |
228 | | - else: |
| 216 | + service_name = resource_attributes.get(ResourceAttributes.SERVICE_NAME) if resource_attributes else None |
| 217 | + service_name_str = str(service_name) if service_name else "" |
| 218 | + # https://github.com/open-telemetry/opentelemetry-python/blob/102fec2be1fe9d0a8e299598a21ad6ec3b96dcca/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/service_attributes.py#L20 |
| 219 | + if ( |
| 220 | + not service_name |
| 221 | + or service_name_str == "unknown_service" |
| 222 | + or service_name_str.startswith("unknown_service:") |
| 223 | + ): |
229 | 224 | service_name = UNKNOWN_SERVICE |
230 | | - dimension_names.insert(0, SERVICE_DIMENSION_NAME) |
231 | | - emf_log[SERVICE_DIMENSION_NAME] = str(service_name) |
| 225 | + dimension_names.append(SERVICE_DIMENSION_NAME) |
| 226 | + emf_log[SERVICE_DIMENSION_NAME] = service_name |
232 | 227 |
|
233 | | - # Add Environment dimension if not already set by user |
234 | 228 | if not self._has_dimension_case_insensitive(dimension_names, ENVIRONMENT_DIMENSION_NAME): |
235 | | - environment_value = None |
236 | | - if resource and resource.attributes: |
237 | | - environment_value = resource.attributes.get(ResourceAttributes.DEPLOYMENT_ENVIRONMENT) |
238 | | - if not environment_value: |
239 | | - environment_value = LAMBDA_DEFAULT |
240 | | - # Insert after Service if it exists, otherwise at the beginning |
241 | | - insert_pos = 1 if SERVICE_DIMENSION_NAME in dimension_names else 0 |
242 | | - dimension_names.insert(insert_pos, ENVIRONMENT_DIMENSION_NAME) |
243 | | - emf_log[ENVIRONMENT_DIMENSION_NAME] = str(environment_value) |
| 229 | + environment_name = self._get_deployment_environment(resource_attributes) |
| 230 | + dimension_names.append(ENVIRONMENT_DIMENSION_NAME) |
| 231 | + emf_log[ENVIRONMENT_DIMENSION_NAME] = environment_name |
| 232 | + |
| 233 | + def _has_dimension_case_insensitive(self, dimension_names: List[str], dimension_to_check: str) -> bool: |
| 234 | + """Check if dimension already exists.""" |
| 235 | + dimension_lower = dimension_to_check.lower() |
| 236 | + return any(dim.lower() == dimension_lower for dim in dimension_names) |
| 237 | + |
| 238 | + def _get_deployment_environment(self, resource_attributes: Optional[Attributes]) -> str: |
| 239 | + """Get deployment environment from resource attributes or cloud platform.""" |
| 240 | + if not resource_attributes: |
| 241 | + return UNKNOWN_ENVIRONMENT |
| 242 | + |
| 243 | + environment_name = resource_attributes.get(DEPLOYMENT_ENVIRONMENT_NAME) |
| 244 | + if not environment_name: |
| 245 | + environment_name = resource_attributes.get(ResourceAttributes.DEPLOYMENT_ENVIRONMENT) |
| 246 | + |
| 247 | + if environment_name: |
| 248 | + return str(environment_name) |
| 249 | + |
| 250 | + platform = resource_attributes.get(ResourceAttributes.CLOUD_PLATFORM) |
| 251 | + if platform: |
| 252 | + platform_defaults = { |
| 253 | + CloudPlatformValues.AWS_EC2.value: EC2_DEFAULT, |
| 254 | + CloudPlatformValues.AWS_ECS.value: ECS_DEFAULT, |
| 255 | + CloudPlatformValues.AWS_EKS.value: EKS_DEFAULT, |
| 256 | + CloudPlatformValues.AWS_LAMBDA.value: LAMBDA_DEFAULT, |
| 257 | + } |
| 258 | + return platform_defaults.get(str(platform), UNKNOWN_ENVIRONMENT) |
| 259 | + return UNKNOWN_ENVIRONMENT |
244 | 260 |
|
245 | 261 | def _get_attributes_key(self, attributes: Attributes) -> str: |
246 | 262 | """ |
@@ -552,7 +568,8 @@ def _create_emf_log( |
552 | 568 | emf_log[name] = str(value) |
553 | 569 |
|
554 | 570 | # Add Service and Environment dimensions if Application Signals EMF export is enabled |
555 | | - self._add_application_signals_dimensions(dimension_names, emf_log, resource) |
| 571 | + resource_attributes = resource.attributes if resource else {} |
| 572 | + self._add_application_signals_dimensions(dimension_names, emf_log, resource_attributes) |
556 | 573 |
|
557 | 574 | # Add CloudWatch Metrics if we have metrics, include dimensions only if they exist |
558 | 575 | if metric_definitions: |
|
0 commit comments