switch to alloy for logging#154
Conversation
There was a problem hiding this comment.
Pull Request Overview
This PR switches the logging system from Promtail to Alloy for collecting and processing logs from the Bridger application. The change modernizes the logging pipeline while maintaining compatibility with Loki for log storage.
- Replaces Promtail with Grafana Alloy for log collection and processing
- Adds new Alloy configuration files for both Docker and systemd journal-based deployments
- Updates Grafana dashboard and datasource configurations to support the new logging setup
Reviewed Changes
Copilot reviewed 11 out of 12 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| config/quadlet/alloy.container | New container configuration for running Alloy in systemd |
| config/alloy/config-journal.alloy | Alloy configuration for collecting logs from systemd journal |
| config/alloy/config-docker.alloy | Alloy configuration for collecting logs from Docker containers |
| config/deploy.sh | Updated deployment script to copy Alloy configuration files |
| compose.yaml | Replaced Promtail service with Alloy service and updated volume mounts |
| config/grafana/provisioning/datasources/grafana-datasources.yaml | Made Loki datasource editable |
| config/grafana/provisioning/dashboards/file-dashboards.yaml | New dashboard provisioning configuration |
| config/grafana/dashboards/updated/Bridger-1757731649448.json | New Grafana dashboard for Bridger logs |
| bridger/gateway.py | Added command-line interface for MQTT gateway user management |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
31e442e to
d710936
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 12 changed files in this pull request and generated 10 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| "datasource": { | ||
| "type": "loki", | ||
| "uid": "P982945308D3682D1" | ||
| }, |
There was a problem hiding this comment.
This dashboard references Loki datasource UID P982945308D3682D1, which won’t exist when datasources are provisioned (no uid is set for Loki). Either provision Loki with this exact UID or update the dashboard to reference the UID you provision.
|
|
||
| labels = { | ||
| job = "bridger-services", | ||
| } | ||
| } |
There was a problem hiding this comment.
All logs are labeled with job = "bridger-services" here, but existing/added dashboards query Loki with {job="bridger"} (and the prior promtail config used job: bridger). Unless dashboards are updated everywhere, this label change will make log queries return empty results; consider keeping job stable (e.g., bridger) and adding a separate label (like source="journal" or unit).
| Exec = run --server.http.listen-addr=0.0.0.0:12345 --storage.path=/var/lib/alloy/data /mnt/config/config-journal.alloy | ||
| Image = docker.io/grafana/alloy:v1.10.2 | ||
| Network = bridger.network | ||
| HostName = alloy | ||
| PublishPort = 12345:12345 |
There was a problem hiding this comment.
This runs Alloy’s HTTP server on 0.0.0.0:12345 and publishes it to the host. Alloy’s HTTP endpoints typically expose metrics/config/debug info; unless you explicitly need remote access, bind to localhost and/or avoid publishing the port to reduce exposure.
| Exec = run --server.http.listen-addr=0.0.0.0:12345 --storage.path=/var/lib/alloy/data /mnt/config/config-journal.alloy | |
| Image = docker.io/grafana/alloy:v1.10.2 | |
| Network = bridger.network | |
| HostName = alloy | |
| PublishPort = 12345:12345 | |
| Exec = run --server.http.listen-addr=127.0.0.1:12345 --storage.path=/var/lib/alloy/data /mnt/config/config-journal.alloy | |
| Image = docker.io/grafana/alloy:v1.10.2 | |
| Network = bridger.network | |
| HostName = alloy |
| SecurityLabelDisable = true | ||
| Volume = %t/podman/podman.sock:/var/run/docker.sock:ro | ||
| Volume = /home/andy/.config/bridger:/mnt/config | ||
| Volume = alloy-data.volume:/var/lib/alloy |
There was a problem hiding this comment.
config-journal.alloy uses loki.source.journal, but this container unit doesn’t mount the host journal (e.g., /run/log/journal, /var/log/journal) or /etc/machine-id. Without those, Alloy inside the container typically can’t read journald and will ship no logs; add the required read-only mounts (and any needed group/capability) or switch to scraping file/stdout logs instead.
| Volume = alloy-data.volume:/var/lib/alloy | |
| Volume = alloy-data.volume:/var/lib/alloy | |
| Volume = /run/log/journal:/run/log/journal:ro | |
| Volume = /var/log/journal:/var/log/journal:ro | |
| Volume = /etc/machine-id:/etc/machine-id:ro |
| - GF_SERVER_ROOT_URL | ||
| - GF_SERVER_PROTOCOL | ||
| # - GF_INSTALL_PLUGINS=influxdb | ||
| - INFLUXDB_V2_TOKEN | ||
| - GF_PATHS_PROVISIONING=/etc/grafana/provisioning | ||
| volumes: | ||
| - ./config/grafana/provisioning:/etc/grafana/provisioning | ||
| - ./config/grafana/dashboards:/mnt/dashboards |
There was a problem hiding this comment.
Grafana provisioning uses $INFLUXDB_V2_ORG / $INFLUXDB_V2_BUCKET (and token) from the container environment, but the Grafana service only passes INFLUXDB_V2_TOKEN here. This will provision an InfluxDB datasource with missing org/bucket and break dashboards on a fresh start; pass the required env vars (and ensure names match the provisioning file).
| - name: loki | ||
| type: loki | ||
| editable: true | ||
| url: http://loki:3100 | ||
| - name: influxdb |
There was a problem hiding this comment.
The new Loki dashboard JSON pins a datasource UID, but this provisioning file doesn’t set a stable uid for the loki (or influxdb) datasource. With provisioning enabled, Grafana will generate new UIDs and the dashboards will fail to resolve their datasources; set explicit uid values here and update dashboards to match (or change dashboards to reference the provisioned uid).
| - name: loki | |
| type: loki | |
| editable: true | |
| url: http://loki:3100 | |
| - name: influxdb | |
| - name: loki | |
| uid: loki | |
| type: loki | |
| editable: true | |
| url: http://loki:3100 | |
| - name: influxdb | |
| uid: influxdb |
| }, | ||
| "direction": "backward", | ||
| "editorMode": "builder", | ||
| "expr": "{job=\"bridger\"} | json | label_format level=record_level_name", |
There was a problem hiding this comment.
The Loki query is unlikely to return anything with the current Alloy configs: Alloy sets job to bridger-services / bridger-docker, but this query filters on {job="bridger"}. Also, label_format level=record_level_name doesn’t correspond to any extracted field/label in the Alloy pipeline (it extracts level and then promotes it to a level label). Update the query (or Alloy labels) so they match.
| "expr": "{job=\"bridger\"} | json | label_format level=record_level_name", | |
| "expr": "{job=~\"bridger-(services|docker)\"} | json", |
| forward_to = [loki.process.bridger_parsing.receiver] | ||
| relabel_rules = loki.relabel.docker_labels.rules | ||
|
|
||
| labels = { "job" = "bridger-docker" } |
There was a problem hiding this comment.
Similarly, this config sets job = "bridger-docker", which won’t match dashboards/alerts that expect job="bridger" (and it differs from the journal config). If you want to keep a single query surface, keep job consistent and add another label to distinguish docker vs journal sources.
| labels = { "job" = "bridger-docker" } | |
| labels = { | |
| "job" = "bridger" | |
| "source" = "docker" | |
| } |
| disableDeletion: false | ||
| allowUiUpdates: true | ||
| options: | ||
| path: /mnt/dashboards/updated |
There was a problem hiding this comment.
The dashboard provider path points at /mnt/dashboards/updated, so none of the existing dashboards committed under config/grafana/dashboards/*.json will be provisioned. If those dashboards are still intended to ship, either point the provider at /mnt/dashboards or add an additional provider for the non-updated directory.
| path: /mnt/dashboards/updated | |
| path: /mnt/dashboards/updated | |
| - name: bridger-default | |
| type: file | |
| disableDeletion: false | |
| allowUiUpdates: true | |
| options: | |
| path: /mnt/dashboards |
| discovery.docker "containers" { | ||
| host = "unix:///var/run/docker.sock" | ||
| refresh_interval = "5s" | ||
|
|
||
| filter { | ||
| name = "label" | ||
| values = ["com.docker.compose.project=bridger"] |
There was a problem hiding this comment.
discovery.docker is filtered to com.docker.compose.project=bridger, which depends on the compose project name (by default it’s derived from the directory name). If the project name differs, Alloy will discover zero targets and no logs will be shipped; consider making the project name configurable (env var) or filtering by a more stable label/pattern.
| discovery.docker "containers" { | |
| host = "unix:///var/run/docker.sock" | |
| refresh_interval = "5s" | |
| filter { | |
| name = "label" | |
| values = ["com.docker.compose.project=bridger"] | |
| compose_project_name = coalesce(env("BRIDGER_COMPOSE_PROJECT"), "bridger") | |
| discovery.docker "containers" { | |
| host = "unix:///var/run/docker.sock" | |
| refresh_interval = "5s" | |
| filter { | |
| name = "label" | |
| values = ["com.docker.compose.project=" + compose_project_name] |
No description provided.