-

-

-

-

+
+

+
+
+
+
+### šÆ Customize notifications and filter log lines for relevant information:
+
+
+
---
>[!TIP]
>For better security use a **[Docker Socket Proxy](#-socket-proxy)**.
You won't be able to trigger container stops/restarts with a proxy, but if you don't need that, consider taking a look at [this section](#-socket-proxy) before you wrap up the Quick Start install and consider using that compose file instead of the basic one.
-## ā”ļø Quick start
+# ā”ļø Quick start
In this quickstart only the most essential settings are covered, [here](#Configuration-Deep-Dive) is a more detailed config walkthrough.
-Choose your preferred setup method - simple environment variables for basic use or a YAML config for advanced control.
+Choose your preferred setup method - a simple docker compose with environment variables for basic use or a YAML config for advanced control.
- Environment variables allow for a **simple** and **much quicker** setup
-- With YAML you can use complex **Regex patterns**, have different keywords & other settings **per container** and set keywords that trigger a **restart/stop** of the container.
+- With a `config.yaml ` you can use complex **Regex patterns**, have different keywords & other settings **per container** and set keywords that trigger a **restart/stop** of the container.
> [!Note]
In previous versions the default location for the `config.yaml` file was `/app/config.yaml`. The old path still works (so not a breaking change) but the new official path is now `/config/config.yaml`.
@@ -91,7 +104,8 @@ LoggiFly will first look in `/config/config.yaml`, and fall back to `/app/config
When `/config` is mounted a config template will be downloaded into that directory.
Click to expand: š Basic Setup: Docker Compose (Environment Variables)
-Ideal for quick setup with minimal configuration
+
+Ideal for quick setup with minimal configuration:
```yaml
version: "3.8"
@@ -117,14 +131,20 @@ services:
GLOBAL_KEYWORDS: "error,failed login,password" # Basic keyword monitoring
GLOBAL_KEYWORDS_WITH_ATTACHMENT: "critical" # Attaches a log file to the notification
restart: unless-stopped
+
```
-
+[Here](#-environment-variables) you can find some more environment variables that you could set.
-
Click to expand: š Advanced Setup: YAML Configuration
-Recommended for granular control, regex patterns and action_keywords.
+
+
+
Click to expand: š Advanced Setup: YAML Configuration
+
+Recommended for granular control, regex patterns and action_keywords:
+
+
**Step 1: Docker Compose**
Use this [docker compose](/docker-compose.yaml) and edit this line:
@@ -138,7 +158,8 @@ If you want you can configure some of the settings or sensitive values like ntfy
If `/config` is mounted a **[template file](/config_template.yaml) will be downloaded** into that directory. You can edit the downloaded template file and rename it to `config.yaml` to use it.
You can also take a look at the [Configuration-Deep-Dive](#-Configuration-Deep-Dive) for all the configuration options.
-Or you can edit and copy paste the following **minimal config** into a newly created `config.yaml` file in `/config`.
+
+Or you can just edit and copy paste the following **minimal config** into a newly created `config.yaml` file in the mounted `/config` directory:
```yaml
# You have to configure at least one container.
containers:
@@ -151,11 +172,12 @@ containers:
keywords_with_attachment:
- warn
# Caution advised! These keywords will trigger a restart/stop of the container
+ # There is an action_cooldown (see config deep dive)
action_keywords:
- stop: traceback
- restart: critical
-# Optional. These keywords are being monitored for all configured containers. There is an action_cooldown (see config deep dive)
+# Optional. These keywords are being monitored for all configured containers.
global_keywords:
keywords:
- failed
@@ -186,17 +208,17 @@ docker compose up -d
---
-## 𤿠Configuration Deep Dive
+# 𤿠Configuration Deep Dive
The Quick Start only covered the essential settings, here is a more detailed walktrough of all the configuration options.
-### š Basic Structure
+## š Basic Structure
The `config.yaml` file is divided into four main sections:
1. **`settings`**: Global settings like cooldowns and log levels. (_Optional since they all have default values_)
-2. **`notifications`**: Configure Ntfy (_URL, Topic, Token, Priority and Tags_) and/or your Apprise URL
+2. **`notifications`**: Ntfy (_URL, Topic, Token, Priority and Tags_), your Apprise URL and/or a custom webhook url
3. **`containers`**: Define which Containers to monitor and their specific Keywords (_plus optional settings_).
4. **`global_keywords`**: Keywords that apply to _all_ monitored Containers.
@@ -209,43 +231,73 @@ For the program to function you need to configure:
>
> The rest is optional or has default values.
+[Here](/config_template.yaml) you can find a **config template** with all available configuration options and explaining comments. When `/config` is mounted in the volumes section of your docker compose this template file will automatically be downloaded.
-### š Detailed Configuration Options
+[Here](/config_example.yaml) you can find an example config with some **use cases**.
+
+
+### āļø Settings
-Click to expand: āļø Settings
-
These are the default values for the settings:
+
+Click to expand: Settings:
```yaml
settings:
log_level: INFO # DEBUG, INFO, WARNING, ERROR
notification_cooldown: 5 # Seconds between alerts for same keyword (per container)
+ notification_title: default # configure a custom template for the notification title (see section below)
action_cooldown: 300 # Cooldown period (in seconds) before the next container action can be performed. Maximum is always at least 60s.
attachment_lines: 20 # Number of Lines to include in log attachments
multi_line_entries: True # Monitor and catch multi-line log entries instead of going line by line.
- reload_config: True # When the config file is changed the program reloads the config
- disable_start_message: False # Suppress startup notification
- disable_shutdown_message: False # Suppress shutdown notification
+ reload_config: True # When the config file is changed the program reloads the config
+ disable_start_message: False # Suppress startup notification
+ disable_shutdown_message: False # Suppress shutdown notification
disable_config_reload_message: False # Suppress config reload notification
disable_container_event_message: False # Suppress notification when monitoring of containers start/stop
```
+
+
+The setting `notification_title` requires a more detailed explanation:
+
+Click to expand: notification_title:
-
+When `notification_title: default` is set LoggiFly uses its own notification titles.
+However, if you prefer something simpler or in another language, you can choose your own template for the notification title.
+This setting can also be configured per container by the way (_see [containers](#-containers) section_).
+These are the two keys that can be inserted into the template:
+`keywords`: _The keywords that were found in a log line_
+`container`: _The name of the container in which the keywords have been found_
-Click to expand: š notifications
-
+
+Here is an example:
+
+```yaml
+notification_title: "The following keywords were found in {container}: {keywords}"
+```
+Or keep it simple:
+```yaml
+notification_title: {container}
+```
+
+
+
+
+### š Notifications
You can send notifications either directly to **Ntfy** or via **Apprise** to [most other notification services](https://github.com/caronc/apprise/wiki).
-If you want the data to be sent to your own **custom endpoint** for integration into a custom workflow, you can set custom webhook URLs. LoggiFly will send all data in JSON format.
+If you want the data to be sent to your own **custom endpoint** to integrate it into a custom workflow, you can set a custom webhook URL. LoggiFly will send all data in JSON format.
You can also set all three notification options at the same time
-**Ntfy**:
+#### Ntfy:
+
+Click to expand: Ntfy:
```yaml
notifications:
@@ -259,7 +311,11 @@ notifications:
tags: kite,mag # Ntfy tags/emojis
```
-**Apprise:**
+
+
+#### Apprise:
+
+Click to expand: Apprise:
```yaml
notifications:
@@ -267,9 +323,11 @@ notifications:
url: "discord://webhook-url" # Any Apprise-compatible URL (https://github.com/caronc/apprise/wiki)
```
-
+
-**Custom Webhook**
+#### Custom Webhook
+
+Click to expand: Custom Webhook:
```yaml
notifications:
@@ -282,39 +340,43 @@ notifications:
```
If a **webhook** is configured LoggiFly will post a JSON to the URL with the following data:
-```
+```yaml
{
- "container": container_name,
- "title": notification_title,
- "message": message (usually log line),
- "host": hostname (None unless LoggiFly is connected to multiple hosts)
+ "container": "...",
+ "keywords": [...],
+ "title": "...",
+ "message": "...",
+ "host": "..." # None unless multiple hosts are monitored
}
+
```
-
+### š³ Containers
-Click to expand: š³ containers
-
-
-This is where you set containers and their respective keywords / regex patterns and optional settings.
+Here you can define containers and assign keywords, regex patterns, and optional settings to each one.
The container names must match the exact container names you would get with `docker ps`.
-This is how you set **keywords, regex patterns and action_keywords** per container. `action_keywords` trigger a start/stop of the monitored container:
+Click to expand: Container Config:
+
+This is how you configure **keywords, regex patterns and action_keywords**. `action_keywords` trigger a start/stop of the monitored container:
```yaml
containers:
container1:
keywords:
- keyword1
- - regex: regex-patern # this is how to set regex patterns
+ - regex: regex-patern1 # this is how to set regex patterns
keywords_with_attachment: # attach a logfile to the notification
- keyword2
+ - regex: regex-pattern2
+ hide_pattern_in_title: true # Exclude the regex pattern from the notification title for a cleaner look. Useful when using very long regex patterns.
+ - keyword3
action_keywords: # trigger a restart/stop of the container. can not be set globally
- - restart: keyword3
+ - restart: keyword4
- stop:
- regex: regex-pattern # this is how to set regex patterns for action_keywords
+ regex: regex-pattern3 # this is how to set regex patterns for action_keywords
```
@@ -322,102 +384,150 @@ containers:
Some of the **settings** from the `settings` section can also be set per container:
+
```yaml
containers:
container2:
ntfy_tags: closed_lock_with_key
ntfy_priority: 5
ntfy_topic: container3
- attachment_lines: 50
+ attachment_lines: 50
+ notification_title: '{keywords} found in {container}'
notification_cooldown: 2
action_cooldown: 60
keywords:
- keyword1
+ - regex: regex-pattern1
```
+
-If `global_keywords` are configured and you don't need additional keywords for a container you can leave it blank:
+If `global_keywords` are configured and you don't need additional keywords for a container you can **leave it blank**:
```yaml
containers:
container3:
container4:
```
+
+
+
+
+### š Global Keywords
+
+When `global_keywords` are configured all containers are monitored for these keywords:
+
+Click to expand: Global Keywords:
+
+```yaml
+global_keywords:
+ keywords:
+ - error
+ keywords_with_attachment: # attach a logfile
+ - regex: (critical|error)
+```
+
+
+
+
+
+## š Customize Notifications (Templates & Log Filtering)
+
+
+For users who want more control over the appearance of their notifications, there is an option to configure templates and filter log entries to display only the relevant parts.
+Here are some [examples](#-customize-notifications-and-filter-log-lines-for-relevant-information).
+Filtering is most straightforward with logs in JSON Format, but plain text logs can also be parsed by using named groups in the regex pattern.
+
+> [!Note]
+> If you want to modify the notification title take a look at the setting `notification_title` in the [settings section](#%EF%B8%8F-settings).
+
+
+Click to expand: Filter Logs and set custom template:
+
-Now for the perfectionists there is a feature to make your **notifications** even **prettier** by using a **template** and filtering the Log Mesage.
+#### Template for JSON Logs:
-Filtering the log line is easiest if the logs of the container in question are in JSON format but there is also a solution for normal log lines using Regex Named Capturing Groups.
-**Filter by using a JSON Template:**
+`json_template` only works if the Logs are in JSON Format. Authelia is one such example.
+You can only use the placeholder variables that exist as keys in the JSON from the log line you want to catch.
-Only works if Logs are in JSON Format. Authelia is one such example:
+Here is an example where you want to catch this very log entry from Authelia:
+```
+{"level":"error","method":"POST","msg":"Unsuccessful 1FA authentication attempt by user 'example_user' and they are banned until 12:23:00PM on May 1 2025 (+02:00)","path":"/api/firstfactor","remote_ip":"192.168.178.191","stack":[{"File":"github.com/authelia/authelia/v4/internal/handlers/response.go","Line":274,"Name":"doMarkAuthenticationAttemptWithRequest"},{"File":"github.com/authelia/authelia/v4/internal/handlers/response.go","Line":258,"Name":"doMarkAuthenticationAttempt"},{"File":"github.com/authelia/authelia/v4/internal/handlers/handler_firstfactor_password.go","Line":51,"Name":"handlerMain.FirstFactorPasswordPOST.func14"},{"File":"github.com/authelia/authelia/v4/internal/middlewares/bridge.go","Line":66,"Name":"handlerMain.(*BridgeBuilder).Build.func7.1"},{"File":"github.com/authelia/authelia/v4/internal/middlewares/headers.go","Line":65,"Name":"SecurityHeadersCSPNone.func1"},{"File":"github.com/authelia/authelia/v4/internal/middlewares/headers.go","Line":105,"Name":"SecurityHeadersNoStore.func1"},{"File":"github.com/authelia/authelia/v4/internal/middlewares/headers.go","Line":30,"Name":"SecurityHeadersBase.func1"},{"File":"github.com/fasthttp/router@v1.5.4/router.go","Line":441,"Name":"(*Router).Handler"},{"File":"github.com/authelia/authelia/v4/internal/middlewares/log_request.go","Line":14,"Name":"handlerMain.LogRequest.func31"},{"File":"github.com/authelia/authelia/v4/internal/middlewares/errors.go","Line":38,"Name":"RecoverPanic.func1"},{"File":"github.com/valyala/fasthttp@v1.59.0/server.go","Line":2380,"Name":"(*Server).serveConn"},{"File":"github.com/valyala/fasthttp@v1.59.0/workerpool.go","Line":225,"Name":"(*workerPool).workerFunc"},{"File":"github.com/valyala/fasthttp@v1.59.0/workerpool.go","Line":197,"Name":"(*workerPool).getCh.func1"},{"File":"runtime/asm_amd64.s","Line":1700,"Name":"goexit"}],"time":"2025-05-01T14:19:29+02:00"}
+```
+
+In the config.yaml you can set a `json_template` for both plain text keywords and regex patterns. In the template I inserted three keys from the JSON Log Entry:
```yaml
containers:
authelia:
keywords:
- - keyword: user not found
- template: 'Somebody tried to log in with a username that does not exist\nError Message:\n{msg}'
- - regex: unsuccessful.*authentication
- json_template: '{msg}\nš IP: {remote_ip}\n{time}'
+ - keyword: Unsuccessful 1FA authentication
+ json_template: 'šØ Failed Login Attempt:\n{msg}\nš IP: {remote_ip}\nš{time}'
+ - regex: Unsuccessful.*authentication
+ json_template: 'šØ Failed Login Attempt:\n{msg}\nš IP: {remote_ip}\nš{time}'
```
-
+
-**Filter by using a Template with Regex Named Capturing Groups:**
+#### Template using named capturing groups in Regex Pattern:
-To filter Log Lines for certain parts you have to use **Named Capturing Groups** in your regex pattern.
-`(?P...)` is one example.
+To filter non JSON Log Lines for certain parts you have to use a regex pattern with **named capturing groups**.
+Lets take `(?P...)` as an example.
`P` assigns the name `group_name` to the group.
-The part inside the parentheses `(...)` is the pattern to match.
+The part inside the parentheses `(...)` is the pattern to match.
+Then you can insert the `{group_name}` into your custom message `template`.
+
+
+Example Log Line from audiobookshelf:
-In the `template` you can insert the named capturing groups you defined in the regex pattern.
+```
+[2025-05-03 10:16:53.154] INFO: [SocketAuthority] Socket VKrcSNa--FjwAqmSAAAU disconnected from client "example user" after 11696ms (Reason: transport close)
+```
-Example:
+Regex pattern & Template:
```yaml
containers:
audiobookshelf:
keywords:
- - regex: '(?P\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3}).*Socket.*disconnected from client "(?P\S+)"'
- template: 'Time: {timestamp}\nš The user {user} was seen!'
+ - regex: '(?P\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3}).*Socket.*disconnected from client "(?P[A-Za-z\s]+)"'
+ template: '\nš The user {user} was seen!\nš {timestamp}'
+ hide_pattern_in_title: true # Exclude the regex pattern from the notification title for a cleaner look
```
-
-
-
+**Result:**
-Click to expand: š global_keywords
-
+- with `template` and `hide_pattern_in_title`:
-When configured all containers are monitored for these keywords:
-```yaml
-global_keywords:
- keywords:
- - error
- keywords_with_attachment: # attach a logfile
- - regex: (critical|error)
-```
+
+

+
-
+- without for comparison:
+
+

+
-
-[Here](/config_template.yaml) you can find a **config template** with all available configuration options and explaining comments. When `/config` is mounted in the volumes section of your docker compose this template file will automatically be downloaded.
+### Add original Log Entry to template:
-[Here](/config_example.yaml) you can find an example config with some **use cases**.
+WIth both `json_template` and `template` you can add the key `original_log_line` to your template to add the full log entry to your notification message.
+
+
-### š Environment Variables
-Except for `restart_keywords`, container specific settings/keywords and regex patterns you can configure most settings via **Docker environment variables**.
+## š Environment Variables
+
+Except for `action_keywords`, container specific settings/keywords and regex patterns you can configure most settings via **Docker environment variables**.
Click to expand: Environment Variables
@@ -434,6 +544,7 @@ Except for `restart_keywords`, container specific settings/keywords and regex pa
| `APPRISE_URL` | Any [Apprise-compatible URL](https://github.com/caronc/apprise/wiki) | _N/A_ |
| `CONTAINERS` | A comma separated list of containers. These are added to the containers from the config.yaml (if you are using one).| _N/A_ |
| `SWARM_SERVICES` | A comma separated list of docker swarm services to monitor. | _N/A_ |
+| `LOGGIFLY_MODE` | Set this variable to `swarm` when wanting to use LoggiFly in swarm mode | _N/A_ |
| `GLOBAL_KEYWORDS` | Keywords that will be monitored for all containers. Overrides `global_keywords.keywords` from the config.yaml.| _N/A_ |
| `GLOBAL_KEYWORDS_WITH_ATTACHMENT`| Notifications triggered by these global keywords have a logfile attached. Overrides `global_keywords.keywords_with_attachment` from the config.yaml.| _N/A_ |
| `NOTIFICATION_COOLDOWN` | Cooldown period (in seconds) per container per keyword before a new message can be sent | 5 |
@@ -451,7 +562,7 @@ Except for `restart_keywords`, container specific settings/keywords and regex pa
---
-## š” Remote Hosts
+# š” Remote Hosts
LoggiFly supports connecting to **multiple remote hosts**.
Remote hosts can be configured by providing a **comma-separated list of addresses** in the `DOCKER_HOST` environment variable.
@@ -462,16 +573,16 @@ You can also combine remote hosts with a mounted docker socket.
>[!NOTE]
When the connection to a docker host is lost, LoggiFly will try to reconnect every 60s
-### Labels
+## Labels
When multiple hosts are set LoggiFly will use **labels** to differentiate between them both in notifications and in logging.
You can set a **label** by appending it to the address with `"|"` ([_see example_](#remote-host-example)).
When no label is set LoggiFly will use the **hostname** retrieved via the docker daemon. If that fails, usually because `INFO=1` has to be set when using a proxy, the labels will just be `Host-{Nr}`.
+
If you want to set a label to the mounted docker socket you can do so by adding `unix://var/run/docker.sock|label` in the `DOCKER_HOST` environment variable (_the socket still has to be mounted_) or just set the address of a [socket proxy](#socket-proxy) with a label.
-### Remote Hosts Example
+## Remote Hosts Example
-In this example LoggiFly monitors container logs on the host it is running on via a mounted docker socket as well as two remote hosts set up with TLS.
-One remote host will be called '_foobar_'. The host mounted via the docker socket and the other remote host have no label set and will be called whatever the hostname is.
+In this example, LoggiFly monitors container logs from the **local host** via a mounted Docker socket, as well as from **two remote Docker hosts** configured with TLS. One of the remote hosts is referred to as āfoobarā. The local host and the second remote host have no custom label and are identified by their respective hostnames.
Click to expand: Remote Hosts: Docker Compose
@@ -502,14 +613,14 @@ services:
```
-### Socket Proxy
+## Socket Proxy
You can also connect via a **Docker Socket Proxy**.
A Socket Proxy adds a security layer by **controlling access to the Docker daemon**, essentially letting LoggiFly only read certain info like container logs without giving it full control over the docker socket.
With the linuxserver image I have had some connection and timeout problems so the recommended proxy is **[Tecnativa/docker-socket-proxy](https://github.com/Tecnativa/docker-socket-proxy)**.
When using the Tecnativa Proxy the log stream connection drops every ~10 minutes for whatever reason, LoggiFly simply resets the connection.
-Here is a sample **docker compose** file:
+Here is a sample **docker compose** file:
Click to expand: Socket Proxy: Docker Compose
@@ -541,16 +652,15 @@ services:
```
-
>[!Note]
`action_keywords` don't work when using a socket proxy.
-## Docker Swarm (_Experimental_)
+# Docker Swarm (_Experimental_)
> [!Important]
-Docker Swarm Support is still experimental because I have little to no experience with it and can not say for certain if it works flawlessly.
+Docker Swarm Support is still experimental because I have little to no experience with it and can not say for certain whether it works flawlessly.
If you notice any bugs or have suggestions let me know.
To use LoggiFly in swarm mode you have to set the environment variable `LOGGIFLY_MODE` to `swarm`.
@@ -558,7 +668,7 @@ To use LoggiFly in swarm mode you have to set the environment variable `LOGGIFLY
The `config.yaml` is passed to each worker via [Docker Configs](https://docs.docker.com/reference/cli/docker/config/) (_see example_).
It stays the same except that you set `swarm_services` instead of `containers` or use the `SWARM_SERVICES` environment variable.
-If normal `containers` are set additionally to `swarm_services` LoggiFly will also look for these containers on every node.
+If normal `containers` are set instead of or additionally to `swarm_services` LoggiFly will also look for these containers on every node.
**Docker Compose**
@@ -581,11 +691,12 @@ services:
environment:
TZ: Europe/Berlin
LOGGIFLY_MODE: swarm
- # Uncomment the next three variables if you are using a config.yaml
- SWARM_SERVICES: nginx,redis
- GLOBAL_KEYWORDS: keyword1,keyword2
- GLOBAL_KEYWORDS_WITH_ATTACHMENT: keyword3
-# Uncomment the rest of this file if you are only using environment variables
+ # Uncomment the next three variables if you want to only use environment variables instead of a config.yaml
+ # SWARM_SERVICES: nginx,redis
+ # GLOBAL_KEYWORDS: keyword1,keyword2
+ # GLOBAL_KEYWORDS_WITH_ATTACHMENT: keyword3
+ # For more environment variables see the environment variables section in the README
+# Comment out the rest of this file if you are only using environment variables
configs:
- source: loggifly-config
target: /config/config.yaml
@@ -623,27 +734,29 @@ swarm_services:
- fatal
```
-For all configuration options take a look at the containers section in the [Detailed Configuration Options](#-detailed-configuration-options) as they work exactly the exact same.
+For all available configuration options, refer to the [Containers section](#-containers) of the configuration walkthrough ā the `swarm_services` configuration is identical to that of `containers`.
-## š” Tips
+# š” Tips
1. Ensure containers names **exactly match** your Docker **container names**.
- Find out your containers names: ```docker ps --format "{{.Names}}" ```
- š” Pro Tip: Define the `container_name:` in your compose files.
2. **`action_keywords`** can not be set via environment variables, they can only be set per container in the `config.yaml`. The `action_cooldown` is always at least 60s long and defaults to 300s
-3. **Test Regex Patterns**: Validate patterns at [regex101.com](https://regex101.com) before adding them to your config.
-4. **Troubleshooting Multi-Line Log Entries**. If LoggiFly only catches single lines from log entries that span over multiple lines:
+3. **Regex Patterns**:
+ - Validate patterns at [regex101.com](https://regex101.com) before adding them to your config.
+ - use `hide_pattern_in_title: true` when using very long regex patterns to have a cleaner notification title _(or hide found keywords from the title altogether with your own custom `notification_title` ([see settings](#%EF%B8%8F-settings))_
+5. **Troubleshooting Multi-Line Log Entries**. If LoggiFly only catches single lines from log entries that span over multiple lines:
- Wait for Patterns: LoggiFly needs to process a few lines in order to detect the pattern the log entries start with (e.g. timestamps/log level)
- Unrecognized Patterns: If issues persist, open an issue and share the affected log samples
---
-## Support
+# Support
If you find LoggiFly useful, drop a āļø on the repo
@@ -652,7 +765,7 @@ If you find LoggiFly useful, drop a āļø on the repo
-## Star History
+# Star History
[](https://www.star-history.com/#clemcer/loggifly&Date)
diff --git a/config_example.yaml b/config_example.yaml
index fda51ed..91632d7 100644
--- a/config_example.yaml
+++ b/config_example.yaml
@@ -8,43 +8,63 @@
# Feel free to contribute to the containers section of this example config with use cases you have found helpful :)
containers:
- audiobookshelf:
- ntfy_topic: abs
- ntfy_tags: books, headphones
- notification_cooldown: 5
- keywords:
- - podcast
- - requested download
- - downloaded item
- - regex: User.*logged in # when a user logs in
- - failed login # Failed login to the web interface
- - Error in openid callback # Error when trying to login with OIDC
+ audiobookshelf:
+ ntfy_topic: abs
+ ntfy_tags: books, headphones
+ notification_cooldown: 5
+ notification_title: "{container}" # hide found keywords from notification title
+ keywords:
+ # Here are some custom templates:
- vaultwarden:
- ntfy_tags: closed_lock_with_key
- ntfy_priority: 5
- ntfy_topic: security
- notification_cooldown: 0
- keywords:
- - regex: (username|password).*incorrect
+ # user requested download:
+ - regex: '(?P\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3}).*User "(?P[A-Za-z\s]+)" requested download for item "(?P- [A-Za-z\s]+)"'
+ template: '\nš The user {user} requested download for ''{item}''!\nš {timestamp}'
+ # user was online
+ - regex: '(?P\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3}).*Socket.*disconnected from client "(?P[A-Za-z\s]+)"'
+ template: '\nš The user {user} was seen!\nš {timestamp}'
+ # Failed Login attempt
+ - regex: '(?P\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3}).*Failed login attempt for username "(?P[A-Za-z\s]+)" from ip (?P\d{1,3}(?:\.\d{1,3}){3})\s+\((?P[A-Za-z\s]+)\)'
+ template: 'šØ Failed login!šāāļø\nUsername: ''{user}''\nš IP Address: {ip_address}\nš {timestamp}'
- ebook2audiobook:
- attachment_lines: 300
- keywords:
- - 100%
- - sentence
- - converting
- keywords_with_attachment:
- - total audio parts saved to
+ - podcast
+ - regex: User.*logged in # when a user logs in
+ - failed login # Failed login to the web interface
+ - Error in openid callback # Error when trying to login with OIDC
- adguard:
- keywords:
- - failed
- - error
+ vaultwarden:
+ ntfy_tags: closed_lock_with_key
+ ntfy_priority: 5
+ ntfy_topic: security
+ notification_cooldown: 0
+ keywords:
+ - regex: '(?P\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}).*Username or password is incorrect. Try again. IP: (?P\d{1,3}(?:\.\d{1,3}){3}). Username: (?P[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})'
+ template: 'šØ Failed login!\nš§ Email: ''{email}''\nš IP Address: {ip_address}\nš {timestamp}'
+ hide_pattern_in_title: true # Hide full regex pattern in notification title
- kitchenowl:
- action_keywords:
- - stop: traceback # I have had container restart loops because kitchenowl couldn't connect to my Authentik Server
+ ebook2audiobook:
+ attachment_lines: 300
+ keywords:
+ - 100%
+ - sentence
+ - converting
+ keywords_with_attachment:
+ - total audio parts saved to
+
+ authelia:
+ keywords:
+ - regex: \bsuccessful.*authentication
+ json_template: '{msg}\nš IP: {remote_ip}\n{time}'
+ - keyword: user not found
+ json_template: 'šØ Somebody tried to log in with a username that does not exist\n\nš{time}\nFull Error Message:\n{msg}'
+
+ adguard:
+ keywords:
+ - failed
+ - error
+
+ kitchenowl:
+ action_keywords:
+ - stop: traceback # I have had container restart loops because kitchenowl couldn't connect to my Authentik Server
global_keywords:
keywords:
@@ -54,7 +74,8 @@ global_keywords:
- panic
-notifications:
+notifications:
+# At least one of these (Ntfy/Apprise/Webhook) is required.
ntfy:
url: http://your-ntfy-server # Required. The URL of your Ntfy instance
topic: loggifly # Required. the topic for Ntfy
@@ -65,16 +86,22 @@ notifications:
tags: kite,mag # Ntfy tags/emojis
apprise:
url: "discord://webhook-url" # Any Apprise-compatible URL (https://github.com/caronc/apprise/wiki)
-
+ webhook:
+ url: https://custom.endpoint.com/post
+ headers: # add headers if needed
+ Authorization: "Bearer token"
+ X-Custom-Header": "Test123"
+
# settings are optional because they all have default values
# These are the default settings
settings:
log_level: INFO # DEBUG, INFO, WARNING, ERROR
notification_cooldown: 5 # Seconds between alerts for same keyword (per container)
action_cooldown: 300 # Cooldown period (in seconds) before the next container action can be performed. Maximum is always at least 60s.
- attachment_lines: 20 # Number of Lines to include in log attachments
+ notification_title: default # configure a custom template for the notification title (see section below) attachment_lines: 20 # Number of Lines to include in log attachments
multi_line_entries: True # Monitor and catch multi-line log entries instead of going line by line.
reload_config: True # When the config file is changed the program reloads the config
disable_start_message: False # Suppress startup notification
disable_shutdown_message: False # Suppress shutdown notification
disable_config_reload_message: False # Suppress config reload notification
+ disable_container_event_message: False # Suppress notification when monitoring of containers start/stop
diff --git a/config_template.yaml b/config_template.yaml
index 0b3fabb..d272385 100644
--- a/config_template.yaml
+++ b/config_template.yaml
@@ -11,6 +11,10 @@
# If you want to use this file after editing it make sure to rename it to config.yaml
#
# With every container exaample you can see some more available configuration options
+#
+# I did not include the option to customize notifications in this template but:
+# - Here is a detailed explanation on that: https://github.com/clemcer/loggifly/main#-customize-notifications-templates--log-filtering
+# - And here are some examples: https://github.com/clemcer/loggifly/blob/main/config_example.yaml
containers:
@@ -35,6 +39,7 @@ containers:
ntfy_tags: closed_lock_with_key
ntfy_priority: 5
ntfy_topic: container4
+ notification_title: '{keywords} found in {container}' # modify the notification title for this container
attachment_lines: 50
notification_cooldown: 2
action_cooldown: 300
@@ -47,7 +52,7 @@ containers:
- stop: keyword3
- restart:
regex: regex-pattern # this is how to set regex patterns for action_keywords
-
+
# Global keywords are applied to all containers
global_keywords:
keywords:
@@ -56,7 +61,7 @@ global_keywords:
- global_keyword2
notifications:
- # At least one of the two (Ntfy/Apprise) is required.
+ # At least one of these (Ntfy/Apprise/Webhook) is required.
ntfy:
url: http://your-ntfy-server # Required. The URL of your Ntfy instance
topic: loggifly # Required. the topic for Ntfy
@@ -67,16 +72,21 @@ notifications:
tags: kite,mag # Ntfy tags/emojis
apprise:
url: "discord://webhook-url" # Any Apprise-compatible URL (https://github.com/caronc/apprise/wiki)
-
+ webhook:
+ url: https://custom.endpoint.com/post
+ headers: # add headers if needed
+ Authorization: "Bearer token"
+ X-Custom-Header": "Test123"
# These are the default settings
settings:
log_level: INFO # DEBUG, INFO, WARNING, ERROR
notification_cooldown: 5 # Seconds between alerts for same keyword (per container)
action_cooldown: 300 # Cooldown period (in seconds) before the next container action can be performed. Maximum is always at least 60s.
- attachment_lines: 20 # Number of Lines to include in log attachments
+ notification_title: default # configure a custom template for the notification title (see section below) attachment_lines: 20 # Number of Lines to include in log attachments
multi_line_entries: True # Monitor and catch multi-line log entries instead of going line by line.
reload_config: True # When the config file is changed the program reloads the config
disable_start_message: False # Suppress startup notification
disable_shutdown_message: False # Suppress shutdown notification
disable_config_reload_message: False # Suppress config reload notification
+ disable_container_event_message: False # Suppress notification when monitoring of containers start/stop
diff --git a/images/abs_template_examples.png b/images/abs_template_examples.png
new file mode 100644
index 0000000000000000000000000000000000000000..f65f0bd097a28932dcb7e02760bee49249ba39bd
GIT binary patch
literal 267978
zcmdSA1y@y3*9HpG-Q6Lgba%J3NJ@7}cXvq$C`gym(g@Ox64Kou-ElZHcj5bf-~9vM
zxMSQsHk>m!hqd=!Ypyw;`OIfVtEtFgppv4(z`$TA$V1!}B&pyJzJOi)z;730R
zi~?T&jR0O@IN3RQ1lgZXY7TZmc1}TV9+>9jH5c#|CL1X!H4Qr(6BroAME{RsirwPG
zeSs+`s^OkJdLK(D3@T2k9Wy_uM&;CW9%H?Daw>yHt+2g-76wsI-ehvuJVdDSq;6{4
zAl9tk4)hk9_5+ICV8`y9YV*zgOIldbw$h*vsZJix$Cv@hGK3XAc7w<{?IqWVg8T_Pc3L&D(SH5P>qgeto3hyeZ@D>>ye#qugFfi0G3epm9
zy)qA$Jl!(4{n@UL-)&`i7ZKs-!_lkjz=X8Me5Kc5PRd{&^Z@6}IILbjCp|-;3YME&GjyB{~NA^f5B_xV|sMW9X4TN+T6Q(>x!EjoIPQ#ochbpstt+
zHm2+&w;9_M$RnQFwES5r7}EbeT)$l9vIRx`=ddm%cEZN_cZ_BB2ZjFk&s)NX>{~eVX58gy;A>(rp
z9oHl_JZuP~BsRZy*nj?YXF_%&X*B6Kj&6ri##oTql|6=f67_(fd<|wkqXagI%YSdp
zU(fnCebR4u+*9Hta7Ih-tstERr=bg4GOrSoIQ$yj)sz=pQsPng?_FShq>lM0jgwR^
zMN25fj4R2BqZ(*1QAdI%U}=upd*USZk)E+MaSidmw!$XW4qjp42AVdX;o3J7
zg&YDSQz`k$(qByDKhI9<6C|w=k*{rKffa9q#V^aOOfu{GztcsfFfBozf;QB{98Z$d
z35M%nroxc=pSwYk`0|;BsgP&&*XIr=8`S?^^)7{p?8}iZIhSB4UA@q&gplX|xl;NF
zyhLX8unf|$*Z)o{cM%-sfA1rIuH*k;nJE8vy!7+`V+;8I(a_0M7YNum!$3v=AhPFEe|{t20E%7z0e0CQXSJH@aj0yldlBa-vU6`uE64D5HvfuPQ%z
z@QhHkhir0bKjw!y;Tp{pFzcuA`PGbz4p?^qkMR4y
zBNMAc=dcY
z*pThXM#ua(+2Al!2gA>5-b_wS<$oapu|moEs03qN0?g<+%3YLw9~-G5!4=9xB3_
zKI^B{)Vzuc!d@Afl82PYo94qm6x=)pQg-jiS`3ft^o3ylZf<^C;Y&O0Gj;az&z`)s
ze0CUK-XCP4@_EiOvYP1837K8IyVy9Ej_k!&PErY?enJWkv`
z!5pj)-Fca0#Jk)WOmG3rsF6;KrZUO4Jq9u(7EJ%_sn&QIfjm4s-0N=aP^aAiqg;zA
zj2tc@BEqNKsDV%}p0Yo&KyGYdhHndI(z4$4ybwc8(_L~VpO4t-YNr}y=HpAvkpeh~
zj>~jyejeri%=hlZf`0H3$3+`{du
i~Cla
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 |