loq 0.1.2
loq: ^0.1.2 copied to clipboard
Structured logging for Dart. Pipeline architecture with pluggable handlers, context propagation via Zones, and OpenTelemetry-ready. Works everywhere — server, Flutter, CLI, web.
0.1.2 - 2026-05-19 #
Processors #
- Added
levelByName(rules, {defaultLevel})for per-scope filtering by dotted logger name. The longest matching prefix wins; the empty-string key is a root catch-all. Records with a null logger name fall back todefaultLevel. Pairs withLogger.named()chains and matches the per-scope filter shape used by Java/Python/.NET logging:LogConfig.configure(processors: [ levelByName({ 'app.db.queries': Level.trace, 'app.db': Level.warn, 'app': Level.info, '': Level.error, // root catch-all }), ]);
Testing (new sub-library package:loq/testing.dart) #
- Added
RecordingHandlerfor test checks. Keeps records in memory and offers filters (at,atOrAbove,from,withField,withFieldValue,messageContaining) and counts (count,countAt,countAtOrAbove). Install it as the only handler to silence other output and capture everything for the test to look at:
The sub-library is kept apart fromimport 'package:loq/loq.dart'; import 'package:loq/testing.dart'; final recorder = RecordingHandler(); LogConfig.configure(handlers: [recorder]); // ... run the code under test ... expect(recorder.atOrAbove(Level.error), isEmpty);package:loq/loq.dartso test helpers stay out of production code.
0.1.1 - 2026-05-15 #
Logger #
LogConfig.globalis now resolved lazily on every log call instead of being snapshotted at construction.LogConfig.configure()updates take effect immediately for any logger that did not pin an explicit config — order betweenLogger()andLogConfig.configure()no longer matters. Explicitconfig:passed toLogger()still pins for that logger's lifetime and propagates throughwithFields.- Added
Logger.named(String suffix)for subsystem-scoped logging. Appends a dotted suffix and inherits the parent's bound context and config-override decision. Chains:Logger('app').named('db')→'app.db'.
LogConfig #
- Added
LogConfig.copyWith()for deriving a config that overrides specific fields while inheriting the rest. Closes the silent-drop footgun where the bareLogConfig(...)constructor reset every unspecified field to its default:Logger('hot', config: LogConfig.global.copyWith( processors: [sample(10)], )) - Added
LogConfig.shutdown()— closes every handler in the current global config in parallel. App-shutdown helper so buffered records reach their destinations. - Added
onHandlerErrorcallback (with default reporter). Exceptions thrown byHandler.isEnabled()orHandler.handle()are now caught and routed to this callback rather than propagated to the caller — a misbehaving handler no longer breaks logging for siblings or the host. Default prints aloq:-prefixed diagnostic viaprint(); override to redirect to Sentry, stderr, etc.
Level #
- Added
Level.tryParse(String)for reading level names from env vars or config files. Case-insensitive, trims whitespace, accepts the six standard names plus'warning'as an alias forwarn. Returnsnullfor unknown input.
ConsoleHandler #
- Added
useColorconstructor flag for ANSI-colored level output (gray / cyan / green / yellow / red / bright-red bold for trace through fatal). Defaultfalseto avoid emitting escape sequences in non-TTY contexts. Wire detection at your app entrypoint:ConsoleHandler( useColor: stdout.supportsAnsiEscapes && Platform.environment['NO_COLOR'] == null, ) - Added
levelColorsconstructor parameter for overriding the default palette per level. Partial overrides keep the rest of the defaults. Custom levels look up by exact match first, then fall through to the nearest band's override or default:ConsoleHandler( useColor: true, levelColors: const { Level.info: '\x1B[35m', // magenta Level(11): '\x1B[1;94m', // custom notice level }, )
JsonHandler & IsolateHandler #
-
Type-aware normalization for common Dart types:
DateTime→ ISO 8601 string.Duration→ integer milliseconds.Uri→ canonical string.
Previously these all hit the
v.toString()fallback —DateTimein particular rendered as the non-ISO'2026-05-15 10:00:00.000'form, which most log pipelines reject.
JsonHandler #
- Added
dateTimeFormatterconstructor parameter for customizing howRecord.timeand any DateTime field value is rendered. Defaults toDateTime.toIso8601String. One handler-level setting controls both paths, so the entire JSON stream has a consistent shape:JsonHandler( dateTimeFormatter: (dt) => dt.millisecondsSinceEpoch.toString(), )IsolateHandlerdeliberately omits this — records round-trip back viadeserialize, which requires ISO 8601 for the time field.
0.1.0 #
New types #
Lazy<T>for deferred field values — only evaluated if the record passes early-out filtering, resolved before Record creation so handlers never seeLazyinstances.FieldGroupfor namespacing related fields —JsonHandlerrenders as nested JSON objects,ConsoleHandleruses dotted-key notation.SourceLocationfor opt-in call-site capture viaLogConfig.captureSourceLocation. Parses Dart VM stack trace frames.
Record #
- Added optional
sourcefield for call-site location. - Added
withSource()andcopyWith()methods.
Logger #
- Added
isEnabled()to guard expensive field computation. - Lazy values are now resolved in
_log()before Record creation. - Source location captured automatically when
captureSourceLocationis enabled.
New processors #
when()— conditionally apply a processor.addTimestamp()— add ISO 8601 timestamp field.addLevel()— add level name field.addLoggerName()— add logger name field.addSource()— copy Record.source to fields map.
New handlers #
MultiHandler— dispatches to multiple sub-handlers.BufferedHandler— abstract base for batched output with size threshold, periodic timer, and concurrent flush guard.IsolateHandler— callback-based cross-isolate logging. Serializes records to plain maps; works withSendPort.sendon native and any messaging callback on web.
Handler improvements #
ConsoleHandlernow renders source location after the message, uses dotted-key notation forFieldGroup, and indents stack trace lines consistently.JsonHandlernow rendersFieldGroupas nested objects, includesrecord.sourcein output, and resolvesLazyvalues as a safety net.
Other #
Levelchanged from enum toextension type const Level(int), enabling custom levels without forking the package.LogConfig.configure()now acceptscaptureSourceLocation.- Split single-file implementation into 15 focused source files.
- Thread safety documentation added to README.
0.0.1 #
- Initial version.