oklog 1.4.0
oklog: ^1.4.0 copied to clipboard
A simple yet capable logging utility for Dart and Flutter. Just log. ok.
oklog #
A simple yet capable logging utility for Dart and Flutter. Just log. ok.
Features #
- Six log levels:
trace,debug,info,notice,warn,error - Colored, emoji-decorated console output via
ConsoleSinkandConsoleFormatter - Filter logs by level via
LevelFilterProcessorand by class name viaNameFilterProcessor - Extensible pipeline: add
LogProcessorinstances to transform/filter, andLogSinkinstances to route output - Global
loginstance (OkLogger) ready to use out of the box - Observability support: structured events and metrics via
log.obs - Error alerting with context:
ContextBufferProcessor+ErrorAlertSink+ErrorExporter; composable HTTP transport viaHttpErrorExporter+ErrorFormatter - Slack integration:
SlackPayloadFormatterandSlackErrorExporteravailable viapackage:oklog/oklog_slack.dart
Getting started #
Add the dependency to your pubspec.yaml:
dependencies:
oklog: ^1.1.0
Then import the library:
import 'package:oklog/oklog.dart';
For Slack integration, add the additional import:
import 'package:oklog/oklog_slack.dart';
Usage #
Basic logging #
import 'package:oklog/oklog.dart';
void main() {
log.level = LogLevel.trace; // show all levels
log.trace('main', 'Trace message');
log.debug('main', 'Debug message');
log.info('main', 'Info message');
log.notice('main', 'Notice message');
log.warn('main', 'Warning message');
try {
throw Exception('Something went wrong!');
} catch (e, st) {
log.error('main', 'An error occurred.', error: e, stackTrace: st);
}
}
Using with a class instance #
Pass this as the first argument and the class name is resolved automatically:
class MyClass {
void myMethod() {
log.info(this, 'Hello from MyClass');
}
}
Attaching structured attributes #
All log methods accept an optional attrs map for structured key-value metadata:
log.debug(this, 'User action', attrs: {'userId': 123, 'action': 'login'});
Filtering by class name #
Use log.nameFilter (a NameFilterProcessor) to restrict which classes are logged:
// Only log messages from classes whose name contains 'main'
log.nameFilter.allowList = ['main'];
// Suppress log messages from classes whose name contains 'MyClass'
log.nameFilter.denyList = ['MyClass'];
// Clear filters
log.nameFilter.allowList = [];
log.nameFilter.denyList = [];
Silencing output #
log.sinks.clear(); // remove all sinks to suppress output
Changing the log level #
log.level = LogLevel.warn; // only warn and error are printed
Custom sinks #
Implement LogSink and override emit to route entries anywhere:
class FileSink extends LogSink {
@override
void emit(LogEntry entry) {
if (entry is LogRecord) {
// write to a file, remote service, etc.
}
}
}
log.sinks.add(FileSink());
Multiple sinks can be active at the same time. The built-in ConsoleSink is
added automatically by OkLogger.
Custom formatters #
ConsoleSink accepts a LogFormatter<String> that converts a LogEntry to a
string. Replace the default ConsoleFormatter to change how entries are rendered
without touching sink behaviour:
class JsonFormatter extends LogFormatter<String> {
@override
String format(LogEntry entry) {
if (entry is LogRecord) {
return jsonEncode({
'level': entry.level.name,
'class': entry.className,
'message': entry.message,
});
}
return entry.toString();
}
}
log.sinks
..clear()
..add(ConsoleSink(formatter: JsonFormatter()));
Custom processors #
Implement LogProcessor and return false to drop an entry from the pipeline:
class SamplingProcessor implements LogProcessor {
@override
bool process(LogEntry entry) => Random().nextDouble() > 0.9; // keep 10%
}
log.processors.add(SamplingProcessor());
Error alerting with context #
ContextBufferProcessor keeps a ring buffer of recent LogRecord entries.
ErrorAlertSink detects error-level records and forwards them — together with
that buffer — to an ErrorExporter, giving the recipient rich context about
what happened before the error.
SlackErrorExporter
SlackErrorExporter is available via package:oklog/oklog_slack.dart.
It sends a formatted Block Kit
message to a Slack channel via an Incoming Webhook.
The notification includes the error message, error object, stack trace, and the
recent context logs captured by ContextBufferProcessor.
import 'package:oklog/oklog.dart';
import 'package:oklog/oklog_slack.dart';
final buffer = ContextBufferProcessor();
final exporter = SlackErrorExporter(
'https://hooks.slack.com/services/YOUR/WEBHOOK/URL',
);
log.processors.add(buffer);
log.sinks.add(
ErrorAlertSink(
buffer,
exporter,
metadata: {
'app': 'MyApp',
'version': '1.0.0',
'env': 'production',
},
),
);
log.info('main', 'Application started.');
log.warn('main', 'Cache miss — fetching from origin.');
try {
throw Exception('Database connection failed');
} catch (e, st) {
// Sends a Slack message that includes the error plus the info/warn above.
log.error('main', 'Unhandled error.', error: e, stackTrace: st);
}
Use extraPayload to merge additional top-level fields when routing through a
proxy that requires extra keys alongside blocks:
final exporter = SlackErrorExporter(
'https://proxy.example.com/slack',
extraPayload: {
'channel': '#alerts',
'username': 'ErrorBot',
'x-routing-key': 'my-service',
},
);
HttpErrorExporter + custom ErrorFormatter
HttpErrorExporter is a generic HTTP transport that accepts any ErrorFormatter
implementation. Use it to send structured payloads to any webhook-based service
without duplicating transport logic.
An optional payloadTransformer callback lets you merge extra fields or reshape
the payload before delivery without subclassing:
final exporter = HttpErrorExporter(
'https://discord.com/api/webhooks/YOUR/WEBHOOK',
DiscordFormatter(),
payloadTransformer: (payload) => {...payload, 'username': 'ErrorBot'},
);
Implement ErrorFormatter to control the request body:
class DiscordFormatter implements ErrorFormatter {
@override
Map<String, dynamic> format(
LogRecord error,
List<LogRecord> contextLogs,
Map<String, String> metadata,
) {
return {
'content': '**${error.className}**: ${error.message}',
};
}
}
final exporter = HttpErrorExporter(
'https://discord.com/api/webhooks/YOUR/WEBHOOK',
DiscordFormatter(),
);
final buffer = ContextBufferProcessor();
log.processors.add(buffer);
log.sinks.add(
ErrorAlertSink(
buffer,
exporter,
metadata: {'app': 'MyApp', 'version': '1.0.0'},
),
);
Custom ErrorExporter
For full control over the transport (non-HTTP, batching, etc.), implement
ErrorExporter directly:
class MyExporter implements ErrorExporter {
@override
Future<void> send(
LogRecord error,
List<LogRecord> contextLogs,
Map<String, String> metadata,
) async {
// `error` — the error-level LogRecord that triggered the alert
// `contextLogs` — recent records from ContextBufferProcessor
// `metadata` — key-value pairs set on ErrorAlertSink (e.g. app name, version)
await myService.report(
message: error.message,
context: contextLogs.map((r) => r.message).toList(),
metadata: metadata,
);
}
}
final buffer = ContextBufferProcessor();
log.processors.add(buffer);
log.sinks.add(
ErrorAlertSink(
buffer,
MyExporter(),
metadata: {'app': 'MyApp', 'version': '1.0.0'},
),
);
Observability #
Access structured observability methods through log.obs.
These are separate from severity-level logs and are designed for structured data
that can later be forwarded to an external observability backend without changing call-site code.
log.obs.event #
Logs a named event with an optional payload and metadata attributes.
log.obs.event(
this, // source: pass `this`, a Type, or a String
'user_signed_in', // event name / message
data: {'userId': '42', 'plan': 'pro'},
attrs: {'env': 'prod'},
);
Console output:
[2026-03-13 10:00:00.000] 📡 [EVENT] MyClass: user_signed_in : {userId: 42, plan: pro} attrs: {env: prod}
log.obs.metric #
Logs a numeric measurement with an optional unit and metadata attributes.
log.obs.metric(
this, // source
'request_duration', // metric name
142, // value
unit: 'ms',
attrs: {'endpoint': '/api/login'},
);
Console output:
[2026-03-13 10:00:00.000] 📊 [METRIC] MyClass: request_duration : 142 [ms] attrs: {endpoint: /api/login}
Parameter reference #
| Parameter | Type | Required | Description |
|---|---|---|---|
source |
Object |
Yes | Origin class. Pass this to resolve the runtime type automatically, or a Type or String. |
message |
String |
Yes (event only) |
Human-readable event description. |
name |
String |
Yes (metric only) |
Metric name (e.g. 'request_duration'). |
value |
num |
Yes (metric only) |
Numeric measurement. |
unit |
String? |
No | Unit label, e.g. 'ms', 'count' (metric only). |
data |
Map<String, dynamic>? |
No | Arbitrary payload (event only). |
attrs |
Map<String, Object>? |
No | Structured metadata, e.g. environment or version. |
Log levels #
| Level | Description |
|---|---|
trace |
Fine-grained diagnostic messages |
debug |
General debugging information |
info |
Informational messages |
notice |
Notable events worth highlighting |
warn |
Warnings with optional error/stack |
error |
Errors with error object and stack |