https://github.com/karbunkul/dart_beholder
https://github.com/karbunkul/dart_beholder
Last synced: 17 days ago
JSON representation
- Host: GitHub
- URL: https://github.com/karbunkul/dart_beholder
- Owner: karbunkul
- Created: 2024-12-22T18:17:04.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2025-03-23T12:18:55.000Z (about 1 year ago)
- Last Synced: 2025-03-23T13:25:32.493Z (about 1 year ago)
- Language: Dart
- Size: 15.6 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
Awesome Lists containing this project
README
# Beholder 👁️
Beholder is a powerful, fully customizable logging library for Dart and Flutter, built with a focus on performance, structured data, and clean architecture. Unlike other solutions, Beholder avoids global singletons in favor of a strictly instance-based approach.
## Key Features 💡
### Instance-Based Architecture
No global state. Create independent logger instances for different modules, layers, or environments. This makes testing and isolation effortless.
### Async Lifecycle & State Guards 🔄
Many transports (databases, files, network clients) require asynchronous setup. Beholder manages their `init()` and `dispose()` cycles, protecting your system from using uninitialized resources via **State Guards**.
### Advanced Transport Filtering 🛡️
Fine-tune which data each transport handles with precision:
- **Type Filtering (`allowedTypes` / `ignoredTypes`)**: Control processing based on the data type (e.g., skip `Heartbeat` or only allow `NetworkRequest`).
- **Tag Filtering (`allowedTags` / `ignoredTags`)**: Filter records based on their string tags. Perfect for isolating "UI" logs from "Network" logs even if they share the same level.
- **Synchronous Checks**: Filtering happens instantly before any expensive record processing (like converter execution) begins.
### Smart Placeholders 🧩
Beholder uses a lazy, high-performance templating system. You can use built-in placeholders or provide custom ones via `ContextPlaceholder`.
**Standard system placeholders (available in every record):**
- `{logName}`: The name of the logger instance.
- `{logLevel}`: Numeric representation of the log level (e.g., `100`).
- `{logLevelName}`: Human-readable level name (e.g., `INFO`).
- `{logTags}`: String representation of the associated tags.
- `{logDateTime}`: Local ISO8601 timestamp.
- `{logDateTimeUtc}`: UTC ISO8601 timestamp.
- `{logData}`: The main data object, formatted using the matching `LogEntryConverter`.
- `{logMessage}`: The optional message string from the `LogEntry`.
All placeholders are resolved **lazily**. For example, `{logData}` will only trigger the converter's `onConvert` function if a transport actually contains this placeholder in its output format.
**Programmatically list all available keys:**
If you are building a custom transport and need to know which keys are available:
```dart
@override
Future log(RecordEntry record) async {
// Print all available placeholders
record.placeholder.help();
// Use the manager to resolve a specific template
return record.placeholder.template('[{logLevelName}] {logData}');
}
```
## Performance ⚡
Beholder v0.9.8 is engineered for high-load applications:
- **Lazy Evaluation**: Expensive operations, such as formatting complex objects into strings (`logData`), are only performed when a transport actually requests them.
- **O(1) Converter Lookups**: Finding the right converter for a data type is a constant-time operation thanks to built-in type caching and "warm-up" during initialization.
- **Internal Caching**: Formatted data is computed exactly once per record, even if dispatched to multiple transports.
- **Zero-Block Dispatch**: Utilizes `Future.value` and `unawaited` to ensure logging never blocks your app's main execution flow.
## Quick Start 🚀
### 1. Configure Options
Define your log levels, converters, and transports:
```dart
final class MyOptions extends BeholderOptions {
@override
int get logLevel => 0;
@override
List get levels => [
LogLevel(
level: 100,
name: 'info',
transports: [
// Use TransportAdapter for fine-grained control
TransportAdapter(
transport: ConsoleTransport(),
ignoredTypes: [Heartbeat], // Skip technical noise
ignoredTags: ['internal', 'sensitive'], // Skip specific tags
onLog: (record) => '[${record.time}] ${record.description}',
),
],
),
];
@override
List get converters => [
LogEntryConverter(onConvert: (dt) => dt.toIso8601String()),
LogEntryConverter(onConvert: (u) => 'User(id: ${u.id})'),
];
}
```
### 2. Create Your Logger
```dart
base class MyLogger extends Beholder {
MyLogger() : super(name: 'App', settings: MyOptions());
void info(Object data, {List? tags}) =>
log(entry: LogEntry(data), level: 100, tags: tags);
}
```
### 3. Use It ✅
```dart
void main() async {
final logger = MyLogger();
// Mandatory initialization for async transports
await logger.init();
logger.info('Application started');
logger.info(Heartbeat(), tags: [AppTag.system]); // Ignored by console transport
// Graceful shutdown
await logger.dispose();
}
```
## Installation 📦
Add `beholder` to your `pubspec.yaml`:
```yaml
dependencies:
beholder: ^0.9.8
```
## License 📄
MIT