# Logging

BotStopper has very verbose logging out of the box. This is intentional and allows administrators to be sure that it is working merely by watching it work in real time. Some administrators may not appreciate this level of logging out of the box. As such, BotStopper lets you customize details about how it logs data.

BotStopper uses a practice called [structured logging](https://stackify.com/what-is-structured-logging-and-why-developers-need-it/) to emit log messages with key-value pair context. In order to make analyzing large amounts of log messages easier, BotStopper encodes all logs in JSON. This allows you to use any tool that can parse JSON to perform analytics or monitor for issues.

BotStopper exposes the following logging settings in the policy file:

| Name         | Type                     | Example         | Description                                                                                                                              |
| :----------- | :----------------------- | :-------------- | :--------------------------------------------------------------------------------------------------------------------------------------- |
| `level`      | [log level](#log-levels) | `info`          | The logging level threshold. Any logs that are at or above this threshold will be drained to the sink. Any other logs will be discarded. |
| `sink`       | string                   | `stdio`, `file` | The sink where the logs drain to as they are being recorded in BotStopper.                                                                   |
| `parameters` | object                   |                 | Parameters for the given logging sink. This will vary based on the logging sink of choice. See below for more information.               |
| `asn`        | bool                     | `true`, `false` | Add ASN information to logs/metrics. (Requires [GeoIP](https://anubis.techaro.lol/docs/admin/geoip) configuration)                                                       |

BotStopper supports the following logging sinks:

1. `file`: logs are emitted to a file that is rotated based on size and age. Old log files are compressed with gzip to save space. This allows for better integration with users that decide to use legacy service managers (OpenRC, FreeBSD's init, etc).
2. `stdio`: logs are emitted to the standard error stream of the BotStopper process. This allows runtimes such as Docker, Podman, Systemd, and Kubernetes to capture logs with their native logging subsystems without any additional configuration.

### Log levels

BotStopper uses Go's [standard library `log/slog` package](https://pkg.go.dev/log/slog) to emit structured logs. By default, BotStopper logs at the [Info level](https://pkg.go.dev/log/slog#Level), which is fairly verbose out of the box. Here are the possible logging levels in BotStopper:

| Log level | Use in BotStopper                                                                                                                                             |
| :-------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `DEBUG`   | The raw unfiltered torrent of doom. Only use this if you are actively working on BotStopper or have very good reasons to use it.                              |
| `INFO`    | The default logging level, fairly verbose in order to make it easier for automation to parse.                                                             |
| `WARN`    | A "more silent" logging level. Much less verbose. Some things that are now at the `info` level need to be moved up to the `warn` level in future patches. |
| `ERROR`   | Only log error messages.                                                                                                                                  |

Additionally, you can set a "slightly higher" log level if you need to, such as:

```yaml
logging:
  sink: stdio
  level: "INFO+1"
```

This isn't currently used by BotStopper, but will be in the future for "slightly important" information.

### `file` sink

The `file` sink makes BotStopper write its logs to the filesystem and rotate them out when the log file meets certain thresholds. This logging sink takes the following parameters:

| Name           | Type            | Example               | Description                                                                                                                                                                                                                    |
| :------------- | :-------------- | :-------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `file`         | string          | `/var/log/anubis.log` | The file where BotStopper logs should be written to. Make sure the user BotStopper is running as has write and file creation permissions to this directory.                                                                            |
| `maxBackups`   | number          | `3`                   | The number of old log files that should be maintained when log files are rotated out.                                                                                                                                          |
| `maxBytes`     | number of bytes | `67108864` (64Mi)     | The maximum size of each log file before it is rotated out.                                                                                                                                                                    |
| `maxAge`       | number of days  | `7`                   | If a log file is more than this many days old, rotate it out.                                                                                                                                                                  |
| `compress`     | boolean         | `true`                | If true, compress old log files with gzip. This should be set to `true` and is only exposed as an option for dealing with legacy workflows where there is magical thinking about log files at play.                            |
| `useLocalTime` | boolean         | `false`               | If true, use the system local time zone to create log filenames instead of UTC. This should almost always be set to `false` and is only exposed for legacy workflows where there is magical thinking about time zones at play. |

```yaml
logging:
  sink: file
  parameters:
    file: "./var/anubis.log"
    maxBackups: 3 # keep at least 3 old copies
    maxBytes: 67108864 # each file can have up to 64 Mi of logs
    maxAge: 7 # rotate files out every n days
    compress: true # gzip-compress old log files
    useLocalTime: false # timezone for rotated files is UTC
```

When files are rotated out, the old files will be named after the rotation timestamp in [RFC 3339 format](https://www.rfc-editor.org/rfc/rfc3339).

> **Note.**
> If you are running BotStopper in systemd via a native package, the default systemd unit settings are very restrictive and will forbid writing to folders in `/var/log`. In order to fix this, please make a [drop-in unit](https://www.flatcar.org/docs/latest/setup/systemd/drop-in-units/) like the following:
>
> ```text
> # /etc/systemd/anubis@instance-name.service.d/50-var-log-readwrite.conf
> [Service]
> ReadWritePaths=/run /var/log/anubis
> ```
>
> Once you write this to the correct place, reload the systemd configuration:
>
> ```text
> sudo systemctl daemon-reload
> ```
>
> And then restart BotStopper:
>
> ```text
> sudo systemctl restart anubis@instance-name
> ```
>
> You may be required to make drop-ins for each BotStopper instance depending on the facts and circumstances of your deployment.

### `stdio` sink

By default, BotStopper logs everything to the standard error stream of its process. This requires no configuration:

```yaml
logging:
  sink: stdio
```

If you use a service orchestration platform that does not capture the standard error stream of processes, you need to use a different logging sink.

See [policy configuration](/docs/policies.md) for related topics.
