Metadata-Version: 2.4
Name: opentelemetry-exporter-cloudwatch-emf
Version: 0.2.2
Summary: OpenTelemetry metrics exporter for AWS CloudWatch using Embedded Metric Format (EMF)
Author-email: Darren Haas <mail+pypi@darrenhaas.com>
License-Expression: Apache-2.0
Project-URL: Homepage, https://github.com/darrenhaas/cw-emf-exporter
Project-URL: Repository, https://github.com/darrenhaas/cw-emf-exporter
Project-URL: Issues, https://github.com/darrenhaas/cw-emf-exporter/issues
Project-URL: Notice, https://github.com/darrenhaas/cw-emf-exporter/blob/main/docs/NOTICE.md
Keywords: opentelemetry,cloudwatch,emf,metrics,aws,observability
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: System :: Monitoring
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: opentelemetry-sdk>=1.20.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
Requires-Dist: black>=24.0.0; extra == "dev"
Requires-Dist: isort>=5.13.0; extra == "dev"
Requires-Dist: flake8>=7.0.0; extra == "dev"
Requires-Dist: mypy>=1.8.0; extra == "dev"
Requires-Dist: twine>=6.0.0; extra == "dev"
Dynamic: license-file

# OpenTelemetry CloudWatch EMF Exporter

An OpenTelemetry metrics exporter for AWS CloudWatch using [Embedded Metric Format (EMF)](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format.html).

This is an unofficial community project. It is not affiliated with, endorsed by,
or maintained by Amazon Web Services (AWS), Amazon CloudWatch, or the
OpenTelemetry project.

## Architecture Overview

```
┌─────────────────────────────────────────────────────────────────────────────┐
│                            Your Application                                  │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│   ┌─────────────┐     ┌─────────────┐     ┌─────────────┐                  │
│   │   Counter   │     │    Gauge    │     │  Histogram  │                  │
│   └──────┬──────┘     └──────┬──────┘     └──────┬──────┘                  │
│          │                   │                   │                          │
│          └───────────────────┼───────────────────┘                          │
│                              ▼                                              │
│                    ┌─────────────────┐                                      │
│                    │   MeterProvider │                                      │
│                    └────────┬────────┘                                      │
│                             │                                               │
│                             ▼                                               │
│               ┌─────────────────────────┐                                   │
│               │ PeriodicExportingReader │                                   │
│               └────────────┬────────────┘                                   │
│                            │                                                │
└────────────────────────────┼────────────────────────────────────────────────┘
                             │
                             ▼
              ┌──────────────────────────────┐
              │  CloudWatchEMFExporter       │
              │  ┌────────────────────────┐  │
              │  │ • Metric Conversion    │  │
              │  │ • Unit Mapping         │  │
              │  │ • Dimension Building   │  │
              │  │ • AWS Auto-Detection   │  │
              │  │ • EMF JSON Formatting  │  │
              │  └────────────────────────┘  │
              └──────────────┬───────────────┘
                             │
                             ▼
                    ┌─────────────────┐
                    │     stdout      │
                    │  (EMF JSON)     │
                    └────────┬────────┘
                             │
        ┌────────────────────┼────────────────────┐
        │                    │                    │
        ▼                    ▼                    ▼
┌───────────────┐   ┌───────────────┐   ┌───────────────┐
│  AWS Lambda   │   │   ECS/Fargate │   │  EC2 + CW     │
│  (auto logs)  │   │  (awslogs)    │   │    Agent      │
└───────┬───────┘   └───────┬───────┘   └───────┬───────┘
        │                   │                   │
        └───────────────────┼───────────────────┘
                            │
                            ▼
                ┌───────────────────────┐
                │   CloudWatch Logs     │
                │   (EMF Processing)    │
                └───────────┬───────────┘
                            │
                            ▼
                ┌───────────────────────┐
                │  CloudWatch Metrics   │
                │  • Dashboards         │
                │  • Alarms             │
                │  • Percentiles (p99)  │
                └───────────────────────┘
```

## Why This Exporter?

OpenTelemetry and AWS support CloudWatch EMF primarily through Collector or
agent workflows. This package fills the smaller collectorless Python SDK use
case: emit valid CloudWatch EMF JSON directly from a Python metrics exporter so
Lambda, ECS/Fargate, and EC2 workloads can route metrics through stdout or logs.

| Approach | Requires Collector | Cold Start Impact | Complexity |
|----------|-------------------|-------------------|------------|
| ADOT Collector | ✅ Yes | High | High |
| ADOT Lambda Layer | ✅ Yes (embedded) | Medium-High | Medium |
| **This Exporter** | ❌ **No** | **Minimal** | **Low** |

## Installation

```bash
# From PyPI after release
pip install opentelemetry-exporter-cloudwatch-emf

# From this repository before the first PyPI release
pip install git+ssh://git@github.com/darrenhaas/cw-emf-exporter.git@v0.2.2
```

## Quick Start

```python
from opentelemetry import metrics
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry_exporter_cloudwatch_emf import CloudWatchEMFExporter

# Create exporter
exporter = CloudWatchEMFExporter(namespace="MyApplication")

# Set up meter provider
reader = PeriodicExportingMetricReader(exporter, export_interval_millis=60000)
provider = MeterProvider(metric_readers=[reader])
metrics.set_meter_provider(provider)

# Create and use metrics
meter = metrics.get_meter("my-service")
counter = meter.create_counter("requests", unit="1")
counter.add(1, {"endpoint": "/api/users", "method": "GET"})
```

## Documentation

| Document | Description |
|----------|-------------|
| [Architecture](docs/ARCHITECTURE.md) | System design, data flow, components |
| [Configuration](docs/CONFIGURATION.md) | All options with examples |
| [API Reference](docs/API.md) | Complete API documentation |
| [Examples](docs/EXAMPLES.md) | Usage patterns and recipes |
| [Downstream Consumers](docs/DOWNSTREAM.md) | Vendored consumers and review process |
| [Release Process](docs/RELEASE.md) | PyPI release checklist |
| [0.2.2 Upgrade Notes](docs/UPGRADE_NOTES_0.2.2.md) | Summary for downstream agents |
| [Project Notice](docs/NOTICE.md) | Unofficial project and trademark notice |
| [Community Gap](docs/COMMUNITY_GAP.md) | Upstream context for collectorless Python EMF export |

## Features

### Metric Types Support

| OTel Metric Type | EMF Output | Percentile Support |
|-----------------|------------|-------------------|
| Counter | Single value | ❌ |
| Gauge | Single value | ❌ |
| Histogram | Values array | ✅ (p50, p99, etc.) |
| ExponentialHistogram | Values array | ✅ (p50, p99, etc.) |

### AWS Environment Auto-Detection

```
┌─────────────────────────────────────────────────────────┐
│              AWS Environment Detection                   │
├─────────────────────────────────────────────────────────┤
│                                                         │
│   Check: AWS_LAMBDA_FUNCTION_NAME?                      │
│          │                                              │
│          ├── Yes ──► Lambda Environment                 │
│          │           • function_name                    │
│          │           • function_version                 │
│          │           • region                           │
│          │           • memory_size                      │
│          │                                              │
│          └── No ──► Check: ECS_CONTAINER_METADATA_URI?  │
│                     │                                   │
│                     ├── Yes ──► ECS/Fargate Environment │
│                     │           • aws_ecs=true          │
│                     │           • region                │
│                     │           • cluster/task metadata │
│                     │                                   │
│                     └── No ──► Check: AWS_REGION?       │
│                                │                        │
│                                ├── Yes ──► EC2/Generic  │
│                                │           • region     │
│                                │                        │
│                                └── No ──► No AWS attrs  │
│                                                         │
└─────────────────────────────────────────────────────────┘
```

### Unit Mapping

| OpenTelemetry (UCUM) | CloudWatch Unit |
|---------------------|-----------------|
| `s` | Seconds |
| `ms` | Milliseconds |
| `us` | Microseconds |
| `By` | Bytes |
| `KiBy` | Kilobytes |
| `MiBy` | Megabytes |
| `GiBy` | Gigabytes |
| `TiBy` | Terabytes |
| `By/s` | Bytes/Second |
| `1`, `{request}`, `{error}` | Count |
| `%` | Percent |

## Configuration Summary

```python
CloudWatchEMFExporter(
    # Required
    namespace="MyApp",              # CloudWatch namespace (1-256 chars)

    # Output
    output=sys.stdout,              # Where to write EMF JSON

    # Dimensions
    dimension_keys=["svc", "env"],  # Filter dimensions (None = all)
    max_dimensions=30,              # Max dimensions (CloudWatch limit: 30)

    # Resolution
    storage_resolution=60,          # 1 = high-res, 60 = standard

    # Log Routing (for CloudWatch Agent)
    log_group_name="/aws/myapp",    # Target log group
    log_stream_name="stream-1",     # Target log stream

    # Features
    auto_detect_aws=True,           # Auto-detect Lambda/ECS/EC2
    histogram_as_values=True,       # Enable percentile calculations

    # Advanced
    timestamp_fn=lambda: int(time.time() * 1000),  # Custom timestamp
)
```

## EMF Output Format

```json
{
  "_aws": {
    "Timestamp": 1700000000000,
    "CloudWatchMetrics": [{
      "Namespace": "MyApplication",
      "Dimensions": [["service", "endpoint"]],
      "Metrics": [{
        "Name": "request_latency",
        "Unit": "Milliseconds",
        "StorageResolution": 60
      }]
    }]
  },
  "request_latency": [5, 5, 15, 15, 15],
  "service": "api",
  "endpoint": "/users"
}
```

Histogram values use bucket representatives repeated by bucket count. Large
histograms are proportionally sampled to fit EMF's 100-value numeric array
limit.

## Development

```bash
# Install dev dependencies
uv sync --extra dev

# Run tests
uv run pytest

# Lint, type check, and package validation
uv run flake8 src tests
uv run mypy src
uv run black --check src tests
uv build
uv run twine check dist/*
```

## License

Apache 2.0 - See [LICENSE](LICENSE)
