Skip to content

Logging Management

Most customers manage loggers through the Console UI. The management API is for infrastructure-as-code, CI/CD pipelines, setup scripts, and automated testing. Management operations are stateless HTTP calls — no install() or WebSocket needed.

Loggers and log groups are independent namespaces:

  • client.manage.loggers.* — individual logger entries (one per logger category in your app).
  • client.manage.log_groups.* (or logGroups / LogGroups) — groups for bulk-setting levels across many loggers.

Create a Logger

A logger is identified by a string id matching the category name in your application's logging framework (com.acme.payments, sqlalchemy.engine, MyApp.Db, etc.). Set the level (and optional environment overrides), then save.

python
import asyncio
from smplkit import AsyncSmplManagementClient, LogLevel

async with AsyncSmplManagementClient() as manage:
    payments = manage.loggers.new("acme.payments")
    payments.set_level(LogLevel.INFO)
    payments.set_level(LogLevel.WARN, environment="production")
    payments.set_level(LogLevel.DEBUG, environment="staging")
    await payments.save()

Log Levels

Available log levels, from finest to most restrictive:

ConstantValueDescription
TRACE"TRACE"Finest-grained diagnostic output
DEBUG"DEBUG"Detailed diagnostic information
INFO"INFO"General operational information
WARN"WARN"Potential issues that don't prevent operation
ERROR"ERROR"Errors that affect operation
FATAL"FATAL"Severe errors that may cause termination
SILENT"SILENT"Suppresses all output

Naming by SDK:

LanguageReference
PythonLogLevel.WARN
TypeScriptLogLevel.WARN
Gosmplkit.LogLevelWarn
JavaLogLevel.WARN
C#LogLevel.Warn

Inheritance

A logger's effective level resolves through a chain (first non-null wins):

  1. Per-environment override on the logger
  2. Logger's own base level
  3. Group level (if the logger belongs to a group)
  4. Dot-notation ancestor (acme.payments falls through to acme)
  5. System fallback: INFO

Create a parent logger and let children inherit:

python
root = manage.loggers.new("acme")
root.set_level(LogLevel.INFO)
await root.save()

# child with no level — inherits "acme.INFO" by dot-notation
db = manage.loggers.new("acme.db")
await db.save()                          # level=None — inherits

# child with explicit level — overrides parent
payments = manage.loggers.new("acme.payments")
payments.set_level(LogLevel.WARN)
await payments.save()

Clear Levels

Clear the base level (falls back to group / ancestor / system default), or clear a specific environment override.

python
root.clear_level(environment="staging")    # remove staging override
root.clear_level()                         # remove base level
await root.save()

Log Groups

Log groups bulk-set levels across many loggers. A group has its own base level and environment overrides; loggers assigned to a group inherit the group's level when they have no explicit level of their own.

Create a Group

python
group = manage.log_groups.new("databases", name="Databases")
group.set_level(LogLevel.WARN)
group.set_level(LogLevel.ERROR, environment="production")
await group.save()

Assign a Logger to a Group

Set the logger's group to the group's id, then save.

python
db_logger = manage.loggers.new("acme.db")
db_logger.group = group.id
await db_logger.save()

List, Get, Delete Groups

python
groups = await manage.log_groups.list()
group = await manage.log_groups.get("databases")
await manage.log_groups.delete("databases")

Promote and Release

Toggle a logger's managed flag. A managed logger has its level applied to the native logger by the runtime; releasing (managed=false) removes it from active management without deleting it.

Auto-demote: If a managed logger has no base level, no environment overrides, and no group assignment, the server automatically sets managed=false on save. This prevents "zombie" managed loggers that have no configuration. To keep a logger managed with no explicit level, set at least one environment override or assign it to a group before saving.

python
payments.managed = False
await payments.save()       # release

payments.managed = True
await payments.save()       # re-promote (level / overrides reset)

List, Get, Delete Loggers

python
loggers = await manage.loggers.list()
fetched = await manage.loggers.get("acme.payments")
await manage.loggers.delete("acme.payments")

Bulk-Register Logger Sources

For seeding fresh accounts or migrating fixtures across services, bulk-register a list of logger sources (key + level + service + environment). The runtime auto-discovery uses the same buffer; calling register(...) directly is for setup scripts that don't have a live application to discover from.

python
from smplkit import LoggerSource, LogLevel

manage.loggers.register([
    LoggerSource(name="acme.db",       level=LogLevel.INFO),
    LoggerSource(name="acme.payments", level=LogLevel.WARN),
])
manage.loggers.flush()                   # or pass flush=True to register

Sync Client (Python)

For synchronous applications (Django, Flask, CLI tools), use SmplManagementClient instead of AsyncSmplManagementClient:

python
from smplkit import SmplManagementClient, LogLevel

with SmplManagementClient() as manage:
    lg = manage.loggers.new("acme.payments")
    lg.set_level(LogLevel.WARN)
    lg.save()

    lg.set_level(LogLevel.ERROR, environment="production")
    lg.save()

    grp = manage.log_groups.new("databases", name="Databases")
    grp.set_level(LogLevel.WARN)
    grp.save()

    lg.group = grp.id
    lg.save()

From a runtime client

Inside a service that already holds a SmplClient, use client.manage.loggers.* and client.manage.log_groups.* to avoid juggling two clients:

python
from smplkit import SmplClient, LogLevel

with SmplClient(environment="production", service="my-service") as client:
    lg = client.manage.loggers.get("acme.payments")
    lg.set_level(LogLevel.ERROR, environment="production")
    lg.save()

The same pattern applies in every SDK: client.manage.loggers.* / client.manage.log_groups.* (Python/TS/C#), client.Manage().Loggers() / client.Manage().LogGroups() (Go), client.manage().loggers / client.manage().logGroups (Java).

Next Steps