Get started Dashboard
Troubleshooting ·

AWS_EMF_ENVIRONMENT: valid values and missing EMF fixes

Explore with AI

AWS_EMF_ENVIRONMENT overrides environment detection in AWS embedded-metrics client libraries. Node.js and Java document Local, Lambda, Agent, EC2 and ECS; Python's current resolver recognizes Local, Lambda, EC2 and Default, case-insensitively. Choose the value supported by your installed library and verify the full route from emitted EMF JSON to CloudWatch Logs and extracted metrics.

Ongoing incident? Our founding team can help you get your AWS app back up.
On this page

AWS_EMF_ENVIRONMENT overrides the environment selected by AWS’s embedded-metrics client libraries. It controls how the library decorates and delivers metric log events; it is not the name of a CloudWatch namespace or a production/staging label. Check the value against your language library before investigating missing metrics, because the supported overrides differ.

The verified Node.js configuration and Java configuration list Local, Lambda, Agent, EC2 and ECS. Python’s current resolver instead recognizes Local, Lambda, EC2 and Default, case-insensitively. Follow the installed release, not an assumed universal AWS value list.

What the variable actually changes

Embedded metric format, or EMF, is JSON that carries both log context and a _aws.CloudWatchMetrics directive. When a valid event reaches CloudWatch Logs, CloudWatch extracts the declared metrics. The EMF specification defines that extraction contract; the library’s environment setting decides which local transport and metadata to use before the event reaches the service.

That gives you three separate checks:

  1. Did the application emit and flush a valid metric event?
  2. Did stdout collection or an agent deliver that event to CloudWatch Logs?
  3. Did CloudWatch extract the expected metric identity and timestamp?

Setting the variable alone does not install the CloudWatch Agent, grant logging permissions or route a container’s stdout. Nor does it change native Lambda metrics such as Errors or AsyncEventsDropped.

Valid values, verified separately by library

The following reflects the AWS-owned library documentation and resolver source checked on 6 October 2026. Package releases can differ from the default branch, so inspect the installed release when debugging an older service.

LibrarySupported override spellingsImportant behavior
Node.js aws-embedded-metricsLocal, Lambda, Agent, EC2, ECSLocal/Lambda write stdout; Agent/EC2 use agent transport; ECS supports its documented FireLens path
Python aws-embedded-metricsLocal, Lambda, EC2, DefaultResolver lowercases input; Default selects the default agent environment; unknown values fall through to detection
Java software.amazon.cloudwatchlogs:aws-embedded-metricsLocal, Lambda, Agent, EC2, ECSDocumented stdout for Local/Lambda and TCP for agent environments; AWS_EMF_WRITE_TO_STDOUT=true can override agent output

Node’s environment detector tests Lambda, then ECS, then EC2 when no valid override is supplied, and falls back to its default environment. It caches resolution and starts resolving at module load. Configure the process environment before importing the library rather than trying to switch modes halfway through a request.

Python’s resolver tests Lambda and then EC2, with a default-environment fallback. Its configuration provider reads AWS_EMF_ENVIRONMENT from the process environment. Python’s README does not provide Node’s override list; the resolver is the evidence for its distinct accepted values.

Use documented casing for Node and Java; do not assume Python’s lowercase normalization exists everywhere. Do not invent values such as EKS, Fargate, Production or CloudWatch. EKS or an on-premises host can use an agent-based delivery path without a same-named environment override.

Confirm the actual package and process configuration

Run these in the application’s environment, not only on your development laptop:

# Node.js project
npm ls aws-embedded-metrics

# Python process environment
python -m pip show aws-embedded-metrics
python -c 'import os; print(os.getenv("AWS_EMF_ENVIRONMENT", "<unset>"))'

# Maven project
mvn dependency:tree -Dincludes=software.amazon.cloudwatchlogs:aws-embedded-metrics

For Lambda, inspect only relevant keys rather than dumping every environment variable, which may include secrets:

aws lambda get-function-configuration --function-name orders-worker \
  --query '{Runtime:Runtime,EMF:Environment.Variables.AWS_EMF_ENVIRONMENT,LogFormat:LoggingConfig.LogFormat}'

Check the version actually reached by the alias too. A changed $LATEST environment does not update an existing published version. For containers, inspect the running task/pod configuration and its log driver or collector, not just the Dockerfile used to build it.

In Python, you can inspect the installed resolver directly from an interactive session with inspect.getsource. That lets you establish which override values your wheel accepts without assuming the current repository matches the installed code.

Produce one known-good metric before changing the pipeline

For Node.js, install a current compatible aws-embedded-metrics release. The README specifically requires versions 4.1.1+, 3.0.2+ or 2.0.7+ for Lambda JSON log format, warning of metric loss with earlier releases in that configuration.

A minimal Lambda handler using the library’s scope wrapper is:

const { metricScope, Unit } = require("aws-embedded-metrics");

exports.handler = metricScope(metrics => async () => {
  metrics.setNamespace("Orders/Diagnostics");
  metrics.setDimensions({ Service: "orders-worker" });
  metrics.putMetric("Probe", 1, Unit.Count);
  metrics.setProperty("Purpose", "emf-delivery-check");
  return { ok: true };
});

The wrapper handles flushing at the end of the scoped invocation. If you create a logger manually, await metrics.flush() before the Lambda handler returns. Do not launch the flush as unobserved background work.

For a local Python smoke check, set the environment before launching the interpreter:

AWS_EMF_ENVIRONMENT=Local python -c '
from aws_embedded_metrics import metric_scope
@metric_scope
def probe(metrics):
    metrics.set_namespace("Orders/Diagnostics")
    metrics.set_dimensions({"Service": "orders-worker"})
    metrics.put_metric("Probe", 1, "Count")
probe()
'

This proves local EMF serialization, not CloudWatch ingestion. In a real Lambda environment, use autodetection or the library’s Lambda override and its supported logging path. For Python running with an agent, the accepted override is Default; for Node or Java it is Agent. Configure the agent endpoint and log group separately according to that library’s configuration documentation.

Missing EMF: trace the first broken step

Nothing leaves the application

Confirm the metric-producing code actually ran, then inspect raw stdout or the agent-facing output. Check that the logger is flushed and errors from flush are observed. In Python, also check AWS_EMF_DISABLE_METRIC_EXTRACTION: the library documentation says it can deliberately omit EMF metadata. Plain log properties without that metadata are not extraction-ready metrics.

An invalid override can fall back to detection rather than failing deployment. That is why a successful startup is insufficient verification. Confirm the emitted event and transport selected by the installed package.

EMF appears locally but not in CloudWatch Logs

Local writes stdout; it does not call the CloudWatch service directly. Follow the actual logging pipeline. On Lambda, check execution-role log permissions and the function’s configured log group. In an agent-based environment, check the configured endpoint, agent listener, connectivity and the agent’s own logs.

If you intentionally route stdout from an agent-based Java environment, the Java README documents AWS_EMF_WRITE_TO_STDOUT=true. That is a Java setting, not a portable instruction for all three clients. Non-Lambda Java agent output can also be asynchronously buffered; follow the README’s graceful-shutdown sink procedure so a process exit does not abandon buffered events.

Logs arrive but no metric appears

Open the raw CloudWatch log event, not just a rendered summary. The specification requires a JSON object with no prefixed or suffixed text. _aws and metric/dimension targets must have the correct case and root-level placement. A timestamp prefix, stringified JSON nested inside message, or a multiline formatting pipeline can alter the extraction contract.

Check the declared Namespace, metric name, dimension sets, numeric values and timestamp. The EMF timestamp is epoch milliseconds, no more than 14 days old or two hours in the future. A seconds timestamp is not interchangeable. Inspect parsing/validation failure metrics in AWS/Logs as AWS recommends, and do not troubleshoot serialization by changing the transport override alone.

For the probe above, locate the metric identity explicitly:

aws cloudwatch list-metrics --namespace Orders/Diagnostics \
  --metric-name Probe \
  --dimensions Name=Service,Value=orders-worker

The namespace and dimensions are case-sensitive identities. A metric with additional default dimensions is distinct from the single-dimension series in this probe. Allow ingestion and discovery time, then query a time range containing the event’s timestamp. Old or incorrectly timestamped events may exist in logs while the current chart remains empty.

Verification and prevention

Success means you can find all three pieces: a valid emitted JSON event, the same event in CloudWatch Logs, and a datapoint with the exact declared metric identity. Run this check in the real target environment after library, runtime, log-format or collector changes. Keep package versions pinned and document why an override is necessary; leave autodetection enabled when it already selects the correct route.

Keep high-cardinality request IDs as log properties rather than metric dimensions. The EMF specification explains that each unique dimension combination creates a custom metric, with corresponding cost. A successful extraction test should not generate an unbounded set of series.

For the separate AWS system collector, use the LambdaInsightsExtension guide. For an automated investigator, the AWS read-access guidance helps separate telemetry reads from the credentials that configure and deliver telemetry.

Polylane’s AWS integration connects it to AWS, so it can see when something breaks there and work out why. The AWS overview has more. When EMF delivery breaks, the useful first result is evidence of which pipeline step failed; missing custom metrics are not evidence that the application is healthy.

Running on AWS? See how Polylane monitors AWS in production.

Common questions.

When should I set the override?

Use it when the installed library's automatic detection selects the wrong metadata or delivery path, or when a controlled local test needs stdout. Set it before library initialization and verify the emitted event and actual destination; leave detection enabled when it already works.

Is AWS_EMF_ENVIRONMENT the deployment stage?

No. It selects a library environment and delivery behavior, such as Lambda stdout or an agent transport. Use an explicit low-cardinality dimension or property for production, staging or a service name.

Can I set AWS_EMF_ENVIRONMENT=Agent in Python?

The current Python environment resolver recognizes Default for its default agent environment, not Agent. It lowercases the supplied value and falls back to detection for unknown values. Check the resolver in your installed release rather than copying Node's value list.

Does Local publish metrics directly to CloudWatch?

No. Local writes EMF to stdout without cloud-environment decoration. Unless a supported pipeline delivers those JSON events into CloudWatch Logs for extraction, the metrics remain local output.

Does the Lambda Insights layer use my application's EMF override?

AWS_EMF_ENVIRONMENT is a setting for the embedded-metrics client library. Lambda Insights is a separate collector with its own installation and log delivery requirements. Do not assume changing the application's variable configures that extension.

Sources (7)

  1. AWS embedded-metrics Node.js configuration
  2. Node.js environment detector
  3. Python environment detector
  4. Python environment configuration provider
  5. AWS embedded-metrics Java configuration
  6. CloudWatch EMF specification
  7. Polylane AWS integration

About the author

Boris Tane

Founder of Polylane

Boris Tane is the founder of Polylane. He previously founded Baselime, observability for the future of the cloud, which Cloudflare acquired. At Cloudflare he built and led the Workers observability team.

Related

Nobody should be on-call. Polylane watches your infra, finds what broke, and writes the fix.

Get started for free