# Introduction

Welcome to the strlog documentation!

![strlog.dart demo](https://github.com/inoutgg/strlog.dart/blob/master/doc/doc/assets/demo.gif)

## Introduction

strlog is a structured logger, in which log records include a message, severity level, and bound set of fields representing key-value pairs.

strlog provides a logger which enables reporting events (records) of interest. Typically, a logger has a record handler assigned to it. A handler is responsible for handling emitted records. It can delegate them to an external interface (like a file or stdout), or simply ignore them.


# Getting started

Requirements:

* Dart 3.0.0 or greater (check out [dart-overlay.nix](https://github.com/roman-vanesyan/dart-overlay.nix) if you use Nix)

### Installation

To start using `strlog` add the package via `pub`

```shell-session
dart pub add strlog
```

### Hello world

The simplest way to get started with the strlog is by using a global logger

```dart
import 'package:strlog/global_logger.dart' as log;
import 'package:strlog/strlog.dart';

void main() {
    log.info('Greeting', const [Str('who', 'world'), Str('what', 'hello')]);
}
```

```log
2024-10-14 22:11:16.927220 [INFO]: Hello world who=world what=hello
```

### Set up the logger

```dart
import 'dart:io';

import 'package:strlog/strlog.dart';
import 'package:strlog/handlers.dart';
import 'package:strlog/formatters.dart';

final _defaultFormatter = TextFormatter.withDefaults();

void main() {
    final logger = Logger.detached()..handler = ConsoleHandler(formatter: _defaultFormatter); 

    logger.info('A new log with bound PID appears on a screen', [Int('pid', pid)]);
}
```

Check out [reference documentation](https://pub.dev/documentation/strlog) for a detailed overview of the API.


# Components


# Handler

Each logger has a handler assigned to it. A logger emits a record and passes it to the handler, which decides how to handle it.

Typically, a handler is associated with a formatter. The formatter formats records passed to the handler and returns the result to the handler so it continues process.

strlog comes with a bunch of handlers bundled.

## ConsoleHandler

`ConsoleHandler` is used to output records to the console. It processes incoming records by utilizing Dart's built-in `print` function to display them.

## FileHandler

`FileHandler` is used to output records to a file.

## MultiHandler

`MultiHandler` allows multiple handlers to be combined into a single `Handler` interface. It receives multiple handlers and broadcasts incoming records to each of them.

```dart
import 'package:strlog/strlog.dart';
import 'package:strlog/handlers.dart' show MultiHandler, StreamHandler, ConsoleHandler;

final logger = Logger.getLogger('strlog.examples.multi_handler');

void main() {
  final h = MultiHandler([
    StreamHandler(...),
    StreamHandler(...),
    ...,
    ConsoleHandler(...)
  ]);
  logger.handler = h;
}
```


# Filter

Filters provides fine-grained control for determining which log records to output.


# Logging context

A logging context is an object that carries a set of fields that are bound to every single log emitted. It is one of the core concepts of the strlog logger.

When a new logger instance is created via

```dart
Logger.getLogger(...);
Logger.detach(...);
```

an implicit logging context is created under the hood.

The logging context creates new log records that are later delegated to a handler. Each emitted record carries a set of logging context's bound fields.

To create a new instance of the context with a bound set of fields, use

```dart
logger.withFields(...);
```


# Recipes

### Forward error logs to stderr, while the rest to stdout

To split logging of records between stderr and stdout, we basically need two constructions provided out of the box by strlog: `Filter` and `MultiHandler`.

The filter needs to be configured

```dart
import 'dart:io' show stderr, stdout;
import 'package:strlog/formatters.dart';
import 'package:strlog/handlers.dart';
import 'package:strlog/strlog.dart';

final _logger = Logger.detached();
final _defaultFormatter = TextFormatter.withDefaults();
final stderrLevels = {Level.warn, Level.error, Level.fatal};

bool _stderrFilter(Record record) => stderrLevels.contains(record.level);
bool _stdoutFilter(Record record) => !stderrLevels.contains(record.level);

void main() {
  _logger.handler = MultiHandler([
    // Forward logs to stderr
    StreamHandler(stderr, formatter: _defaultFormatter)..filter = _stderrFilter,
    
    // Forward logs to stdout
    StreamHandler(stdout, formatter: _defaultFormatter)..filter = _stdoutFilter
  ]);

  _logger.info("This log will be logged to stdout");  // stdout
  _logger.error("This log will be logged to stderr"); // stderr
}
```


