|
1 | 1 | import glob |
2 | 2 | import os |
| 3 | +import re |
3 | 4 | import subprocess |
4 | 5 | import sys |
5 | 6 | import time |
@@ -63,6 +64,37 @@ def _exec_rms(self) -> int: |
63 | 64 | comp_process = subprocess.run(args=args, check=False) |
64 | 65 | return comp_process.returncode |
65 | 66 |
|
| 67 | + def _find_job_failures(self, logfile_path: Path) -> str: |
| 68 | + """Reads through a .log file and returns logs regarding failing jobs. |
| 69 | +
|
| 70 | + Any failure logs must be at the end of the log file as a failing job |
| 71 | + breaks the workflow and will be the last thing that is logged. |
| 72 | +
|
| 73 | + The method first finds the logs for the last job that was started and returns |
| 74 | + the findings as a formatted string if any job failures are found. |
| 75 | + """ |
| 76 | + current_job_line_no = 0 |
| 77 | + |
| 78 | + # Find the line number where the logging of the last job starts |
| 79 | + with open(logfile_path) as logfile: |
| 80 | + for line_no, line in enumerate(logfile, start=1): |
| 81 | + if line.strip().startswith("<pre>"): |
| 82 | + current_job_line_no = line_no |
| 83 | + last_job_line_no = current_job_line_no |
| 84 | + if last_job_line_no == 0: |
| 85 | + return "" |
| 86 | + |
| 87 | + # Collect the logs for the last job and look for failures |
| 88 | + with open(logfile_path) as logfile: |
| 89 | + job_failed_msg = "" |
| 90 | + for line_no, line in enumerate(logfile, start=1): |
| 91 | + if line_no >= last_job_line_no: |
| 92 | + cleaned_text = re.sub(r"<[^>]*>", "", line) |
| 93 | + if cleaned_text != "": |
| 94 | + job_failed_msg += cleaned_text |
| 95 | + |
| 96 | + return job_failed_msg if "failed" in job_failed_msg else "" |
| 97 | + |
66 | 98 | def print_failure(self, exit_status: int) -> None: |
67 | 99 | run_path = self.config.run_path.resolve() |
68 | 100 | # Reverse sort so workflow.log is (probably) first and |
@@ -111,14 +143,33 @@ def print_failure(self, exit_status: int) -> None: |
111 | 143 | * RMS.stderr.NN and RMS.stdout.NN |
112 | 144 | * rms/model/workflow.log |
113 | 145 | * Other named log files in rms/model, e.g. workflow_sim2seis.log |
114 | | - * rms/model/YYYYMMDD-HHMMSS-XXXXX-RMS.log corresponding to your run |
| 146 | + * rms/model/YYYYMMDD-HHMMSS-XXXXXX-RMS.log corresponding to your run |
115 | 147 |
|
116 | 148 | The following log files were found in this realization's run path: |
117 | 149 |
|
118 | 150 | """ |
119 | 151 | ) |
120 | 152 | fail_msg += "\n".join([f"* {f}" for f in log_files]) |
121 | 153 |
|
| 154 | + for log_file in log_files: |
| 155 | + if re.match( |
| 156 | + r"^\d{8}-\d{6}-[A-Za-z0-9]{6}-RMS", os.path.basename(log_file) |
| 157 | + ): |
| 158 | + # These logfiles are unstructured and will not give any results |
| 159 | + continue |
| 160 | + try: |
| 161 | + job_failed_msg = self._find_job_failures(Path(log_file)) |
| 162 | + except Exception: |
| 163 | + job_failed_msg = "" |
| 164 | + if job_failed_msg: |
| 165 | + fail_msg += dedent( |
| 166 | + f""" |
| 167 | + \nThe following logs regarding failed jobs were found in the log file {log_file}:" |
| 168 | + """ |
| 169 | + ) |
| 170 | + fail_msg += job_failed_msg |
| 171 | + break |
| 172 | + |
122 | 173 | print(fail_msg, file=sys.stderr) |
123 | 174 |
|
124 | 175 | @property |
|
0 commit comments