Skip to content

chore: reduce CI test log noise by suppressing known-structural error loggers#22905

Merged
jason-p-pickering merged 20 commits into
masterfrom
quiet-ci-test-logs
May 13, 2026
Merged

chore: reduce CI test log noise by suppressing known-structural error loggers#22905
jason-p-pickering merged 20 commits into
masterfrom
quiet-ci-test-logs

Conversation

@jason-p-pickering

@jason-p-pickering jason-p-pickering commented Feb 3, 2026

Copy link
Copy Markdown
Contributor

Adds targeted logger suppressions in test log4j2 configs to reduce noise in CI run output from loggers that always fire on passing tests due to structural facts of the test environment. These are not actual test failures, but rather errors which are emitted to the logs as part of the test themselves.

Why these loggers?
Each suppressed logger fires because of a structural fact of the test environment, not a real problem:

  • BundledAppManager / DefaultSystemService : apps-bundle.json and
    build.properties are absent from the test classpath by design.

  • JdbcAnalyticsManager / DataHandler : analytics tables do not exist in the
    test database; tests that exercise ADEX code paths produce expected
    "relation does not exist" SQL errors.

  • SqlExceptionHelper : tests deliberately trigger uniqueness constraint
    violations to verify rejection behaviour; Hibernate logs every such exception
    at ERROR.

  • scheduling / sms / route / dxf2.gml / rules.engine etc. : tests
    that verify error paths cause the application to log at ERROR. The tests pass;
    the errors are the test's intent.

  • CrudControllerAdvice : the global @ControllerAdvice logs at ERROR when
    tests deliberately exercise controller error paths (e.g. SQL injection tests).
    Scoped to this class only, not the whole controller package.

Do real test failures still appear?
Yes. JUnit assertion failures and unexpected exceptions surface through Surefire
reporting, not through log4j2. The suppressions only affect the application's own
log.error(...) calls, not the test framework's failure output.

Toggling verbosity

  • In CI: add the ci-verbose label to the PR to restore full error output.
  • Locally: TEST_LOG_SUPPRESSED_LEVEL=error mvn test ...

On a passing integration test run (PR #23855, run 25673244856), this eliminates the vast majority of these "expected" errors which are nothing other than an artifact of the testing scenario.

Adds package-level log suppressions in test log4j2 configs to hide
expected ERROR messages that fire during tests (e.g. missing
build.properties, apps-bundle.json, and tests exercising error paths).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@jason-p-pickering
jason-p-pickering marked this pull request as draft February 3, 2026 16:59
jason-p-pickering and others added 7 commits February 3, 2026 18:05
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use TEST_LOG_SUPPRESSED_LEVEL env var to control log4j2 suppression of
expected test errors. Defaults to fatal (suppressed). The ci-verbose
PR label or debug workflow dispatch sets it to error (visible).

Also removes broken log.fatal() call (SLF4J has no fatal level).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
OFF is the canonical log4j2 level for disabling a logger, making the
intent self-documenting rather than relying on the SLF4J/log4j2 level
mismatch where FATAL works only because SLF4J has no fatal() method.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@sonarqubecloud

sonarqubecloud Bot commented Feb 9, 2026

Copy link
Copy Markdown

@jason-p-pickering
jason-p-pickering marked this pull request as ready for review March 18, 2026 20:18
@jason-p-pickering
jason-p-pickering requested a review from a team as a code owner March 18, 2026 20:18
@sonarqubecloud

Copy link
Copy Markdown

@jason-p-pickering
jason-p-pickering requested review from a team and netroms May 11, 2026 07:10
@david-mackessy

Copy link
Copy Markdown
Contributor

not sure about this, some thoughts:

  • if default is hiding errors, probably not good
  • what criteria has been used to select those packages?
    • risk of hiding actual errors not expected in tests?
  • are there now no errors at all in the test logs?
    • if no, why leave some?
    • if yes, does this now need to be maintained and updated frequently?

@jason-p-pickering

jason-p-pickering commented May 11, 2026

Copy link
Copy Markdown
Contributor Author

@david-mackessy good questions, let me address each:

Default logging level is not changed. The root logger is already set to error for the console appender. I have added narrow suppressions for a small number of specific loggers on top of that baseline.

Criteria for selection. : Each suppressed logger fires on passing tests due to structural facts of the test environment, not from real problems, e.g.:

  • JdbcAnalyticsManager / DataHandler / AnalyticsUtils; Analytics aggregate tables (analytics, analytics_*) do not exist in the test container by design. Any test that exercises the ADEX code path will hit "relation does not exist" SQL errors. The tests pass; the errors are noise.

  • SqlExceptionHelper: tests that verify uniqueness constraints intentionally trigger duplicate-key violations. Hibernate logs every such exception at ERROR regardless. Again, expected noise, but still pollutes the logs.

Risk of hiding real errors. The suppressions use FATAL, not OFF, so genuinely catastrophic database errors still surface. The loggers are specified at class level (not broad packages), which keeps the blast radius small.

Are there still errors in the logs? Yes. The root level is still at "error". Only these specific loggers are suppressed. Real errors from real failures will still appear (i.e. actual test failures).

Maintenance burden. Well, its a good question, but my stance on this is once we get the CI logs down to a reasonable level, it will be much easier for developers to see if they are spewing expected errors in the logs and then either a) Add another suppression or B) Handle the echoing of expected errors in the test itself.

The goal here is purely signal-to-noise: when a test actually fails in CI, the error that matters should be easy to find rather than buried in thousands of lines of expected noise. See https://github.com/dhis2/dhis2-core/actions/runs/25660816457/job/75320626269?pr=22905 for an example of what the logs look like when the unit tests pass (but the expected errors from tests are suppressed).

@david-mackessy

Copy link
Copy Markdown
Contributor

I get the intention and frustration at trying to find meaningful, relatable error messages for tests 👍 (less noise the better).
The PR title and description are a bit misleading though unless I'm misunderstanding it? It will not only suppress expected test errors (which is good), it will suppress all errors within that configured scope (errors that might not even fail a test but could be hinting at issues somewhere).
I've tagged Analytics and Tracker team as some of their tests/code are impacted by the change.
Happy for others to approve or chime in also.

@david-mackessy
david-mackessy requested review from a team May 11, 2026 12:34
@jason-p-pickering jason-p-pickering changed the title chore: suppress expected test errors from CI console output chore: reduce CI test log noise by suppressing known-structural error loggers May 11, 2026
@jason-p-pickering

jason-p-pickering commented May 11, 2026

Copy link
Copy Markdown
Contributor Author

@david-mackessy Fair point on the title. "Expected test errors" implied we were filtering specific known messages, when the mechanism is actually logger-level suppression. Updated the title and description to reflect that accurately.

On the "suppress all errors in scope" concern: each logger was chosen because in the test context its error output is structurally unavoidable (missing classpath resources, tests deliberately hitting error paths, etc). On a passing integration run (PR #23855) these produce 84 ERROR lines with multi-line stack traces. All noise,zero signal.

For the webapi.controller concern specifically: narrowed from the whole package to CrudControllerAdvice only, which is the one logger that actually fires (from SQL injection tests hitting the global exception handler). The rest of the controller package remains visible.

Real test failures are not affected . JUnit/Surefire reports those independently of log4j2 config.

One thing that may not be obvious: the suppression applies locally too, not just in CI. TEST_LOG_SUPPRESSED_LEVEL=error mvn test ... restores full output (previous behavior) without needing a GitHub run.

@sonarqubecloud

Copy link
Copy Markdown

@jason-p-pickering
jason-p-pickering merged commit 98e83c6 into master May 13, 2026
18 checks passed
@jason-p-pickering
jason-p-pickering deleted the quiet-ci-test-logs branch May 13, 2026 16:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants