container-superposition 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +843 -0
- package/dist/scripts/init.d.ts +3 -0
- package/dist/scripts/init.d.ts.map +1 -0
- package/dist/scripts/init.js +1190 -0
- package/dist/scripts/init.js.map +1 -0
- package/dist/scripts/migrate-to-manifests.d.ts +12 -0
- package/dist/scripts/migrate-to-manifests.d.ts.map +1 -0
- package/dist/scripts/migrate-to-manifests.js +230 -0
- package/dist/scripts/migrate-to-manifests.js.map +1 -0
- package/dist/tool/questionnaire/composer.d.ts +6 -0
- package/dist/tool/questionnaire/composer.d.ts.map +1 -0
- package/dist/tool/questionnaire/composer.js +1232 -0
- package/dist/tool/questionnaire/composer.js.map +1 -0
- package/dist/tool/readme/markdown-parser.d.ts +30 -0
- package/dist/tool/readme/markdown-parser.d.ts.map +1 -0
- package/dist/tool/readme/markdown-parser.js +139 -0
- package/dist/tool/readme/markdown-parser.js.map +1 -0
- package/dist/tool/readme/readme-generator.d.ts +9 -0
- package/dist/tool/readme/readme-generator.d.ts.map +1 -0
- package/dist/tool/readme/readme-generator.js +422 -0
- package/dist/tool/readme/readme-generator.js.map +1 -0
- package/dist/tool/schema/custom-loader.d.ts +17 -0
- package/dist/tool/schema/custom-loader.d.ts.map +1 -0
- package/dist/tool/schema/custom-loader.js +149 -0
- package/dist/tool/schema/custom-loader.js.map +1 -0
- package/dist/tool/schema/overlay-loader.d.ts +47 -0
- package/dist/tool/schema/overlay-loader.d.ts.map +1 -0
- package/dist/tool/schema/overlay-loader.js +252 -0
- package/dist/tool/schema/overlay-loader.js.map +1 -0
- package/dist/tool/schema/types.d.ts +212 -0
- package/dist/tool/schema/types.d.ts.map +1 -0
- package/dist/tool/schema/types.js +5 -0
- package/dist/tool/schema/types.js.map +1 -0
- package/docs/README.md +308 -0
- package/docs/architecture.md +233 -0
- package/docs/creating-overlays.md +549 -0
- package/docs/custom-patches.md +540 -0
- package/docs/dependencies.md +279 -0
- package/docs/examples/custom-patches-example.md +85 -0
- package/docs/examples.md +576 -0
- package/docs/messaging-comparison.md +265 -0
- package/docs/messaging-quick-start.md +385 -0
- package/docs/observability-workflow.md +537 -0
- package/docs/overlay-manifest-refactoring.md +214 -0
- package/docs/overlay-metadata-archive.md +54 -0
- package/docs/overlays.md +523 -0
- package/docs/presets-architecture.md +498 -0
- package/docs/presets.md +366 -0
- package/docs/publishing.md +476 -0
- package/docs/quick-reference.md +326 -0
- package/docs/ux.md +170 -0
- package/features/README.md +85 -0
- package/features/cross-distro-packages/README.md +146 -0
- package/features/cross-distro-packages/devcontainer-feature.json +20 -0
- package/features/cross-distro-packages/install.sh +58 -0
- package/features/local-secrets-manager/devcontainer-feature.json +18 -0
- package/features/local-secrets-manager/install.sh +127 -0
- package/features/project-scaffolder/devcontainer-feature.json +24 -0
- package/features/project-scaffolder/install.sh +100 -0
- package/features/team-conventions/devcontainer-feature.json +24 -0
- package/features/team-conventions/install.sh +93 -0
- package/overlays/.registry/README.md +14 -0
- package/overlays/.registry/base-images.yml +26 -0
- package/overlays/.registry/base-templates.yml +7 -0
- package/overlays/README.md +155 -0
- package/overlays/alertmanager/.env.example +5 -0
- package/overlays/alertmanager/README.md +465 -0
- package/overlays/alertmanager/alert-rules.yml +56 -0
- package/overlays/alertmanager/alertmanager.yml +42 -0
- package/overlays/alertmanager/devcontainer.patch.json +12 -0
- package/overlays/alertmanager/docker-compose.yml +20 -0
- package/overlays/alertmanager/overlay.yml +17 -0
- package/overlays/alertmanager/setup.sh +53 -0
- package/overlays/alertmanager/verify.sh +31 -0
- package/overlays/aws-cli/README.md +473 -0
- package/overlays/aws-cli/devcontainer.patch.json +13 -0
- package/overlays/aws-cli/overlay.yml +13 -0
- package/overlays/azure-cli/README.md +551 -0
- package/overlays/azure-cli/devcontainer.patch.json +8 -0
- package/overlays/azure-cli/overlay.yml +13 -0
- package/overlays/bun/README.md +312 -0
- package/overlays/bun/devcontainer.patch.json +41 -0
- package/overlays/bun/overlay.yml +16 -0
- package/overlays/bun/setup.sh +79 -0
- package/overlays/bun/verify.sh +30 -0
- package/overlays/codex/README.md +128 -0
- package/overlays/codex/devcontainer.patch.json +3 -0
- package/overlays/codex/overlay.yml +14 -0
- package/overlays/codex/setup.sh +24 -0
- package/overlays/codex/verify.sh +30 -0
- package/overlays/commitlint/README.md +333 -0
- package/overlays/commitlint/devcontainer.patch.json +8 -0
- package/overlays/commitlint/overlay.yml +16 -0
- package/overlays/commitlint/setup.sh +234 -0
- package/overlays/direnv/README.md +504 -0
- package/overlays/direnv/devcontainer.patch.json +6 -0
- package/overlays/direnv/overlay.yml +13 -0
- package/overlays/direnv/setup.sh +139 -0
- package/overlays/docker-in-docker/README.md +534 -0
- package/overlays/docker-in-docker/devcontainer.patch.json +10 -0
- package/overlays/docker-in-docker/overlay.yml +13 -0
- package/overlays/docker-sock/README.md +256 -0
- package/overlays/docker-sock/devcontainer.patch.json +9 -0
- package/overlays/docker-sock/docker-compose.yml +8 -0
- package/overlays/docker-sock/overlay.yml +13 -0
- package/overlays/dotnet/README.md +147 -0
- package/overlays/dotnet/devcontainer.patch.json +51 -0
- package/overlays/dotnet/global-tools.txt +24 -0
- package/overlays/dotnet/overlay.yml +13 -0
- package/overlays/dotnet/setup.sh +51 -0
- package/overlays/dotnet/verify.sh +26 -0
- package/overlays/gcloud/README.md +269 -0
- package/overlays/gcloud/devcontainer.patch.json +14 -0
- package/overlays/gcloud/overlay.yml +14 -0
- package/overlays/gcloud/verify.sh +52 -0
- package/overlays/git-helpers/README.md +168 -0
- package/overlays/git-helpers/devcontainer.patch.json +33 -0
- package/overlays/git-helpers/overlay.yml +15 -0
- package/overlays/git-helpers/setup.sh +91 -0
- package/overlays/go/README.md +293 -0
- package/overlays/go/devcontainer.patch.json +43 -0
- package/overlays/go/overlay.yml +15 -0
- package/overlays/go/setup.sh +33 -0
- package/overlays/go/verify.sh +40 -0
- package/overlays/grafana/.env.example +9 -0
- package/overlays/grafana/README.md +462 -0
- package/overlays/grafana/dashboard-provider.yml +11 -0
- package/overlays/grafana/dashboards/observability-overview.json +263 -0
- package/overlays/grafana/devcontainer.patch.json +12 -0
- package/overlays/grafana/docker-compose.yml +27 -0
- package/overlays/grafana/grafana-datasources.yml +57 -0
- package/overlays/grafana/overlay.yml +21 -0
- package/overlays/grafana/verify.sh +34 -0
- package/overlays/jaeger/.env.example +7 -0
- package/overlays/jaeger/README.md +867 -0
- package/overlays/jaeger/devcontainer.patch.json +12 -0
- package/overlays/jaeger/docker-compose.yml +17 -0
- package/overlays/jaeger/overlay.yml +19 -0
- package/overlays/java/README.md +267 -0
- package/overlays/java/devcontainer.patch.json +44 -0
- package/overlays/java/overlay.yml +16 -0
- package/overlays/java/setup.sh +41 -0
- package/overlays/java/verify.sh +42 -0
- package/overlays/just/README.md +443 -0
- package/overlays/just/devcontainer.patch.json +3 -0
- package/overlays/just/overlay.yml +13 -0
- package/overlays/just/setup.sh +182 -0
- package/overlays/kubectl-helm/README.md +660 -0
- package/overlays/kubectl-helm/devcontainer.patch.json +10 -0
- package/overlays/kubectl-helm/overlay.yml +13 -0
- package/overlays/loki/.env.example +5 -0
- package/overlays/loki/README.md +1156 -0
- package/overlays/loki/devcontainer.patch.json +12 -0
- package/overlays/loki/docker-compose.yml +18 -0
- package/overlays/loki/loki-config.yaml +45 -0
- package/overlays/loki/overlay.yml +17 -0
- package/overlays/minio/.env.example +9 -0
- package/overlays/minio/README.md +639 -0
- package/overlays/minio/devcontainer.patch.json +30 -0
- package/overlays/minio/docker-compose.yml +28 -0
- package/overlays/minio/overlay.yml +18 -0
- package/overlays/minio/setup.sh +61 -0
- package/overlays/minio/verify.sh +64 -0
- package/overlays/mkdocs/README.md +309 -0
- package/overlays/mkdocs/devcontainer.patch.json +24 -0
- package/overlays/mkdocs/overlay.yml +15 -0
- package/overlays/modern-cli-tools/README.md +556 -0
- package/overlays/modern-cli-tools/devcontainer.patch.json +3 -0
- package/overlays/modern-cli-tools/overlay.yml +13 -0
- package/overlays/modern-cli-tools/setup.sh +153 -0
- package/overlays/mongodb/.env.example +9 -0
- package/overlays/mongodb/README.md +481 -0
- package/overlays/mongodb/devcontainer.patch.json +32 -0
- package/overlays/mongodb/docker-compose.yml +44 -0
- package/overlays/mongodb/overlay.yml +17 -0
- package/overlays/mongodb/verify.sh +48 -0
- package/overlays/mysql/.env.example +11 -0
- package/overlays/mysql/README.md +542 -0
- package/overlays/mysql/devcontainer.patch.json +34 -0
- package/overlays/mysql/docker-compose.yml +55 -0
- package/overlays/mysql/overlay.yml +16 -0
- package/overlays/mysql/verify.sh +48 -0
- package/overlays/nats/.env.example +5 -0
- package/overlays/nats/README.md +762 -0
- package/overlays/nats/devcontainer.patch.json +24 -0
- package/overlays/nats/docker-compose.yml +31 -0
- package/overlays/nats/overlay.yml +18 -0
- package/overlays/nats/verify.sh +50 -0
- package/overlays/ngrok/README.md +503 -0
- package/overlays/ngrok/devcontainer.patch.json +3 -0
- package/overlays/ngrok/overlay.yml +14 -0
- package/overlays/ngrok/setup.sh +125 -0
- package/overlays/nodejs/README.md +192 -0
- package/overlays/nodejs/devcontainer.patch.json +49 -0
- package/overlays/nodejs/global-packages.txt +16 -0
- package/overlays/nodejs/overlay.yml +14 -0
- package/overlays/nodejs/setup.sh +46 -0
- package/overlays/nodejs/verify.sh +32 -0
- package/overlays/otel-collector/.env.example +9 -0
- package/overlays/otel-collector/README.md +1257 -0
- package/overlays/otel-collector/devcontainer.patch.json +28 -0
- package/overlays/otel-collector/docker-compose.yml +22 -0
- package/overlays/otel-collector/otel-collector-config.yaml +68 -0
- package/overlays/otel-collector/overlay.yml +21 -0
- package/overlays/otel-collector/setup.sh +49 -0
- package/overlays/otel-demo-nodejs/.env.example +2 -0
- package/overlays/otel-demo-nodejs/Dockerfile-otel-demo-nodejs +17 -0
- package/overlays/otel-demo-nodejs/README.md +409 -0
- package/overlays/otel-demo-nodejs/devcontainer.patch.json +12 -0
- package/overlays/otel-demo-nodejs/docker-compose.yml +19 -0
- package/overlays/otel-demo-nodejs/overlay.yml +23 -0
- package/overlays/otel-demo-nodejs/package-otel-demo-nodejs.json +20 -0
- package/overlays/otel-demo-nodejs/server-otel-demo-nodejs.js +259 -0
- package/overlays/otel-demo-nodejs/tracing-otel-demo-nodejs.js +57 -0
- package/overlays/otel-demo-nodejs/verify.sh +31 -0
- package/overlays/otel-demo-python/.env.example +2 -0
- package/overlays/otel-demo-python/Dockerfile-otel-demo-python +16 -0
- package/overlays/otel-demo-python/README.md +82 -0
- package/overlays/otel-demo-python/app-otel-demo-python.py +208 -0
- package/overlays/otel-demo-python/devcontainer.patch.json +12 -0
- package/overlays/otel-demo-python/docker-compose.yml +19 -0
- package/overlays/otel-demo-python/overlay.yml +23 -0
- package/overlays/otel-demo-python/requirements-otel-demo-python.txt +4 -0
- package/overlays/otel-demo-python/verify.sh +31 -0
- package/overlays/playwright/README.md +629 -0
- package/overlays/playwright/devcontainer.patch.json +9 -0
- package/overlays/playwright/overlay.yml +13 -0
- package/overlays/postgres/.env.example +6 -0
- package/overlays/postgres/README.md +602 -0
- package/overlays/postgres/devcontainer.patch.json +21 -0
- package/overlays/postgres/docker-compose.yml +22 -0
- package/overlays/postgres/overlay.yml +15 -0
- package/overlays/postgres/verify.sh +45 -0
- package/overlays/powershell/README.md +314 -0
- package/overlays/powershell/devcontainer.patch.json +22 -0
- package/overlays/powershell/overlay.yml +13 -0
- package/overlays/powershell/setup.sh +29 -0
- package/overlays/powershell/verify.sh +38 -0
- package/overlays/pre-commit/README.md +263 -0
- package/overlays/pre-commit/devcontainer.patch.json +9 -0
- package/overlays/pre-commit/overlay.yml +16 -0
- package/overlays/pre-commit/setup.sh +129 -0
- package/overlays/presets/docs-site.yml +118 -0
- package/overlays/presets/fullstack.yml +181 -0
- package/overlays/presets/microservice.yml +118 -0
- package/overlays/presets/web-api.yml +109 -0
- package/overlays/prometheus/.env.example +5 -0
- package/overlays/prometheus/README.md +1246 -0
- package/overlays/prometheus/devcontainer.patch.json +12 -0
- package/overlays/prometheus/docker-compose.yml +22 -0
- package/overlays/prometheus/overlay.yml +17 -0
- package/overlays/prometheus/prometheus.yml +12 -0
- package/overlays/prometheus/verify.sh +34 -0
- package/overlays/promtail/.env.example +2 -0
- package/overlays/promtail/README.md +357 -0
- package/overlays/promtail/devcontainer.patch.json +5 -0
- package/overlays/promtail/docker-compose.yml +16 -0
- package/overlays/promtail/overlay.yml +17 -0
- package/overlays/promtail/promtail-config.yaml +60 -0
- package/overlays/promtail/verify.sh +31 -0
- package/overlays/pulumi/README.md +472 -0
- package/overlays/pulumi/devcontainer.patch.json +13 -0
- package/overlays/pulumi/overlay.yml +14 -0
- package/overlays/pulumi/verify.sh +31 -0
- package/overlays/python/README.md +919 -0
- package/overlays/python/devcontainer.patch.json +41 -0
- package/overlays/python/overlay.yml +12 -0
- package/overlays/python/requirements-overlay.txt +13 -0
- package/overlays/python/setup.sh +47 -0
- package/overlays/python/verify.sh +32 -0
- package/overlays/rabbitmq/.env.example +7 -0
- package/overlays/rabbitmq/README.md +680 -0
- package/overlays/rabbitmq/devcontainer.patch.json +28 -0
- package/overlays/rabbitmq/docker-compose.yml +30 -0
- package/overlays/rabbitmq/overlay.yml +18 -0
- package/overlays/rabbitmq/verify.sh +41 -0
- package/overlays/redis/.env.example +4 -0
- package/overlays/redis/README.md +776 -0
- package/overlays/redis/devcontainer.patch.json +21 -0
- package/overlays/redis/docker-compose.yml +21 -0
- package/overlays/redis/overlay.yml +15 -0
- package/overlays/redis/verify.sh +41 -0
- package/overlays/redpanda/.env.example +10 -0
- package/overlays/redpanda/README.md +703 -0
- package/overlays/redpanda/devcontainer.patch.json +37 -0
- package/overlays/redpanda/docker-compose.yml +67 -0
- package/overlays/redpanda/overlay.yml +21 -0
- package/overlays/redpanda/verify.sh +48 -0
- package/overlays/rust/README.md +299 -0
- package/overlays/rust/devcontainer.patch.json +39 -0
- package/overlays/rust/overlay.yml +15 -0
- package/overlays/rust/setup.sh +36 -0
- package/overlays/rust/verify.sh +51 -0
- package/overlays/sqlite/README.md +584 -0
- package/overlays/sqlite/devcontainer.patch.json +14 -0
- package/overlays/sqlite/overlay.yml +15 -0
- package/overlays/sqlite/setup.sh +27 -0
- package/overlays/sqlite/verify.sh +43 -0
- package/overlays/sqlserver/.env.example +6 -0
- package/overlays/sqlserver/README.md +592 -0
- package/overlays/sqlserver/devcontainer.patch.json +22 -0
- package/overlays/sqlserver/docker-compose.yml +32 -0
- package/overlays/sqlserver/overlay.yml +17 -0
- package/overlays/sqlserver/verify.sh +30 -0
- package/overlays/tempo/.env.example +5 -0
- package/overlays/tempo/README.md +273 -0
- package/overlays/tempo/devcontainer.patch.json +12 -0
- package/overlays/tempo/docker-compose.yml +20 -0
- package/overlays/tempo/overlay.yml +20 -0
- package/overlays/tempo/tempo-config.yaml +32 -0
- package/overlays/tempo/verify.sh +31 -0
- package/overlays/terraform/README.md +389 -0
- package/overlays/terraform/devcontainer.patch.json +15 -0
- package/overlays/terraform/overlay.yml +14 -0
- package/overlays/terraform/verify.sh +63 -0
- package/package.json +74 -0
- package/templates/README.md +285 -0
- package/templates/compose/.devcontainer/devcontainer.json +46 -0
- package/templates/compose/.devcontainer/docker-compose.yml +12 -0
- package/templates/compose/README.md +20 -0
- package/templates/plain/.devcontainer/devcontainer.json +35 -0
- package/templates/plain/README.md +21 -0
- package/tool/README.md +281 -0
- package/tool/schema/base-images.schema.json +43 -0
- package/tool/schema/base-templates.schema.json +34 -0
- package/tool/schema/config.schema.json +71 -0
- package/tool/schema/overlay-manifest.schema.json +86 -0
|
@@ -0,0 +1,1156 @@
|
|
|
1
|
+
# Loki Overlay
|
|
2
|
+
|
|
3
|
+
Multi-tenant log aggregation system designed for storing and querying logs from all your applications and infrastructure.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Grafana Loki** - Horizontally scalable, highly available log aggregation
|
|
8
|
+
- **LogQL query language** - Inspired by PromQL for familiar syntax
|
|
9
|
+
- **Label-based indexing** - Cost-effective storage with selective indexing
|
|
10
|
+
- **Persistent storage** - Filesystem-backed with BoltDB
|
|
11
|
+
- **Multi-tenancy support** - Isolated log streams per tenant
|
|
12
|
+
- **Native Grafana integration** - Seamless visualization in Grafana
|
|
13
|
+
- **Compressed storage** - Efficient log storage with compression
|
|
14
|
+
|
|
15
|
+
## How It Works
|
|
16
|
+
|
|
17
|
+
Loki is a log aggregation system inspired by Prometheus. Unlike traditional log systems that index the entire log line, Loki only indexes metadata (labels), making it extremely cost-effective for storing large volumes of logs.
|
|
18
|
+
|
|
19
|
+
**Architecture:**
|
|
20
|
+
|
|
21
|
+
```mermaid
|
|
22
|
+
graph TD
|
|
23
|
+
A[Application<br/>Sends logs via OTLP<br/>Or via Promtail shipper] -->|HTTP POST| B[Grafana Loki<br/>Ingester → Storage<br/>Index labels only<br/>Store logs compressed<br/>Query via LogQL<br/>API: http://...:3100]
|
|
24
|
+
B -->|Query| C[Grafana Dashboard<br/>Explore logs<br/>Build dashboards<br/>Alert on log patterns]
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Key Concepts:**
|
|
28
|
+
|
|
29
|
+
- **Labels** - Indexed metadata (service, level, host)
|
|
30
|
+
- **Log streams** - Unique combination of labels
|
|
31
|
+
- **Chunks** - Compressed log data stored on disk
|
|
32
|
+
- **LogQL** - Query language for filtering and aggregating logs
|
|
33
|
+
|
|
34
|
+
## Configuration
|
|
35
|
+
|
|
36
|
+
### Ports
|
|
37
|
+
|
|
38
|
+
- `3100` - Loki HTTP API (push, query, health)
|
|
39
|
+
|
|
40
|
+
### Environment Variables
|
|
41
|
+
|
|
42
|
+
The overlay includes a `.env.example` file. Copy it to `.env` and customize:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
cd .devcontainer
|
|
46
|
+
cp .env.example .env
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Available variables:**
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# Loki version
|
|
53
|
+
LOKI_VERSION=latest
|
|
54
|
+
|
|
55
|
+
# Loki port (default 3100)
|
|
56
|
+
LOKI_PORT=3100
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Loki Configuration File
|
|
60
|
+
|
|
61
|
+
Loki is configured via `loki-config.yaml`:
|
|
62
|
+
|
|
63
|
+
**Key sections:**
|
|
64
|
+
|
|
65
|
+
```yaml
|
|
66
|
+
auth_enabled: false # Disable multi-tenancy auth
|
|
67
|
+
|
|
68
|
+
server:
|
|
69
|
+
http_listen_port: 3100 # HTTP API port
|
|
70
|
+
|
|
71
|
+
ingester: # In-memory log processing
|
|
72
|
+
chunk_idle_period: 5m # How long before flushing chunks
|
|
73
|
+
chunk_retain_period: 30s # Retain in memory after flush
|
|
74
|
+
|
|
75
|
+
schema_config: # Index schema
|
|
76
|
+
configs:
|
|
77
|
+
- from: 2020-10-24
|
|
78
|
+
store: boltdb-shipper # Index storage
|
|
79
|
+
object_store: filesystem
|
|
80
|
+
schema: v11
|
|
81
|
+
|
|
82
|
+
storage_config: # Where to store data
|
|
83
|
+
boltdb_shipper:
|
|
84
|
+
active_index_directory: /loki/index
|
|
85
|
+
filesystem:
|
|
86
|
+
directory: /loki/chunks
|
|
87
|
+
|
|
88
|
+
limits_config:
|
|
89
|
+
reject_old_samples: true # Reject logs older than 168h
|
|
90
|
+
reject_old_samples_max_age: 168h
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Enabling Retention
|
|
94
|
+
|
|
95
|
+
Edit `loki-config.yaml` to enable automatic log deletion:
|
|
96
|
+
|
|
97
|
+
```yaml
|
|
98
|
+
limits_config:
|
|
99
|
+
retention_deletes_enabled: true
|
|
100
|
+
retention_period: 168h # 7 days for development
|
|
101
|
+
|
|
102
|
+
compactor:
|
|
103
|
+
working_directory: /loki/compactor
|
|
104
|
+
shared_store: filesystem
|
|
105
|
+
compaction_interval: 10m
|
|
106
|
+
retention_enabled: true
|
|
107
|
+
retention_delete_delay: 2h
|
|
108
|
+
retention_delete_worker_count: 150
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Adjusting Performance
|
|
112
|
+
|
|
113
|
+
```yaml
|
|
114
|
+
ingester:
|
|
115
|
+
chunk_idle_period: 10m # Longer period = larger chunks
|
|
116
|
+
chunk_block_size: 262144 # 256KB chunks
|
|
117
|
+
chunk_retain_period: 1m # Keep in memory longer
|
|
118
|
+
max_chunk_age: 2h # Force flush after 2h
|
|
119
|
+
|
|
120
|
+
limits_config:
|
|
121
|
+
ingestion_rate_mb: 10 # Max MB/s per stream
|
|
122
|
+
ingestion_burst_size_mb: 20
|
|
123
|
+
max_streams_per_user: 10000
|
|
124
|
+
max_line_size: 256000 # 256KB max line size
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Port Configuration
|
|
128
|
+
|
|
129
|
+
Ports can be changed via `--port-offset`:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# Offset all ports by 100
|
|
133
|
+
container-superposition --port-offset 100
|
|
134
|
+
|
|
135
|
+
# Loki will be on 3200 instead of 3100
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Sending Logs to Loki
|
|
139
|
+
|
|
140
|
+
### Via OpenTelemetry Collector (Recommended)
|
|
141
|
+
|
|
142
|
+
The otel-collector overlay is pre-configured to forward logs to Loki:
|
|
143
|
+
|
|
144
|
+
```javascript
|
|
145
|
+
// Node.js - Send logs via OTLP
|
|
146
|
+
const { logs } = require('@opentelemetry/api-logs');
|
|
147
|
+
const { LoggerProvider } = require('@opentelemetry/sdk-logs');
|
|
148
|
+
const { OTLPLogExporter } = require('@opentelemetry/exporter-logs-otlp-http');
|
|
149
|
+
|
|
150
|
+
const loggerProvider = new LoggerProvider();
|
|
151
|
+
loggerProvider.addLogRecordProcessor(
|
|
152
|
+
new BatchLogRecordProcessor(
|
|
153
|
+
new OTLPLogExporter({
|
|
154
|
+
url: 'http://otel-collector:4318/v1/logs',
|
|
155
|
+
})
|
|
156
|
+
)
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
const logger = loggerProvider.getLogger('my-service');
|
|
160
|
+
logger.emit({
|
|
161
|
+
severityText: 'INFO',
|
|
162
|
+
body: 'User logged in',
|
|
163
|
+
attributes: {
|
|
164
|
+
'user.id': '12345',
|
|
165
|
+
'http.method': 'POST',
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Direct Push API
|
|
171
|
+
|
|
172
|
+
Send logs directly to Loki's push endpoint:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
curl -X POST http://localhost:3100/loki/api/v1/push \
|
|
176
|
+
-H "Content-Type: application/json" \
|
|
177
|
+
-d '{
|
|
178
|
+
"streams": [
|
|
179
|
+
{
|
|
180
|
+
"stream": {
|
|
181
|
+
"service": "my-app",
|
|
182
|
+
"level": "info",
|
|
183
|
+
"environment": "development"
|
|
184
|
+
},
|
|
185
|
+
"values": [
|
|
186
|
+
["'$(date +%s)000000000'", "User logged in successfully"],
|
|
187
|
+
["'$(date +%s)000000000'", "Session created"]
|
|
188
|
+
]
|
|
189
|
+
}
|
|
190
|
+
]
|
|
191
|
+
}'
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
**Push format:**
|
|
195
|
+
|
|
196
|
+
- Timestamp in nanoseconds (string)
|
|
197
|
+
- Log line (string)
|
|
198
|
+
- Labels in `stream` object
|
|
199
|
+
|
|
200
|
+
### Using Promtail (Log Shipper)
|
|
201
|
+
|
|
202
|
+
Promtail tails log files and ships them to Loki:
|
|
203
|
+
|
|
204
|
+
**docker-compose.promtail.yml:**
|
|
205
|
+
|
|
206
|
+
```yaml
|
|
207
|
+
version: '3.8'
|
|
208
|
+
services:
|
|
209
|
+
promtail:
|
|
210
|
+
image: grafana/promtail:latest
|
|
211
|
+
volumes:
|
|
212
|
+
- /var/log:/var/log:ro
|
|
213
|
+
- ./promtail-config.yaml:/etc/promtail/config.yaml:ro
|
|
214
|
+
command: -config.file=/etc/promtail/config.yaml
|
|
215
|
+
networks:
|
|
216
|
+
- devnet
|
|
217
|
+
depends_on:
|
|
218
|
+
- loki
|
|
219
|
+
|
|
220
|
+
networks:
|
|
221
|
+
devnet:
|
|
222
|
+
name: devnet
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**promtail-config.yaml:**
|
|
226
|
+
|
|
227
|
+
```yaml
|
|
228
|
+
server:
|
|
229
|
+
http_listen_port: 9080
|
|
230
|
+
grpc_listen_port: 0
|
|
231
|
+
|
|
232
|
+
positions:
|
|
233
|
+
filename: /tmp/positions.yaml
|
|
234
|
+
|
|
235
|
+
clients:
|
|
236
|
+
- url: http://loki:3100/loki/api/v1/push
|
|
237
|
+
|
|
238
|
+
scrape_configs:
|
|
239
|
+
- job_name: system
|
|
240
|
+
static_configs:
|
|
241
|
+
- targets:
|
|
242
|
+
- localhost
|
|
243
|
+
labels:
|
|
244
|
+
job: varlogs
|
|
245
|
+
host: devcontainer
|
|
246
|
+
__path__: /var/log/*.log
|
|
247
|
+
|
|
248
|
+
- job_name: application
|
|
249
|
+
static_configs:
|
|
250
|
+
- targets:
|
|
251
|
+
- localhost
|
|
252
|
+
labels:
|
|
253
|
+
job: app
|
|
254
|
+
service: my-app
|
|
255
|
+
__path__: /var/log/app/*.log
|
|
256
|
+
pipeline_stages:
|
|
257
|
+
- json:
|
|
258
|
+
expressions:
|
|
259
|
+
level: level
|
|
260
|
+
timestamp: time
|
|
261
|
+
message: msg
|
|
262
|
+
- labels:
|
|
263
|
+
level:
|
|
264
|
+
- timestamp:
|
|
265
|
+
source: timestamp
|
|
266
|
+
format: RFC3339
|
|
267
|
+
- output:
|
|
268
|
+
source: message
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Using Docker Logging Driver
|
|
272
|
+
|
|
273
|
+
Send container logs directly to Loki:
|
|
274
|
+
|
|
275
|
+
```yaml
|
|
276
|
+
# docker-compose.yml
|
|
277
|
+
services:
|
|
278
|
+
my-app:
|
|
279
|
+
image: my-app:latest
|
|
280
|
+
logging:
|
|
281
|
+
driver: loki
|
|
282
|
+
options:
|
|
283
|
+
loki-url: 'http://loki:3100/loki/api/v1/push'
|
|
284
|
+
loki-batch-size: '400'
|
|
285
|
+
loki-retries: '2'
|
|
286
|
+
labels: 'service,environment'
|
|
287
|
+
labels:
|
|
288
|
+
service: 'my-app'
|
|
289
|
+
environment: 'development'
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### Using Application Libraries
|
|
293
|
+
|
|
294
|
+
#### Node.js (Winston + winston-loki)
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
npm install winston winston-loki
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
```javascript
|
|
301
|
+
const winston = require('winston');
|
|
302
|
+
const LokiTransport = require('winston-loki');
|
|
303
|
+
|
|
304
|
+
const logger = winston.createLogger({
|
|
305
|
+
transports: [
|
|
306
|
+
new LokiTransport({
|
|
307
|
+
host: 'http://loki:3100',
|
|
308
|
+
labels: {
|
|
309
|
+
service: 'my-app',
|
|
310
|
+
environment: 'development',
|
|
311
|
+
},
|
|
312
|
+
json: true,
|
|
313
|
+
format: winston.format.json(),
|
|
314
|
+
replaceTimestamp: true,
|
|
315
|
+
onConnectionError: (err) => console.error(err),
|
|
316
|
+
}),
|
|
317
|
+
],
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
logger.info('User logged in', { userId: '12345', method: 'POST' });
|
|
321
|
+
logger.error('Payment failed', { orderId: '67890', error: 'timeout' });
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
#### Python (python-logging-loki)
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
pip install python-logging-loki
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
```python
|
|
331
|
+
import logging
|
|
332
|
+
import logging_loki
|
|
333
|
+
|
|
334
|
+
handler = logging_loki.LokiHandler(
|
|
335
|
+
url="http://loki:3100/loki/api/v1/push",
|
|
336
|
+
tags={"service": "my-app", "environment": "development"},
|
|
337
|
+
version="1",
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
logger = logging.getLogger("my-app")
|
|
341
|
+
logger.addHandler(handler)
|
|
342
|
+
logger.setLevel(logging.INFO)
|
|
343
|
+
|
|
344
|
+
logger.info("User logged in", extra={"user_id": "12345"})
|
|
345
|
+
logger.error("Database connection failed", extra={"db": "postgres", "retry": 3})
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
#### Go (grafana/loki-client-go)
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
go get github.com/grafana/loki-client-go/loki
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
```go
|
|
355
|
+
package main
|
|
356
|
+
|
|
357
|
+
import (
|
|
358
|
+
"github.com/grafana/loki-client-go/loki"
|
|
359
|
+
"github.com/prometheus/common/model"
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
func main() {
|
|
363
|
+
cfg := loki.Config{
|
|
364
|
+
URL: "http://loki:3100",
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
client, err := loki.New(cfg)
|
|
368
|
+
if err != nil {
|
|
369
|
+
panic(err)
|
|
370
|
+
}
|
|
371
|
+
defer client.Stop()
|
|
372
|
+
|
|
373
|
+
labels := model.LabelSet{
|
|
374
|
+
"service": "my-app",
|
|
375
|
+
"environment": "development",
|
|
376
|
+
"level": "info",
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
client.Handle(labels, time.Now(), "User logged in successfully")
|
|
380
|
+
}
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
## Querying Logs with LogQL
|
|
384
|
+
|
|
385
|
+
### Basic Queries
|
|
386
|
+
|
|
387
|
+
**All logs from a service:**
|
|
388
|
+
|
|
389
|
+
```logql
|
|
390
|
+
{service="my-app"}
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
**Logs from multiple services:**
|
|
394
|
+
|
|
395
|
+
```logql
|
|
396
|
+
{service=~"api|web"}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
**Exclude specific services:**
|
|
400
|
+
|
|
401
|
+
```logql
|
|
402
|
+
{service!="health-check"}
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
**Multiple label filters:**
|
|
406
|
+
|
|
407
|
+
```logql
|
|
408
|
+
{service="api", environment="production", level="error"}
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### Line Filtering
|
|
412
|
+
|
|
413
|
+
**Logs containing text (case-sensitive):**
|
|
414
|
+
|
|
415
|
+
```logql
|
|
416
|
+
{service="my-app"} |= "error"
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
**Logs NOT containing text:**
|
|
420
|
+
|
|
421
|
+
```logql
|
|
422
|
+
{service="my-app"} != "debug"
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
**Case-insensitive search:**
|
|
426
|
+
|
|
427
|
+
```logql
|
|
428
|
+
{service="my-app"} |~ "(?i)error"
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
**Regex pattern:**
|
|
432
|
+
|
|
433
|
+
```logql
|
|
434
|
+
{service="my-app"} |~ "user_id=[0-9]+"
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
**Multiple filters:**
|
|
438
|
+
|
|
439
|
+
```logql
|
|
440
|
+
{service="my-app"} |= "error" != "timeout" |~ "database"
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
### Parsing and Formatting
|
|
444
|
+
|
|
445
|
+
**JSON parsing:**
|
|
446
|
+
|
|
447
|
+
```logql
|
|
448
|
+
{service="my-app"} | json
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
**JSON parsing with field extraction:**
|
|
452
|
+
|
|
453
|
+
```logql
|
|
454
|
+
{service="my-app"} | json | status_code >= 400
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
**Logfmt parsing:**
|
|
458
|
+
|
|
459
|
+
```logql
|
|
460
|
+
{service="my-app"} | logfmt | level="error"
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
**Pattern extraction:**
|
|
464
|
+
|
|
465
|
+
```logql
|
|
466
|
+
{service="my-app"} | pattern `<ip> - - <_> "<method> <uri> <_>" <status> <_>`
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
**Regex extraction:**
|
|
470
|
+
|
|
471
|
+
```logql
|
|
472
|
+
{service="my-app"} | regexp `user_id=(?P<user_id>\d+)`
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
**Line formatting:**
|
|
476
|
+
|
|
477
|
+
```logql
|
|
478
|
+
{service="my-app"} | json | line_format "{{.level}}: {{.message}}"
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
**Label formatting:**
|
|
482
|
+
|
|
483
|
+
```logql
|
|
484
|
+
{service="my-app"} | json | label_format user="user_{{.user_id}}"
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
### Aggregations and Metrics
|
|
488
|
+
|
|
489
|
+
**Count log lines:**
|
|
490
|
+
|
|
491
|
+
```logql
|
|
492
|
+
count_over_time({service="my-app"}[5m])
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
**Rate of log lines (per second):**
|
|
496
|
+
|
|
497
|
+
```logql
|
|
498
|
+
rate({service="my-app"}[5m])
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
**Sum of extracted values:**
|
|
502
|
+
|
|
503
|
+
```logql
|
|
504
|
+
sum(rate({service="my-app"} | json | __error__="" [5m]))
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
**Rate of errors:**
|
|
508
|
+
|
|
509
|
+
```logql
|
|
510
|
+
sum(rate({service="my-app"} |= "error" [5m]))
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
**Error ratio:**
|
|
514
|
+
|
|
515
|
+
```logql
|
|
516
|
+
sum(rate({service="my-app"} |= "error" [5m])) /
|
|
517
|
+
sum(rate({service="my-app"} [5m]))
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
**Bytes processed:**
|
|
521
|
+
|
|
522
|
+
```logql
|
|
523
|
+
sum(bytes_over_time({service="my-app"}[5m]))
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
**Bytes rate:**
|
|
527
|
+
|
|
528
|
+
```logql
|
|
529
|
+
sum(bytes_rate({service="my-app"}[5m]))
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
### Aggregation Functions
|
|
533
|
+
|
|
534
|
+
**Sum by label:**
|
|
535
|
+
|
|
536
|
+
```logql
|
|
537
|
+
sum by (service) (rate({environment="production"}[5m]))
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
**Average:**
|
|
541
|
+
|
|
542
|
+
```logql
|
|
543
|
+
avg(rate({service="my-app"} | json | unwrap response_time [5m]))
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
**Min/Max:**
|
|
547
|
+
|
|
548
|
+
```logql
|
|
549
|
+
min(rate({service="my-app"} | json | unwrap duration [5m]))
|
|
550
|
+
max(rate({service="my-app"} | json | unwrap duration [5m]))
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
**Count distinct:**
|
|
554
|
+
|
|
555
|
+
```logql
|
|
556
|
+
count(count by (user_id) ({service="my-app"} | json))
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
**Top K:**
|
|
560
|
+
|
|
561
|
+
```logql
|
|
562
|
+
topk(10, sum by (endpoint) (rate({service="api"}[5m])))
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
**Bottom K:**
|
|
566
|
+
|
|
567
|
+
```logql
|
|
568
|
+
bottomk(5, avg by (service) (rate({environment="production"}[1h])))
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
### Advanced Queries
|
|
572
|
+
|
|
573
|
+
**Quantile (percentile):**
|
|
574
|
+
|
|
575
|
+
```logql
|
|
576
|
+
quantile_over_time(0.95,
|
|
577
|
+
{service="my-app"} | json | unwrap response_time [5m]
|
|
578
|
+
)
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
**Histogram:**
|
|
582
|
+
|
|
583
|
+
```logql
|
|
584
|
+
histogram_over_time(
|
|
585
|
+
{service="my-app"} | json | unwrap duration [5m]
|
|
586
|
+
)
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
**Stddev/Stdvar:**
|
|
590
|
+
|
|
591
|
+
```logql
|
|
592
|
+
stddev_over_time({service="my-app"} | json | unwrap latency [5m])
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
**IP filtering:**
|
|
596
|
+
|
|
597
|
+
```logql
|
|
598
|
+
{service="nginx"}
|
|
599
|
+
| json
|
|
600
|
+
| remote_addr != "127.0.0.1"
|
|
601
|
+
| remote_addr !~ "192.168.*"
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
**Complex parsing and filtering:**
|
|
605
|
+
|
|
606
|
+
```logql
|
|
607
|
+
{service="my-app"}
|
|
608
|
+
| json
|
|
609
|
+
| level="error"
|
|
610
|
+
| http_status >= 500
|
|
611
|
+
| duration > 1000
|
|
612
|
+
| line_format "{{.timestamp}} [{{.level}}] {{.message}}"
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
## Structured Logging Best Practices
|
|
616
|
+
|
|
617
|
+
### Node.js (Pino)
|
|
618
|
+
|
|
619
|
+
```bash
|
|
620
|
+
npm install pino pino-pretty
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
```javascript
|
|
624
|
+
const pino = require('pino');
|
|
625
|
+
|
|
626
|
+
const logger = pino({
|
|
627
|
+
level: process.env.LOG_LEVEL || 'info',
|
|
628
|
+
formatters: {
|
|
629
|
+
level: (label) => ({ level: label }),
|
|
630
|
+
},
|
|
631
|
+
base: {
|
|
632
|
+
service: 'my-app',
|
|
633
|
+
environment: process.env.NODE_ENV,
|
|
634
|
+
},
|
|
635
|
+
timestamp: pino.stdTimeFunctions.isoTime,
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
// Structured logging
|
|
639
|
+
logger.info({ userId: '12345', action: 'login', ip: '192.168.1.1' }, 'User logged in successfully');
|
|
640
|
+
|
|
641
|
+
logger.error(
|
|
642
|
+
{
|
|
643
|
+
userId: '12345',
|
|
644
|
+
orderId: '67890',
|
|
645
|
+
error: 'timeout',
|
|
646
|
+
duration: 5000,
|
|
647
|
+
},
|
|
648
|
+
'Payment processing failed'
|
|
649
|
+
);
|
|
650
|
+
|
|
651
|
+
// With child loggers
|
|
652
|
+
const requestLogger = logger.child({ requestId: 'abc-123' });
|
|
653
|
+
requestLogger.info('Processing request');
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
### Python (structlog)
|
|
657
|
+
|
|
658
|
+
```bash
|
|
659
|
+
pip install structlog
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
```python
|
|
663
|
+
import structlog
|
|
664
|
+
|
|
665
|
+
structlog.configure(
|
|
666
|
+
processors=[
|
|
667
|
+
structlog.processors.TimeStamper(fmt="iso"),
|
|
668
|
+
structlog.processors.StackInfoRenderer(),
|
|
669
|
+
structlog.processors.format_exc_info,
|
|
670
|
+
structlog.processors.JSONRenderer()
|
|
671
|
+
],
|
|
672
|
+
context_class=dict,
|
|
673
|
+
logger_factory=structlog.PrintLoggerFactory(),
|
|
674
|
+
cache_logger_on_first_use=True,
|
|
675
|
+
)
|
|
676
|
+
|
|
677
|
+
logger = structlog.get_logger()
|
|
678
|
+
|
|
679
|
+
# Bind context
|
|
680
|
+
logger = logger.bind(service="my-app", environment="production")
|
|
681
|
+
|
|
682
|
+
# Log with structure
|
|
683
|
+
logger.info(
|
|
684
|
+
"user.login",
|
|
685
|
+
user_id="12345",
|
|
686
|
+
ip="192.168.1.1",
|
|
687
|
+
method="POST"
|
|
688
|
+
)
|
|
689
|
+
|
|
690
|
+
logger.error(
|
|
691
|
+
"payment.failed",
|
|
692
|
+
user_id="12345",
|
|
693
|
+
order_id="67890",
|
|
694
|
+
error="timeout",
|
|
695
|
+
duration_ms=5000
|
|
696
|
+
)
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
### .NET (Serilog)
|
|
700
|
+
|
|
701
|
+
```bash
|
|
702
|
+
dotnet add package Serilog.AspNetCore
|
|
703
|
+
dotnet add package Serilog.Sinks.Console
|
|
704
|
+
dotnet add package Serilog.Formatting.Compact
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
```csharp
|
|
708
|
+
using Serilog;
|
|
709
|
+
using Serilog.Formatting.Compact;
|
|
710
|
+
|
|
711
|
+
Log.Logger = new LoggerConfiguration()
|
|
712
|
+
.MinimumLevel.Information()
|
|
713
|
+
.Enrich.WithProperty("Service", "my-app")
|
|
714
|
+
.Enrich.WithProperty("Environment", "production")
|
|
715
|
+
.Enrich.FromLogContext()
|
|
716
|
+
.WriteTo.Console(new CompactJsonFormatter())
|
|
717
|
+
.CreateLogger();
|
|
718
|
+
|
|
719
|
+
// Structured logging
|
|
720
|
+
Log.Information(
|
|
721
|
+
"User {UserId} logged in from {IpAddress}",
|
|
722
|
+
"12345",
|
|
723
|
+
"192.168.1.1"
|
|
724
|
+
);
|
|
725
|
+
|
|
726
|
+
Log.Error(
|
|
727
|
+
"Payment processing failed for order {OrderId}: {Error}",
|
|
728
|
+
"67890",
|
|
729
|
+
"timeout"
|
|
730
|
+
);
|
|
731
|
+
|
|
732
|
+
// With scoped properties
|
|
733
|
+
using (LogContext.PushProperty("RequestId", "abc-123"))
|
|
734
|
+
{
|
|
735
|
+
Log.Information("Processing request");
|
|
736
|
+
}
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
### Go (zap)
|
|
740
|
+
|
|
741
|
+
```bash
|
|
742
|
+
go get go.uber.org/zap
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
```go
|
|
746
|
+
package main
|
|
747
|
+
|
|
748
|
+
import "go.uber.org/zap"
|
|
749
|
+
|
|
750
|
+
func main() {
|
|
751
|
+
logger, _ := zap.NewProduction()
|
|
752
|
+
defer logger.Sync()
|
|
753
|
+
|
|
754
|
+
logger = logger.With(
|
|
755
|
+
zap.String("service", "my-app"),
|
|
756
|
+
zap.String("environment", "production"),
|
|
757
|
+
)
|
|
758
|
+
|
|
759
|
+
// Structured logging
|
|
760
|
+
logger.Info("User logged in",
|
|
761
|
+
zap.String("user_id", "12345"),
|
|
762
|
+
zap.String("ip", "192.168.1.1"),
|
|
763
|
+
zap.String("method", "POST"),
|
|
764
|
+
)
|
|
765
|
+
|
|
766
|
+
logger.Error("Payment processing failed",
|
|
767
|
+
zap.String("user_id", "12345"),
|
|
768
|
+
zap.String("order_id", "67890"),
|
|
769
|
+
zap.String("error", "timeout"),
|
|
770
|
+
zap.Int("duration_ms", 5000),
|
|
771
|
+
)
|
|
772
|
+
}
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
## Grafana Integration
|
|
776
|
+
|
|
777
|
+
### Adding Loki Data Source
|
|
778
|
+
|
|
779
|
+
1. Open Grafana: http://localhost:3000
|
|
780
|
+
2. Go to **Configuration** → **Data Sources**
|
|
781
|
+
3. Click **Add data source**
|
|
782
|
+
4. Select **Loki**
|
|
783
|
+
5. Set URL: `http://loki:3100`
|
|
784
|
+
6. Click **Save & Test**
|
|
785
|
+
|
|
786
|
+
### Exploring Logs in Grafana
|
|
787
|
+
|
|
788
|
+
1. Click **Explore** (compass icon)
|
|
789
|
+
2. Select **Loki** data source
|
|
790
|
+
3. Enter LogQL query
|
|
791
|
+
4. Adjust time range
|
|
792
|
+
5. Click **Run query**
|
|
793
|
+
|
|
794
|
+
**Features:**
|
|
795
|
+
|
|
796
|
+
- Live tailing (follow logs in real-time)
|
|
797
|
+
- Log context (view surrounding log lines)
|
|
798
|
+
- Log labels (filter by clicking labels)
|
|
799
|
+
- Share query link
|
|
800
|
+
- Export to dashboard
|
|
801
|
+
|
|
802
|
+
### Creating Dashboards
|
|
803
|
+
|
|
804
|
+
**Log volume panel:**
|
|
805
|
+
|
|
806
|
+
```logql
|
|
807
|
+
sum(rate({service="my-app"}[1m]))
|
|
808
|
+
```
|
|
809
|
+
|
|
810
|
+
**Error rate panel:**
|
|
811
|
+
|
|
812
|
+
```logql
|
|
813
|
+
sum(rate({service="my-app"} |= "error" [5m]))
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
**Response time distribution:**
|
|
817
|
+
|
|
818
|
+
```logql
|
|
819
|
+
histogram_over_time(
|
|
820
|
+
{service="my-app"} | json | unwrap response_time [5m]
|
|
821
|
+
)
|
|
822
|
+
```
|
|
823
|
+
|
|
824
|
+
### Alerting on Logs
|
|
825
|
+
|
|
826
|
+
Create an alert rule in Grafana:
|
|
827
|
+
|
|
828
|
+
```logql
|
|
829
|
+
# Alert if error rate > 10 per minute
|
|
830
|
+
sum(rate({service="my-app"} |= "error" [5m])) > 10
|
|
831
|
+
```
|
|
832
|
+
|
|
833
|
+
**Alert configuration:**
|
|
834
|
+
|
|
835
|
+
- Evaluation interval: 1m
|
|
836
|
+
- For: 5m (alert after 5 minutes)
|
|
837
|
+
- Notification channel: Slack, email, PagerDuty
|
|
838
|
+
|
|
839
|
+
## Best Practices
|
|
840
|
+
|
|
841
|
+
### Label Strategy
|
|
842
|
+
|
|
843
|
+
**✅ Good labels (low cardinality):**
|
|
844
|
+
|
|
845
|
+
```json
|
|
846
|
+
{
|
|
847
|
+
"service": "api",
|
|
848
|
+
"environment": "production",
|
|
849
|
+
"level": "error",
|
|
850
|
+
"host": "server-1"
|
|
851
|
+
}
|
|
852
|
+
```
|
|
853
|
+
|
|
854
|
+
**❌ Bad labels (high cardinality):**
|
|
855
|
+
|
|
856
|
+
```json
|
|
857
|
+
{
|
|
858
|
+
"user_id": "12345", // Millions of unique values
|
|
859
|
+
"request_id": "abc-123", // Every request unique
|
|
860
|
+
"timestamp": "..." // Always unique
|
|
861
|
+
}
|
|
862
|
+
```
|
|
863
|
+
|
|
864
|
+
**Rule:** Keep total unique label combinations under 100-1000.
|
|
865
|
+
|
|
866
|
+
### Label vs. Log Content
|
|
867
|
+
|
|
868
|
+
**Use labels for:**
|
|
869
|
+
|
|
870
|
+
- Service identification
|
|
871
|
+
- Environment (dev/staging/prod)
|
|
872
|
+
- Log level (debug/info/warn/error)
|
|
873
|
+
- Component/module
|
|
874
|
+
- Deployment/cluster
|
|
875
|
+
|
|
876
|
+
**Use log content for:**
|
|
877
|
+
|
|
878
|
+
- User IDs, transaction IDs
|
|
879
|
+
- Error messages
|
|
880
|
+
- Detailed context
|
|
881
|
+
- Variable data
|
|
882
|
+
- Metrics to extract
|
|
883
|
+
|
|
884
|
+
### Log Levels
|
|
885
|
+
|
|
886
|
+
Use consistent levels:
|
|
887
|
+
|
|
888
|
+
```javascript
|
|
889
|
+
logger.debug({ details: '...' }, 'Detailed debugging');
|
|
890
|
+
logger.info({ user: '123' }, 'Normal operation');
|
|
891
|
+
logger.warn({ threshold: 80 }, 'Warning condition');
|
|
892
|
+
logger.error({ err }, 'Error occurred');
|
|
893
|
+
logger.fatal({ err }, 'Fatal error, exiting');
|
|
894
|
+
```
|
|
895
|
+
|
|
896
|
+
### Performance Optimization
|
|
897
|
+
|
|
898
|
+
**Batch log entries:**
|
|
899
|
+
|
|
900
|
+
```javascript
|
|
901
|
+
// Bad: One network request per log
|
|
902
|
+
logger.info('Log 1');
|
|
903
|
+
logger.info('Log 2');
|
|
904
|
+
|
|
905
|
+
// Good: Batch multiple logs
|
|
906
|
+
const batch = [
|
|
907
|
+
{ timestamp: Date.now(), message: 'Log 1' },
|
|
908
|
+
{ timestamp: Date.now(), message: 'Log 2' },
|
|
909
|
+
];
|
|
910
|
+
sendBatch(batch);
|
|
911
|
+
```
|
|
912
|
+
|
|
913
|
+
**Use appropriate retention:**
|
|
914
|
+
|
|
915
|
+
```yaml
|
|
916
|
+
limits_config:
|
|
917
|
+
retention_period: 168h # 7 days for dev
|
|
918
|
+
# retention_period: 2160h # 90 days for prod
|
|
919
|
+
```
|
|
920
|
+
|
|
921
|
+
**Limit log line size:**
|
|
922
|
+
|
|
923
|
+
```yaml
|
|
924
|
+
limits_config:
|
|
925
|
+
max_line_size: 256000 # 256KB
|
|
926
|
+
```
|
|
927
|
+
|
|
928
|
+
**Rate limiting:**
|
|
929
|
+
|
|
930
|
+
```yaml
|
|
931
|
+
limits_config:
|
|
932
|
+
ingestion_rate_mb: 10
|
|
933
|
+
ingestion_burst_size_mb: 20
|
|
934
|
+
```
|
|
935
|
+
|
|
936
|
+
## Troubleshooting
|
|
937
|
+
|
|
938
|
+
### Logs not appearing
|
|
939
|
+
|
|
940
|
+
**Check Loki is running:**
|
|
941
|
+
|
|
942
|
+
```bash
|
|
943
|
+
docker-compose ps loki
|
|
944
|
+
docker-compose logs loki
|
|
945
|
+
```
|
|
946
|
+
|
|
947
|
+
**Check Loki health:**
|
|
948
|
+
|
|
949
|
+
```bash
|
|
950
|
+
curl http://localhost:3100/ready
|
|
951
|
+
# Should return: ready
|
|
952
|
+
|
|
953
|
+
curl http://localhost:3100/metrics
|
|
954
|
+
# Should return Prometheus metrics
|
|
955
|
+
```
|
|
956
|
+
|
|
957
|
+
**Test push API:**
|
|
958
|
+
|
|
959
|
+
```bash
|
|
960
|
+
curl -X POST http://localhost:3100/loki/api/v1/push \
|
|
961
|
+
-H "Content-Type: application/json" \
|
|
962
|
+
-d '{
|
|
963
|
+
"streams": [{
|
|
964
|
+
"stream": {"test": "value"},
|
|
965
|
+
"values": [["'$(date +%s)000000000'", "test log"]]
|
|
966
|
+
}]
|
|
967
|
+
}'
|
|
968
|
+
```
|
|
969
|
+
|
|
970
|
+
**Query for test logs:**
|
|
971
|
+
|
|
972
|
+
```bash
|
|
973
|
+
curl 'http://localhost:3100/loki/api/v1/query?query={test="value"}'
|
|
974
|
+
```
|
|
975
|
+
|
|
976
|
+
### Out of memory
|
|
977
|
+
|
|
978
|
+
**Reduce chunk sizes:**
|
|
979
|
+
|
|
980
|
+
```yaml
|
|
981
|
+
ingester:
|
|
982
|
+
chunk_block_size: 131072 # 128KB (default 256KB)
|
|
983
|
+
chunk_idle_period: 3m # Flush more frequently
|
|
984
|
+
```
|
|
985
|
+
|
|
986
|
+
**Enable retention:**
|
|
987
|
+
|
|
988
|
+
```yaml
|
|
989
|
+
limits_config:
|
|
990
|
+
retention_deletes_enabled: true
|
|
991
|
+
retention_period: 168h # 7 days
|
|
992
|
+
```
|
|
993
|
+
|
|
994
|
+
**Limit ingestion rate:**
|
|
995
|
+
|
|
996
|
+
```yaml
|
|
997
|
+
limits_config:
|
|
998
|
+
ingestion_rate_mb: 5 # Reduce from 10
|
|
999
|
+
max_streams_per_user: 5000
|
|
1000
|
+
```
|
|
1001
|
+
|
|
1002
|
+
**Check memory usage:**
|
|
1003
|
+
|
|
1004
|
+
```bash
|
|
1005
|
+
docker stats loki
|
|
1006
|
+
```
|
|
1007
|
+
|
|
1008
|
+
### Slow queries
|
|
1009
|
+
|
|
1010
|
+
**Add label filters:**
|
|
1011
|
+
|
|
1012
|
+
```logql
|
|
1013
|
+
# ✅ Good - Specific labels
|
|
1014
|
+
{service="my-app", level="error"}
|
|
1015
|
+
|
|
1016
|
+
# ❌ Bad - No label filters
|
|
1017
|
+
{} |= "error"
|
|
1018
|
+
```
|
|
1019
|
+
|
|
1020
|
+
**Narrow time range:**
|
|
1021
|
+
|
|
1022
|
+
```logql
|
|
1023
|
+
# Query last 5 minutes, not last 24 hours
|
|
1024
|
+
{service="my-app"}[5m]
|
|
1025
|
+
```
|
|
1026
|
+
|
|
1027
|
+
**Avoid expensive parsing:**
|
|
1028
|
+
|
|
1029
|
+
```logql
|
|
1030
|
+
# ❌ Slow - Parses all lines
|
|
1031
|
+
{service="my-app"} | json | user_id="12345"
|
|
1032
|
+
|
|
1033
|
+
# ✅ Fast - Filter first, then parse
|
|
1034
|
+
{service="my-app"} |= "12345" | json | user_id="12345"
|
|
1035
|
+
```
|
|
1036
|
+
|
|
1037
|
+
**Use metrics for aggregations:**
|
|
1038
|
+
|
|
1039
|
+
```logql
|
|
1040
|
+
# For high-volume logs, use Prometheus for metrics
|
|
1041
|
+
# Use Loki for actual log content
|
|
1042
|
+
```
|
|
1043
|
+
|
|
1044
|
+
### High cardinality warnings
|
|
1045
|
+
|
|
1046
|
+
**Check stream count:**
|
|
1047
|
+
|
|
1048
|
+
```bash
|
|
1049
|
+
curl http://localhost:3100/loki/api/v1/streams
|
|
1050
|
+
```
|
|
1051
|
+
|
|
1052
|
+
**Fix high cardinality:**
|
|
1053
|
+
|
|
1054
|
+
```yaml
|
|
1055
|
+
# Before (bad):
|
|
1056
|
+
labels:
|
|
1057
|
+
user_id: "12345" # Millions of streams
|
|
1058
|
+
request_id: "abc-123" # Every request creates new stream
|
|
1059
|
+
|
|
1060
|
+
# After (good):
|
|
1061
|
+
labels:
|
|
1062
|
+
service: "my-app" # Few streams
|
|
1063
|
+
level: "info" # Few streams
|
|
1064
|
+
# Move user_id to log content:
|
|
1065
|
+
message: "User 12345 logged in"
|
|
1066
|
+
```
|
|
1067
|
+
|
|
1068
|
+
**Monitor ingester streams:**
|
|
1069
|
+
|
|
1070
|
+
```promql
|
|
1071
|
+
loki_ingester_streams
|
|
1072
|
+
```
|
|
1073
|
+
|
|
1074
|
+
### Data not persisting
|
|
1075
|
+
|
|
1076
|
+
**Check volume mount:**
|
|
1077
|
+
|
|
1078
|
+
```bash
|
|
1079
|
+
docker volume ls | grep loki
|
|
1080
|
+
docker volume inspect <loki_volume>
|
|
1081
|
+
```
|
|
1082
|
+
|
|
1083
|
+
**Verify configuration:**
|
|
1084
|
+
|
|
1085
|
+
```yaml
|
|
1086
|
+
storage_config:
|
|
1087
|
+
boltdb_shipper:
|
|
1088
|
+
active_index_directory: /loki/index
|
|
1089
|
+
filesystem:
|
|
1090
|
+
directory: /loki/chunks
|
|
1091
|
+
```
|
|
1092
|
+
|
|
1093
|
+
**Check disk space:**
|
|
1094
|
+
|
|
1095
|
+
```bash
|
|
1096
|
+
docker-compose exec loki df -h /loki
|
|
1097
|
+
```
|
|
1098
|
+
|
|
1099
|
+
## Use Cases
|
|
1100
|
+
|
|
1101
|
+
### Application Logging
|
|
1102
|
+
|
|
1103
|
+
- Centralized log aggregation from microservices
|
|
1104
|
+
- Error tracking and debugging
|
|
1105
|
+
- Audit trails and compliance logs
|
|
1106
|
+
- Request/response logging
|
|
1107
|
+
|
|
1108
|
+
### Infrastructure Monitoring
|
|
1109
|
+
|
|
1110
|
+
- System logs (syslog, journald)
|
|
1111
|
+
- Container logs (Docker, Kubernetes)
|
|
1112
|
+
- Network device logs
|
|
1113
|
+
- Security logs
|
|
1114
|
+
|
|
1115
|
+
### Correlation with Metrics and Traces
|
|
1116
|
+
|
|
1117
|
+
- Link logs to traces via trace ID
|
|
1118
|
+
- Correlate errors with metrics spikes
|
|
1119
|
+
- Debug performance issues
|
|
1120
|
+
- Root cause analysis
|
|
1121
|
+
|
|
1122
|
+
### Real-time Monitoring
|
|
1123
|
+
|
|
1124
|
+
- Live log tailing in Grafana
|
|
1125
|
+
- Alert on log patterns
|
|
1126
|
+
- Security incident detection
|
|
1127
|
+
- Business event tracking
|
|
1128
|
+
|
|
1129
|
+
## Related Overlays
|
|
1130
|
+
|
|
1131
|
+
- **grafana** - Visualization and querying interface for Loki
|
|
1132
|
+
- **otel-collector** - Centralized telemetry collection (logs, traces, metrics)
|
|
1133
|
+
- **prometheus** - Metrics correlation with logs
|
|
1134
|
+
- **jaeger** - Trace-log correlation
|
|
1135
|
+
- **nodejs/python/dotnet/go** - Application frameworks with structured logging
|
|
1136
|
+
|
|
1137
|
+
## Additional Resources
|
|
1138
|
+
|
|
1139
|
+
- [Loki Documentation](https://grafana.com/docs/loki/latest/)
|
|
1140
|
+
- [LogQL Query Language](https://grafana.com/docs/loki/latest/logql/)
|
|
1141
|
+
- [Best Practices](https://grafana.com/docs/loki/latest/best-practices/)
|
|
1142
|
+
- [Promtail Configuration](https://grafana.com/docs/loki/latest/clients/promtail/)
|
|
1143
|
+
- [Loki API](https://grafana.com/docs/loki/latest/api/)
|
|
1144
|
+
- [Label Best Practices](https://grafana.com/docs/loki/latest/best-practices/#labels)
|
|
1145
|
+
|
|
1146
|
+
## Notes
|
|
1147
|
+
|
|
1148
|
+
- This overlay **requires compose stack** (uses docker-compose)
|
|
1149
|
+
- Loki runs on port **3100** (configurable with port-offset)
|
|
1150
|
+
- Data persists in Docker volume `loki_data`
|
|
1151
|
+
- Default retention is **disabled** (enable in config for auto-cleanup)
|
|
1152
|
+
- Keep label cardinality **low** (< 1000 unique combinations)
|
|
1153
|
+
- Use hostname **`loki`** from other containers
|
|
1154
|
+
- Use **`localhost`** from host machine
|
|
1155
|
+
- Index only labels, not log content (cost-effective storage)
|
|
1156
|
+
- Use **structured logging** for better queryability
|