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 NONE (silent) to INTERNAL_DIAG_2 (full trace).

Method

The operation category whose level you want to control (e.g. TS_READ, LOCKING, OPEN).

GLOBAL

Special Method that sets the level for all operation categories at once. This is the default when no method is specified.

get_dss_logger()

Factory function that returns a cached DssLogger for a given method — analogous to logging.getLogger().

TERSE

The pydsstools default level. Limits output to open/close events and errors. The C library’s own default is GENERAL.

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.NONE suppresses 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

logging.CRITICAL

50

CRITICAL

logging.ERROR

40

CRITICAL

logging.WARNING

30

TERSE

logging.INFO

20

GENERAL

logging.DEBUG

10

USER_DIAG

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, use Level.NONE or "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

NONE

0

NULL

No messages at all (including errors). Use "none" as a string shorthand.

CRITICAL

1

ERROR

Error messages only.

TERSE

2

WARNING

Open/close events and critical errors. pydsstools default.

GENERAL

3

INFO

General messages: writes, reads, housekeeping. C library default.

USER_DIAG

4

DEBUG

Diagnostic messages including input parameters. ASCII output only.

INTERNAL_DIAG_1

5

Internal debug messages (level 1). Not recommended for end-users.

INTERNAL_DIAG_2

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

GLOBAL

0

All groups — also sets DSS-6 Fortran layer. Use this for broad level control.

GENERAL

1

General DSS-7 operations. Fans out to all groups but does not set the DSS-6 layer.

GET

2

Low-level record-get operations.

PUT

3

Low-level record-put operations.

READ

4

High-level read operations.

WRITE

5

High-level write operations.

PERM

6

Permanent-storage / housekeeping operations.

OPEN

7

File open and close operations.

CHECK

8

Record existence checks.

LOCKING

9

Multi-user file-locking operations.

TS_READ

10

Time-series read operations.

TS_WRITE

11

Time-series write operations.

ALIAS

12

Alias management.

COPY

13

Record copy / duplicate operations.

UTILITY

14

Miscellaneous utility functions.

CATALOG

15

Catalog operations.

FILE_CHECK

16

File-integrity check operations.

JNI

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)