Skip to content

Commit 5fe7f57

Browse files
committed
Enhancements by removing redundent code
1 parent 1b34c6f commit 5fe7f57

File tree

2 files changed

+41
-77
lines changed

2 files changed

+41
-77
lines changed

aws-opentelemetry-distro/src/amazon/opentelemetry/distro/exporter/otlp/aws/metrics/otlp_aws_emf_exporter.py

Lines changed: 11 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -120,18 +120,15 @@ def _ensure_log_group_exists(self):
120120
logger.error("Failed to create log group %s : %s", self.log_group_name, error)
121121
raise
122122

123-
def _get_metric_name(self, record) -> Optional[str]:
123+
def _get_metric_name(self, record: Any) -> Optional[str]:
124124
"""Get the metric name from the metric record or data point."""
125-
# For metrics in MetricsData format
126-
if hasattr(record, "name") and record.name.strip():
127-
return record.name
128125
# For compatibility with older record format
129-
if hasattr(record, "instrument") and hasattr(record.instrument, "name") and record.instrument.name.strip():
126+
if hasattr(record, "instrument") and hasattr(record.instrument, "name") and record.instrument.name:
130127
return record.instrument.name
131128
# Return None if no valid metric name found
132129
return None
133130

134-
def _get_unit(self, instrument_or_metric) -> Optional[str]:
131+
def _get_unit(self, instrument_or_metric: Any) -> Optional[str]:
135132
"""Get CloudWatch unit from OTel instrument or metric unit."""
136133
# Check if we have an Instrument object or a metric with unit attribute
137134
if isinstance(instrument_or_metric, Instrument):
@@ -198,7 +195,7 @@ def _create_metric_record(self, metric_name: str, metric_unit: str, metric_descr
198195

199196
return record
200197

201-
def _convert_gauge(self, metric, dp) -> Tuple[Any, int]:
198+
def _convert_gauge(self, metric: Any, dp: Any) -> Tuple[Any, int]:
202199
"""Convert a Gauge metric datapoint to a metric record.
203200
204201
Args:
@@ -225,7 +222,7 @@ def _convert_gauge(self, metric, dp) -> Tuple[Any, int]:
225222

226223
return record, timestamp_ms
227224

228-
def _group_by_attributes_and_timestamp(self, record, timestamp_ms) -> Tuple[str, int]:
225+
def _group_by_attributes_and_timestamp(self, record: Any, timestamp_ms: int) -> Tuple[str, int]:
229226
"""Group metric record by attributes and timestamp.
230227
231228
Args:
@@ -239,7 +236,7 @@ def _group_by_attributes_and_timestamp(self, record, timestamp_ms) -> Tuple[str,
239236
attrs_key = self._get_attributes_key(record.attributes)
240237
return (attrs_key, timestamp_ms)
241238

242-
def _create_emf_log(self, metric_records, resource: Resource, timestamp: Optional[int] = None) -> Dict:
239+
def _create_emf_log(self, metric_records: List[Any], resource: Resource, timestamp: Optional[int] = None) -> Dict:
243240
"""
244241
Create EMF log dictionary from metric records.
245242
@@ -305,31 +302,13 @@ def _create_emf_log(self, metric_records, resource: Resource, timestamp: Optiona
305302
return emf_log
306303

307304
# pylint: disable=no-member
308-
def _send_log_event(self, log_event: Dict):
305+
def _send_log_event(self, log_event: Dict[str, Any]):
309306
"""
310307
Send a log event to CloudWatch Logs.
311308
312309
Basic implementation for PR 1 - sends individual events directly.
313310
"""
314311
try:
315-
# Create log group and stream if they don't exist
316-
try:
317-
self.logs_client.create_log_group(logGroupName=self.log_group_name)
318-
logger.debug("Created log group: %s", self.log_group_name)
319-
except ClientError as error:
320-
# Check if it's a ResourceAlreadyExistsException (botocore exception handling)
321-
if error.response.get("Error", {}).get("Code") == "ResourceAlreadyExistsException":
322-
logger.debug("Log group %s already exists", self.log_group_name)
323-
324-
# Create log stream if it doesn't exist
325-
try:
326-
self.logs_client.create_log_stream(logGroupName=self.log_group_name, logStreamName=self.log_stream_name)
327-
logger.debug("Created log stream: %s", self.log_stream_name)
328-
except ClientError as error:
329-
# Check if it's a ResourceAlreadyExistsException (botocore exception handling)
330-
if error.response.get("Error", {}).get("Code") == "ResourceAlreadyExistsException":
331-
logger.debug("Log stream %s already exists", self.log_stream_name)
332-
333312
# Send the log event
334313
response = self.logs_client.put_log_events(
335314
logGroupName=self.log_group_name, logStreamName=self.log_stream_name, logEvents=[log_event]
@@ -343,7 +322,9 @@ def _send_log_event(self, log_event: Dict):
343322
raise
344323

345324
# pylint: disable=too-many-nested-blocks
346-
def export(self, metrics_data: MetricsData, timeout_millis: Optional[int] = None, **kwargs) -> MetricExportResult:
325+
def export(
326+
self, metrics_data: MetricsData, timeout_millis: Optional[int] = None, **kwargs: Any
327+
) -> MetricExportResult:
347328
"""
348329
Export metrics as EMF logs to CloudWatch.
349330
@@ -418,7 +399,7 @@ def force_flush(self, timeout_millis: int = 10000) -> bool:
418399
logger.debug("CloudWatchEMFExporter force flushes the buffered metrics")
419400
return True
420401

421-
def shutdown(self, timeout_millis=None, **kwargs):
402+
def shutdown(self, timeout_millis: Optional[int] = None, **kwargs: Any) -> bool:
422403
"""
423404
Shutdown the exporter.
424405
Override to handle timeout and other keyword arguments, but do nothing.
@@ -438,7 +419,6 @@ def create_emf_exporter(
438419
log_group_name: str = "/aws/otel/python",
439420
log_stream_name: Optional[str] = None,
440421
aws_region: Optional[str] = None,
441-
debug: bool = False,
442422
**kwargs,
443423
) -> CloudWatchEMFExporter:
444424
"""
@@ -466,11 +446,6 @@ def create_emf_exporter(
466446
UpDownCounter: AggregationTemporality.DELTA,
467447
}
468448

469-
# Configure logging if debug is enabled
470-
if debug:
471-
logging.basicConfig(level=logging.DEBUG)
472-
logger.setLevel(logging.DEBUG)
473-
474449
# Create and return the exporter
475450
return CloudWatchEMFExporter(
476451
namespace=namespace,

aws-opentelemetry-distro/tests/amazon/opentelemetry/distro/exporter/otlp/aws/metrics/test_otlp_aws_emf_exporter.py

Lines changed: 30 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -105,23 +105,6 @@ def test_create_emf_exporter_custom_args(self, mock_session):
105105
self.assertEqual(exporter.namespace, "CustomNamespace")
106106
self.assertEqual(exporter.log_group_name, "/custom/log/group")
107107

108-
@patch("botocore.session.Session")
109-
@patch("logging.basicConfig")
110-
def test_create_emf_exporter_debug_mode(self, mock_logging_config, mock_session):
111-
"""Test creating exporter with debug mode enabled."""
112-
# Mock the botocore session to avoid AWS calls
113-
mock_client = Mock()
114-
mock_session_instance = Mock()
115-
mock_session.return_value = mock_session_instance
116-
mock_session_instance.create_client.return_value = mock_client
117-
mock_client.describe_log_groups.return_value = {"logGroups": []}
118-
mock_client.create_log_group.return_value = {}
119-
120-
exporter = create_emf_exporter(debug=True)
121-
122-
self.assertIsInstance(exporter, CloudWatchEMFExporter)
123-
mock_logging_config.assert_called_once()
124-
125108

126109
# pylint: disable=too-many-public-methods
127110
class TestCloudWatchEMFExporter(unittest.TestCase):
@@ -190,17 +173,17 @@ def test_get_metric_name(self):
190173
record = Mock()
191174
record.instrument = Mock()
192175
record.instrument.name = "test_metric"
193-
del record.name # Ensure record.name doesn't exist
194176

195177
result = self.exporter._get_metric_name(record)
196178
self.assertEqual(result, "test_metric")
197179

198-
# Test with record that has direct name attribute
199-
record_with_name = Mock()
200-
record_with_name.name = "direct_metric"
180+
# Test with record that has empty instrument name (should return None)
181+
record_empty = Mock()
182+
record_empty.instrument = Mock()
183+
record_empty.instrument.name = ""
201184

202-
result2 = self.exporter._get_metric_name(record_with_name)
203-
self.assertEqual(result2, "direct_metric")
185+
result_empty = self.exporter._get_metric_name(record_empty)
186+
self.assertIsNone(result_empty)
204187

205188
def test_get_dimension_names(self):
206189
"""Test dimension names extraction."""
@@ -445,30 +428,18 @@ def test_export_with_gauge_metrics(self, mock_session):
445428

446429
def test_get_metric_name_fallback(self):
447430
"""Test metric name extraction fallback."""
448-
# Test with record that has no name or instrument
431+
# Test with record that has no instrument attribute
449432
record = Mock(spec=[])
450433

451434
result = self.exporter._get_metric_name(record)
452-
# Note: This test may fail against old installed version which returns "unknown_metric"
453-
# The new implementation correctly returns None
454-
self.assertTrue(result is None or result == "unknown_metric")
435+
self.assertIsNone(result)
455436

456437
def test_get_metric_name_empty_name(self):
457-
"""Test metric name extraction with empty name."""
458-
# Test with record that has empty name
459-
record = Mock()
460-
record.name = ""
461-
462-
self.exporter._get_metric_name(record)
463-
# Just verify the method can handle empty names without crashing
464-
# The method should return some value and not crash
465-
self.assertIsNotNone(self.exporter._get_metric_name) # Method exists
466-
438+
"""Test metric name extraction with empty instrument name."""
467439
# Test with record that has empty instrument name
468440
record = Mock()
469441
record.instrument = Mock()
470442
record.instrument.name = ""
471-
del record.name # Ensure record.name doesn't exist
472443

473444
result = self.exporter._get_metric_name(record)
474445
self.assertIsNone(result)
@@ -496,10 +467,9 @@ def test_create_emf_log_skips_empty_metric_names(self):
496467
self.assertIn("valid_metric", result)
497468
self.assertEqual(result["valid_metric"], 20.0)
498469

499-
# Check that the valid metric is in the definitions
470+
# Check that only the valid metric is in the definitions (empty names are skipped)
500471
cw_metrics = result["_aws"]["CloudWatchMetrics"][0]
501-
# Note: Old version may include both metrics (1 or 2), new version skips empty names (only 1)
502-
self.assertTrue(len(cw_metrics["Metrics"]) >= 1)
472+
self.assertEqual(len(cw_metrics["Metrics"]), 1)
503473
# Ensure our valid metric is present
504474
metric_names = [m["Name"] for m in cw_metrics["Metrics"]]
505475
self.assertIn("valid_metric", metric_names)
@@ -543,6 +513,25 @@ def test_ensure_log_group_exists_create_failure(self, mock_session):
543513
with self.assertRaises(ClientError):
544514
CloudWatchEMFExporter(namespace="TestNamespace", log_group_name="test-log-group")
545515

516+
@patch("botocore.session.Session")
517+
def test_ensure_log_group_exists_success(self, mock_session):
518+
"""Test log group existence check when log group already exists."""
519+
# Mock the botocore session
520+
mock_client = Mock()
521+
mock_session_instance = Mock()
522+
mock_session.return_value = mock_session_instance
523+
mock_session_instance.create_client.return_value = mock_client
524+
525+
# Make describe succeed (log group exists)
526+
mock_client.describe_log_groups.return_value = {"logGroups": [{"logGroupName": "test-log-group"}]}
527+
528+
# This should not raise an exception
529+
exporter = CloudWatchEMFExporter(namespace="TestNamespace", log_group_name="test-log-group")
530+
self.assertIsNotNone(exporter)
531+
# Verify describe was called but create was not
532+
mock_client.describe_log_groups.assert_called_once_with(logGroupNamePrefix="test-log-group", limit=1)
533+
mock_client.create_log_group.assert_not_called()
534+
546535
def test_export_with_unsupported_metric_type(self):
547536
"""Test export with unsupported metric types."""
548537
# Create mock metrics data with unsupported metric type

0 commit comments

Comments
 (0)