DSS Logging
This guide covers how to control the verbosity of HEC-DSS C library messages
in pydsstools using the pydsstools.heclib.logging module.
When pydsstools opens, reads, or writes DSS files the underlying HEC-DSS C library prints status lines to the console — for example:
-----DSS---zopen Existing file opened, File: mydata.dss
-----DSS---zwrite Handle 1; Version 7: /Basin/Gauge/Flow/...
By default pydsstools keeps this output minimal (open/close events and errors
only). The logging module lets you increase verbosity for debugging or silence
output entirely, using a get_dss_logger() factory modelled on Python’s own
logging.getLogger().
Key Concepts
Concept |
Description |
|---|---|
Level |
How much the C library prints. Ranges from |
Method |
The operation category whose level you want to control (e.g. |
GLOBAL |
Special |
get_dss_logger() |
Factory function that returns a cached |
TERSE |
The pydsstools default level. Limits output to open/close events and errors. The C library’s own default is |
Level scale
DSS levels run in the opposite direction to Python’s logging levels:
a higher DSS integer means more verbose output (not less).
NONE (0) — silent
│
CRITICAL (1) — errors only
│
TERSE (2) — open/close events and errors ← pydsstools default
│
GENERAL (3) — writes, reads, housekeeping ← C library default
│
USER_DIAG (4) — input parameters (ASCII output)
│
INTERNAL_DIAG_1 (5) — internal debug messages
│
INTERNAL_DIAG_2 (6) — full trace, extremely verbose
Example 1 — Increase global verbosity
Raise the level for all operation categories at once using the GLOBAL
logger (returned when no argument is passed to get_dss_logger()).
from pydsstools.heclib.logging import get_dss_logger, Level
# Show general operational messages (writes, reads, housekeeping)
get_dss_logger().set_level(Level.GENERAL)
# Alternatively, use a string — any case is accepted
get_dss_logger().set_level("general")
Example 2 — Reduce verbosity for one noisy group
Leave all other groups unchanged and silence locking messages.
from pydsstools.heclib.logging import get_dss_logger, Level, Method
get_dss_logger(Method.LOCKING).set_level(Level.CRITICAL)
# Equivalent — string name accepted, case-insensitive
get_dss_logger("locking").set_level(Level.CRITICAL)
# Equivalent — integer method ID
get_dss_logger(9).set_level(Level.CRITICAL)
Example 3 — Silence all DSS output permanently
from pydsstools.heclib.logging import get_dss_logger, Level
# Using the Level enum
get_dss_logger().set_level(Level.NONE)
# Equivalent string shorthand
get_dss_logger().set_level("none")
Note:
Level.NONEsuppresses all output including errors. Use it only when you are certain that DSS errors are handled elsewhere.
Example 4 — Silence output for a block only
Use the suppress() context manager. The previous level is restored
automatically when the block exits, even if an exception is raised.
from pydsstools.heclib.logging import get_dss_logger
from pydsstools.heclib.dss import HecDss
with HecDss.Open("mydata.dss") as fid:
# Silence all DSS messages just for this read
with get_dss_logger().suppress():
ts = fid.read_ts("/Basin/Gauge/Flow//1HOUR//")
# Previous level is restored here
Example 5 — Temporarily raise verbosity for a block
Use the at_level() context manager to enable diagnostic output for a
specific operation, then restore the previous level.
from pydsstools.heclib.logging import get_dss_logger, Level, Method
from pydsstools.heclib.dss import HecDss
with HecDss.Open("mydata.dss") as fid:
with get_dss_logger(Method.TS_READ).at_level(Level.USER_DIAG):
ts = fid.read_ts("/Basin/Gauge/Flow//1HOUR//")
# TS_READ level restored here
Example 6 — Use Python logging integers and name strings
set_level() accepts Python logging module integers and name strings
directly, so existing logging configuration can be forwarded without a
lookup table.
import logging
from pydsstools.heclib.logging import get_dss_logger
get_dss_logger().set_level(logging.WARNING) # → Level.TERSE
get_dss_logger().set_level(logging.DEBUG) # → Level.USER_DIAG
get_dss_logger().set_level("warning") # → Level.TERSE
get_dss_logger("ts_write").set_level("debug") # → Level.USER_DIAG
Python logging integers map to DSS levels as follows (the directions are opposite — a higher Python integer means less output):
Python constant |
int |
DSS Level |
|---|---|---|
|
50 |
|
|
40 |
|
|
30 |
|
|
20 |
|
|
10 |
|
Note:
logging.NOTSET(integer 0) and the string"notset"are treated as a no-op — the call returns without changing the current level. This prevents forwarding a Python logger’s effective level from accidentally silencing DSS output. To silence output, useLevel.NONEor"none"explicitly.
Example 7 — Query the current level
The level property reads the current level directly from the C library.
from pydsstools.heclib.logging import get_dss_logger, Method
logger = get_dss_logger(Method.TS_READ)
print(logger.level) # Level.TERSE
print(logger.level.name) # "TERSE"
print(logger.level.value) # 2
print(logger) # DssLogger(TS_READ, level=TERSE)
Level reference
Name |
Value |
Aliases |
Description |
|---|---|---|---|
|
0 |
|
No messages at all (including errors). Use |
|
1 |
|
Error messages only. |
|
2 |
|
Open/close events and critical errors. pydsstools default. |
|
3 |
|
General messages: writes, reads, housekeeping. C library default. |
|
4 |
|
Diagnostic messages including input parameters. ASCII output only. |
|
5 |
— |
Internal debug messages (level 1). Not recommended for end-users. |
|
6 |
— |
Full internal trace (level 2). Extremely verbose. |
The alias names (ERROR, WARNING, INFO, DEBUG) match the Python
logging module constants so that both naming conventions work
interchangeably.
Method reference
Name |
ID |
Description |
|---|---|---|
|
0 |
All groups — also sets DSS-6 Fortran layer. Use this for broad level control. |
|
1 |
General DSS-7 operations. Fans out to all groups but does not set the DSS-6 layer. |
|
2 |
Low-level record-get operations. |
|
3 |
Low-level record-put operations. |
|
4 |
High-level read operations. |
|
5 |
High-level write operations. |
|
6 |
Permanent-storage / housekeeping operations. |
|
7 |
File open and close operations. |
|
8 |
Record existence checks. |
|
9 |
Multi-user file-locking operations. |
|
10 |
Time-series read operations. |
|
11 |
Time-series write operations. |
|
12 |
Alias management. |
|
13 |
Record copy / duplicate operations. |
|
14 |
Miscellaneous utility functions. |
|
15 |
Catalog operations. |
|
16 |
File-integrity check operations. |
|
17 |
Java Native Interface bridge (not used from pure Python). |
GLOBAL vs GENERAL
Both Method.GLOBAL and Method.GENERAL fan the level out to all 18 C
library method slots simultaneously. The difference is that GLOBAL
additionally writes to the DSS-6 Fortran messaging layer (via zset6_),
making it effective for DSS-6 files opened with zopen6. GENERAL only
covers DSS-7.
Prefer GLOBAL (the default) when you want a single level for
everything.
Backward compatibility
Earlier pydsstools code used dss_logging from pydsstools.heclib.utils:
# Old API — still works but raises DeprecationWarning
from pydsstools.heclib.utils import dss_logging
dss_logging.setLevel("General")
dss_logging.config(method=9, level="Critical")
These calls delegate to get_dss_logger() internally, so state is
consistent regardless of which API is used. Prefer the new API in new code:
# New API
from pydsstools.heclib.logging import get_dss_logger, Level, Method
get_dss_logger().set_level(Level.GENERAL)
get_dss_logger(Method.LOCKING).set_level(Level.CRITICAL)