Configuring Logging in gdptools#

gdptools uses Python’s standard logging module for all diagnostic output. By default the library emits no messages because it registers a NullHandler. This notebook shows how to enable and customize logging so you can monitor progress, debug issues, and control verbosity.

Note: This notebook uses zonal_engine="serial" for logging demonstration purposes. The serial, parallel, and dask engines are deprecated — use zonal_engine="exactextract" for new work. See the zonal statistics notebook for details.

Setup#

We use the same local test data as the zonal statistics notebook so this notebook works fully offline.

import logging

import geopandas as gpd
import rioxarray as rxr

from gdptools import ZonalGen
from gdptools.data.user_data import UserTiffData
rds = rxr.open_rasterio("../../../tests/data/rasters/slope/slope.tif")
gdf = gpd.read_file("../../../tests/data/Oahu.shp")

data = UserTiffData(
    source_var="slope",
    source_ds=rds,
    source_crs=26904,
    source_x_coord="x",
    source_y_coord="y",
    band=1,
    bname="band",
    target_gdf=gdf,
    target_id="fid",
)

Enabling INFO-level logging#

The simplest way to see gdptools output is to call logging.basicConfig. At INFO level you get workflow milestones, timing summaries, and data dimensions — enough to confirm things are progressing.

logging.basicConfig(
    level=logging.INFO,
    format="%(name)s | %(levelname)s | %(message)s",
    force=True,
)

zg = ZonalGen(
    user_data=data,
    zonal_engine="serial",
    zonal_writer="csv",
    out_path=".",
    file_prefix="logging_demo",
)
stats = zg.calculate_zonal(categorical=False)
stats.head()

Switching to DEBUG level#

At DEBUG level gdptools logs internal state: geometry validation details, intersection data, and spatial-index timing. This is useful when diagnosing unexpected results.

logging.basicConfig(
    level=logging.DEBUG,
    format="%(name)s | %(levelname)s | %(message)s",
    force=True,
)

stats = zg.calculate_zonal(categorical=False)
stats.head()

Selective logging: gdptools only#

Setting the root logger to DEBUG can produce a flood of output from third-party libraries (rasterio, fiona, urllib3, etc.). A better approach is to keep the root level at WARNING and lower only the gdptools logger.

logging.basicConfig(
    level=logging.WARNING,
    format="%(name)s | %(levelname)s | %(message)s",
    force=True,
)
logging.getLogger("gdptools").setLevel(logging.DEBUG)

stats = zg.calculate_zonal(categorical=False)
stats.head()

Logging to a file#

For long-running jobs you may want to send log output to a file while keeping the console quiet. Add a FileHandler to the gdptools logger:

# Reset root logger to suppress console output
logging.basicConfig(level=logging.WARNING, force=True)

gdp_logger = logging.getLogger("gdptools")
gdp_logger.setLevel(logging.DEBUG)

fh = logging.FileHandler("gdptools_debug.log", mode="w")
fh.setFormatter(logging.Formatter("%(asctime)s | %(name)s | %(levelname)s | %(message)s"))
gdp_logger.addHandler(fh)

stats = zg.calculate_zonal(categorical=False)

# Show the first few lines of the log file
with open("gdptools_debug.log") as f:
    for line in f.readlines()[:10]:
        print(line, end="")

# Clean up the handler so it doesn't persist into later cells
gdp_logger.removeHandler(fh)
fh.close()

Quick reference#

Level

What gdptools emits

DEBUG

Internal state, geometry validation details, intersection data

INFO

Workflow milestones, timing summaries, data dimensions, weight-gen progress

WARNING

Recoverable issues (e.g., antimeridian wrapping, CRS validation fallbacks)

ERROR

Failures that precede an exception being raised

Common patterns#

import logging

# Quick: see everything
logging.basicConfig(level=logging.DEBUG)

# Recommended: gdptools detail, quiet third-party
logging.basicConfig(level=logging.WARNING)
logging.getLogger("gdptools").setLevel(logging.INFO)

# Production: log to file
fh = logging.FileHandler("gdp.log")
logging.getLogger("gdptools").addHandler(fh)