DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Newsletter
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Exploring Cloud-Based Testing With the Elastic Execution Grid
  • Why We Still Struggle With Manual Test Execution in 2025
  • Is It Okay To Stop Running Your Tests After the First Failure?
  • Error Handling Inside Kumologica Subflow

Trending

  • The SDLC After AI
  • Spring Security JWT Validation Best Practices
  • Beyond Manual Mapping: AI-Orchestrated Governance for Hybrid Cloud
  • Idempotency Under Concurrency: Why Your Application Check Isn't the Last Line of Defense

Draft This article hasn't been published, so readers can't see it.

In moderation This article is waiting for review, so readers can't see it yet.

Deleted This article has been deleted, so readers can't see it.

  1. DZone
  2. Testing, Deployment, and Maintenance
  3. Testing, Tools, and Frameworks
  4. Diagnosing Test Failures at Scale: Correlating Execution Evidence Across Ephemeral Runners

Diagnosing Test Failures at Scale: Correlating Execution Evidence Across Ephemeral Runners

Learn how to capture test-failure artifacts, correlate distributed logs, preserve evidence of retries, and securely store CI/CD diagnostics.

By 
Anil Reddy Putlacheru
·
Nov. 06, 26 · Analysis
Post on X (opens in a new tab) Share on LinkedIn (opens in a new tab)
47 Views

Join the DZone community Save articles, join the discussion, and download Refcards and Trend Reports with a free account.

Join for free Already a member? Log in

Intermittent test failures in a distributed CI/CD environment can slow down development and make failures harder to investigate. When an automated test suite fails on a local dev or staging machine, an engineer can view local logs, attach a debugger, or manually follow state changes. But when tests execute in a temporary container, AWS Lambda, or an agentless runner, post-mortem debugging is significantly more involved.

In a traditional setup, a failed build only shows a generic assertion error (504, ElementNotFoundException, etc.). An engineer may need to manually sift through CloudWatch log groups for an approximate timestamp, query Splunk, or reason about state changes across downstream microservices to understand why the test failed. Once the log retention period expires or if the execution did not have a shared, identifiable correlation ID, the engineer has no further information about the cause of the failure.

An enterprise test framework should save the test results and logs before the test runner shuts down.

This article explores how to capture rich failure artifacts, stitch distributed logs (via correlation ID), and preserve retry states per-attempt.

1. Capturing Evidence Before the Ephemeral Runner Exits

Capturing telemetry on failure seems trivial, but ephemeral environments introduce special exit modes which may skip cleanup routines:

  1. Assertion failures: The framework captures the exception; standard teardown hooks execute
  2. Execution timeouts: The process or handler is externally aborted; finalizers may be skipped
  3. Hard kills (OOM/SIGKILL): The underlying container or VM is killed; no in-process code executed

Your test harness needs to capture evidence in gracefully terminated failure modes and before hard timeouts

Managing Runner Lifecycles

  • AWS Lambda: A test runner deployed as a Lambda must check context.get_remaining_time_in_millis(). If it falls below a certain threshold (2,000 ms), it must terminate early, flush diagnostic buffers, and write metadata to S3. Your ephemeral /tmp storage defaults to 512 MB; holding large trace files or video recordings in memory would lead to out-of-memory (OOM) errors, so artifacts should be streamed directly to object store.
  • AWS Fargate/ECS: When a task is stopped, it will receive a SIGTERM followed by a SIGKILL after a configured stopTimeout (30 seconds). Signal handlers in your test suite must capture SIGTERM and begin uploading artifacts.
  • CI Build Agents (Bamboo/Jenkins): Agent-side logs are ephemeral and will be deleted when the agent's workspace is cleaned. Remote execution tasks should push telemetry to central storage (like AWS S3) and pass back the stable HTTP reference, not the local agent's file path.

Pytest Hook Architecture

In a Python setup, pairing a pytest_runtest_makereport hookwrapper with an autouse fixture guarantees that failure status is captured and attached to the test item before final teardown.

Python
 
# conftest.py
import pytest

@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    # Execute all other hooks to obtain the report object
    outcome = yield
    rep = outcome.get_result()
    
    # Store the report on the item for fixture access
    setattr(item, f"rep_{rep.when}", rep)

@pytest.fixture(autouse=True)
def failure_evidence_collector(request):
    yield
    # Code below executes during fixture teardown
    rep_call = getattr(request.node, "rep_call", None)
    
    if rep_call and rep_call.failed:
        execution_id = request.config.getoption("--execution-id", default="local")
        attempt = getattr(request.node, "execution_count", 1)
        
        # Extract environment details captured at run start
        env_metadata = {
            "test_name": request.node.name,
            "stage": getattr(request.node, "current_stage", "unknown"),
            "commit_hash": os.getenv("GIT_COMMIT", "HEAD"),
            "aws_request_id": getattr(request.node, "aws_request_id", None)
        }
        
        upload_failure_artifacts(execution_id, attempt, env_metadata)


2. Run IDs and Cross-Service Correlation

Although capturing client-side logs is easy, if an integration test makes an HTTP call to a microservice architecture, the underlying defect may be found in downstream service logs. Without a unified identifier, correlating a client-side test failure with backend anomalies requires time-consuming manual timestamp matching.

Propagating the Correlation Context

To stitch frontend execution to backend telemetry, every outgoing request from the test runner must inject a unique tracking ID into transport headers.

  • HTTP headers: Standardize on custom headers such as X-Correlation-ID or X-Run-ID. Initialize this ID once on the test client's session object so that every HTTP call automatically inherits it
  • OpenTelemetry baggage: The standard W3C Trace Context (traceparent) requires a rigid 32-hex trace ID and 16-hex span ID format. CI build keys (e.g., PROJECT-JOB1-102) will not fit in standard trace fields. Instead, carry build context in the W3C baggage header (baggage: ci_build_key=PROJECT-JOB1-102,test_id=test_user_checkout)
  • Async boundaries (SQS/SNS): HTTP headers do not automatically transition across message queues. When a test triggers an async workflow via AWS SQS, inject correlation metadata in the Message attributes. If there is an SNS in front of SQS, enable raw message Delivery in order to prevent SNS from wrapping the message payload in a JSON envelope (SQS attributes would need to be explicitly parsed by the consumer).
  • Unowned platform services: Managed components like AWS API Gateway or Application Load Balancers (ALBs) will not process your application headers, but they will include their own tracking keys. API Gateway returns x-amzn-requestid in every response header, while ALBs preserve incoming X-Amzn-Trace-Id headers and record them in access logs.

When an assertion fails, the test framework outputs a pre-formatted log query targeting the exact correlation key, enabling one-click diagnostic retrieval in CloudWatch Logs Insights or Splunk.

MS SQL
 
-- CloudWatch Logs Insights Query
fields @timestamp, @message, service_name, log_level, error.type
| filter correlation_id = "BUILD-8492-test_checkout_flow-att1"
| sort @timestamp asc
| limit 500


Fallback strategy: For legacy third-party microservices that do not accept or log correlation headers, configure the evidence collector to generate a time window query that is constrained to the specific UTC start and end timestamps of that specific test case execution.

3. Retries Kept Per Attempt

Retries as implemented, say, by pytest-rerunfailures are often used to mitigate the risk of infrastructure-related failures, but such practice has its costs: retrying all tests that failed on the first attempt may mask intermittent bugs in the application under test. If a test fails on the first attempt but passes on the retry, the build will be marked as successful, even though the intermittent bug may still be present.

If a test failed on attempt 1 due to a race condition but passed on attempt 2 because a resource freed up, overwriting attempt 1's evidence erases the bug's trace. Standard JUnit XML output often only records the final execution state, hiding the initial failure.

Never write diagnostic files to a static path (e.g., s3://bucket/logs/test_checkout.log). Telemetry storage paths must include both the run identifier and the attempt counter.

PowerShell
 
s3://ci-test-evidence-bucket/
└── build-key=PROJECT-8492/
    └── test_checkout_user/
        ├── attempt-1/
        │   ├── metadata.json
        │   ├── client_session.log
        │   └── failure_screenshot.png
        └── attempt-2/
            ├── metadata.json
            └── client_session.log


Once per-attempt isolation is implemented, diff analysis can be performed:

JSON
 
// S3 Path: build-key=PROJECT-8492/test_checkout_user/attempt-1/metadata.json
{
  "test_name": "test_checkout_user",
  "execution_id": "PROJECT-8492",
  "attempt": 1,
  "status": "FAILED",
  "duration_seconds": 12.4,
  "http_status_code": 503,
  "target_host": "ip-10-0-4-12.ec2.internal",
  "failure_reason": "ServiceUnavailable: Database connection pool exhausted"
}


If attempt 1 fails with a 503 Service Unavailable from a particular host, and attempt 2 succeeds against a different host, the delta immediately indicates a node-specific configuration drift or pool exhaustion, not a code-level assertion flaw.

4. Keeping Artifacts Safe, Small, and Reachable

Collecting unlimited telemetry rapidly leads to two major issues: credential leakage and excessive storage costs.

Automated Redaction at Write Time

Diagnostic artifacts (typically HTTP request/response dumps) often contain authorization tokens, cookies, or personal data. They must be redacted from memory before writing to disk:

  • Strip headers that match Authorization, Cookie, X-Api-Key, or Set-Cookie.
  • Remove signature tokens from URL parameters. Note that AWS S3 Presigned URLs act as credentials, and logging them directly would accidentally expose an ability to read/write to any S3 bucket the application has access to.

Storage Optimization and Retention

To keep storage costs at enterprise scale under control, enforce explicit lifecycle and collection rules:

  • Selective retention: only store full-payload logs and screenshots if a test failed. Store only a short tail of a log output for passing tests.
  • S3 Lifecycle Rules: configure S3 bucket lifecycle rules to delete failed test artifacts 30 days after creation, and passing test run artifacts 3 days after creation. It should be configured with either prefix- or tag-based policies.
  • Do not put raw S3 presigned links inside JUnit XML or CI failure logs. Create links using a stable internal proxy/route that is capable of signing requests dynamically upon access. Presigned links created with temporary IAM role credentials will become invalid as soon as the web server process that issued them reloads. It would make sense to have it turned into a dead link if an engineer looks at a CI failure several hours (or days) after its occurrence.

Failure Summary Surface

Inject the direct artifact link and correlation ID straight into the framework's assertion exception message. Because virtually all CI engines parse and display the top-level exception string in build summaries, engineers can jump directly to the evidence:

Python
 
# Custom assertion failure formatter
def raise_with_evidence(message: str, correlation_id: str, evidence_url: str):
    formatted_msg = (
        f"{message}\n"
        f"--------------------------------------------------\n"
        f"Correlation ID : {correlation_id}\n"
        f"Evidence Bundle: {evidence_url}\n"
        f"--------------------------------------------------"
    )
    raise AssertionError(formatted_msg)


Architectural Limitations to Consider

While evidence collection is made dramatically easier by the structured nature of the solution, there are a few boundary conditions that should be considered:

  • Hard OS/platform kills: An AWS Lambda function reaching its 15-minute hard limit or worker node power loss will prevent in-process code execution. Secondary monitoring (container stdout monitoring via CloudWatch/Datadog) becomes a necessary alternative.
  • Unowned external third parties: SaaS downstream APIs or gateway services will not instrument custom correlation IDs. In these scenarios, evidence would be strictly client-side HTTP request timestamp logging.
  • Redaction leakage: Regex sanitization could miss non-standard secret key formats in unstructured response bodies.

References

  1. W3C Recommendation: Trace Context - W3C Recommendation (2020).
  2. Luo, Q., Hariri, F., Eloussi, L., & Marinov, D.: "An Empirical Analysis of Flaky Tests" (ACM SIGSOFT International Symposium on Foundations of Software Engineering, 2014).
  3. Google Testing Blog: "Flaky Tests at Google and How We Mitigate Them" (2016).
  4. AWS Documentation: AWS Lambda Context Object in Python & ECS Task Lifecycle.
  5. Pytest Documentation: Writing and using plugins & pytest-rerunfailures specification.
Execution (computing) Testing

Opinions expressed by DZone contributors are their own.

Related

  • Exploring Cloud-Based Testing With the Elastic Execution Grid
  • Why We Still Struggle With Manual Test Execution in 2025
  • Is It Okay To Stop Running Your Tests After the First Failure?
  • Error Handling Inside Kumologica Subflow

Partner Resources

×

Comments

Loading comments

The comments didn't load

Please try again in a moment.

Likes

Loading

The next likes didn't load. Please try again.

The likes didn't load

Please try again in a moment.

See who liked this

Log in or join DZone for free to see who liked this article.

Log in Join for free
  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook