Centralized logging with Loki
Loki stores and indexes logs for search in Grafana — without SSHing into every server.
App → log file → Promtail → Loki → Grafana Explore
Why it matters
Multiple servers, containers, and restarts make local log files painful. Centralize once, query everywhere.
Add Loki as a Grafana datasource (http://loki:3100). Use Explore to filter streams (for example {job="varlogs"} depending on your Promtail config).
Promtail (or another shipper) must run on hosts that produce logs. Loki alone does not scrape application stdout unless you ship logs to it.
Production alerting
Alerting = automatic notification when production crosses a threshold.
| Example alert | Condition |
|---|---|
| High CPU | CPU > 85% |
| API down | Target down / health fails |
| High errors | 5xx rate spike |
| Slow API | p95 > 2s |
| Low memory | Available RAM < 10% |
Prometheus metrics → Grafana alert rule → SMTP / Slack → Engineer
Channels: Email, Slack, Discord, Telegram, PagerDuty, Teams.
SMTP email (lab outline)
- Create a Gmail app password (or other SMTP credentials)
- Configure Grafana SMTP (
GF_SMTP_*env vars orgrafana.ini) - Restart Grafana
- Create a Contact point (email)
- Create an alert rule (for example high CPU from Node Exporter)
- Optionally force a condition to verify notification
Prometheus metrics
↓
Grafana alert rule
↓
SMTP email
↓
Inbox
Never commit SMTP passwords. Use env vars or a secrets store. Prefer Slack/PagerDuty in real on-call setups.
Node.js metrics with prom-client
Expose Prometheus metrics from the app:
npm install prom-clientconst client = require("prom-client");
const register = new client.Registry();
client.collectDefaultMetrics({ register });
const httpRequests = new client.Counter({
name: "http_requests_total",
help: "Total HTTP requests",
labelNames: ["method", "route", "status_code"],
registers: [register],
});
// after each response:
httpRequests.inc({ method, route, status_code: String(status) });
app.get("/metrics", async (_req, res) => {
res.set("Content-Type", register.contentType);
res.end(await register.metrics());
});Scrape from Prometheus:
- job_name: "nodejs"
static_configs:
- targets: ["<APP_IP>:3000"]
metrics_path: /metricsVerify: curl http://localhost:3000/metrics
Day 1 wrap-up
Fundamentals & architecture
↓
Docker stack (Prometheus / Grafana / Loki)
↓
App host (Exporter + Nginx + PM2)
↓
PromQL + Grafana dashboards
↓
Logs + alerts + Node.js /metrics