Skip to content

Logging standardization: standardize logger declarations, parameterize log calls, fix stack traces, replace println, add outcome logging - #137

Merged
jordanpadams merged 21 commits into
mainfrom
super_log
Aug 27, 2026
Merged

Logging standardization: standardize logger declarations, parameterize log calls, fix stack traces, replace println, add outcome logging#137
jordanpadams merged 21 commits into
mainfrom
super_log

Conversation

@al-niessner

@al-niessner al-niessner commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Comprehensive logging standardization across the common, harvest, and manager modules, addressing a backlog of logging issues and adding operator-facing outcome logging:

  • Consolidated Log4jConfigurator from harvest + manager into common with a shared LogLevels class (includes SUMMARY, LABEL_SUCCESS, LABEL_FAILURE, LABEL_SKIPPED levels)
  • Per-product outcome logging (SUCCESS / FAILED / SKIPPED / MATCHED) emitted per LIDVID so operators can grep logs by outcome
  • Fixed exception logging to always pass the exception object so full stack traces are preserved
  • Replaced System.out.println calls in production code with proper log.warn/log.debug calls
  • Standardized logger field declarations across all classes to private static final Logger log = LogManager.getLogger(ClassName.class); eliminates per-instance loggers and local-variable loggers
  • Converted string-concatenation log calls to Log4j 2 parameterized form ("msg: {}", var) across all three modules
  • Aligned slf4j-nop version in parent pom so both modules use the same version
  • Removed dead code: legacy Elasticsearch (es/) client classes, UpdateAltIdsCmd, and related test infrastructure

AI Assistance Disclosure

  • No AI assistance used
  • AI used for light assistance
  • AI used for moderate content generation
  • AI generated substantial portions of this code

Estimated % of code influenced by AI: 40 %

Test Data and/or Report

Related Issues

Fixes #126
Fixes #127
Fixes #128
Fixes #129
Fixes #130
Fixes #131
Fixes #132

Reviewer Checklist

Documentation and PR Content

  • Documentation: README, Wiki, or inline documentation updated to reflect these changes.
  • Issue Traceability: The PR is linked to a valid GitHub Issue
  • PR Title: The PR title is "user-friendly" clearly identifying what is being fixed or the new feature being added.

Security & Quality

  • SonarCloud: Confirmed no new High or Critical security findings.
  • Secrets Detection: Verified no sensitive information is exposed.
  • Code Quality: Code follows organization style guidelines.

Testing & Validation

  • Test Accuracy: Verified test data is accurate and representative.
  • Coverage: Automated tests cover new logic and edge cases.
  • Local Verification: Successfully built and ran the changes locally.

Maintenance

  • Backward Compatibility: Confirmed no breaking changes (or clearly documented).

@nutjob4life nutjob4life left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm getting a compilation error when I try this patch:

mirasol 283 % cd /tmp
/tmp
mirasol 284 % git clone --branch super_log --quiet git@github.com:NASA-PDS/registry-loader.git
mirasol 285 % cd registry-loader/
/tmp/registry-loader
mirasol 286 % mvn --quiet clean test
                          null  -->  null
                                -->  null
          2013-10-24T00:00:00Z  -->  2013-10-24T00:00:00Z
      2013-10-24T00:49:37.457Z  -->  2013-10-24T00:49:37.457Z
         2013-302T01:02:03.123  -->  2013-10-29T01:02:03.123Z
        2013-302T01:02:03.123Z  -->  2013-10-29T01:02:03.123Z
                   2016-09-08Z  -->  2016-09-08T00:00:00Z
                    2013-03-02  -->  2013-03-02T00:00:00Z
                       2013-12  -->  2013-12-01T00:00:00Z
                          2013  -->  2013-01-01T00:00:00Z
                         2015Z  -->  2015-01-01T00:00:00Z
                      2013-001  -->  2013-01-01T00:00:00Z
[ERROR] COMPILATION ERROR : 
[ERROR] /private/tmp/registry-loader/manager/src/main/java/gov/nasa/pds/registry/mgr/dao/RegistryDao.java:[61,42] cannot find symbol
  symbol:   method altIds()
  location: interface gov.nasa.pds.registry.common.Response.Search
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project registry-manager: Compilation failure
[ERROR] /private/tmp/registry-loader/manager/src/main/java/gov/nasa/pds/registry/mgr/dao/RegistryDao.java:[61,42] cannot find symbol
[ERROR]   symbol:   method altIds()
[ERROR]   location: interface gov.nasa.pds.registry.common.Response.Search
[ERROR] 
[ERROR] -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
[ERROR] 
[ERROR] After correcting the problems, you can resume the build with the command
[ERROR]   mvn <args> -rf :registry-manager
48.925u 7.000s 0:29.19 191.5%	0+0k 0+0io 16pf+0w

Maybe I have an out-of-date dependency?

@al-niessner

Copy link
Copy Markdown
Contributor Author

@nutjob4life

Well, the build works!

@jordanpadams jordanpadams left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Integration tests fail which is expected at this time

@jordanpadams

jordanpadams commented Jul 29, 2026

Copy link
Copy Markdown
Member

Thanks for the work here — the direction is right. A few gaps and one bug to address before merging:


#127 — Bug: log.error format string drops the error message

DataLoader.java line 352:

log.error("LIDVID = {}, Message = ", sanitizedLidvid, sanitizedMessage);

The format string has only one {} but two parameters. Log4j will treat sanitizedMessage as the Throwable cause, so the actual error message is silently dropped from the log output. Should be:

log.error("LIDVID = {}, Message = {}", sanitizedLidvid, sanitizedMessage);

#129 — Two System.out.println sites still remain

  • common/.../util/xml/XmlNamespaces.java — the entire debug() method still uses System.out.println throughout (lines 28–56). Per the issue AC, debug utility methods should use log.debug(...).
  • common/.../es/dao/dd/LddVersions.java lines 81–82 — System.out.println("Last date: ...") and System.out.println("Files: ...") are still present.

#132slf4j-nop version is aligned but not centralized

Both modules are now on 2.0.18, which satisfies the "same version" part. However, the issue AC specifically requires the version to be managed in the parent pom.xml <dependencyManagement> section so future updates only need one change. That step wasn't done.


#126 — Per-product outcome logging incomplete

Three gaps against the acceptance criteria:

  1. No LABEL_SUCCESS in the happy path: When resp.errors() is false (all documents accepted in a bulk call), todo.clear() is called but no per-product outcome line is emitted. Operators will see silence for successfully loaded products on that code path.

  2. Log format doesn't match the AC: The issue requires a grep-friendly format, e.g.:

    [SUCCESS] lid=urn:nasa:pds:... vid=1.0
    

    Currently the lidvid string is logged as the bare message. The custom level name appears as the log level field in the appender pattern, not as a structured prefix in the message body. Operators can't grep for [SUCCESS] or lid=/vid= tokens.

  3. Manager module not instrumented: The AC says "both harvest and manager emit outcome lines." Only harvest has been wired up.

🤖 Generated with help of Claude Code

@jordanpadams jordanpadams left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment

@nutjob4life

Copy link
Copy Markdown
Member

@al-niessner wrote:

Well, the build works!

Woo-hoo!
cat-thumbs-up

@al-niessner

Copy link
Copy Markdown
Contributor Author

@jordanpadams

Hey Mr Ai,

Yes to #129. See note on ticket. It states these are used by test to see private parts of class. Good as is. If tests are unused/unwanted that is something different.

#132 read the title only.
#127 ooops.
#126 ooops.

@al-niessner

Copy link
Copy Markdown
Contributor Author

Still builds!

…rized calls

- Fix #130: Convert all logger fields to private static final Logger log =
  LogManager.getLogger(ClassName.class) across common, harvest, and manager.
  Eliminates per-instance logger objects, local-variable loggers, and LOG/logger
  naming inconsistencies.
- Fix #131: Replace string-concatenation log calls ("msg: " + var) with
  parameterized form ("msg: {}", var) across all three modules.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@jordanpadams jordanpadams changed the title Super log cleanup Logging standardization: standardize logger declarations, parameterize log calls, fix stack traces, replace println, add outcome logging Aug 12, 2026
@jordanpadams

Copy link
Copy Markdown
Member

@al-niessner @nutjob4life rolled in additional updates for #130 and #131

@nutjob4life
nutjob4life self-requested a review August 12, 2026 17:41

@nutjob4life nutjob4life left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An artificial intelligence assisted with the review (this is was a huge PR, 81 files), and we came up with a couple issues (see comments below).

log.log(LogUtils.LEVEL_SUMMARY, "Loaded files: " + processedCount);
log.log(LogUtils.LEVEL_SUMMARY, "Label Failure: {} (database rejected label/product)", counter.failedFileCount);
log.log(LogUtils.LEVEL_SUMMARY, "Label Ignored: {} (files that are not readable as label/product)", counter.ignoredFileCount);
log.log(LogUtils.LEVEL_SUMMARY, "Label Matched: {} (batch rejected as duplicate lidvid)", counter.matchedFileCount);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this will always say:

Label Matched: 0 (batch rejected as duplicate lidvid)

Because DataLoader.loadBatch() now takes Set<String> matchedIds and MetadataWriter expects it to drive:

counter.matchedFileCount += matchedIds.size();

But when the code encounters an HTTP code 409 (which, by the way, could be replaced with HttpURLConnection.HTTP_CONFLICT) , it only does:

log.log(LogLevels.LABEL_MATCHED, sanitizedLidvid);
todo.remove(asKey(item));

There is no counterpart code that does something like

if (matchedIds != null) matchedIds.add(item.id());

so the new summary ends up always being zero.

If that logic makes sense, then I'd consider this a blocker.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You want matched to always be 0. There is a race condition from latency with AOSS or there is the case of two harvests over same data at same time problem. harvest checks and thinks lidvid needs to be inserted and uploads it. Mean while already in place batch or other process has already injected it so this harvest gets an error from opensearch that lidvid matches. hence the count.

Scoff if you like. The check would not be there if we had not tripped over at least once. So either/both of these conditions exist in the real world.

|| (nonRegisteredIds != null && nonRegisteredIds.contains(item.lidvid)
&& !failedIds.contains(item.lidvid))) {
counter.prodCounters.inc(item.prodClass);
log.log(LogLevels.LABEL_SUCCESS, item.lidvid);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this handles the !failedIds.contains(item.lidvid) case, where is the !matchedIds.contains(item.lidvid) case? Even after fixing the "blocker" described above for Label Matched: 0, a duplicate rejected with HTTP CONFLICT ("409"), can still fall through this code and get

LABEL MATCHED urn:...
LABEL SUCCESS urn:...

and increment the product-success counter.

Depending on the intended semantics of nonRegisteredIds, this should be

if (nonRegisteredIds == null
    || (nonRegisteredIds.contains(item.lidvid)
        && !failedIds.contains(item.lidvid)
        && !matchedIds.contains(item.lidvid))) {

More generally, there are 5 states, right?

  1. FAILURE
  2. IGNORED
  3. MATCHED
  4. SKIPPED
  5. SUCCESS

And they're all mutually exclusive. The implementation should enforce that as an invariant rather than deriving each counter independently.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are 5 states but your bot needs to look around to see them all. Since we want clear counting, ignored, matched, and skipped are all similar quasi pass or fail. See summary comments to their difference. All 5 states are accounted for but lumping them would contravene the clarity requirement/request.

Comment on lines +7 to +11
public static final Level LABEL_FAILURE = Level.forName("LABEL FAILURE", 121); // batch did not accept lidvid
public static final Level LABEL_IGNORED = Level.forName("LABEL IGNORED", 122); // file could not be read as a label
public static final Level LABEL_MATCHED = Level.forName("LABEL MATCHED", 123); // batch rejected as duplicate lidvid
public static final Level LABEL_SKIPPED = Level.forName("LABEL SKIPPED", 124); // the lidvid was already in database
public static final Level LABEL_SUCCESS = Level.forName("LABEL SUCCESS", 125); // batch accepted lidvid

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These levels are 121…125, but the Log4j standard levels are roughly

FATAL 100
ERROR 200
WARN 300
INFO 400
DEBUG 500
TRACE 600

So all the LABEL_* levels aren't fatal, but are more severe than ERROR.

If someone runs with

-v ERROR

they'll still get

LABEL SUCCESS
LABEL SKIPPED
…
LABEL FAILURE

and likewith with WARN. If this is deliberate, great, and ignore this comment.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@al-niessner ☝️

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just expanded what was there, but think the idea is that 100 makes them non-filtered. They will still show with -v ERROR because it works on being less than 200. I guess you could turn them off -v FATAL. Anyway, just extended what was there. If the user does -v FATAL then they deserve not see the summary.

@jordanpadams

Copy link
Copy Markdown
Member

@al-niessner I hate to be a pain here, but can you attach what an example output log would actually look like so I can verify these meet the requirement expectations? Or provide examples of each? It may just be easier to update the end user documentation at https://github.com/NASA-PDS/registry regarding logging

@sonarqubecloud

Copy link
Copy Markdown

@al-niessner

Copy link
Copy Markdown
Contributor Author

@al-niessner I hate to be a pain here, but can you attach what an example output log would actually look like so I can verify these meet the requirement expectations? Or provide examples of each? It may just be easier to update the end user documentation at https://github.com/NASA-PDS/registry regarding logging

Happy to update, but what files actually need editing? Not sure how things are built and did not see anything specific in root readme as I scrolled through it in 5 seconds nor a file that said set-user-expectation.edit.me or logging.edit.me or anything else.

@jordanpadams

Copy link
Copy Markdown
Member

@al-niessner nothing needs updating, I just don't know what an output log would look like from a user perspective when harvest is run. I need that to determine if this is what we want.

@al-niessner

Copy link
Copy Markdown
Contributor Author

@jordanpadams

Did a small run so here is the full log file:

2026-08-26T16:48:10.644815353Z main SUMMARY Reading configuration from /home/niessner/Projects/PDS/registry-ref-data/custom-datasets/naif3.mt.xml
2026-08-26T16:48:10.838893086Z main SUMMARY Output directory: /tmp/harvest/out
2026-08-26T16:48:11.552304964Z main SUMMARY Connection: gov.nasa.pds.registry.common.connection.UseOpensearchSDK2@3c017078
2026-08-26T16:49:02.349145600Z main LABEL SUCCESS urn:nasa:pds:mars2020.spice::1.0
2026-08-26T16:49:02.349443177Z main LABEL SUCCESS urn:nasa:pds:mars2020.spice::2.0
2026-08-26T16:49:02.349760302Z main LABEL SUCCESS urn:nasa:pds:mars2020.spice::3.0
2026-08-26T16:49:02.350021671Z main LABEL SUCCESS urn:nasa:pds:mars2020.spice:document::1.0
2026-08-26T16:49:02.350210314Z main LABEL SUCCESS urn:nasa:pds:mars2020.spice:spice_kernels::1.0
2026-08-26T16:49:02.350506902Z main LABEL SUCCESS urn:nasa:pds:mars2020.spice:spice_kernels::3.0
2026-08-26T16:49:02.350798163Z main LABEL SUCCESS urn:nasa:pds:mars2020.spice:spice_kernels::2.0
2026-08-26T16:49:02.351012534Z main LABEL SUCCESS urn:nasa:pds:mars2020.spice:document:spiceds::1.0
2026-08-26T16:49:02.351336447Z main LABEL SUCCESS urn:nasa:pds:mars2020.spice:spice_kernels:ck_m2020_surf_rover_tlm_0000_0089_v1.bc::1.0
2026-08-26T16:49:02.351616514Z main LABEL SUCCESS urn:nasa:pds:mars2020.spice:spice_kernels:ck_m2020_surf_rover_tlm_0089_0179_v1.bc::1.0
2026-08-26T16:49:02.351799242Z main LABEL SUCCESS urn:nasa:pds:mars2020.spice:spice_kernels:ck_m2020_surf_rover_tlm_0179_0299_v1.bc::1.0
2026-08-26T16:49:02.351960533Z main LABEL SUCCESS urn:nasa:pds:mars2020.spice:spice_kernels:mk_m2020::1.0
2026-08-26T16:49:02.352145Z main LABEL SUCCESS urn:nasa:pds:mars2020.spice:spice_kernels:mk_m2020::2.0
2026-08-26T16:49:02.352317994Z main LABEL SUCCESS urn:nasa:pds:mars2020.spice:spice_kernels:mk_m2020::3.0
2026-08-26T16:49:02.352488589Z main LABEL SUCCESS urn:nasa:pds:mars2020.spice:spice_kernels:sclk_m2020_168_sclkscet_00007.tsc::1.0
2026-08-26T16:49:02.352663429Z main LABEL SUCCESS urn:nasa:pds:mars2020.spice:spice_kernels:sclk_m2020_168_sclkscet_refit_v01.tsc::1.0
2026-08-26T16:49:02.352848670Z main LABEL SUCCESS urn:nasa:pds:mars2020.spice:spice_kernels:sclk_m2020_168_sclkscet_refit_v02.tsc::1.0
2026-08-26T16:49:02.353054647Z main LABEL SUCCESS urn:nasa:pds:mars2020.spice:spice_kernels:sclk_m2020_168_sclkscet_refit_v03.tsc::1.0
2026-08-26T16:49:02.353232979Z main LABEL SUCCESS urn:nasa:pds:mars2020.spice:spice_kernels:ck_m2020_surf_ra_tlmres_0000_0089_v1.bc::1.0
2026-08-26T16:49:02.353384761Z main LABEL SUCCESS urn:nasa:pds:mars2020.spice:spice_kernels:ck_m2020_surf_ra_tlmres_0089_0179_v1.bc::1.0
2026-08-26T16:49:02.353546099Z main LABEL SUCCESS urn:nasa:pds:mars2020.spice:spice_kernels:ck_m2020_surf_ra_tlmres_0179_0299_v1.bc::1.0
2026-08-26T16:49:02.353804730Z main SUMMARY Summary:
2026-08-26T16:49:02.353998564Z main SUMMARY Label Failure: 0 (database rejected label/product)
2026-08-26T16:49:02.354157598Z main SUMMARY Label Ignored: 0 (files that are not readable as label/product)
2026-08-26T16:49:02.354306748Z main SUMMARY Label Matched: 0 (batch rejected as duplicate lidvid)
2026-08-26T16:49:02.354477025Z main SUMMARY Label Skipped: 0 (lidvids that already existed in database)
2026-08-26T16:49:02.354632606Z main SUMMARY Label Success: 21 (database accepted label/product)
2026-08-26T16:49:02.354891025Z main SUMMARY   Product_Bundle: 3
2026-08-26T16:49:02.355022655Z main SUMMARY   Product_Collection: 4
2026-08-26T16:49:02.355158856Z main SUMMARY   Product_Document: 1
2026-08-26T16:49:02.355284220Z main SUMMARY   Product_SPICE_Kernel: 13
2026-08-26T16:49:02.355405307Z main SUMMARY Package ID: 5b1064e3-dbdf-46c6-bce4-3585a823ac56

Are Product_Document counted? Looks like not. Maybe there are 2 versions of something so success is 1 less than total?

@jordanpadams

jordanpadams commented Aug 26, 2026

Copy link
Copy Markdown
Member

@al-niessner all labels (products) should be counted. the product type is irrelevant.

@al-niessner

Copy link
Copy Markdown
Contributor Author

@jordanpadams

Well, you can make this ticket last indefinitely but having me chase down the count discrepancy again, or call it done and create a new ticket for counting. Fixing the counting is a repetitive chore. I would prefer to do it with the test harness rather than ad-hoc in this ticket though.

@al-niessner

Copy link
Copy Markdown
Contributor Author

@jordanpadams I count 3 bundles, 3 collections, 1 document, and 13 kernels in the harvest (easy, short log). Seems collections is off by 1.

@jordanpadams
jordanpadams merged commit 7c9c66e into main Aug 27, 2026
3 checks passed
@jordanpadams
jordanpadams deleted the super_log branch August 27, 2026 15:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment