{"id":28376173,"url":"https://github.com/ebean-orm/ebean-insight-server","last_synced_at":"2026-06-13T03:00:17.721Z","repository":{"id":293337821,"uuid":"983717237","full_name":"ebean-orm/ebean-insight-server","owner":"ebean-orm","description":null,"archived":false,"fork":false,"pushed_at":"2026-06-13T02:59:40.000Z","size":757,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-06-13T03:00:12.898Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ebean-orm.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-05-14T20:04:02.000Z","updated_at":"2026-06-13T02:59:43.000Z","dependencies_parsed_at":"2025-07-28T18:20:25.076Z","dependency_job_id":"d790f605-1ffd-40c0-ac21-dece4852bf9c","html_url":"https://github.com/ebean-orm/ebean-insight-server","commit_stats":null,"previous_names":["ebean-orm/ebean-insight-server"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/ebean-orm/ebean-insight-server","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ebean-orm%2Febean-insight-server","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ebean-orm%2Febean-insight-server/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ebean-orm%2Febean-insight-server/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ebean-orm%2Febean-insight-server/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ebean-orm","download_url":"https://codeload.github.com/ebean-orm/ebean-insight-server/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ebean-orm%2Febean-insight-server/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34270417,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-13T02:00:06.617Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-05-30T00:05:42.853Z","updated_at":"2026-06-13T03:00:17.706Z","avatar_url":"https://github.com/ebean-orm.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ebean-insight-server\n\n- Collects metrics from applications using Ebean ORM that send it their metrics.\n- Performs a rollup of metrics on 1 min, 10 min, 1 hour basis.\n- Supports ability to request Query plans and collect query plans\n- For Postgres query plans, uses pev2 to view query plan details\n- Can also forward ingested metrics to any OTLP/HTTP collector\n  (Grafana Alloy, OpenTelemetry Collector, Grafana Cloud, etc.)\n- Exposes a versioned, agent-friendly REST API at `/v1` (contract-first via\n  OpenAPI; see [`api/src/main/openapi/v1.yaml`](api/src/main/openapi/v1.yaml)).\n\n## Install\n\n- **Server** — Kubernetes, Docker, or standalone native binary:\n  [`docs/install-server.md`](docs/install-server.md)\n- **CLI** (`insight`) — per-OS binaries for macOS, Linux, Windows:\n  [`docs/install-cli.md`](docs/install-cli.md) · non-interactive recipe for AI\n  agents: [`docs/install-cli-agents.md`](docs/install-cli-agents.md)\n- **MCP server** — expose the `/v1` API to AI agents over the Model Context\n  Protocol: [`docs/install-mcp.md`](docs/install-mcp.md) ·\n  [connecting clients](docs/connect-mcp-clients.md)\n\nFor the server's full configuration / mode reference see\n[`docs/deployment-modes.md`](docs/deployment-modes.md). To require OAuth2 JWT\nbearer auth on the server's endpoints see [`docs/auth.md`](docs/auth.md). For\nday-to-day CLI usage see [`cli/README.md`](cli/README.md).\n\nPlaybooks — task-oriented guides that drive the API/CLI/MCP to an outcome:\n\n- [`docs/playbook-slow-query-trace.md`](docs/playbook-slow-query-trace.md) — a slow-query trace → root cause\n- [`docs/playbook-topn-triage.md`](docs/playbook-topn-triage.md) — find the most expensive queries\n- [`docs/playbook-missing-plans-backfill.md`](docs/playbook-missing-plans-backfill.md) — capture EXPLAINs for expensive queries\n- [`docs/playbook-plan-change-alert.md`](docs/playbook-plan-change-alert.md) — respond to a plan-shape regression\n\n## Modules\n\n| Module | Purpose |\n|--------|---------|\n| `api`  | OpenAPI spec (`v1.yaml`) and generated `/v1` API interfaces + DTOs (record types). |\n| `client` | Generated typed HTTP client (`*HttpClient`) for the `/v1` API. |\n| `server` | The running service: ingest endpoints, rollups, and `/v1` controllers. |\n| `forwarder` | Library that maintains a stable local endpoint to the server via a supervised `kubectl port-forward`. See [`forwarder/README.md`](forwarder/README.md). |\n| `cli` | `insight` command line tool over the `/v1` API; compiles to a native binary. See [`cli/README.md`](cli/README.md). |\n| `mcp` | Model Context Protocol server exposing the `/v1` API to AI agents; compiles to a native binary. See [`docs/install-mcp.md`](docs/install-mcp.md). |\n\n## /v1 API (agent / CLI / tooling)\n\nThe `/v1` API uses **natural keys** in the path (app name, env name) instead\nof internal numeric ids — making it suitable for CLIs, agents, and ad-hoc\ntooling. The OpenAPI spec is the source of truth:\n\n- Spec: [`api/src/main/openapi/v1.yaml`](api/src/main/openapi/v1.yaml)\n\nAll endpoints expect the `Insight-Key` header (same auth as `/api`).\nTime windows accept either `sinceMinutes` or `sinceHours` (not both → 400).\n\n### Example recipes\n\nDiscover apps + envs:\n```shell\ncurl -H \"Insight-Key: $KEY\" http://localhost:8090/v1/apps\ncurl -H \"Insight-Key: $KEY\" http://localhost:8090/v1/envs\ncurl -H \"Insight-Key: $KEY\" http://localhost:8090/v1/apps/myapp\n```\n\nTop-N most expensive metrics for an app (last hour):\n```shell\ncurl -H \"Insight-Key: $KEY\" \\\n  \"http://localhost:8090/v1/apps/myapp/metrics/top?orderBy=total\u0026sinceMinutes=60\u0026limit=20\"\n```\n\nTop-N across all apps (last 24h, plan-capable only):\n```shell\ncurl -H \"Insight-Key: $KEY\" \\\n  \"http://localhost:8090/v1/metrics/top?sinceHours=24\u0026planCapable=true\u0026limit=50\"\n```\n\nFind ORM metrics lacking a recent plan capture, ranked by execution cost\n(omit the `/apps/myapp` segment to rank across all apps):\n```shell\ncurl -H \"Insight-Key: $KEY\" \\\n  \"http://localhost:8090/v1/apps/myapp/metrics/missing-plans?by=total\u0026sinceHours=24\u0026limit=50\"\ncurl -H \"Insight-Key: $KEY\" \\\n  \"http://localhost:8090/v1/metrics/missing-plans?by=total\u0026limit=50\"\n```\n\nTrace → plan: take a hash from a span attribute (`ebean.query_hash`) and\nlook up the matching metric and any captured plans, optionally requesting\na fresh capture:\n```shell\nHASH=8a519a4c120289bd505a4a79c27f2895\ncurl -H \"Insight-Key: $KEY\" \\\n  \"http://localhost:8090/v1/apps/myapp/metrics/by-hash/$HASH\"\ncurl -H \"Insight-Key: $KEY\" \\\n  \"http://localhost:8090/v1/plans?app=myapp\u0026hash=$HASH\"\ncurl -X POST -H \"Insight-Key: $KEY\" \\\n  \"http://localhost:8090/v1/apps/myapp/plans/by-hash/$HASH/request?env=prod\"\n```\n\nTime-series for one hash (mean is derived client-side from the additive\n`count`/`total`; bucket resolution is chosen automatically from the window):\n```shell\ncurl -H \"Insight-Key: $KEY\" \\\n  \"http://localhost:8090/v1/apps/myapp/metrics/by-hash/$HASH/timeseries?sinceHours=6\"\n```\n\nFetch a specific plan (full SQL + plan + bind values) by id:\n```shell\ncurl -H \"Insight-Key: $KEY\" http://localhost:8090/v1/plans/12345\n```\n\nList in-flight capture requests — requested but not yet collected (tracked\ndurably; survives forwarder polls and server restarts; a request whose query\nnever executes ages out after ~15 minutes):\n```shell\ncurl -H \"Insight-Key: $KEY\" \"http://localhost:8090/v1/plans/pending\"\ncurl -H \"Insight-Key: $KEY\" \"http://localhost:8090/v1/plans/pending?app=myapp\u0026env=test\"\n```\n\n### Conventions\n\n- **Natural keys** — `{app}` and `?env=` are names, not ids. Unknown\n  natural keys return `200` with an empty list (not `404`) — except\n  `GET /v1/apps/{app}` and `POST .../plans/by-hash/{hash}/request` which\n  return `404` when the app doesn't exist.\n- **planCapable** — derived from the metric name (`orm.`, `dto.`, or\n  `sql.query.`, excluding `orm.update.`) and stored on\n  `app_metric.plan_capable`. Only plan-capable metrics support\n  `POST .../plans/by-hash/{hash}/request`; other metrics return `400`.\n- **orderBy** — allowlist on `/metrics/top`: `total` (default), `mean`,\n  `max`, `count`. Anything else → `400`.\n- **Hash vs label** — `hash` is the metric `key` (deterministic SQL hash;\n  ORM-only), `label` is the human metric name (e.g. `orm.OrderDao.find`).\n\n### CLI\n\nFor interactive / scripted use there is an `insight` CLI ([`cli/README.md`](cli/README.md))\nthat wraps these endpoints. It can reach the server via a static `--url`, or — by\ndefault — a supervised `kubectl port-forward` that reuses your cluster RBAC as\nauth (no `Insight-Key` needed), optionally held open by a background\n`insight forward` daemon:\n\n```shell\ninsight forward            # optional: hold one supervised tunnel open\ninsight apps\ninsight plans -n 5\ninsight plan 12345 --raw\n```\n\n## Deployment modes\n\n| Mode | What it does | Postgres |\n|------|--------------|----------|\n| **Persist** (default) | Stores metrics + query plans in Postgres, runs rollups, serves the `/v1` API. | required |\n| **Forward-only** | Pure smart-proxy — forwards each ingest via OTLP, optionally logs query plans. | not required |\n\nSet both `METRICS_STORE_ENABLED=false` and `PLANS_STORE_ENABLED=false` to\nenable forward-only mode. Either mode can additionally forward via\n`FORWARD_OTEL_ENABLED=true` + `FORWARD_OTEL_ENDPOINT=http://...`.\n\nSee [`docs/deployment-modes.md`](docs/deployment-modes.md) for full\nconfiguration, env-var reference, Docker examples and expected startup logs.\n\n#### Future TODOs:\n- Support reporting aggregated metrics onto Graphite, StatsD, etc\n- Provide automation for automatically collecting query plans for:\n  - new queries,\n  - queries that exceed a threshold (anomalies)\n\n\n## Building local native image\nRequires GraalVM installed\n```shell\nsdk install java 24-graal\n\nsdk use java 24-graal\n```\n\nBuild on a Mac (no G1GC supported)\n```shell\nmvn clean package -P native,mac -DskipTests\n```\nBuild on a Linux (with G1GC)\n```shell\nmvn clean package -P native,linux -DskipTests\n```\n\n## Run the application locally\n\n#### Step 1\n- Requires docker to be installed locally\n- Run the main method on src/test/java/main/StartPostgresDocker\n\n#### Step 2\nRun the native application. We pass it an external configuration file via `-Dprops.file=`.\n```shell\n./target/ebean-insight -Dprops.file=application.yaml\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Febean-orm%2Febean-insight-server","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Febean-orm%2Febean-insight-server","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Febean-orm%2Febean-insight-server/lists"}