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.
@@ -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"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
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,2 @@
1
+ [phlo.plugins.services]
2
+ alloy = phlo_alloy.plugin:AlloyServicePlugin
@@ -0,0 +1,6 @@
1
+ phlo>=0.1.0
2
+ pyyaml>=6.0.1
3
+
4
+ [dev]
5
+ pytest>=7.0
6
+ ruff>=0.1.0
@@ -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)