From 6c891b7876cc291b97eeaaa9d781dd0639320bc4 Mon Sep 17 00:00:00 2001 From: Shea Stewart Date: Wed, 25 Mar 2026 14:24:22 +0000 Subject: [PATCH 1/3] Add support for excluding specific containers in log analysis across multiple healthcheck runbooks - Introduced a new variable `${EXCLUDED_CONTAINER_NAMES}` to allow users to specify container names to exclude from log analysis. - Updated relevant log analysis functions in the DaemonSet, StatefulSet, Deployment, and Stacktrace healthcheck runbooks to utilize the new exclusion feature. - Enhanced the suite initialization to handle the conversion of the excluded container names into a list for processing. - Improved documentation to clarify the purpose and usage of the new exclusion functionality. --- .../k8s-daemonset-healthcheck/runbook.robot | 20 +++++- .../k8s-deployment-healthcheck/runbook.robot | 47 +++++++++++-- .../k8s-stacktrace-health/runbook.robot | 7 +- codebundles/k8s-stacktrace-health/sli.robot | 7 +- .../k8s-statefulset-healthcheck/runbook.robot | 20 +++++- libraries/RW/CLI/CLI.py | 6 +- libraries/RW/CLI/json_parser.py | 16 ++--- libraries/RW/CLI/stdout_parser.py | 4 +- libraries/RW/K8sApplications/_test_parsers.py | 2 +- .../RW/K8sApplications/k8s_applications.py | 8 +-- libraries/RW/K8sApplications/repository.py | 10 +-- libraries/RW/K8sHelper/k8s_helper.py | 2 +- libraries/RW/K8sLog/k8s_log.py | 69 +++++++++++-------- 13 files changed, 158 insertions(+), 60 deletions(-) diff --git a/codebundles/k8s-daemonset-healthcheck/runbook.robot b/codebundles/k8s-daemonset-healthcheck/runbook.robot index 501235e66..d2398a114 100644 --- a/codebundles/k8s-daemonset-healthcheck/runbook.robot +++ b/codebundles/k8s-daemonset-healthcheck/runbook.robot @@ -39,6 +39,7 @@ Analyze Application Log Patterns for DaemonSet `${DAEMONSET_NAME}` in Namespace ... context=${CONTEXT} ... kubeconfig=${kubeconfig} ... log_age=${LOG_AGE} + ... excluded_containers=${EXCLUDED_CONTAINERS} ${scan_results}= RW.K8sLog.Scan Logs For Issues ... log_dir=${log_dir} @@ -46,6 +47,7 @@ Analyze Application Log Patterns for DaemonSet `${DAEMONSET_NAME}` in Namespace ... workload_name=${DAEMONSET_NAME} ... namespace=${NAMESPACE} ... categories=@{LOG_PATTERN_CATEGORIES} + ... excluded_containers=${EXCLUDED_CONTAINERS} ${log_health_score}= RW.K8sLog.Calculate Log Health Score scan_results=${scan_results} @@ -98,12 +100,14 @@ Detect Log Anomalies for DaemonSet `${DAEMONSET_NAME}` in Namespace `${NAMESPACE ... context=${CONTEXT} ... kubeconfig=${kubeconfig} ... log_age=${LOG_AGE} + ... excluded_containers=${EXCLUDED_CONTAINERS} ${anomaly_results}= RW.K8sLog.Analyze Log Anomalies ... log_dir=${log_dir} ... workload_type=daemonset ... workload_name=${DAEMONSET_NAME} ... namespace=${NAMESPACE} + ... excluded_containers=${EXCLUDED_CONTAINERS} # Process anomaly issues ${anomaly_issues}= Evaluate $anomaly_results.get('issues', []) @@ -945,9 +949,21 @@ Suite Initialization ... pattern=\d+ ... example=1 ... default=1 + ${EXCLUDED_CONTAINER_NAMES}= RW.Core.Import User Variable EXCLUDED_CONTAINER_NAMES + ... type=string + ... description=Comma-separated list of container names to exclude from log analysis (e.g., linkerd-proxy, istio-proxy, vault-agent). + ... pattern=.* + ... example=linkerd-proxy,istio-proxy,vault-agent + ... default=linkerd-proxy,istio-proxy,vault-agent - # Convert comma-separated string to list + # Convert comma-separated strings to lists @{LOG_PATTERN_CATEGORIES}= Split String ${LOG_PATTERN_CATEGORIES_STR} , + @{EXCLUDED_CONTAINERS_RAW}= Run Keyword If "${EXCLUDED_CONTAINER_NAMES}" != "" Split String ${EXCLUDED_CONTAINER_NAMES} , ELSE Create List + @{EXCLUDED_CONTAINERS}= Create List + FOR ${container} IN @{EXCLUDED_CONTAINERS_RAW} + ${trimmed_container}= Strip String ${container} + Append To List ${EXCLUDED_CONTAINERS} ${trimmed_container} + END Set Suite Variable ${kubeconfig} Set Suite Variable ${KUBERNETES_DISTRIBUTION_BINARY} @@ -961,6 +977,8 @@ Suite Initialization Set Suite Variable ${ANOMALY_THRESHOLD} Set Suite Variable ${CONTAINER_RESTART_AGE} Set Suite Variable ${CONTAINER_RESTART_THRESHOLD} + Set Suite Variable ${EXCLUDED_CONTAINER_NAMES} + Set Suite Variable @{EXCLUDED_CONTAINERS} ${env}= Evaluate {"KUBECONFIG":"${kubeconfig.key}","KUBERNETES_DISTRIBUTION_BINARY":"${KUBERNETES_DISTRIBUTION_BINARY}","CONTEXT":"${CONTEXT}","NAMESPACE":"${NAMESPACE}","DAEMONSET_NAME":"${DAEMONSET_NAME}","CONTAINER_RESTART_AGE":"${CONTAINER_RESTART_AGE}","CONTAINER_RESTART_THRESHOLD":"${CONTAINER_RESTART_THRESHOLD}"} Set Suite Variable ${env} diff --git a/codebundles/k8s-deployment-healthcheck/runbook.robot b/codebundles/k8s-deployment-healthcheck/runbook.robot index 6bfc95274..c2f8fe83a 100755 --- a/codebundles/k8s-deployment-healthcheck/runbook.robot +++ b/codebundles/k8s-deployment-healthcheck/runbook.robot @@ -112,6 +112,12 @@ Suite Initialization ... pattern=.* ... example=linkerd-proxy,istio-proxy,vault-agent ... default=linkerd-proxy,istio-proxy,vault-agent + ${CONTAINER_NAME}= RW.Core.Import User Variable CONTAINER_NAME + ... type=string + ... description=Optional: the specific container name to fetch logs from. If not set, the primary application container is auto-detected by excluding known sidecars. + ... pattern=.* + ... example=controller + ... default= ${CONTAINER_RESTART_AGE}= RW.Core.Import User Variable CONTAINER_RESTART_AGE ... type=string @@ -152,6 +158,7 @@ Suite Initialization Set Suite Variable ${LOG_SCAN_TIMEOUT} Set Suite Variable ${EXCLUDED_CONTAINER_NAMES} Set Suite Variable @{EXCLUDED_CONTAINERS} + Set Suite Variable ${CONTAINER_NAME} Set Suite Variable ${CONTAINER_RESTART_AGE} Set Suite Variable ${CONTAINER_RESTART_THRESHOLD} @@ -401,9 +408,41 @@ Fetch Deployment Logs for `${DEPLOYMENT_NAME}` in Namespace `${NAMESPACE}` ... data:logs-bulk # Skip pod-related checks if deployment is scaled to 0 IF not ${SKIP_POD_CHECKS} - # Fetch raw logs + # Determine which container to fetch logs from + IF "${CONTAINER_NAME}" != "" + ${target_container}= Set Variable ${CONTAINER_NAME} + ELSE + # Auto-detect primary container by listing containers and excluding known sidecars + ${container_json}= RW.CLI.Run Cli + ... cmd=${KUBERNETES_DISTRIBUTION_BINARY} get deployment/${DEPLOYMENT_NAME} --context ${CONTEXT} -n ${NAMESPACE} -o jsonpath='{.spec.template.spec.containers[*].name}' + ... env=${env} + ... secret_file__kubeconfig=${kubeconfig} + ... include_in_history=false + @{all_containers}= Split String ${container_json.stdout} + ${container_count}= Get Length ${all_containers} + ${target_container}= Set Variable ${EMPTY} + IF ${container_count} > 0 + FOR ${cname} IN @{all_containers} + ${is_excluded}= Evaluate "${cname}" in ${EXCLUDED_CONTAINERS} + IF not ${is_excluded} + ${target_container}= Set Variable ${cname} + BREAK + END + END + IF "${target_container}" == "" + ${target_container}= Set Variable ${all_containers}[0] + END + END + END + + # Build the kubectl logs command with or without -c flag + IF "${target_container}" != "" + ${logs_cmd}= Set Variable ${KUBERNETES_DISTRIBUTION_BINARY} logs deployment/${DEPLOYMENT_NAME} -c ${target_container} --context ${CONTEXT} -n ${NAMESPACE} --tail=${LOG_LINES} --since=${LOG_AGE} + ELSE + ${logs_cmd}= Set Variable ${KUBERNETES_DISTRIBUTION_BINARY} logs deployment/${DEPLOYMENT_NAME} --context ${CONTEXT} -n ${NAMESPACE} --tail=${LOG_LINES} --since=${LOG_AGE} + END ${deployment_logs}= RW.CLI.Run Cli - ... cmd=${KUBERNETES_DISTRIBUTION_BINARY} logs deployment/${DEPLOYMENT_NAME} --context ${CONTEXT} -n ${NAMESPACE} --tail=${LOG_LINES} --since=${LOG_AGE} + ... cmd=${logs_cmd} ... env=${env} ... secret_file__kubeconfig=${kubeconfig} ... show_in_rwl_cheatsheet=true @@ -412,13 +451,13 @@ Fetch Deployment Logs for `${DEPLOYMENT_NAME}` in Namespace `${NAMESPACE}` IF ${deployment_logs.returncode} == 0 # Filter logs to remove repetitive health check messages and focus on meaningful content ${filtered_logs}= RW.CLI.Run Cli - ... cmd=echo "${deployment_logs.stdout}" | grep -v -E "(Checking.*Health|Health.*Check|healthcheck|/health|GET /|POST /health|probe|liveness|readiness)" | grep -E "(error|ERROR|warn|WARN|exception|Exception|fail|FAIL|fatal|FATAL|panic|stack|trace|timeout|connection.*refused|unable.*connect|authentication.*failed|denied|forbidden|unauthorized|500|502|503|504)" | tail -50 || echo "No significant errors or warnings found in recent logs" + ... cmd=echo "${deployment_logs.stdout}" | grep -v -E "(Checking.*Health|Health.*Check|healthcheck|/health|GET /health|POST /health|probe|liveness|readiness)" | grep -E "(error|ERROR|warn|WARN|exception|Exception|fail|FAIL|fatal|FATAL|panic|stack|trace|timeout|connection.*refused|unable.*connect|authentication.*failed|denied|forbidden|unauthorized|500|502|503|504)" | tail -50 || echo "No significant errors or warnings found in recent logs" ... env=${env} ... include_in_history=false # Also get a sample of non-health-check logs for context ${context_logs}= RW.CLI.Run Cli - ... cmd=echo "${deployment_logs.stdout}" | grep -v -E "(Checking.*Health|Health.*Check|healthcheck|/health|GET /|POST /health|probe|liveness|readiness)" | head -20 | tail -10 + ... cmd=echo "${deployment_logs.stdout}" | grep -v -E "(Checking.*Health|Health.*Check|healthcheck|/health|GET /health|POST /health|probe|liveness|readiness)" | head -20 | tail -10 ... env=${env} ... include_in_history=false diff --git a/codebundles/k8s-stacktrace-health/runbook.robot b/codebundles/k8s-stacktrace-health/runbook.robot index 568a8026c..92cbc147d 100755 --- a/codebundles/k8s-stacktrace-health/runbook.robot +++ b/codebundles/k8s-stacktrace-health/runbook.robot @@ -162,7 +162,12 @@ Analyze Workload Stacktraces for ${WORKLOAD_TYPE} `${WORKLOAD_NAME}` in Namespac # Skip pod-related checks if workload is scaled to 0 IF not ${SKIP_STACKTRACE_CHECKS} # Convert comma-separated string to list for excluded containers - @{EXCLUDED_CONTAINERS}= Run Keyword If "${EXCLUDED_CONTAINER_NAMES}" != "" Split String ${EXCLUDED_CONTAINER_NAMES} , ELSE Create List + @{EXCLUDED_CONTAINERS_RAW}= Run Keyword If "${EXCLUDED_CONTAINER_NAMES}" != "" Split String ${EXCLUDED_CONTAINER_NAMES} , ELSE Create List + @{EXCLUDED_CONTAINERS}= Create List + FOR ${container} IN @{EXCLUDED_CONTAINERS_RAW} + ${trimmed_container}= Strip String ${container} + Append To List ${EXCLUDED_CONTAINERS} ${trimmed_container} + END # Fetch logs using RW.K8sLog library (same pattern as deployment healthcheck) ${log_dir}= RW.K8sLog.Fetch Workload Logs diff --git a/codebundles/k8s-stacktrace-health/sli.robot b/codebundles/k8s-stacktrace-health/sli.robot index 87270c420..258296f67 100755 --- a/codebundles/k8s-stacktrace-health/sli.robot +++ b/codebundles/k8s-stacktrace-health/sli.robot @@ -83,7 +83,12 @@ Suite Initialization # Convert comma-separated string to list - @{EXCLUDED_CONTAINERS}= Run Keyword If "${EXCLUDED_CONTAINER_NAMES}" != "" Split String ${EXCLUDED_CONTAINER_NAMES} , ELSE Create List + @{EXCLUDED_CONTAINERS_RAW}= Run Keyword If "${EXCLUDED_CONTAINER_NAMES}" != "" Split String ${EXCLUDED_CONTAINER_NAMES} , ELSE Create List + @{EXCLUDED_CONTAINERS}= Create List + FOR ${container} IN @{EXCLUDED_CONTAINERS_RAW} + ${trimmed_container}= Strip String ${container} + Append To List ${EXCLUDED_CONTAINERS} ${trimmed_container} + END Set Suite Variable @{EXCLUDED_CONTAINERS} # Initialize score variables diff --git a/codebundles/k8s-statefulset-healthcheck/runbook.robot b/codebundles/k8s-statefulset-healthcheck/runbook.robot index 5cd67f063..d8dbbdee4 100644 --- a/codebundles/k8s-statefulset-healthcheck/runbook.robot +++ b/codebundles/k8s-statefulset-healthcheck/runbook.robot @@ -39,6 +39,7 @@ Analyze Application Log Patterns for StatefulSet `${STATEFULSET_NAME}` in Namesp ... context=${CONTEXT} ... kubeconfig=${kubeconfig} ... log_age=${LOG_AGE} + ... excluded_containers=${EXCLUDED_CONTAINERS} ${scan_results}= RW.K8sLog.Scan Logs For Issues ... log_dir=${log_dir} @@ -46,6 +47,7 @@ Analyze Application Log Patterns for StatefulSet `${STATEFULSET_NAME}` in Namesp ... workload_name=${STATEFULSET_NAME} ... namespace=${NAMESPACE} ... categories=@{LOG_PATTERN_CATEGORIES} + ... excluded_containers=${EXCLUDED_CONTAINERS} ${log_health_score}= RW.K8sLog.Calculate Log Health Score scan_results=${scan_results} @@ -99,12 +101,14 @@ Detect Log Anomalies for StatefulSet `${STATEFULSET_NAME}` in Namespace `${NAMES ... context=${CONTEXT} ... kubeconfig=${kubeconfig} ... log_age=${LOG_AGE} + ... excluded_containers=${EXCLUDED_CONTAINERS} ${anomaly_results}= RW.K8sLog.Analyze Log Anomalies ... log_dir=${log_dir} ... workload_type=statefulset ... workload_name=${STATEFULSET_NAME} ... namespace=${NAMESPACE} + ... excluded_containers=${EXCLUDED_CONTAINERS} # Process anomaly issues ${anomaly_issues}= Evaluate $anomaly_results.get('issues', []) @@ -970,9 +974,21 @@ Suite Initialization ... pattern=\d+ ... example=1 ... default=1 + ${EXCLUDED_CONTAINER_NAMES}= RW.Core.Import User Variable EXCLUDED_CONTAINER_NAMES + ... type=string + ... description=Comma-separated list of container names to exclude from log analysis (e.g., linkerd-proxy, istio-proxy, vault-agent). + ... pattern=.* + ... example=linkerd-proxy,istio-proxy,vault-agent + ... default=linkerd-proxy,istio-proxy,vault-agent - # Convert comma-separated string to list + # Convert comma-separated strings to lists @{LOG_PATTERN_CATEGORIES}= Split String ${LOG_PATTERN_CATEGORIES_STR} , + @{EXCLUDED_CONTAINERS_RAW}= Run Keyword If "${EXCLUDED_CONTAINER_NAMES}" != "" Split String ${EXCLUDED_CONTAINER_NAMES} , ELSE Create List + @{EXCLUDED_CONTAINERS}= Create List + FOR ${container} IN @{EXCLUDED_CONTAINERS_RAW} + ${trimmed_container}= Strip String ${container} + Append To List ${EXCLUDED_CONTAINERS} ${trimmed_container} + END Set Suite Variable ${kubeconfig} Set Suite Variable ${KUBERNETES_DISTRIBUTION_BINARY} @@ -986,6 +1002,8 @@ Suite Initialization Set Suite Variable ${ANOMALY_THRESHOLD} Set Suite Variable ${CONTAINER_RESTART_AGE} Set Suite Variable ${CONTAINER_RESTART_THRESHOLD} + Set Suite Variable ${EXCLUDED_CONTAINER_NAMES} + Set Suite Variable @{EXCLUDED_CONTAINERS} ${env}= Evaluate {"KUBECONFIG":"${kubeconfig.key}","KUBERNETES_DISTRIBUTION_BINARY":"${KUBERNETES_DISTRIBUTION_BINARY}","CONTEXT":"${CONTEXT}","NAMESPACE":"${NAMESPACE}","STATEFULSET_NAME":"${STATEFULSET_NAME}","CONTAINER_RESTART_AGE":"${CONTAINER_RESTART_AGE}","CONTAINER_RESTART_THRESHOLD":"${CONTAINER_RESTART_THRESHOLD}"} Set Suite Variable ${env} diff --git a/libraries/RW/CLI/CLI.py b/libraries/RW/CLI/CLI.py index ac998b282..03a984f23 100644 --- a/libraries/RW/CLI/CLI.py +++ b/libraries/RW/CLI/CLI.py @@ -184,7 +184,7 @@ def _create_secrets_from_kwargs(**kwargs) -> list[platform.ShellServiceRequestSe if not key.startswith(SECRET_PREFIX) and not key.startswith(SECRET_FILE_PREFIX): continue if not isinstance(value, platform.Secret): - logger.warning( + logger.warn( f"kwarg secret {value} in key {key} is the wrong type, should be platform.Secret" ) continue @@ -217,7 +217,7 @@ def _copy_files_to_staging_dir(source_dir: str, staging_dir: str) -> dict: with open(staged_path, "w", encoding="utf-8") as out_f: out_f.write(content) except Exception as e: - logger.warning(f"Could not stage file '{full_path}': {e}") + logger.warn(f"Could not stage file '{full_path}': {e}") return files_dict def find_file(*paths): @@ -326,7 +326,7 @@ def run_bash_file( else: # Not found directly, so do fallback logic with resolve_path_to_robot cwd = os.getcwd() - logger.warning(f"File '{bash_file}' not found in '{cwd}'. Attempting fallback logic...") + logger.warn(f"File '{bash_file}' not found in '{cwd}'. Attempting fallback logic...") # Might return something like "/path/to/.../sli.robot" rw_path_to_robot = resolve_path_to_robot() diff --git a/libraries/RW/CLI/json_parser.py b/libraries/RW/CLI/json_parser.py index d40172ff5..b88b98d3a 100644 --- a/libraries/RW/CLI/json_parser.py +++ b/libraries/RW/CLI/json_parser.py @@ -134,13 +134,13 @@ def parse_cli_json_output( try: jmespath_result = jmespath.search(jmespath_str, json_data) if jmespath_result == None: - logger.warning( + logger.warn( f"The jmespath extraction string: {jmespath_str} returned None for the variable: {varname} with kwarg parts: {kwarg_parts} - did a previous extract fail?" ) variable_results[varname] = jmespath_result variable_from_path[varname] = jmespath_str except Exception as e: - logger.warning( + logger.warn( f"Failed to extract jmespath data: {json.dumps(variable_results[varname])} with path: {jmespath_str} due to: {e}" ) variable_results[varname] = None @@ -154,7 +154,7 @@ def parse_cli_json_output( jmespath_str = kwargs[key] from_varname = kwarg_parts[1] if from_varname not in variable_results.keys(): - logger.warning( + logger.warn( f"attempted to reference from_var {from_varname} when it has not been created yet. Available vars: {variable_results.keys()}" ) continue @@ -167,7 +167,7 @@ def parse_cli_json_output( variable_results[to_varname] = jmespath.search(jmespath_str, variable_results[from_varname]) variable_from_path[to_varname] = jmespath_str except Exception as e: - logger.warning( + logger.warn( f"Failed to extract jmespath data: {json.dumps(variable_results[from_varname])} with path: {jmespath_str} due to: {e}" ) variable_results[to_varname] = None @@ -181,7 +181,7 @@ def parse_cli_json_output( varname = kwarg_parts[0] filter_type = kwarg_parts[1] if filter_type not in RECOGNIZED_FILTERS: - logger.warning(f"filter: {filter_type} is not in the expected types: {RECOGNIZED_FILTERS}") + logger.warn(f"filter: {filter_type} is not in the expected types: {RECOGNIZED_FILTERS}") continue filter_amount = kwarg_parts[2] field_to_filter_on = kwargs[key] @@ -231,7 +231,7 @@ def parse_cli_json_output( continue from_varname = kwargs[key] if from_varname not in variable_results.keys(): - logger.warning( + logger.warn( f"attempted to reference from_var {from_varname} when it has not been created yet. Available vars: {variable_results.keys()}" ) continue @@ -289,7 +289,7 @@ def _check_for_json_issue( logger.info(f"Query {query} not in recognized list: {RECOGNIZED_JSON_PARSE_QUERIES}") continue if prefix not in variable_results.keys(): - logger.warning( + logger.warn( f"Variable {prefix} hasn't been defined by assignment or extract, try define it in {variable_results.keys()} first" ) continue @@ -303,7 +303,7 @@ def _check_for_json_issue( variable_value = float(variable_value) numeric_castable = True except Exception as e: - logger.warning( + logger.warn( f"Numeric parse query requested but values not castable: {query_value} and {variable_value}" ) # If True/False string passed from robot layer, cast it to bool diff --git a/libraries/RW/CLI/stdout_parser.py b/libraries/RW/CLI/stdout_parser.py index 89275e372..53a58c90a 100644 --- a/libraries/RW/CLI/stdout_parser.py +++ b/libraries/RW/CLI/stdout_parser.py @@ -173,7 +173,7 @@ def parse_cli_output_by_line( query_parts = parse_query.split("__") if len(query_parts) != 2: if not squelch_further_warnings: - logger.warning(f"Could not parse query: {parse_query}") + logger.warn(f"Could not parse query: {parse_query}") squelch_further_warnings = True continue prefix = query_parts[0] @@ -192,7 +192,7 @@ def parse_cli_output_by_line( capture_group_value = float(capture_group_value) numeric_castable = True except Exception as e: - logger.warning( + logger.warn( f"Numeric parse query requested but values not castable: {query_value} and {capture_group_value}" ) # process applicable query diff --git a/libraries/RW/K8sApplications/_test_parsers.py b/libraries/RW/K8sApplications/_test_parsers.py index c4fee6508..97db2da89 100644 --- a/libraries/RW/K8sApplications/_test_parsers.py +++ b/libraries/RW/K8sApplications/_test_parsers.py @@ -73,7 +73,7 @@ def test_golang_report(): with open(f"{THIS_DIR}/{TEST_DATA_DIR}/golang.log", "r") as f: data = f.read() if len(data) > MAX_LOG_LINES: - logger.warning( + logger.warn( f"Length of logs provided for parsing stacktraces is greater than {MAX_LOG_LINES}, be aware this could effect performance" ) results = parse_stacktraces( diff --git a/libraries/RW/K8sApplications/k8s_applications.py b/libraries/RW/K8sApplications/k8s_applications.py index 3712d88e9..9a6e60a55 100644 --- a/libraries/RW/K8sApplications/k8s_applications.py +++ b/libraries/RW/K8sApplications/k8s_applications.py @@ -101,7 +101,7 @@ def stacktrace_report_data(stacktraces: list[StackTraceData], max_report_stacktr elif st not in formatted_stacktraces: formatted_stacktraces.append(st) if len(formatted_stacktraces) == 0: - logger.warning( + logger.warn( f"No stacktraces formatted, review formatted: {formatted_stacktraces}\n\nvs\n\nunformatted: {stacktraces})" ) mcst: StackTraceData = formatted_stacktraces[0] @@ -109,7 +109,7 @@ def stacktrace_report_data(stacktraces: list[StackTraceData], max_report_stacktr if st.occurences > mcst.occurences and st.raw and st.error_messages: mcst = st if not mcst.raw or not mcst.error_messages: - logger.warning(f"Most common stacktrace has empty content: {mcst}\n given list: {formatted_stacktraces}") + logger.warn(f"Most common stacktrace has empty content: {mcst}\n given list: {formatted_stacktraces}") data = { "stacktraces": formatted_stacktraces[:max_report_stacktraces], "stacktrace_count": len(stacktraces), @@ -203,7 +203,7 @@ def determine_parser(first_line: str) -> BaseStackTraceParse: parser_to_use = parser break if not parser_to_use: - logger.warning(f"No parser found for log line: {first_line}") + logger.warn(f"No parser found for log line: {first_line}") return parser_to_use @@ -214,7 +214,7 @@ def parse_stacktraces( show_debug: bool = False, ) -> list[StackTraceData]: if len(logs) > MAX_LOG_LINES: - logger.warning( + logger.warn( f"Length of logs provided for parsing exceptions is greater than {MAX_LOG_LINES}, be aware this could effect performance" ) # allow keyword callers to override the parser used diff --git a/libraries/RW/K8sApplications/repository.py b/libraries/RW/K8sApplications/repository.py index a2dcc45a2..360dd4c08 100644 --- a/libraries/RW/K8sApplications/repository.py +++ b/libraries/RW/K8sApplications/repository.py @@ -381,7 +381,7 @@ def git_pr( if check_open: rsp = requests.get(url, headers=headers) if rsp.status_code < 200 or rsp.status_code >= 300: - logger.warning(f"Error: {rsp.status_code}, {rsp.text}") + logger.warn(f"Error: {rsp.status_code}, {rsp.text}") return {} rsp = rsp.json() for pr in rsp: @@ -401,7 +401,7 @@ def git_pr( response = requests.post(url, headers=headers, json=data) if response.status_code >= 300: - logger.warning(f"Error: {response.status_code}, {response.text}") + logger.warn(f"Error: {response.status_code}, {response.text}") return {} return response.json() @@ -451,7 +451,7 @@ def search( # search_match_score_min: int = 90, ) -> [RepositorySearchResult]: if not search_files and not search_words: - logger.warning(f"You must provide files and/or words to search with") + logger.warn(f"You must provide files and/or words to search with") return [] logger.info(f"Performing search with words: {search_words}") logger.info(f"Performing search with files: {search_files}") @@ -579,7 +579,7 @@ def list_issues(self, state="open"): response = requests.get(url, headers=headers, params=params) if response.status_code != 200: - logger.warning(f"Error: {response.status_code}") + logger.warn(f"Error: {response.status_code}") return {} return response.json() @@ -601,7 +601,7 @@ def create_issue(self, title, body=None, labels=None, assignees=None): response = requests.post(url, headers=headers, json=data) if response.status_code != 201: - logger.warning(f"Error: {response.status_code}, {response.text}") + logger.warn(f"Error: {response.status_code}, {response.text}") return {} return response.json() diff --git a/libraries/RW/K8sHelper/k8s_helper.py b/libraries/RW/K8sHelper/k8s_helper.py index 69e23f8e5..8602bfa0f 100644 --- a/libraries/RW/K8sHelper/k8s_helper.py +++ b/libraries/RW/K8sHelper/k8s_helper.py @@ -71,7 +71,7 @@ def verify_cluster_connectivity( next_steps=next_steps, ) except Exception as e: - logger.warning(f"Could not add issue via RW.Core: {e}") + logger.warn(f"Could not add issue via RW.Core: {e}") bi.fatal_error( f"Kubernetes cluster connectivity check failed for context '{context}'. Aborting suite." ) diff --git a/libraries/RW/K8sLog/k8s_log.py b/libraries/RW/K8sLog/k8s_log.py index 6b6581479..14c24a88d 100644 --- a/libraries/RW/K8sLog/k8s_log.py +++ b/libraries/RW/K8sLog/k8s_log.py @@ -51,7 +51,7 @@ def _get_timestamp_handler(self): from RW.LogAnalysis.java.timestamp_handler import TimestampHandler self._timestamp_handler = TimestampHandler() except ImportError as e: - logger.warning(f"Could not import TimestampHandler: {e}") + logger.warn(f"Could not import TimestampHandler: {e}") self._timestamp_handler = None return self._timestamp_handler @@ -388,17 +388,27 @@ def _load_error_patterns(self) -> Dict[str, Any]: def _cleanup_log_line_for_grouping(self, line: str) -> str: """Remove variable parts of a log line for better grouping.""" - # Remove timestamps (ISO format or custom 'dd-mm-yyyy hh:mm:ss.ms') - line = re.sub(r'\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z?', '', line) + # Remove timestamps - ISO format + line = re.sub(r'\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z?', '', line) + # Remove timestamps - custom 'dd-mm-yyyy hh:mm:ss.ms' line = re.sub(r'\d{2}-\d{2}-\d{4} \d{2}:\d{2}:\d{2}\.\d{3}', '', line) - # Remove thread names in brackets + # Remove timestamps - nginx/CLF format: [25/Mar/2026:14:11:41 +0000] + line = re.sub(r'\[\d{2}/\w{3}/\d{4}:\d{2}:\d{2}:\d{2} [+-]\d{4}\]', '', line) + # Remove timestamps - syslog/common format: Mar 25 14:11:41 or 2026-03-25 14:11:41 + line = re.sub(r'\b\w{3}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2}\b', '', line) + line = re.sub(r'\b\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}(?:\.\d+)?\b', '', line) + # Remove IP addresses (v4) + line = re.sub(r'\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(?::\d+)?\b', '', line) + # Remove thread names and bracketed content line = re.sub(r'\[[^\][]*\]', '', line) # Remove UUIDs and similar trace/transaction IDs (hex or alphanumeric) line = re.sub(r'[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}', '', line, flags=re.IGNORECASE) - line = re.sub(r'\b[a-f0-9]{10,}\b', '', line, flags=re.IGNORECASE) # long hex strings (trace ids) - line = re.sub(r'\b[a-zA-Z0-9]*unknown[a-zA-Z0-9]*\b', '', line, flags=re.IGNORECASE) # IDs like '11989unknown...' - # Remove any remaining numbers that look like IDs or counters - line = re.sub(r'\b\d{5,}\b', '', line) + line = re.sub(r'\b[a-f0-9]{10,}\b', '', line, flags=re.IGNORECASE) + line = re.sub(r'\b[a-zA-Z0-9]*unknown[a-zA-Z0-9]*\b', '', line, flags=re.IGNORECASE) + # Remove standalone floating-point numbers (response times like 0.093, not version strings like v1.8.1) + line = re.sub(r'(? Dict[str, Any]: @@ -749,9 +759,9 @@ def _fetch_container_logs(self, pod_name: str, container_name: str, namespace: s if result.returncode == 0: logs_content += result.stdout except subprocess.TimeoutExpired: - logger.warning(f"Timeout fetching current logs for {pod_name}/{container_name}") + logger.warn(f"Timeout fetching current logs for {pod_name}/{container_name}") except Exception as e: - logger.warning(f"Error fetching current logs for {pod_name}/{container_name}: {e}") + logger.warn(f"Error fetching current logs for {pod_name}/{container_name}: {e}") # Fetch previous logs (if any) cmd_prev = cmd + ['--previous'] @@ -760,7 +770,7 @@ def _fetch_container_logs(self, pod_name: str, container_name: str, namespace: s if result.returncode == 0: logs_content += result.stdout except subprocess.TimeoutExpired: - logger.warning(f"Timeout fetching previous logs for {pod_name}/{container_name}") + logger.warn(f"Timeout fetching previous logs for {pod_name}/{container_name}") except Exception as e: # Previous logs might not exist, which is normal pass @@ -903,7 +913,7 @@ def scan_logs_for_issues(self, log_dir: str, workload_type: str, workload_name: return self._scan_logs_for_issues_impl(log_dir, workload_type, workload_name, namespace, categories, custom_patterns_file, excluded_containers) except TimeoutError: - logger.warning(f"Log scanning timed out after {timeout_seconds} seconds") + logger.warn(f"Log scanning timed out after {timeout_seconds} seconds") return { "issues": [], "summary": [ @@ -945,7 +955,7 @@ def _scan_logs_for_issues_impl(self, log_dir: str, workload_type: str, workload_ patterns_data = custom_data logger.info(f"Loaded custom patterns from {custom_patterns_file}") except Exception as e: - logger.warning(f"Failed to load custom patterns from {custom_patterns_file}: {e}") + logger.warn(f"Failed to load custom patterns from {custom_patterns_file}: {e}") logger.info("Using embedded patterns as fallback") # Pre-compile exclude patterns from JSON for better performance @@ -956,7 +966,7 @@ def _scan_logs_for_issues_impl(self, log_dir: str, workload_type: str, workload_ exclude_patterns.append(re.compile(exclude_pattern, re.IGNORECASE)) logger.debug(f"Compiled exclude pattern: {exclude_pattern}") except re.error as e: - logger.warning(f"Invalid exclude regex pattern: {exclude_pattern} - {e}") + logger.warn(f"Invalid exclude regex pattern: {exclude_pattern} - {e}") logger.info(f"Loaded {len(exclude_patterns)} exclude patterns from JSON") else: logger.info("No exclude_patterns found in patterns data") @@ -969,7 +979,7 @@ def _scan_logs_for_issues_impl(self, log_dir: str, workload_type: str, workload_ with open(pods_json_path, "r", encoding="utf-8") as f: pods_data = json.load(f) except Exception as e: - logger.warning(f"Error reading pods JSON: {e}") + logger.warn(f"Error reading pods JSON: {e}") return {"issues": [], "summary": ["No pods data found for analysis."]} # Pre-compile all regex patterns for better performance @@ -986,7 +996,7 @@ def _scan_logs_for_issues_impl(self, log_dir: str, workload_type: str, workload_ "config": pattern_config }) except re.error as e: - logger.warning(f"Invalid regex pattern in {category}: {pattern_config['pattern']} - {e}") + logger.warn(f"Invalid regex pattern in {category}: {pattern_config['pattern']} - {e}") continue # Pattern aggregators @@ -1011,30 +1021,32 @@ def _scan_logs_for_issues_impl(self, log_dir: str, workload_type: str, workload_ max_total_lines = 200000 # Limit total lines across all files total_lines_processed = 0 + excluded = excluded_containers or [] for pod in pods: logger.info(f"Processing Pod: {pod}") pod_obj = next((p for p in pods_data if p["metadata"]["name"] == pod), None) if not pod_obj: continue - containers = [c["name"] for c in pod_obj["spec"]["containers"]] + all_containers = [c["name"] for c in pod_obj["spec"]["containers"]] + containers = [c for c in all_containers if c not in excluded] for container in containers: logger.info(f" Processing Container: {container}") log_file = log_path / f"{workload_type}_{workload_name}_logs" / f"{pod}_{container}_logs.txt" if not log_file.is_file(): - logger.warning(f" Warning: No log file found at {log_file}") + logger.warn(f" Warning: No log file found at {log_file}") continue # Check file size before processing try: file_size = log_file.stat().st_size if file_size > 50 * 1024 * 1024: # 50MB limit - logger.warning(f" Skipping large log file {log_file} ({file_size / 1024 / 1024:.1f}MB)") + logger.warn(f" Skipping large log file {log_file} ({file_size / 1024 / 1024:.1f}MB)") continue except Exception as e: - logger.warning(f" Could not check file size for {log_file}: {e}") + logger.warn(f" Could not check file size for {log_file}: {e}") continue with open(log_file, "r", encoding="utf-8") as lf: @@ -1081,14 +1093,14 @@ def _scan_logs_for_issues_impl(self, log_dir: str, workload_type: str, workload_ for line_num, line in enumerate(log_lines, 1): if pattern.search(line): # Apply exclude patterns from JSON before collecting matches - excluded = False + line_excluded = False for exclude_pattern in exclude_patterns: if exclude_pattern.search(line): - excluded = True + line_excluded = True logger.debug(f"Line excluded by pattern {exclude_pattern.pattern}: {line.strip()[:100]}...") break - if not excluded: + if not line_excluded: # Extract timestamp from the matching log line log_timestamp = self._extract_timestamp_from_log_line(line.strip()) @@ -1302,7 +1314,7 @@ def analyze_log_anomalies(self, log_dir: str, workload_type: str, workload_name: with open(pods_json_path, "r", encoding="utf-8") as f: pods_data = json.load(f) except Exception as e: - logger.warning(f"Error reading pods JSON: {e}") + logger.warn(f"Error reading pods JSON: {e}") return {"issues": [], "summary": ["No pods data found for anomaly analysis."]} issues_json = {"issues": [], "summary": []} @@ -1310,20 +1322,22 @@ def analyze_log_anomalies(self, log_dir: str, workload_type: str, workload_name: logger.info(f"Scanning logs for frequent log anomalies in {workload_type}/{workload_name} in namespace {namespace}...") + excluded = excluded_containers or [] for pod in pods: logger.info(f"Processing Pod: {pod}") pod_obj = next((p for p in pods_data if p["metadata"]["name"] == pod), None) if not pod_obj: continue - containers = [c["name"] for c in pod_obj["spec"]["containers"]] + all_containers = [c["name"] for c in pod_obj["spec"]["containers"]] + containers = [c for c in all_containers if c not in excluded] for container in containers: logger.info(f" Processing Container: {container}") log_file = log_path / f"{workload_type}_{workload_name}_logs" / f"{pod}_{container}_logs.txt" if not log_file.is_file(): - logger.warning(f" Warning: No log file found at {log_file}") + logger.warn(f" Warning: No log file found at {log_file}") continue with open(log_file, "r", encoding="utf-8") as lf: @@ -1331,7 +1345,6 @@ def analyze_log_anomalies(self, log_dir: str, workload_type: str, workload_name: if not log_content.strip(): continue - # logger.error(f"Hrithvika: {log_content}") # Count occurrences of repeating log messages log_lines = log_content.split('\n') @@ -1777,7 +1790,7 @@ def cleanup_temp_files(self): self.temp_dir = None logger.info("Cleaned up temporary log analysis files") except Exception as e: - logger.warning(f"Failed to cleanup temporary files: {str(e)}") + logger.warn(f"Failed to cleanup temporary files: {str(e)}") @keyword def extract_timestamp_from_line(self, log_data: str) -> str: From cc8fa3d285b8a2e456f1efb79a1f5d7060fb99dc Mon Sep 17 00:00:00 2001 From: Shea Stewart Date: Wed, 25 Mar 2026 14:41:31 +0000 Subject: [PATCH 2/3] Refactor logging calls to use `logger.warning` instead of `logger.warn` for consistency across CLI and K8s applications. This change enhances clarity in log messages and aligns with updated logging practices. --- libraries/RW/CLI/CLI.py | 6 +++--- libraries/RW/CLI/json_parser.py | 16 ++++++++-------- libraries/RW/CLI/stdout_parser.py | 4 ++-- libraries/RW/K8sApplications/_test_parsers.py | 2 +- libraries/RW/K8sApplications/k8s_applications.py | 8 ++++---- libraries/RW/K8sApplications/repository.py | 10 +++++----- libraries/RW/K8sHelper/k8s_helper.py | 2 +- 7 files changed, 24 insertions(+), 24 deletions(-) diff --git a/libraries/RW/CLI/CLI.py b/libraries/RW/CLI/CLI.py index 03a984f23..ac998b282 100644 --- a/libraries/RW/CLI/CLI.py +++ b/libraries/RW/CLI/CLI.py @@ -184,7 +184,7 @@ def _create_secrets_from_kwargs(**kwargs) -> list[platform.ShellServiceRequestSe if not key.startswith(SECRET_PREFIX) and not key.startswith(SECRET_FILE_PREFIX): continue if not isinstance(value, platform.Secret): - logger.warn( + logger.warning( f"kwarg secret {value} in key {key} is the wrong type, should be platform.Secret" ) continue @@ -217,7 +217,7 @@ def _copy_files_to_staging_dir(source_dir: str, staging_dir: str) -> dict: with open(staged_path, "w", encoding="utf-8") as out_f: out_f.write(content) except Exception as e: - logger.warn(f"Could not stage file '{full_path}': {e}") + logger.warning(f"Could not stage file '{full_path}': {e}") return files_dict def find_file(*paths): @@ -326,7 +326,7 @@ def run_bash_file( else: # Not found directly, so do fallback logic with resolve_path_to_robot cwd = os.getcwd() - logger.warn(f"File '{bash_file}' not found in '{cwd}'. Attempting fallback logic...") + logger.warning(f"File '{bash_file}' not found in '{cwd}'. Attempting fallback logic...") # Might return something like "/path/to/.../sli.robot" rw_path_to_robot = resolve_path_to_robot() diff --git a/libraries/RW/CLI/json_parser.py b/libraries/RW/CLI/json_parser.py index b88b98d3a..d40172ff5 100644 --- a/libraries/RW/CLI/json_parser.py +++ b/libraries/RW/CLI/json_parser.py @@ -134,13 +134,13 @@ def parse_cli_json_output( try: jmespath_result = jmespath.search(jmespath_str, json_data) if jmespath_result == None: - logger.warn( + logger.warning( f"The jmespath extraction string: {jmespath_str} returned None for the variable: {varname} with kwarg parts: {kwarg_parts} - did a previous extract fail?" ) variable_results[varname] = jmespath_result variable_from_path[varname] = jmespath_str except Exception as e: - logger.warn( + logger.warning( f"Failed to extract jmespath data: {json.dumps(variable_results[varname])} with path: {jmespath_str} due to: {e}" ) variable_results[varname] = None @@ -154,7 +154,7 @@ def parse_cli_json_output( jmespath_str = kwargs[key] from_varname = kwarg_parts[1] if from_varname not in variable_results.keys(): - logger.warn( + logger.warning( f"attempted to reference from_var {from_varname} when it has not been created yet. Available vars: {variable_results.keys()}" ) continue @@ -167,7 +167,7 @@ def parse_cli_json_output( variable_results[to_varname] = jmespath.search(jmespath_str, variable_results[from_varname]) variable_from_path[to_varname] = jmespath_str except Exception as e: - logger.warn( + logger.warning( f"Failed to extract jmespath data: {json.dumps(variable_results[from_varname])} with path: {jmespath_str} due to: {e}" ) variable_results[to_varname] = None @@ -181,7 +181,7 @@ def parse_cli_json_output( varname = kwarg_parts[0] filter_type = kwarg_parts[1] if filter_type not in RECOGNIZED_FILTERS: - logger.warn(f"filter: {filter_type} is not in the expected types: {RECOGNIZED_FILTERS}") + logger.warning(f"filter: {filter_type} is not in the expected types: {RECOGNIZED_FILTERS}") continue filter_amount = kwarg_parts[2] field_to_filter_on = kwargs[key] @@ -231,7 +231,7 @@ def parse_cli_json_output( continue from_varname = kwargs[key] if from_varname not in variable_results.keys(): - logger.warn( + logger.warning( f"attempted to reference from_var {from_varname} when it has not been created yet. Available vars: {variable_results.keys()}" ) continue @@ -289,7 +289,7 @@ def _check_for_json_issue( logger.info(f"Query {query} not in recognized list: {RECOGNIZED_JSON_PARSE_QUERIES}") continue if prefix not in variable_results.keys(): - logger.warn( + logger.warning( f"Variable {prefix} hasn't been defined by assignment or extract, try define it in {variable_results.keys()} first" ) continue @@ -303,7 +303,7 @@ def _check_for_json_issue( variable_value = float(variable_value) numeric_castable = True except Exception as e: - logger.warn( + logger.warning( f"Numeric parse query requested but values not castable: {query_value} and {variable_value}" ) # If True/False string passed from robot layer, cast it to bool diff --git a/libraries/RW/CLI/stdout_parser.py b/libraries/RW/CLI/stdout_parser.py index 53a58c90a..89275e372 100644 --- a/libraries/RW/CLI/stdout_parser.py +++ b/libraries/RW/CLI/stdout_parser.py @@ -173,7 +173,7 @@ def parse_cli_output_by_line( query_parts = parse_query.split("__") if len(query_parts) != 2: if not squelch_further_warnings: - logger.warn(f"Could not parse query: {parse_query}") + logger.warning(f"Could not parse query: {parse_query}") squelch_further_warnings = True continue prefix = query_parts[0] @@ -192,7 +192,7 @@ def parse_cli_output_by_line( capture_group_value = float(capture_group_value) numeric_castable = True except Exception as e: - logger.warn( + logger.warning( f"Numeric parse query requested but values not castable: {query_value} and {capture_group_value}" ) # process applicable query diff --git a/libraries/RW/K8sApplications/_test_parsers.py b/libraries/RW/K8sApplications/_test_parsers.py index 97db2da89..c4fee6508 100644 --- a/libraries/RW/K8sApplications/_test_parsers.py +++ b/libraries/RW/K8sApplications/_test_parsers.py @@ -73,7 +73,7 @@ def test_golang_report(): with open(f"{THIS_DIR}/{TEST_DATA_DIR}/golang.log", "r") as f: data = f.read() if len(data) > MAX_LOG_LINES: - logger.warn( + logger.warning( f"Length of logs provided for parsing stacktraces is greater than {MAX_LOG_LINES}, be aware this could effect performance" ) results = parse_stacktraces( diff --git a/libraries/RW/K8sApplications/k8s_applications.py b/libraries/RW/K8sApplications/k8s_applications.py index 9a6e60a55..3712d88e9 100644 --- a/libraries/RW/K8sApplications/k8s_applications.py +++ b/libraries/RW/K8sApplications/k8s_applications.py @@ -101,7 +101,7 @@ def stacktrace_report_data(stacktraces: list[StackTraceData], max_report_stacktr elif st not in formatted_stacktraces: formatted_stacktraces.append(st) if len(formatted_stacktraces) == 0: - logger.warn( + logger.warning( f"No stacktraces formatted, review formatted: {formatted_stacktraces}\n\nvs\n\nunformatted: {stacktraces})" ) mcst: StackTraceData = formatted_stacktraces[0] @@ -109,7 +109,7 @@ def stacktrace_report_data(stacktraces: list[StackTraceData], max_report_stacktr if st.occurences > mcst.occurences and st.raw and st.error_messages: mcst = st if not mcst.raw or not mcst.error_messages: - logger.warn(f"Most common stacktrace has empty content: {mcst}\n given list: {formatted_stacktraces}") + logger.warning(f"Most common stacktrace has empty content: {mcst}\n given list: {formatted_stacktraces}") data = { "stacktraces": formatted_stacktraces[:max_report_stacktraces], "stacktrace_count": len(stacktraces), @@ -203,7 +203,7 @@ def determine_parser(first_line: str) -> BaseStackTraceParse: parser_to_use = parser break if not parser_to_use: - logger.warn(f"No parser found for log line: {first_line}") + logger.warning(f"No parser found for log line: {first_line}") return parser_to_use @@ -214,7 +214,7 @@ def parse_stacktraces( show_debug: bool = False, ) -> list[StackTraceData]: if len(logs) > MAX_LOG_LINES: - logger.warn( + logger.warning( f"Length of logs provided for parsing exceptions is greater than {MAX_LOG_LINES}, be aware this could effect performance" ) # allow keyword callers to override the parser used diff --git a/libraries/RW/K8sApplications/repository.py b/libraries/RW/K8sApplications/repository.py index 360dd4c08..a2dcc45a2 100644 --- a/libraries/RW/K8sApplications/repository.py +++ b/libraries/RW/K8sApplications/repository.py @@ -381,7 +381,7 @@ def git_pr( if check_open: rsp = requests.get(url, headers=headers) if rsp.status_code < 200 or rsp.status_code >= 300: - logger.warn(f"Error: {rsp.status_code}, {rsp.text}") + logger.warning(f"Error: {rsp.status_code}, {rsp.text}") return {} rsp = rsp.json() for pr in rsp: @@ -401,7 +401,7 @@ def git_pr( response = requests.post(url, headers=headers, json=data) if response.status_code >= 300: - logger.warn(f"Error: {response.status_code}, {response.text}") + logger.warning(f"Error: {response.status_code}, {response.text}") return {} return response.json() @@ -451,7 +451,7 @@ def search( # search_match_score_min: int = 90, ) -> [RepositorySearchResult]: if not search_files and not search_words: - logger.warn(f"You must provide files and/or words to search with") + logger.warning(f"You must provide files and/or words to search with") return [] logger.info(f"Performing search with words: {search_words}") logger.info(f"Performing search with files: {search_files}") @@ -579,7 +579,7 @@ def list_issues(self, state="open"): response = requests.get(url, headers=headers, params=params) if response.status_code != 200: - logger.warn(f"Error: {response.status_code}") + logger.warning(f"Error: {response.status_code}") return {} return response.json() @@ -601,7 +601,7 @@ def create_issue(self, title, body=None, labels=None, assignees=None): response = requests.post(url, headers=headers, json=data) if response.status_code != 201: - logger.warn(f"Error: {response.status_code}, {response.text}") + logger.warning(f"Error: {response.status_code}, {response.text}") return {} return response.json() diff --git a/libraries/RW/K8sHelper/k8s_helper.py b/libraries/RW/K8sHelper/k8s_helper.py index 8602bfa0f..69e23f8e5 100644 --- a/libraries/RW/K8sHelper/k8s_helper.py +++ b/libraries/RW/K8sHelper/k8s_helper.py @@ -71,7 +71,7 @@ def verify_cluster_connectivity( next_steps=next_steps, ) except Exception as e: - logger.warn(f"Could not add issue via RW.Core: {e}") + logger.warning(f"Could not add issue via RW.Core: {e}") bi.fatal_error( f"Kubernetes cluster connectivity check failed for context '{context}'. Aborting suite." ) From 04965f7b8352b2c8398743a3ca6888c49633977c Mon Sep 17 00:00:00 2001 From: Shea Stewart Date: Wed, 25 Mar 2026 14:52:56 +0000 Subject: [PATCH 3/3] Update K8sLog to refine number filtering in log messages - Adjusted regex to remove numbers with 5 or more digits, improving log clarity by preserving smaller byte counts and HTTP status codes. - Enhanced comments for better understanding of the filtering logic. --- libraries/RW/K8sLog/k8s_log.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/RW/K8sLog/k8s_log.py b/libraries/RW/K8sLog/k8s_log.py index 14c24a88d..0259fa8d0 100644 --- a/libraries/RW/K8sLog/k8s_log.py +++ b/libraries/RW/K8sLog/k8s_log.py @@ -407,8 +407,8 @@ def _cleanup_log_line_for_grouping(self, line: str) -> str: line = re.sub(r'\b[a-zA-Z0-9]*unknown[a-zA-Z0-9]*\b', '', line, flags=re.IGNORECASE) # Remove standalone floating-point numbers (response times like 0.093, not version strings like v1.8.1) line = re.sub(r'(? Dict[str, Any]: