statoss
Error tracking and logs for StatOSS in Python: a
thin layer over sentry-sdk that
adds your app's own levels, categories and fields. Python 3.9 or later,
sentry-sdk 2.35 or later.
Install
pip install statoss # or: uv add statoss, poetry add statoss
Start
Call init once, as early as you can, with your app's DSN. The DSN is on the
app's setup page in StatOSS and looks like
https://<key>@ingest.statoss.com/<app number>.
import os
import statoss
statoss.init(
dsn=os.environ["STATOSS_DSN"],
environment="production",
release="shop@1.4.0",
)
The options are sentry-sdk's, and without a schema statoss.init is
sentry_sdk.init: integrations, scopes and everything else work as its
documentation says. statoss.flush, set_user, set_tag, set_context and
add_breadcrumb are sentry-sdk's own; import sentry_sdk for the rest.
Levels, categories and fields
Sentry has five levels: fatal, error, warning, info and debug.
StatOSS lets you name your own, and adds a category and fields.
schema = statoss.define_schema(
levels={
"payment-failed": statoss.Level(like="error", label="Payment failed"),
"slow-query": "warning",
},
categories={
"billing": statoss.Category(label="Billing", color="plum"),
"auth": statoss.Category(),
},
fields={
"route": statoss.Field(label="Route", filterable=True),
"duration_ms": statoss.Field(type="number", unit="ms", label="Duration"),
},
)
statoss.init(dsn=os.environ["STATOSS_DSN"], schema=schema)
try:
charge(order)
except CardError as error:
statoss.capture_exception(
error, level="payment-failed", category="billing", fields={"route": "/pay"}
)
# level "error", tags statoss.level=payment-failed, statoss.category=billing, route=/pay
statoss.capture_message("Search took 2.4 s", level="slow-query", fields={"duration_ms": 2400})
# level "warning", tags statoss.level=slow-query, duration_ms=2400
statoss.set_category("billing") # every later event in this scope
- An event is sent at the nearest Sentry level, with its own level in the tag
statoss.leveland its category instatoss.category, so Sentry and other Sentry-compatible tools still read it. Fields are sent as tags. leveltakes a Sentry level or a level of the schema. A name the schema does not hold is sent aserrorand, withdebug=True, logged. A level or category with characters a name cannot have (see below) is left out and logged the same way.- The level given to a capture wins over a level set on a scope.
set_category(name, scope=None)sets it on the scope you pass, else on the isolation scope assentry_sdk.set_tagdoes.set_category(None)takes it off.- An event whose level is one of the schema's own, as
scope.set_levelorsentry_sdk.capture_messagemay leave it, is sent the same way, after yourbefore_send.
Logs
With enable_logs=True, statoss.logger sends log lines through
sentry-sdk's logger, which StatOSS's Logs area searches. Without it they
are dropped.
statoss.init(dsn=os.environ["STATOSS_DSN"], schema=schema, enable_logs=True)
statoss.logger.info("Order {order_id} paid", order_id=7, attributes={"amount": 12.5})
statoss.logger.log("payment-failed", "Card declined", attributes={"order_id": 7})
# level "error", attributes statoss.level=payment-failed, order_id=7
trace,debug,info,warning,errorandfataltake a template and its values as sentry-sdk's do:"Paid {amount}"withamount=12sends "Paid 12", the template and each value as attributes.attributes=adds more.log(level, template, ...)takes a level by name: one of the six (warntoo), a Sentry level, or one of the schema's. A schema level is sent at its range's level (tracestaystrace), with its severity assentry.severity_numberwhen the schema gives one, and its name as the attributestatoss.level. A name the schema does not hold is sent aserrorand, withdebug=True, logged.client.loggerfrominittakes only the schema's levels when they are typed (see Typing).- Lines are sent in batches by sentry-sdk;
statoss.flush()sends what waits. sentry-sdk'sbefore_send_logsees each line,statoss.levelincluded. - Records of Python's
loggingmodule at INFO and up go as log lines too withenable_logs=True, except on sentry-sdk 2.68.0, where they needLoggingIntegration(capture_sentry_logs=True).
The schema
define_schema checks everything as StatOSS does and raises SchemaError
naming the place of the first problem.
| Kind | Name | Settings |
|---|---|---|
| Level | 1 to 32 lowercase letters, digits, dots, dashes or underscores | like (fatal, error, warning, info, debug or trace), label, color, severity (1 to 24, inside the like range) |
| Category | 1 to 64 of the same | label, color, hidden, owner (a team member's email) |
| Field | any tag key not starting statoss. |
label, type (text or number), unit, filterable |
-
A level may be given as its
likealone ("slow-query": "warning"); categories and fields as a list of names (categories=["billing", "auth"]). -
A level ranking like
traceis sent asdebug. Sentry's own levels take a label and a colour ("error": statoss.Level(label="Error")). -
Colours: crimson, red, orange, amber, yellow, green, aqua, teal, blue, violet, indigo, magenta, plum, grey.
-
Rules sort events that name no level or category of their own:
rules = [ statoss.Rule(subject="logger", value="payments", category="billing"), statoss.Rule(subject="tag", tag_key="db", match="starts", value="pg", level="slow-query"), ]
subjectistag,logger,exception,culprit,titleormessage;matchisis(the default),startsorcontains.
schema.to_desired() gives the schema as StatOSS's desired state, the JSON
that POST /api/v1/apps/{app}/schema/plan takes as desired. Rules left out
(rules=None) leave the app's rules as they are; rules=[] says there are
none.
Typing
The package ships type hints. Name your levels and categories with Literal
and a type checker (mypy, pyright) refuses a name the schema does not hold:
from typing import Literal
import statoss
LevelName = Literal["payment-failed", "slow-query"]
CategoryName = Literal["billing", "auth"]
schema: statoss.Schema[LevelName, CategoryName] = statoss.define_schema(
levels={"payment-failed": "error", "slow-query": "warning"},
categories=["billing", "auth"],
)
client = statoss.init(dsn=os.environ["STATOSS_DSN"], schema=schema)
client.capture_exception(error, level="payment-failed", category="billing")
client.capture_exception(error, level="payment-faild") # error: not a LevelName
client.logger.log("slow-query", "Search took {ms} ms", ms=2400)
client.logger.log("slow-qeury", "Search took {ms} ms", ms=2400) # error: not a LevelName
init returns a statoss.Client[LevelName, CategoryName] whose
capture_exception, capture_message, set_category and logger.log
take only those names and Sentry's levels. The module functions and
statoss.logger take any name.
Developing
From sdk-python/, with uv:
uv run pytest # tests, including the mypy check of tests/typing_check.py
uv run --with pyright pytest # the same, with pyright as well
uv build # wheel and sdist in dist/
tests/fixtures holds a schema's desired state and the lists and limits the
package checks against; src/lib/python-sdk-schema.test.ts holds the server
to them. After changing either side, run uv run python tests/sample_schema.py
and pnpm prettier --write sdk-python/tests/fixtures.
License
MIT
Metadata
Release files for statoss 0.2.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 | Provenance | |
|---|---|---|---|---|
| statoss-0.2.0.tar.gz | 14.4 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| statoss-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 30.6 kB
Release files / statoss-0.2.0.tar.gz
| Download URL | statoss-0.2.0.tar.gz |
|---|---|
| Size | 14.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a9fe96619e2e9a7778cced1f1f8a52bd58a5c90a09919f5dd8b4475cf5d3f9b2
|
|
BLAKE2b-256 checksum How to use checksums |
153e58e79c041aa7b0c0394968e5ed1b04d60d1a700e08dca0d2028520645b71
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.
Transparency logRelease files / statoss-0.2.0-py3-none-any.whl
| Download URL | statoss-0.2.0-py3-none-any.whl |
|---|---|
| Size | 16.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
cdae8a98d6c7642aba3c3001b7d25ea07de6f8ac915bca932d322198c22a408e
|
|
BLAKE2b-256 checksum How to use checksums |
f86d0b9dac57029fc4554229f4f421bf0c954a9e78c3286470c76ac1702c233c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.
Transparency logSecurity for statoss 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for statoss-0.2.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.