Logging
MPAS-Tools includes capabilities for logging output from many of its function
calls to either stdout/stderr or to a log file.
Using logging
Logging is performed by creating a
mpas_tools.logging.LoggingContext object inside a with
statement:
with LoggingContext(__name__) as logger:
...
logger.info('Step 1. Generate mesh with JIGSAW')
...
logger.info('Step 2. Convert triangles from jigsaw format to netcdf')
...
logger.info('Step 3. Convert from triangles to MPAS mesh')
...
A LoggingContext is given a unique name (typically the name of the module,
__name__). If no other arguments are supplied, the logger will write to
stdout with calls to logger.info() and logger.debug(), and to
stderr for calls to logger.error().
It is convenient to create this kind of a logger in contexts where logging
might be to a file or might be to stdout/stderr, depending on the
calling code.
Often, a function should support output to a logger but you do not
necessarily want the calling code to have to crate one if the calling code just
wants output to stdout/stderr. In such contexts, the function can take
logger=None as an optional argument. Then, in the function itself, a new
LoggingContext can be created that will just use the existing logger
if there is one or create a new ong for stdout/stderr if none is
provided. As an example, here is a snippet from
mpas_tools.mesh.creation.build_mesh.build_spherical_mesh():
def build_spherical_mesh(cellWidth, lon, lat, earth_radius,
out_filename='base_mesh.nc', plot_cellWidth=True,
dir='./', logger=None):
...
with LoggingContext(__name__, logger=logger) as logger:
...
logger.info('Step 1. Generate mesh with JIGSAW')
jigsaw_driver(cellWidth, lon, lat, on_sphere=True,
earth_radius=earth_radius, logger=logger)
logger.info('Step 2. Convert triangles from jigsaw format to netcdf')
jigsaw_to_netcdf(msh_filename='mesh-MESH.msh',
output_name='mesh_triangles.nc', on_sphere=True,
sphere_radius=earth_radius)
logger.info('Step 3. Convert from triangles to MPAS mesh')
write_netcdf(convert(xarray.open_dataset('mesh_triangles.nc'), dir=dir,
logger=logger),
out_filename)
The optional argument logger is passed to a new LoggingContext. That
way logger is guaranteed not to be None (so calls to logger.info
work properly). The logger is also passed on to
mpas_tools.mesh.creation.jigsaw_driver.jigsaw_driver() and
mpas_tools.io.write_netcdf() for output from those functions.
To log to a new log file, simply pass a file name to the log_filename
argument:
with LoggingContext(name=__name__, log_filename='output.log') as logger:
...
logger.info('Step 1. Generate mesh with JIGSAW')
If both the logger and log_filename arguments are not None, the
log_filename is ignored and the existing logger is simply used for
logging.
Logging subprocess calls
You can also run subprocesses and capture the output to a logger. This is
accomplished with the function mpas_tools.logging.check_call(), which
acts a lot like subprocess.check_call() but with output going to
the logger, as in this example from
mpas_tools.mesh.creation.jigsaw_driver.jigsaw_driver()
from mpas_tools.logging import check_call
def jigsaw_driver(cellWidth, x, y, on_sphere=True, earth_radius=6371.0e3,
geom_points=None, geom_edges=None, logger=None):
...
opts.jcfg_file = 'mesh.jig'
...
check_call(['jigsaw', opts.jcfg_file], logger=logger)