Skip to content

Improve harvester log grouping and error reporting#831

Open
TahaKhan998 wants to merge 1 commit into
CERNDocumentServer:masterfrom
TahaKhan998:feat/issue-813-harvester-log-grouping
Open

Improve harvester log grouping and error reporting#831
TahaKhan998 wants to merge 1 commit into
CERNDocumentServer:masterfrom
TahaKhan998:feat/issue-813-harvester-log-grouping

Conversation

@TahaKhan998

Copy link
Copy Markdown

Closes #813
This PR improves INSPIRE harvester run error reporting by turning errors and warning into clear grouped errors and warnings, making log output much more useful for curators, and ensuring the report highlights the real failure reasons instead of vague or repetitive raw lines. It adds structured grouping for repeated failures and warnings, improves the plain-text download output to match that grouped view, and updates harvester writer messages so record-level failures explain what went wrong in a more actionable way, including which records were affected and why they failed.

Screen.Recording.2026-06-26.at.12.00.16.1.mp4

This is what a downloaded file of harvester logs looks like now:

harvester_logs_4a7fc391-5b9a-45b3-873f-bd2d2a297aaa_20260626_120105.log

Comment thread site/cds_rdm/harvester_runs/logs.py Outdated
def format_timestamp(value):
"""Format timestamps for display."""
if value is None or value == "":
if value in (None, ""):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
if value in (None, ""):
if not value:

Comment thread site/cds_rdm/harvester_runs/logs.py Outdated

def _compact(value):
"""Collapse whitespace while keeping the original text readable."""
text = str(value or "").replace("\\n", " ").replace("\n", " ")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

where are the whitespaces originally added?

Comment thread site/cds_rdm/harvester_runs/logs.py Outdated
return " ".join(text.split()).strip()


def _remove_prefix(text, prefix):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

which prefixes are removed?

Comment thread site/cds_rdm/harvester_runs/logs.py Outdated
return None


def _strip_leading_bracket_prefixes(message):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

there is a lot of things happening here which I need some narration from your side to understand. Please book (on calendar) some time for us to go through the code together

@TahaKhan998 TahaKhan998 force-pushed the feat/issue-813-harvester-log-grouping branch 18 times, most recently from 1e2134c to 47666b0 Compare July 8, 2026 07:27
Comment thread site/cds_rdm/harvester_runs/logs.py Outdated
return match.group("id") if match else None


def _unwrap_skipped_entry_error(reason):

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

remove dictonary

Comment thread site/cds_rdm/harvester_runs/logs.py Outdated
)


def _safe_strip_wrapper_prefixes(reason):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

check if this can be fixed while logging in the first place - are the classes added in the harvester task? if yes we can reorganise there instead of parsing. If these classes are added in the jobs - let's discuss

Comment thread site/cds_rdm/harvester_runs/logs.py Outdated
return "Unknown error"

if _PLACEHOLDER_ONLY_TITLE.match(stripped):
return "Record validation failed." if stripped == "<payload>" else "Log message"

@kpsherva kpsherva Jul 8, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

please check how to fix .. errors and display the error correctly instead of stripping the payload

Comment thread site/cds_rdm/harvester_runs/logs.py Outdated


def _known_error_stem(reason):
"""Normalize legacy and current variants of recurring harvester errors."""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

is this normalisation needed?

@TahaKhan998 TahaKhan998 force-pushed the feat/issue-813-harvester-log-grouping branch 4 times, most recently from f07326b to 40ede92 Compare July 8, 2026 15:29
@TahaKhan998 TahaKhan998 force-pushed the feat/issue-813-harvester-log-grouping branch 5 times, most recently from 6851e19 to 52262d7 Compare July 9, 2026 08:48
return None


def _peel_string_list(text):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

what is the case for which we are returning a list in the log? maybe the log is incorrect?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good question. The screenshot shows where this comes from, in invenio-vocabularies, when a record fails it logs result.errors directly, and that's a list. So what ends up in the log looks like ['error message here'] instead of plain text. _peel_string_list just strips that wrapper off so the report can group errors properly. For example, without it we'd group on something like skipped entry with errors: ['more than 1 doi was found.'], brackets, quotes and all. After peeling we group on just more than 1 doi was found., so records that failed for the same reason actually end up in the same group.

It would be nicer if invenio-vocabularies wrote it as plain text in the first place. I handled it in the report for now since past harvester runs already have logs saved this way, but if you'd rather fix it at the source I can do that instead.

Image

"""INSPIRE writer."""

@staticmethod
def _format_validation_error(error):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

these methods would fit better in the logger.py, as logging is not the responsibility of this class. What do you think?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

You're right — moved them to logger.py.

@@ -16,7 +16,7 @@
from cds_rdm.harvester_runs.logs import (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

feedback not connected with this file but with the directory:
could you move harvester_download and harvester_runs into inspire_harvester/reports/download and inspire_harvester/reports/runs - to package the whole functionality of the harvester together

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done

if search_after:
params["search_after"] = search_after

result = current_jobs_logs_service.search(system_identity, params=params)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

.search function will return only 10k results max, even with the pagination

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

In invenio-jobs it’s 2000 per .search() by default (screenshot). Not sure about the 10k, maybe that’s in the CDS deployment config? Our loop just keeps fetching batches of 2000 with search_after until we’ve got all the logs or hit the page limit. The old code only fetched once.

Image

Comment thread site/cds_rdm/utils.py Outdated
return " ".join(text.split()).strip()


def flatten_error_payload(value, prefix=""):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think there is a function which does something very similar: search for validation_error_to_list_errors/_iter_errors_dict in invenio_records_resources.errors

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yep, switched to the invenio helper and dropped the duplicate.

@TahaKhan998 TahaKhan998 force-pushed the feat/issue-813-harvester-log-grouping branch 3 times, most recently from e3a6f16 to 3548734 Compare July 10, 2026 15:12
Move harvester run/download reports under inspire_harvester/reports,
stabilise writer and transformer error messages for grouping, and add
lean grouped error reporting in the admin harvester run view.
@TahaKhan998 TahaKhan998 force-pushed the feat/issue-813-harvester-log-grouping branch from 5f504a8 to 167c385 Compare July 10, 2026 15:27
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.

harvester: error log without raising to sentry

2 participants