BetterstackLogger is the Logger component’s Better Stack backend. It wraps the official @logtail/node client to ship structured logs to Better Stack’s Logs (Logtail) platform, attaching your data object to every entry and flattening exception details onto the payload. It implements the framework’s ILogger interface, so call sites stay provider-agnostic and you can swap the backend without touching the code that emits logs.
Why Better Stack
- Hosted log management. Ship logs to Better Stack for search, dashboards, and alerting without running your own pipeline.
- Structured payloads. Pass a
dataobject and it’s sent alongside the message as structured fields. - Exception-aware.
error()accepts anIExceptionand flattens its name, status, and stack trace onto the payload. - Configurable ingest host. Point at a custom ingesting endpoint or use the Logtail default.
- Buffered delivery. The client batches logs and
flush()drains the buffer before the process exits. - Container-managed. Registered with
@decorator.logger()and resolved from the container.
Installation
BetterstackLogger ships with @ooneex/logger and depends on the Logtail Node client.
Environment variables
| Variable | Required | Purpose |
|---|---|---|
BETTERSTACK_LOGGER_SOURCE_TOKEN | Yes | Better Stack source token. Missing throws LoggerException (LOG_FAILED). |
BETTERSTACK_LOGGER_INGESTING_HOST | No | Custom ingesting endpoint. Omitted uses the Logtail default. |
BetterstackLogger is constructed, so a missing token fails fast at startup.
How it works
On construction, the backend creates a singleLogtail client with your source token (and endpoint, if set). Each level method forwards the message and data to the matching Logtail method — error, warn, info, debug — while log and success map to info with a level field (LOG / SUCCESS) so the original level is preserved. When an IException is passed to error(), its name, status, and JSON stack trace are extracted and merged into the payload. Delivery is buffered; call flush() to drain it.
| Method | Purpose |
|---|---|
init() | No-op; returns immediately. |
error(message, data?) | Ship an error. message may be a string or IException. |
warn(message, data?) | Ship a warning. |
info(message, data?) | Ship informational output. |
debug(message, data?) | Ship debug detail. |
log(message, data?) | Ship a general message (sent as info with level: "LOG"). |
success(message, data?) | Ship a success message (sent as info with level: "SUCCESS"). |
flush() | Drain the buffered logs. |
Usage
error() accepts a plain message or an IException — when given an exception it flattens the name, status, and stack trace onto the payload:
Use in the app
In an@ooneex/app application, register BetterstackLogger by adding it to the loggers array of your App config. The loggers slot of AppConfigType is typed as LoggerClassType[], so you pass the class itself — the framework registers it with the container at startup.
loggers accepts an array, so you can ship to Better Stack while also printing to the terminal in development:
BETTERSTACK_LOGGER_SOURCE_TOKEN (and optionally BETTERSTACK_LOGGER_INGESTING_HOST) in your .env.yml (or environment), then inject the logger into a service or controller:
Exceptions
BetterstackLogger throws LoggerException on misconfiguration, carrying a machine-readable key.
| Key | When |
|---|---|
LOG_FAILED | BetterstackLogger is constructed without BETTERSTACK_LOGGER_SOURCE_TOKEN. |
Best practices
- Call
flush()on exit. Delivery is buffered; drain it before the process terminates so you don’t drop the last logs. - Pass structured data, not interpolated strings. Keep the message stable and put variable detail in the
dataobject so logs stay searchable. - Log exceptions as exceptions. Pass the
IExceptiontoerror()so the name, status, and stack trace are captured. - Keep the source token in the environment. Load it from
.env; never hard-code it. - Match the ingest host to your source. Set
BETTERSTACK_LOGGER_INGESTING_HOSTwhen your source uses a region-specific endpoint.