sl 1.1.0
sl: ^1.1.0 copied to clipboard
A structured logging library.
sl #
A structured logging library.
Setup #
import 'package:sl/sl.dart';
final logger = Logger(
handler: LogTextHandler(level: .debug),
);
Basic Logging #
logger.debug('connecting to database...');
logger.info('application started');
logger.warn('memory usage is high');
logger.error('failed to process request');
Structured Attributes #
logger.info('user profile updated', attrs: [
.string('username', 'alice'),
.int('age', 30),
.double('height', 1.75),
.bool('verified', true),
]);
Attribute Groups #
logger.info('request metadata', attrs: [
.group('http', [
.string('method', 'POST'),
.int('status', 201),
]),
]);
Errors and Stack Traces #
try {
throw StateError('connection timed out');
} catch (e, stack) {
logger.error('database operation failed', attrs: [
.error(e),
.stackTrace(stack),
]);
}
Contextual Loggers #
Create child loggers that inherit and merge parent attributes:
final dbLogger = logger.withAttrs([
.string('component', 'database'),
]);
dbLogger.info('executing query');
// Output: INFO [database] executing query
Logger Groups #
final groupedLogger = logger.withGroup('request').withAttrs([
.string('id', '123'),
]);
groupedLogger.info('processing', attrs: [
.string('path', '/users'),
]);
-
LogJsonHandleroutput:{ "time": "2026-06-28T12:00:00.000Z", "level": "INFO", "msg": "processing", "request": { "id": "123", "path": "/users" } } -
LogTextHandleroutput:INFO processing request.id=123 request.path=/users
Bridging Standard Logging #
To capture and proxy logs from Dart's standard logging package:
import 'package:logging/logging.dart' as logging;
import 'package:sl/sl.dart';
// Captures root logger logs and forwards them to our logger
final detach = StdLoggerBridge().attach(logger);
// Stop bridging later:
detach();
Context Support #
Log records can carry a Context (from package:ctx) to pass request metadata, trace IDs, and scoped data to handlers and middlewares.
Explicit Context #
Pass a Context instance directly to any log method using the optional ctx: parameter:
final context = const Context.empty().withValue('trace_id', 'trace-12345');
logger.info('processing request', ctx: context);
Zone Context Propagation #
When logging inside context.run(...), Logger automatically captures Context.current if no explicit ctx: parameter is passed:
final context = const Context.empty().withValue('trace_id', 'trace-12345');
context.run(() {
// `logger.info()` automatically receives `Context.current`
logger.info('processing request');
});
Middlewares #
Log handlers accept middlewares to transform log records before writing. This is useful for extracting values from a Context and appending them as structured attributes:
final logger = Logger(
handler: LogTextHandler(
middlewares: [
(context, record) {
if (context.value('trace_id') case final String traceId) {
return record.copyWith(
attrs: [...record.attrs, .string('trace_id', traceId)],
);
}
return record;
},
],
),
);
Source Locations #
To automatically capture and include the calling source code location:
final logger = Logger(
handler: LogTextHandler(addSource: true),
);
LogTextHandler output:
INFO application started source=package:my_app/main.dart:42
LogJsonHandler output:
{
"time": "2026-06-27T02:00:00.000Z",
"level": "INFO",
"msg": "started",
"source": {
"file": "package:my_app/main.dart",
"line": 42,
"function": "main"
}
}
Warning Capturing stack traces is relatively expensive. It is not recommended for production environments where logging performance is critical.
Handlers #
Text Output (LogTextHandler) #
final logger = Logger(
handler: LogTextHandler(
level: .debug,
scopeKey: 'component', // optional, formats value in brackets [value]
theme: .ansi, // optional, enables ANSI colors
),
);
Output:
INFO [database] query completed duration_ms=45
JSON Output (LogJsonHandler) #
final logger = Logger(
handler: LogJsonHandler(level: .info),
);
Output:
{"time":"2026-06-17T18:45:00.000Z","level":"INFO","msg":"query completed","duration_ms":45}
Multi Handler (LogMultiHandler) #
Duplicates and routes log records to multiple downstream handlers:
final logger = Logger(
handler: LogMultiHandler([
LogJsonHandler(level: .info),
externalHandler, // e.g. sending logs to a file or a remote service
]),
);