4.2.3.2. Failure reporting in the ESA Climate Toolbox
The ESA Climate Toolbox provides a mechanism whereby unrecoverable failures can be reported automatically to an external server. The user must explicitly consent to this data sharing before it can be activated.
This notebook demonstrates the mechanism by setting up a simple local reporting server, which receives failure reports and writes them to a file. The notebook then activates automated failure reporting and deliberately raises an error from the toolbox. Finally, the server’s log file is shown to demonstrate that the report was received and logged.
Import some necessary classes and modules, including the FailureReporter class.
[1]:
import datetime
import json
import pathlib
from http.server import BaseHTTPRequestHandler
from http.server import HTTPServer
import multiprocessing
from esa_climate_toolbox.util.reporting import FailureReporter
Define a minimal server which will receive failure reports, and start it in the background.
[2]:
log_path = pathlib.Path("server-log.txt")
class ReportRequestHandler(BaseHTTPRequestHandler):
def do_POST(self):
self.send_response(200)
self.send_header("Content-Type", "text/plain")
self.end_headers()
content_length = int(self.headers.get("Content-Length", 0))
content = self.rfile.read(content_length).decode("utf-8")
data = json.loads(content)
with open(log_path, "a") as fh:
fh.write(datetime.datetime.now(datetime.UTC).isoformat() + "\n")
for k, v in data.items():
fh.write(k + ":\n")
fh.write("\n".join(v) if isinstance(v, list) else str(v) + "\n")
fh.write("\n")
self.wfile.write("OK".encode("utf-8"))
server = HTTPServer(("127.0.0.1", 9898), ReportRequestHandler)
def start_server():
server.serve_forever()
log_path.unlink(missing_ok=True)
process = multiprocessing.Process(target=start_server)
process.start()
127.0.0.1 - - [04/Dec/2025 11:53:17] "POST / HTTP/1.1" 200 -
Create and activate a FailureReporter, which will automatically detect and report any unhandled exceptions involving the ESA Climate Toolbox. The URL of the server which receives the reports can be passed as a parameter or using the environment variable ESA_CLIMATE_TOOLBOX_FAILURE_REPORTING_SERVER_URL. The user is asked for their explicit consent before the reporter is activated.
[3]:
reporter = FailureReporter("http://localhost:9898")
reporter.activate()
Please enter YES below to consent to reporting.
YES
Activating reporting.
IPython reporting activated.
Now test the functionality. The FailureReporter class provides a method called raise_error to deliberately trigger an error within the toolbox code for testing purposes.
[4]:
FailureReporter.raise_error()
Reporting exception.
---------------------------------------------------------------------------
Exception Traceback (most recent call last)
Cell In[4], line 1
----> 1 FailureReporter.raise_error()
File ~/projects/esa-cci/esa-climate-toolbox/esa_climate_toolbox/util/reporting.py:89, in FailureReporter.raise_error()
87 @staticmethod
88 def raise_error():
---> 89 raise Exception("This is an error to test the reporting functionality.")
Exception: This is an error to test the reporting functionality.
For comparison, trigger an exception not involving the climate toolbox. This will still be reported in the notebook, but will not be reported to the server.
[5]:
1 / 0
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
Cell In[5], line 1
----> 1 1 / 0
ZeroDivisionError: division by zero
Show the contents of the server’s log file. Note that the exception from the climate toolbox is logged, but the other exception is not.
[6]:
with open(log_path, "r") as fh:
for line in fh.readlines():
print(line, end="")
2025-12-04T10:53:17.960178+00:00
toolbox-version:
1.5.1.dev0
exception-class:
Exception
exception-instance:
Exception('This is an error to test the reporting functionality.')
traceback:
File "/home/tonio/miniconda3/envs/ect/lib/python3.13/site-packages/IPython/core/interactiveshell.py", line 3577, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/ipykernel_16423/2859698634.py", line 1, in <module>
FailureReporter.raise_error()
~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
File "/home/tonio/projects/esa-cci/esa-climate-toolbox/esa_climate_toolbox/util/reporting.py", line 89, in raise_error
raise Exception("This is an error to test the reporting functionality.")
Shut down the server.
[7]:
process.kill()
server.socket.close()
[ ]: