hannah-grpc-lib (Python)
Shared gRPC client code for Hannah components. Until 0.3.1 this package was called
hannah-logging (import hannah_logging); that package gets no further updates.
Logging (hannah_grpc.logging)
Ships the logs of a Hannah component to the Hannah log collector, so a user can download one archive of all components' logs from the WebUI.
The library adds one logging.Handler. Your existing handlers (stdout, journald, syslog)
keep working unchanged.
Installation
pip install hannah-grpc-lib
The library relies on the hannah-proto version the component already uses, because
every call to Hannah Core has to carry the matching protocol version.
Usage
import logging
import hannah_grpc.logging as hannah_logging # alias it: `from hannah_grpc import logging` would shadow the stdlib
logging.basicConfig(level=logging.INFO)
# As early as possible: everything logged from here on is buffered.
shipping = hannah_logging.install(
"telegram",
version=__version__,
secrets=[config.bot_token], # masked wherever they appear
logger_categories={"hannah.stt": hannah_logging.TRANSCRIPT},
)
# Once the config is loaded:
shipping.connect(
hannah_address="localhost:50051", # discovery via Hannah Core
collector_address=None, # optional static fallback
)
Setting the collector address directly
A component that knows the collector address itself (Hannah Core, which announces it) passes it in instead of subscribing to discovery:
shipping.set_collector_address("192.168.1.10:50061") # None withdraws it
It starts shipping if connect hasn't been called yet, takes precedence over the static
collector_address, and a new address moves the stream.
Categories
Each line is tagged GENERAL, TRANSCRIPT (speech transcripts, user utterances) or
METADATA (room and device names, presence). Exports can leave out the last two, and
the WebUI does so by default.
- By logger name:
logger_categories={"hannah.stt": TRANSCRIPT}also covers child loggers such ashannah.stt.whisper. - Per call:
log.info("heard: %s", text, extra={hannah_logging.CATEGORY_ATTR: hannah_logging.TRANSCRIPT})
Secrets
Before a line leaves the process, the library masks:
- values of keys such as
password,token,api_key,secret,psk Bearertokens, JWTs, credentials in URLs (mqtt://user:pw@host), PEM private keys- Telegram bot tokens and well-known token prefixes (
glpat-,ghp_, …) - every value passed as
secrets=[…]or later throughshipping.add_secret(…)
Extra regexes go in secret_patterns=[…]. The component's own handlers still see the
unmasked line.
Behaviour
- Buffer: 4 MiB by default (
max_buffer_bytes). When it is full, the oldest lines are dropped and reported to the collector as a gap. While no collector is known, the buffer simply keeps running as a ring. - Timestamps are taken when a line is logged, not when it is sent.
- Instance name: tells several instances of a component apart in the export. Without
instance, the library uses theHANNAH_LOG_INSTANCEenvironment variable, else the hostname. In a container whose hostname is just its ID (a new one with every recreate), it usescontainerinstead. Running the same component in several containers? SetHANNAH_LOG_INSTANCE, orhostname:in Compose. - Discovery: subscribes to Hannah Core's infrastructure announcements and follows the
log collector when it moves. If Core is unreachable, the last known collector is kept.
The static
collector_addressis used while none is announced. - Never blocks the component: sending runs on its own threads. Connection problems
are logged once to the
hannah_grpc.logginglogger (not shipped) and retried with backoff. - Shutdown: at exit, the library tries for up to 2 seconds to send what is still
buffered (
shipping.close(timeout=…)).
Calling Hannah Core (hannah_grpc.client)
Works with hannah.v1 types and falls back to the previous, unversioned API when Hannah
Core is too old for hannah.v1, so a component can be updated before Core.
import grpc.aio
from hannah_proto.v1 import hannah_pb2
from hannah_grpc import client as hannah_client
channel = grpc.aio.insecure_channel(address, interceptors=hannah_client.aio_interceptors())
stubs = hannah_client.VersionedStub(channel)
stub = await stubs.resolve()
await stub.SubmitText(hannah_pb2.SubmitTextRequest(text="..."))
# after a reconnect (e.g. a stream was lost):
stubs.reset()
Synchronous channels: grpc.intercept_channel(channel, *hannah_client.sync_interceptors())
and SyncVersionedStub.
- Probe: once per connection,
GetSatelliteson thehannah.v1path decides. If Core answersUNIMPLEMENTED, all calls use the previous API and a warning is logged once (hannah_grpc.clientlogger). Any other error isn't cached; the next call probes again. A single method Core doesn't know yet does not switch the connection. - Headers: the interceptors attach
x-proto-versionandx-compat-version. Forgrpc.aiostreams, passmetadata=hannah_client.stream_metadata(stubs.service, "Method")explicitly; grpc.aio's stream interceptors don't reliably apply metadata.
Release files for hannah-grpc-lib 0.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| hannah_grpc_lib-0.5.0.tar.gz | 23.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| hannah_grpc_lib-0.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 42.7 kB
Release files / hannah_grpc_lib-0.5.0.tar.gz
| Download URL | hannah_grpc_lib-0.5.0.tar.gz |
|---|---|
| Size | 23.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
edc0a349efd5605ac66f4a66b5f1580a1c8303169e97c32764bff6b62a7aac9a
|
|
BLAKE2b-256 checksum How to use checksums |
3e16b866ba00e0a0deb8659d2feb7d7c21ed4bf86f9b5d85cd3fda238bbe85a3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|
Release files / hannah_grpc_lib-0.5.0-py3-none-any.whl
| Download URL | hannah_grpc_lib-0.5.0-py3-none-any.whl |
|---|---|
| Size | 19.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c2152c82e1d342cbc2feda89f8548136e112853da84dc254b94475f1fe64b29a
|
|
BLAKE2b-256 checksum How to use checksums |
91d2d0a6c3df056634f6a38422a9c04219f5e3d0fa0318428810c0ae35a9a7a4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|