phlo-alloy 0.1.0__tar.gz
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.
- phlo_alloy-0.1.0/PKG-INFO +15 -0
- phlo_alloy-0.1.0/README.md +64 -0
- phlo_alloy-0.1.0/pyproject.toml +41 -0
- phlo_alloy-0.1.0/setup.cfg +4 -0
- phlo_alloy-0.1.0/src/phlo_alloy/__init__.py +0 -0
- phlo_alloy-0.1.0/src/phlo_alloy/config.alloy +80 -0
- phlo_alloy-0.1.0/src/phlo_alloy/plugin.py +29 -0
- phlo_alloy-0.1.0/src/phlo_alloy/service.yaml +48 -0
- phlo_alloy-0.1.0/src/phlo_alloy.egg-info/PKG-INFO +15 -0
- phlo_alloy-0.1.0/src/phlo_alloy.egg-info/SOURCES.txt +14 -0
- phlo_alloy-0.1.0/src/phlo_alloy.egg-info/dependency_links.txt +1 -0
- phlo_alloy-0.1.0/src/phlo_alloy.egg-info/entry_points.txt +2 -0
- phlo_alloy-0.1.0/src/phlo_alloy.egg-info/requires.txt +6 -0
- phlo_alloy-0.1.0/src/phlo_alloy.egg-info/top_level.txt +1 -0
- phlo_alloy-0.1.0/tests/test_alloy_plugin.py +19 -0
- phlo_alloy-0.1.0/tests/test_integration_alloy.py +24 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: phlo-alloy
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: alloy service plugin for Phlo
|
|
5
|
+
Author-email: Phlo Team <team@phlo.dev>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Description-Content-Type: text/plain
|
|
9
|
+
Requires-Dist: phlo>=0.1.0
|
|
10
|
+
Requires-Dist: pyyaml>=6.0.1
|
|
11
|
+
Provides-Extra: dev
|
|
12
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
13
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
14
|
+
|
|
15
|
+
alloy service plugin for Phlo.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# phlo-alloy
|
|
2
|
+
|
|
3
|
+
Grafana Alloy log collector for Phlo.
|
|
4
|
+
|
|
5
|
+
## Description
|
|
6
|
+
|
|
7
|
+
Collects logs from all Docker containers and ships them to Loki for aggregation and querying.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install phlo-alloy
|
|
13
|
+
# or
|
|
14
|
+
phlo plugin install alloy
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Profile
|
|
18
|
+
|
|
19
|
+
Part of the `observability` profile.
|
|
20
|
+
|
|
21
|
+
## Configuration
|
|
22
|
+
|
|
23
|
+
| Variable | Default | Description |
|
|
24
|
+
| ------------ | ------- | --------------- |
|
|
25
|
+
| `ALLOY_PORT` | `12345` | Alloy HTTP port |
|
|
26
|
+
|
|
27
|
+
## Auto-Configuration
|
|
28
|
+
|
|
29
|
+
This package is **fully auto-configured**:
|
|
30
|
+
|
|
31
|
+
| Feature | How It Works |
|
|
32
|
+
| ----------------------- | ------------------------------------------------------ |
|
|
33
|
+
| **Container Discovery** | Auto-discovers all Docker containers via Docker socket |
|
|
34
|
+
| **Log Collection** | Collects stdout/stderr from all containers |
|
|
35
|
+
| **Loki Shipping** | Ships logs to Loki for storage and querying |
|
|
36
|
+
| **Metrics Labels** | Exposes Alloy metrics for Prometheus |
|
|
37
|
+
|
|
38
|
+
### Docker Socket Access
|
|
39
|
+
|
|
40
|
+
Alloy mounts the Docker socket to discover and collect logs from all containers:
|
|
41
|
+
|
|
42
|
+
```yaml
|
|
43
|
+
volumes:
|
|
44
|
+
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Usage
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Start with observability profile
|
|
51
|
+
phlo services start --profile observability
|
|
52
|
+
|
|
53
|
+
# Or start individually
|
|
54
|
+
phlo services start --service alloy
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Endpoints
|
|
58
|
+
|
|
59
|
+
- **HTTP**: `http://localhost:12345`
|
|
60
|
+
- **Ready**: `http://localhost:12345/-/ready`
|
|
61
|
+
|
|
62
|
+
## Entry Points
|
|
63
|
+
|
|
64
|
+
- `phlo.plugins.services` - Provides `AlloyServicePlugin`
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=45", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "phlo-alloy"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "alloy service plugin for Phlo"
|
|
9
|
+
readme = {text = "alloy service plugin for Phlo.", content-type = "text/plain"}
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
authors = [
|
|
12
|
+
{name = "Phlo Team", email = "team@phlo.dev"},
|
|
13
|
+
]
|
|
14
|
+
license = {text = "MIT"}
|
|
15
|
+
dependencies = [
|
|
16
|
+
"phlo>=0.1.0",
|
|
17
|
+
"pyyaml>=6.0.1",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[project.optional-dependencies]
|
|
21
|
+
dev = [
|
|
22
|
+
"pytest>=7.0",
|
|
23
|
+
"ruff>=0.1.0",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.entry-points."phlo.plugins.services"]
|
|
27
|
+
alloy = "phlo_alloy.plugin:AlloyServicePlugin"
|
|
28
|
+
|
|
29
|
+
[tool.setuptools]
|
|
30
|
+
package-dir = {"" = "src"}
|
|
31
|
+
include-package-data = true
|
|
32
|
+
|
|
33
|
+
[tool.setuptools.packages.find]
|
|
34
|
+
where = ["src"]
|
|
35
|
+
|
|
36
|
+
[tool.setuptools.package-data]
|
|
37
|
+
phlo_alloy = ['service.yaml', 'config.alloy']
|
|
38
|
+
|
|
39
|
+
[tool.ruff]
|
|
40
|
+
line-length = 100
|
|
41
|
+
target-version = "py311"
|
|
File without changes
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Grafana Alloy Configuration for Phlo
|
|
2
|
+
// Collects Docker container logs and ships to Loki
|
|
3
|
+
|
|
4
|
+
// Log discovery from Docker
|
|
5
|
+
discovery.docker "containers" {
|
|
6
|
+
host = "unix:///var/run/docker.sock"
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// Relabel Docker labels to Loki labels
|
|
10
|
+
discovery.relabel "docker_logs" {
|
|
11
|
+
targets = discovery.docker.containers.targets
|
|
12
|
+
|
|
13
|
+
// Keep container name as primary label
|
|
14
|
+
rule {
|
|
15
|
+
source_labels = ["__meta_docker_container_name"]
|
|
16
|
+
regex = "/(.*)"
|
|
17
|
+
target_label = "container"
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Add project/compose labels
|
|
21
|
+
rule {
|
|
22
|
+
source_labels = ["__meta_docker_container_label_com_docker_compose_project"]
|
|
23
|
+
target_label = "project"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
rule {
|
|
27
|
+
source_labels = ["__meta_docker_container_label_com_docker_compose_service"]
|
|
28
|
+
target_label = "service"
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Keep image name
|
|
32
|
+
rule {
|
|
33
|
+
source_labels = ["__meta_docker_container_image"]
|
|
34
|
+
target_label = "image"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Collect logs from Docker containers
|
|
39
|
+
loki.source.docker "containers" {
|
|
40
|
+
host = "unix:///var/run/docker.sock"
|
|
41
|
+
targets = discovery.relabel.docker_logs.output
|
|
42
|
+
forward_to = [loki.process.docker_logs.receiver]
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Process logs - add timestamps and parse JSON
|
|
46
|
+
loki.process "docker_logs" {
|
|
47
|
+
forward_to = [loki.write.loki.receiver]
|
|
48
|
+
|
|
49
|
+
// Try to extract JSON fields
|
|
50
|
+
stage.json {
|
|
51
|
+
expressions = {
|
|
52
|
+
level = "level",
|
|
53
|
+
msg = "msg",
|
|
54
|
+
message = "message",
|
|
55
|
+
run_id = "run_id",
|
|
56
|
+
asset_key = "asset_key",
|
|
57
|
+
partition_key = "partition_key",
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Normalize level
|
|
62
|
+
stage.labels {
|
|
63
|
+
values = {
|
|
64
|
+
level = "",
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Add timestamp if present in log
|
|
69
|
+
stage.timestamp {
|
|
70
|
+
source = "time"
|
|
71
|
+
format = "RFC3339"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Write logs to Loki
|
|
76
|
+
loki.write "loki" {
|
|
77
|
+
endpoint {
|
|
78
|
+
url = "http://loki:3100/loki/api/v1/push"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Alloy service plugin."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from importlib import resources
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import yaml
|
|
9
|
+
|
|
10
|
+
from phlo.plugins import PluginMetadata, ServicePlugin
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AlloyServicePlugin(ServicePlugin):
|
|
14
|
+
"""Service plugin for alloy."""
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def metadata(self) -> PluginMetadata:
|
|
18
|
+
return PluginMetadata(
|
|
19
|
+
name="alloy",
|
|
20
|
+
version="0.1.0",
|
|
21
|
+
description="Grafana Alloy for log collection and shipping to Loki",
|
|
22
|
+
author="Phlo Team",
|
|
23
|
+
tags=["observability", "logs", "agent"],
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def service_definition(self) -> dict[str, Any]:
|
|
28
|
+
service_path = resources.files("phlo_alloy").joinpath("service.yaml")
|
|
29
|
+
return yaml.safe_load(service_path.read_text(encoding="utf-8"))
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
name: alloy
|
|
2
|
+
description: Grafana Alloy for log collection and shipping to Loki
|
|
3
|
+
category: observability
|
|
4
|
+
default: false
|
|
5
|
+
profile: observability
|
|
6
|
+
|
|
7
|
+
image: grafana/alloy:v1.4.2
|
|
8
|
+
|
|
9
|
+
compose:
|
|
10
|
+
restart: unless-stopped
|
|
11
|
+
labels:
|
|
12
|
+
phlo.metrics.enabled: "true"
|
|
13
|
+
phlo.metrics.port: "alloy:12345"
|
|
14
|
+
phlo.metrics.path: "/metrics"
|
|
15
|
+
command:
|
|
16
|
+
- run
|
|
17
|
+
- --server.http.listen-addr=0.0.0.0:12345
|
|
18
|
+
- /etc/alloy/config.alloy
|
|
19
|
+
ports:
|
|
20
|
+
- "${ALLOY_PORT:-12345}:12345"
|
|
21
|
+
volumes:
|
|
22
|
+
- ./alloy/config.alloy:/etc/alloy/config.alloy:ro
|
|
23
|
+
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
24
|
+
depends_on:
|
|
25
|
+
loki:
|
|
26
|
+
condition: service_healthy
|
|
27
|
+
healthcheck:
|
|
28
|
+
test:
|
|
29
|
+
[
|
|
30
|
+
"CMD",
|
|
31
|
+
"wget",
|
|
32
|
+
"--quiet",
|
|
33
|
+
"--tries=1",
|
|
34
|
+
"--spider",
|
|
35
|
+
"http://localhost:12345/-/ready",
|
|
36
|
+
]
|
|
37
|
+
interval: 10s
|
|
38
|
+
timeout: 5s
|
|
39
|
+
retries: 5
|
|
40
|
+
|
|
41
|
+
env_vars:
|
|
42
|
+
ALLOY_PORT:
|
|
43
|
+
default: 12345
|
|
44
|
+
description: Alloy HTTP port
|
|
45
|
+
|
|
46
|
+
files:
|
|
47
|
+
- source: config.alloy
|
|
48
|
+
dest: alloy/config.alloy
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: phlo-alloy
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: alloy service plugin for Phlo
|
|
5
|
+
Author-email: Phlo Team <team@phlo.dev>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Description-Content-Type: text/plain
|
|
9
|
+
Requires-Dist: phlo>=0.1.0
|
|
10
|
+
Requires-Dist: pyyaml>=6.0.1
|
|
11
|
+
Provides-Extra: dev
|
|
12
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
13
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
14
|
+
|
|
15
|
+
alloy service plugin for Phlo.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/phlo_alloy/__init__.py
|
|
4
|
+
src/phlo_alloy/config.alloy
|
|
5
|
+
src/phlo_alloy/plugin.py
|
|
6
|
+
src/phlo_alloy/service.yaml
|
|
7
|
+
src/phlo_alloy.egg-info/PKG-INFO
|
|
8
|
+
src/phlo_alloy.egg-info/SOURCES.txt
|
|
9
|
+
src/phlo_alloy.egg-info/dependency_links.txt
|
|
10
|
+
src/phlo_alloy.egg-info/entry_points.txt
|
|
11
|
+
src/phlo_alloy.egg-info/requires.txt
|
|
12
|
+
src/phlo_alloy.egg-info/top_level.txt
|
|
13
|
+
tests/test_alloy_plugin.py
|
|
14
|
+
tests/test_integration_alloy.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
phlo_alloy
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Tests for Alloy service plugin."""
|
|
2
|
+
|
|
3
|
+
from phlo_alloy.plugin import AlloyServicePlugin
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_alloy_service_definition():
|
|
7
|
+
plugin = AlloyServicePlugin()
|
|
8
|
+
defn = plugin.service_definition
|
|
9
|
+
|
|
10
|
+
assert defn["name"] == "alloy"
|
|
11
|
+
assert defn["profile"] == "observability"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_alloy_plugin_metadata():
|
|
15
|
+
plugin = AlloyServicePlugin()
|
|
16
|
+
meta = plugin.metadata
|
|
17
|
+
|
|
18
|
+
assert meta.name == "alloy"
|
|
19
|
+
assert "observability" in meta.tags
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Integration tests for phlo-alloy."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
pytestmark = pytest.mark.integration
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_alloy_plugin_initializes():
|
|
9
|
+
"""Test that Alloy plugin can be instantiated."""
|
|
10
|
+
from phlo_alloy.plugin import AlloyServicePlugin
|
|
11
|
+
|
|
12
|
+
plugin = AlloyServicePlugin()
|
|
13
|
+
assert plugin is not None
|
|
14
|
+
assert plugin.metadata.name == "alloy"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_alloy_service_definition():
|
|
18
|
+
"""Test that service definition can be loaded."""
|
|
19
|
+
from phlo_alloy.plugin import AlloyServicePlugin
|
|
20
|
+
|
|
21
|
+
plugin = AlloyServicePlugin()
|
|
22
|
+
service_def = plugin.service_definition
|
|
23
|
+
|
|
24
|
+
assert isinstance(service_def, dict)
|