Metadata-Version: 2.1
Name: wexample-wex-addon-services-monitoring
Version: 7.9.9
Summary: Registers monitoring services — Grafana, Kibana, Plausible, and Elastic Fleet — as wex app services with lifecycle commands
Author-Email: weeger <contact@wexample.com>
License: MIT
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.10
Requires-Dist: wexample-wex-addon-app>=30.0.0
Requires-Dist: wexample-wex-core>=30.0.0
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Description-Content-Type: text/markdown

# wex_addon_services_monitoring

Version: 7.9.9

`wex-addon-services-monitoring` is a wex addon that registers Grafana, Kibana, Plausible, and Elastic Fleet as wex app services, making each available as a named service with lifecycle commands inside any wex-managed application. Each service exposes at minimum a `ready` command that health-checks its container's HTTP endpoint; Plausible additionally ships an `install` command that generates its required secrets (`SECRET_KEY_BASE`, `TOTP_VAULT_KEY`) and writes database names into the app config. It is aimed at developers who want to add observability or analytics tooling to a wex app without hand-writing the service definitions.

## Table of Contents

- [Installation](#installation)
- [Quickstart](#quickstart)
- [Tests](#tests)
- [Architecture](#architecture)
- [Integration in the Suite](#integration-in-the-suite)
- [Dependencies](#dependencies)
- [Versioning & Compatibility Policy](#versioning--compatibility-policy)
- [License](#license)
- [About us](#about-us)
- [Known Limitations & Roadmap](#known-limitations--roadmap)
- [Status & Compatibility](#status--compatibility)
- [Useful Links](#useful-links)
- [Migration Notes](#migration-notes)

## Installation

```bash
pip install wexample-wex-addon-services-monitoring
```

Requires Python >=3.10.

## Quickstart

Install the package:

```bash
pip install wexample-wex-addon-services-monitoring
```

The public entry point is `ServicesMonitoringAddonManager`, exported from src/wexample_wex_addon_services_monitoring/services_monitoring_addon_manager.py:

```python
from wexample_wex_addon_services_monitoring import ServicesMonitoringAddonManager
```

Passing it to a wex kernel registers six monitoring services — `grafana`, `kibana`, `matomo`, `plausible`, `fleet`, and `fleet_agent` — each backed by a Docker Compose definition.

**Plausible** shows the full lifecycle. Before starting the container, run `plausible service install`. It generates two secrets:

- `SECRET_KEY_BASE` — 64-byte URL-safe token, used by Phoenix to sign sessions.
- `TOTP_VAULT_KEY` — 32-byte base64-encoded key, used to encrypt user TOTP secrets in the database.

It also derives `db_name` (`<app>_plausible`) and `events_db_name` (`<app>_plausible_events`) from the app name and writes all four values into the app config file. Running it a second time is safe — existing values are preserved. It logs:

```
Configured plausible service for app '<name>' (db: <name>_plausible, events_db: <name>_plausible_events)
```

Once the container is running, `plausible service ready` checks liveness by executing inside the container:

```bash
docker exec <project>_plausible wget -qO- http://127.0.0.1:8000/api/health
```

It returns `True` when the exit code is 0 — meaning Phoenix has finished migrations and is serving traffic, not just listening on the port. `fleet service ready` and `kibana service ready` follow the same shape: Fleet polls `https://localhost:8220/api/status`, Kibana polls `http://localhost:5601/api/status`.

Service declarations live in src/wexample_wex_addon_services_monitoring/services/plausible/service.yml and its siblings; each service's Docker Compose file is in the `docker/` subdirectory alongside it.

## Tests

This project uses `pytest` for testing and `pytest-cov` for code coverage analysis.

### Installation

First, install the required testing dependencies:
```bash
.venv/bin/python -m pip install pytest pytest-cov
```

### Basic Usage

Run all tests with coverage:
```bash
.venv/bin/python -m pytest --cov --cov-report=html
```

### Common Commands
```bash
# Run tests with coverage for a specific module
.venv/bin/python -m pytest --cov=your_module

# Show which lines are not covered
.venv/bin/python -m pytest --cov=your_module --cov-report=term-missing

# Generate an HTML coverage report
.venv/bin/python -m pytest --cov=your_module --cov-report=html

# Combine terminal and HTML reports
.venv/bin/python -m pytest --cov=your_module --cov-report=term-missing --cov-report=html

# Run specific test file with coverage
.venv/bin/python -m pytest tests/test_file.py --cov=your_module --cov-report=term-missing
```

### Viewing HTML Reports

After generating an HTML report, open `htmlcov/index.html` in your browser to view detailed line-by-line coverage information.

### Coverage Threshold

To enforce a minimum coverage percentage:
```bash
.venv/bin/python -m pytest --cov=your_module --cov-fail-under=80
```

This will cause the test suite to fail if coverage drops below 80%.

## Architecture

The addon is a registration layer. It contributes six monitoring services to any wex app that loads it; each service is self-contained in a subdirectory under `services/`.

### Addon entry point

src/wexample_wex_addon_services_monitoring/services_monitoring_addon_manager.py contains `ServicesMonitoringAddonManager`, which subclasses `AbstractAddonManager` from `wexample-wex-core`:

```python
@base_class
class ServicesMonitoringAddonManager(AbstractAddonManager):
    @classmethod
    def get_package_module(cls) -> Any:
        import wexample_wex_addon_services_monitoring
        return wexample_wex_addon_services_monitoring
```

`get_package_module` returns the package object; wex core uses it to walk the package tree and auto-discover every service and command beneath it. The class is re-exported from src/wexample_wex_addon_services_monitoring/__init__.py as the sole public symbol.

### Services

Six services live under `src/wexample_wex_addon_services_monitoring/services/`, one subdirectory each:

| Service | Depends on | Proxy |
|---|---|---|
| `grafana` | — | yes |
| `kibana` | elasticsearch | yes |
| `matomo` | mysql | yes |
| `plausible` | postgres, clickhouse | yes |
| `fleet` | elasticsearch, kibana | no |
| `fleet_agent` | fleet | no |

Every service directory has the same shape:

- `service.yml` — declares the service name, tags, dependencies, and the path to its Docker Compose file.
- `docker/docker-compose.yml` — the Compose definition wex merges when the service is active.
- `app_service.py` (where present) — a class that subclasses `AppService` from `wexample-wex-addon-app`.
- `commands/service/` — one module per lifecycle command.

### service.yml

src/wexample_wex_addon_services_monitoring/services/plausible/service.yml is representative:

```yaml
name: plausible
tags:
  - monitoring
  - analytics
config:
  proxy: true
dependencies:
  - postgres
  - clickhouse
docker:
  compose: docker/docker-compose.yml
```

`dependencies` tells wex which other services must be present before this one starts. `proxy: true` means the service is exposed through the app's reverse proxy.

### app_service.py

Services with no custom behaviour — kibana, fleet, fleet_agent — inherit `AppService` and add nothing. Plausible overrides `get_workdir_contribution` to return `{"children": []}`, telling wex it contributes no filesystem mounts; all state lives inside postgres and clickhouse.

### Commands

Commands live in `commands/service/`. Each is a plain function decorated with `@command`:

```python
@command(
    type=COMMAND_TYPE_SERVICE,
    tags=[DomainTag.MONITORING, DomainTag.SERVICE, EffectTag.READ_ONLY, ...],
)
def plausible__service__ready(context: ExecutionContext, service: AppService) -> BooleanResponse:
    ...
```

`type=COMMAND_TYPE_SERVICE` binds the function to a service rather than to the app or the global CLI. The domain tags (`domain:monitoring`, `domain:service`) are defined in src/wexample_wex_addon_services_monitoring/const/tags.py.

### ready commands

Plausible, fleet, and kibana each ship a `ready` command. The pattern is identical across all three:

1. Read `app.project_name` from the runtime config.
2. Build the container name as `{project_name}_{service_name}`.
3. Run `docker exec {container} <probe>` and return `BooleanResponse(returncode == 0)`.

The probes differ by service: Plausible executes `wget -qO- http://127.0.0.1:8000/api/health` (ready only after Phoenix finishes migrations), Kibana runs `curl -sf http://localhost:5601/api/status`, Fleet runs `curl -sf https://localhost:8220/api/status -k`.

### plausible service install

src/wexample_wex_addon_services_monitoring/services/plausible/commands/service/install.py is the only command that writes state. It sets four keys under `service.plausible.*` in the app config file:

- `secret_key_base` — 64-byte URL-safe token, used by Phoenix to sign sessions and cookies.
- `totp_vault_key` — 32-byte base64-encoded key, used to encrypt TOTP secrets in the database.
- `db_name` — defaults to `{app_name}_plausible`.
- `events_db_name` — defaults to `{app_name}_plausible_events`.

It also sets `service.postgres.user` to `plausible` so that the postgres superuser matches the credentials Plausible's `libpq` client sends. Existing values are never overwritten, making the command safe to run more than once.

### Call path

When a command such as `plausible service ready` is invoked:

1. The wex kernel resolves `ServicesMonitoringAddonManager` and, through `get_package_module`, locates the `plausible` service directory.
2. It instantiates the service's `AppService` and passes it alongside an `ExecutionContext` to the command function.
3. The command calls `service.app_workdir.get_runtime_config()` to read `app.project_name`, assembles the container name, and shells out to `docker exec`.
4. The `BooleanResponse` travels back through the kernel to the caller.

`install` follows the same routing up to step 2; from there it calls `service.app_workdir.get_config_file()`, mutates the config object, writes it back, and rebuilds the runtime config with `get_runtime_config(rebuild=True)`.

## Integration in the Suite

This package is part of the Wexample Suite — a collection of high-quality, modular tools designed to work seamlessly together across multiple languages and environments.

### Related Packages

The suite includes packages for configuration management, file handling, prompts, and more. Each package can be used independently or as part of the integrated suite.

Visit the [Wexample Suite documentation](https://docs.wexample.com) for the complete package ecosystem.

## Dependencies

- wexample-wex-addon-app: >=30.0.0
- wexample-wex-core: >=30.0.0

## Versioning & Compatibility Policy

Wexample packages follow **Semantic Versioning** (SemVer):

- **MAJOR**: Breaking changes
- **MINOR**: New features, backward compatible
- **PATCH**: Bug fixes, backward compatible

We maintain backward compatibility within major versions and provide clear migration guides for breaking changes.

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

Free to use in both personal and commercial projects.

## About us

[Wexample](https://wexample.com) stands as a cornerstone of the digital ecosystem — a collective of seasoned engineers, researchers, and creators driven by a relentless pursuit of technological excellence. More than a media platform, it has grown into a vibrant community where innovation meets craftsmanship, and where every line of code reflects a commitment to clarity, durability, and shared intelligence.

This packages suite embodies this spirit. Trusted by professionals and enthusiasts alike, it delivers a consistent, high-quality foundation for modern development — open, elegant, and battle-tested. Its reputation is built on years of collaboration, refinement, and rigorous attention to detail, making it a natural choice for those who demand both robustness and beauty in their tools.

Wexample cultivates a culture of mastery. Each package, each contribution carries the mark of a community that values precision, ethics, and innovation — a community proud to shape the future of digital craftsmanship.

## Known Limitations & Roadmap

Current limitations and planned features are tracked in the GitHub issues.

See the [project roadmap](https://github.com/wexample/python-wex_addon_services_monitoring/issues) for upcoming features and improvements.

## Status & Compatibility

**Maturity**: Production-ready

**Python Support**: >=3.10

**OS Support**: Linux, macOS, Windows

**Status**: Actively maintained

## Useful Links

- **Homepage**: https://github.com/wexample/python-wex-addon-services-monitoring
- **Documentation**: [docs.wexample.com](https://docs.wexample.com)
- **Issue Tracker**: https://github.com/wexample/python-wex-addon-services-monitoring/issues
- **Discussions**: https://github.com/wexample/python-wex-addon-services-monitoring/discussions
- **PyPI**: [pypi.org/project/wexample-wex-addon-services-monitoring](https://pypi.org/project/wexample-wex-addon-services-monitoring/)

## Migration Notes

When upgrading between major versions, refer to the migration guides in the documentation.

Breaking changes are clearly documented with upgrade paths and examples.
