> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Demo Collecting Application Logs

> Guide to configuring Alloy to scrape a file-based application log and forward it to Grafana Loki, including LogQL queries, configuration changes, restart, and verification.

In this lesson you'll add a file-based scrape target to Alloy so application logs are forwarded to Grafana Loki. We'll:

1. Confirm you can query existing system logs with LogQL.
2. Verify the application is writing logs to a local file.
3. Update Alloy's configuration to scrape that file and send logs to Loki.
4. Restart Alloy and verify logs appear in Grafana.

## 1) Verify system logs (LogQL)

Start by confirming you can query system logs using a LogQL query:

```logql theme={null}
{filename="/var/log/dpkg.log"} |= "nodejs"
```

This validates that Loki is receiving and indexing files from the configured system log paths.

## 2) Confirm the application is writing to a file

On the node, inspect the application directory and confirm the app log exists:

```bash theme={null}
bob@node02 ~ ➜ ls
app

bob@node02 ~ ➜ cd app/
bob@node02 ~/app ➜ ls
app.js  app.log
```

Tail the application log to ensure entries are being written in real time:

```bash theme={null}
bob@node02 ~/app ➜ tail -f app.log
{"level":30,"time":1785094352680,"pid":2078,"hostname":"node02","method":"GET","route":"/products","code":"201"}
{"level":40,"time":1785094353681,"pid":2078,"hostname":"node02","method":"GET","route":"/cart","code":"401"}
{"level":40,"time":1785094354682,"pid":2078,"hostname":"node02","method":"GET","route":"/cart","code":"200"}
{"level":40,"time":1785094355684,"pid":2078,"hostname":"node02","method":"PUT","route":"/orders","code":"404"}
{"level":40,"time":1785094356684,"pid":2078,"hostname":"node02","method":"POST","route":"/products","code":"200"}
{"level":50,"time":1785094357685,"pid":2078,"hostname":"node02","method":"GET","route":"/products","code":"400"}
{"level":30,"time":1785094358686,"pid":2078,"hostname":"node02","method":"PATCH","route":"/cart","code":"204"}
{"level":30,"time":1785094359687,"pid":2078,"hostname":"node02","method":"PATCH","route":"/products","code":"200"}
{"level":40,"time":1785094360688,"pid":2078,"hostname":"node02","method":"GET","route":"/products","code":"404"}
{"level":50,"time":1785094361689,"pid":2078,"hostname":"node02","method":"GET","route":"/users","code":"404"}
{"level":40,"time":1785094362690,"pid":2078,"hostname":"node02","method":"GET","route":"/health","code":"404"}
{"level":40,"time":1785094363691,"pid":2078,"hostname":"node02","method":"GET","route":"/health","code":"200"}
{"level":60,"time":1785094364692,"pid":2078,"hostname":"node02","method":"GET","route":"/products","code":"500"}
{"level":40,"time":1785094365694,"pid":2078,"hostname":"node02","method":"GET","route":"/products","code":"404"}
^C
```

## 3) Update Alloy configuration to scrape the application log

Edit `/etc/alloy/config.alloy` and add a file match for the application log plus a Loki write endpoint. Below is a concise example showing the minimal blocks required to collect both system logs (wildcard path) and the specific application log:

```hcl theme={null}
// Sample config for Alloy.
// For a full configuration reference, see https://grafana.com/docs/alloy

logging {
  level = "warn"
}

# Collect all system logs matching /var/log/*log
local.file_match "local_files" {
  path_targets = [{"__path__" = "/var/log/*log"}]
  sync_period  = "5s"
}

# A separate file_match to target the application log
local.file_match "app_files" {
  path_targets = [{"__path__" = "/home/bob/app/app.log"}]
  sync_period  = "5s"
}

# Source that tails files matched by local_files (system logs)
loki.source.file "log_scrape" {
  targets       = local.file_match.local_files.targets
  forward_to    = [loki.write.grafana_loki.receiver]
  tail_from_end = true
}

# If you want a separate source for the app log, create another source that uses the app_files targets:
loki.source.file "app_log_scrape" {
  targets       = local.file_match.app_files.targets
  forward_to    = [loki.write.grafana_loki.receiver]
  tail_from_end = true
}

# Loki remote write configuration
loki.write "grafana_loki" {
  endpoint {
    url = "http://node01:3100/loki/api/v1/push"
  }
}
```

Key points:

* `path_targets` identifies files to scrape; for the app example we used `/home/bob/app/app.log`.
* `tail_from_end = true` ensures the collector tails from the end of the file on startup, avoiding re-ingestion of historical lines after restarts.
* You can either reuse an existing source and add targets, or create a dedicated `loki.source.file` for application logs as shown.

**Table of configuration blocks**

| Configuration block | Purpose | Example |
| - | - | - |
| File discovery | Define file paths to tail | `local.file_match "app_files" { path_targets = [{"__path__" = "/home/bob/app/app.log"}] }` |
| Source | Configure a file tailer and routing | `loki.source.file "app_log_scrape" { targets = local.file_match.app_files.targets forward_to = [loki.write.grafana_loki.receiver] tail_from_end = true }` |
| Remote write | Send logs to Loki | `loki.write "grafana_loki" { endpoint { url = "http://node01:3100/loki/api/v1/push" } }` |

Be sure to use `sudo` when editing `/etc/alloy/config.alloy` and restarting the service.

## 4) Restart Alloy and confirm it’s running

After saving your changes, restart Alloy and check service status:

```bash theme={null}
sudo systemctl restart alloy
sudo systemctl status alloy --no-pager
```

Example status output:

```text theme={null}
● alloy.service - Vendor-agnostic OpenTelemetry Collector distribution with programmable pipelines
    Loaded: loaded (/lib/systemd/system/alloy.service; disabled; vendor preset: enabled)
    Active: active (running) since Sun 2026-07-26 15:24:10 EDT
    Docs: https://grafana.com/docs/alloy
    Main PID: 4704 (alloy)
    Tasks: 19
    Memory: 74.9M
    CPU: 280ms
    CGroup: /system.slice/alloy.service
           └─4704 /usr/bin/alloy run --storage.path=/var/lib/alloy/data /etc/alloy/config.alloy
```

## 5) Verify logs in Grafana / Loki

Open Grafana (or your Loki query UI) and run a LogQL query targeting the application filename:

```logql theme={null}
{filename="/home/bob/app/app.log"}
```

To filter for specific text (for example, Node.js-specific entries):

```logql theme={null}
{filename="/home/bob/app/app.log"} |= "nodejs"
```

After the configuration update and restart, you should see your application JSON logs streaming into Loki and visible in Grafana Explore.

## Links and references

* Alloy documentation: [https://grafana.com/docs/alloy](https://grafana.com/docs/alloy)
* Grafana Loki documentation: [https://grafana.com/docs/loki](https://grafana.com/docs/loki)
* LogQL reference: [https://grafana.com/docs/loki/latest/logql/](https://grafana.com/docs/loki/latest/logql/)

> **[Watch Video](https://learn.kodekloud.com/user/courses/grafana-loki/module/d9262c89-9213-4e21-8599-d54b9c773885/lesson/0b77b9da-1d36-44bc-bad0-0320ed307959)**


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.