From ec94f055719bc0b939e3c0fc425d2665badaa2f3 Mon Sep 17 00:00:00 2001 From: Karel Srot Date: Mon, 9 Sep 2024 10:57:12 +0200 Subject: [PATCH] reportportal: add logfile size upload limit --- tmt/steps/report/reportportal.py | 43 +++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/tmt/steps/report/reportportal.py b/tmt/steps/report/reportportal.py index 41e67280fa..26c2f3e488 100644 --- a/tmt/steps/report/reportportal.py +++ b/tmt/steps/report/reportportal.py @@ -15,6 +15,7 @@ from tmt._compat.typing import TypeAlias JSON: 'TypeAlias' = Any +DEFAULT_LOG_SIZE_LIMIT: int = 1024 * 1024 def _flag_env_to_default(option: str, default: bool) -> bool: @@ -41,13 +42,38 @@ def _str_env_to_default(option: str, default: Optional[str]) -> Optional[str]: return str(os.getenv(env_var)) -def _filter_invalid_chars(data: str) -> str: +def _filter_invalid_chars(data: str, step_data: 'ReportReportPortalData') -> str: return re.sub( '[^\u0020-\uD7FF\u0009\u000A\u000D\uE000-\uFFFD\U00010000-\U0010FFFF]+', '', data) +def _filter_log_per_size(data: str, step_data: 'ReportReportPortalData') -> str: + size = len(data) + header = (f"WARNING: Uploaded log has been truncated because its size {size} bytes " + f"exceeds tmt reportportal plugin limit of {step_data.log_size_limit} bytes." + "The limit is controlled with --log-size-limit plugin option or" + "TMT_PLUGIN_REPORT_REPORTPORTAL_LOG_SIZE_LIMIT environment variable.\n\n") + if size > step_data.log_size_limit: + return (f"WARNING: Log file was truncated because its size {size} bytes " + f"exceeds {step_data.log_size_limit} bytes.\n\n" + f"{data[:step_data.log_size_limit]}") + return data + + +_LOG_FILTERS = [ + _filter_log_per_size, + _filter_invalid_chars, + ] + + +def _filter_log(log: str, step_data: 'ReportReportPortalData') -> str: + for log_filter in _LOG_FILTERS: + log = log_filter(log, step_data) + return log + + @dataclasses.dataclass class ReportReportPortalData(tmt.steps.report.ReportStepData): @@ -142,6 +168,17 @@ class ReportReportPortalData(tmt.steps.report.ReportStepData): (e.g. 'Idle'). 'To Investigate' is used by default. """) + log_size_limit: int = field( + option="--log-size-limit", + metavar="LOG_SIZE_LIMIT", + default=int( + _str_env_to_default('log_size_limit', str(DEFAULT_LOG_SIZE_LIMIT))), + help=f""" + Size limit in bytes for log upload to ReportPortal. + The default limit is {DEFAULT_LOG_SIZE_LIMIT} bytes + ({DEFAULT_LOG_SIZE_LIMIT / 1024 / 1024} MB). + """) + exclude_variables: str = field( option="--exclude-variables", metavar="PATTERN", @@ -590,7 +627,7 @@ def go(self, *, logger: Optional[tmt.log.Logger] = None) -> None: response = self.rp_api_post( session=session, path="log/entry", - json={"message": _filter_invalid_chars(log), + json={"message": _filter_log(log, self.data), "itemUuid": item_uuid, "launchUuid": launch_uuid, "level": level, @@ -598,7 +635,7 @@ def go(self, *, logger: Optional[tmt.log.Logger] = None) -> None: # Write out failures if index == 0 and status == "FAILED": - message = _filter_invalid_chars(result.failures(log)) + message = _filter_log(result.failures(log), self.data) response = self.rp_api_post( session=session, path="log/entry",