{"id":32638623,"url":"https://github.com/defigli/rust-otel-template","last_synced_at":"2026-05-05T17:31:50.576Z","repository":{"id":321296361,"uuid":"1083663995","full_name":"defigli/rust-otel-template","owner":"defigli","description":"Minimal Rust template for async services with OpenTelemetry-based tracing, metrics, and logging—ready for observability stacks like Grafana, Tempo, and Prometheus.","archived":false,"fork":false,"pushed_at":"2025-10-28T21:25:26.000Z","size":32,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-28T23:22:49.222Z","etag":null,"topics":["async","devops","grafana","logging","metrics","monitoring","observability","opentelemetry","otlp","prometheus","rust","telemetry","template","tokio","tracing"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/defigli.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","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-10-26T13:39:06.000Z","updated_at":"2025-10-28T21:22:47.000Z","dependencies_parsed_at":"2025-10-28T23:23:02.643Z","dependency_job_id":"0d582418-e599-4b82-8e4a-19e96194febf","html_url":"https://github.com/defigli/rust-otel-template","commit_stats":null,"previous_names":["defigli/rust-otel-template"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/defigli/rust-otel-template","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/defigli%2Frust-otel-template","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/defigli%2Frust-otel-template/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/defigli%2Frust-otel-template/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/defigli%2Frust-otel-template/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/defigli","download_url":"https://codeload.github.com/defigli/rust-otel-template/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/defigli%2Frust-otel-template/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32660196,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-05T11:29:49.557Z","status":"ssl_error","status_checked_at":"2026-05-05T11:29:48.587Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["async","devops","grafana","logging","metrics","monitoring","observability","opentelemetry","otlp","prometheus","rust","telemetry","template","tokio","tracing"],"created_at":"2025-10-31T02:03:23.093Z","updated_at":"2026-05-05T17:31:50.569Z","avatar_url":"https://github.com/defigli.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Rust Project Template\n\n![CI](https://github.com/defigli/rust-otel-template/actions/workflows/rust.yml/badge.svg)\n![Security Audit](https://github.com/defigli/rust-otel-template/actions/workflows/security-audit.yml/badge.svg)\n![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)\n\n[🔎 View latest security audit report (HTML)](https://defigli.github.io/rust-otel-template/audit.html)\n\nA comprehensive Rust project template featuring logging, telemetry, OpenTelemetry metrics (opt-in), and async runtime with Tokio.\n\n## Features\n\n- **Console Logging**: Structured logging to console using `tracing`\n- **HTTP Telemetry**: Distributed tracing via OpenTelemetry OTLP over HTTP\n- **Metrics (opt-in)**: OpenTelemetry metrics (enable the `metrics` feature to export metrics)\n- **Async Runtime**: Powered by Tokio for high-performance async operations\n\n## Dependencies\n\n- `tokio` - Async runtime\n- `tracing` - Structured logging\n- `tracing-subscriber` - Log formatting and filtering (optional feature `console-log`)\n- `tracing-opentelemetry` - OpenTelemetry integration for traces\n- `opentelemetry` - OpenTelemetry SDK (trace-only by default)\n- `opentelemetry-otlp` - OTLP exporter for traces (+ logs when `otlp-log` feature enabled). Uses the blocking HTTP client via `reqwest-blocking-client` to avoid reactor panics during shutdown; this ensures batch processors can flush on their own worker threads during shutdown.\n- `anyhow` - Error handling\n\n## Quick Start\n\n1. **Run the application**:\n   ```bash\n   cargo run\n   ```\n\n2. **With custom log level**:\n   ```bash\n   RUST_LOG=debug cargo run\n   ```\n\n## Observability Stack (Alloy + Loki + Tempo + Grafana)\n\nBring everything up:\n\n```bash\ndocker compose up -d\n```\n\n### Feature Flags\n\n- `console-log`: human-readable console output (file \u0026 line)\n- `otlp-log`: OTLP log exporter + tracing bridge\n- Default build enables neither; you opt-in explicitly\n\n### Example Usage\n\n```bash\n# Traces only (default)\ncargo run\n\n# Console only\ncargo run --no-default-features --features console-log\n\n# OTLP logs only\ncargo run --no-default-features --features otlp-log\n\n# Console + OTLP logs\ncargo run --no-default-features --features console-log,otlp-log\n```\n\n### Behavior Matrix\n\n| Features Enabled         | Console Output | OTLP Logs | OTLP Traces |\n|-------------------------|:--------------:|:---------:|:-----------:|\n| (default, no features)  |      ❌        |    ❌     |     ✅      |\n| console-log             |      ✅        |    ❌     |     ✅      |\n| otlp-log                |      ❌        |    ✅     |     ✅      |\n| console-log, otlp-log   |      ✅        |    ✅     |     ✅      |\n\n### Host Ports\n\n| Component | Purpose                        | Port  |\n|-----------|--------------------------------|-------|\n| Alloy     | Unified OTLP ingest \u0026 pipeline | 12345 |\n| Alloy     | OTLP HTTP endpoint             | 4318  |\n| Tempo     | OTLP HTTP endpoint (direct)    | 43180 |\n| Tempo     | Query / HTTP API (traces UI)   | 3200  |\n| Loki      | Log storage                    | 3100  |\n| Grafana   | UI                             | 3000  |\n\nRun the app pointing at Alloy (recommended):\n\n```bash\nOTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 cargo run\n```\n\nOptional explicit per-signal overrides:\n\n```bash\nOTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4318/v1/traces \\\nOTEL_EXPORTER_OTLP_LOGS_ENDPOINT=http://localhost:4318/v1/logs \\\ncargo run\n```\n\nAlloy routing (see `config.alloy`):\n- Logs -\u003e Loki (`http://loki:3100/otlp`)\n- Traces -\u003e Tempo (`http://tempo:4318` base, exporter appends /v1/traces)\n- Metrics -\u003e Prometheus remote write\n\n### Viewing Logs in Grafana / Loki\n\n1. Open Grafana: [http://localhost:3000](http://localhost:3000)\n2. Add Loki datasource (URL `http://loki:3100`) if missing.\n3. Explore queries:\n    - `{service_name=\"rust-otel-template\"}`\n    - If not found, try `{service.name=\"rust-otel-template\"}` or `{resource_service_name=\"rust-otel-template\"}` (label names vary).\n4. Span-context logs (from `#[instrument]` spans) can be filtered: `{service_name=\"rust-otel-template\", span_name=\"simulated_work\"}`.\n\n**Troubleshooting missing logs:**\n- Ensure you used Alloy's 4318 port, not Tempo's 43180.\n- Check Alloy UI ([http://localhost:12345](http://localhost:12345)) to confirm pipeline shows log throughput.\n- Increase verbosity: `RUST_LOG=opentelemetry=debug,info OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 cargo run`.\n- Connectivity probe: `curl -v http://localhost:4318/v1/logs` (expect 405/404 if method without body; reaching Alloy is key).\n\n### Viewing Traces (Tempo)\n\nAdd Tempo datasource (URL `http://tempo:3200`) or the Tempo query API URL used in your docker compose. Use Explore → Trace Search with service name or span name once traces arrive. (Future enhancement: inject trace/span IDs into console JSON.)\n\n### Environment Variables Summary\n\n| Variable                        | Description                                         | Default                  |\n|----------------------------------|-----------------------------------------------------|--------------------------|\n| OTEL_EXPORTER_OTLP_ENDPOINT      | Base OTLP endpoint (Alloy)                          | http://localhost:4318    |\n| OTEL_SERVICE_NAME                | Service name resource attribute                     | rust-otel-template       |\n| RUST_LOG                         | Console + subscriber filter                         | info                     |\n| RUST_ENV                         | Environment resource attribute                      | dev                      |\n| OTEL_EXPORTER_OTLP_TRACES_ENDPOINT | Override traces endpoint                          | unset                    |\n| OTEL_EXPORTER_OTLP_LOGS_ENDPOINT | Override logs endpoint                              | unset                    |\n| LOG_FORMAT                       | (Deprecated, removed) previously toggled JSON; now use cargo feature | n/a |\n\n### Loki Label Mapping\n\n| Resource Attribute      | Possible Loki Labels                        |\n|------------------------|---------------------------------------------|\n| service.name           | service_name, service.name, resource_service_name |\n| service.version        | service_version, resource_service_version    |\n| deployment.environment | deployment_environment                      |\n\nUse Grafana Explore label browser to confirm actual naming.\n\n### Configuration\n\nThe application uses the following default configuration:\n\n- **Service Name**: `rust-otel-template`\n- **Service Version**: `0.1.0`\n- **OTLP Endpoint**: `http://localhost:4318`\n\nYou can modify these in the `AppConfig::default()` implementation or add environment variable support.\n\n## Project Structure\n\n```\n├── Cargo.toml          # Dependencies and project metadata\n├── src/\n│   └── main.rs         # Main application with telemetry setup\n├── README.md           # This file\n└── target/             # Build artifacts\n```\n\n## Logging Levels\n\nThe application uses structured logging with different levels:\n\n- `INFO`: General application flow\n- `WARN`: Potentially problematic situations\n- `ERROR`: Error conditions\n- `DEBUG`: Detailed information for debugging\n\n## Metrics\n\nThe application exports the following metrics (if you enable metrics instrumentation / feature):\n\n- `requests_total`: Counter for total requests processed\n- `request_duration_seconds`: Histogram of request processing duration\n- `errors_total`: Counter for total errors\n\n## Traces\n\nEach request is traced with:\n- Span names and attributes\n- Request IDs for correlation\n- Timing information\n- Error conditions\n\n## Customization\n\n### Adding New Metrics\n\n```rust\nlet meter = global::meter(\"your-service-name\");\nlet custom_counter = meter\n    .u64_counter(\"custom_metric\")\n    .with_description(\"Description of your metric\")\n    .init();\n\ncustom_counter.add(1, \u0026[KeyValue::new(\"label\", \"value\")]);\n```\n\n### Adding New Traces\n\n```rust\nuse tracing::{info, span, Level};\n\nlet span = span!(Level::INFO, \"operation_name\", attribute = \"value\");\nlet _enter = span.enter();\ninfo!(\"Operation started\");\n// Your code here\n```\n\n### Custom Configuration\n\nExtend the `AppConfig` struct to add environment variable support:\n\n```rust\nimpl AppConfig {\n    fn from_env() -\u003e Self {\n        Self {\n            service_name: std::env::var(\"OTEL_SERVICE_NAME\")\n                .unwrap_or_else(|_| \"rust-otel-template\".to_string()),\n            otlp_endpoint: std::env::var(\"OTEL_EXPORTER_OTLP_ENDPOINT\")\n                .unwrap_or_else(|_| \"http://localhost:4318\".to_string()),\n            // ... other fields\n        }\n    }\n}\n```\n\n## Production Considerations\n\n1. **Error Handling**: Add comprehensive error handling for your business logic\n2. **Configuration**: Use environment variables or config files for production settings\n3. **Security**: Ensure OTLP endpoints are properly secured\n4. **Performance**: Monitor the overhead of telemetry in production\n5. **Sampling**: Consider trace sampling for high-volume applications\n\n## Next Steps / Enhancements\n\n- Add metrics exporter usage example (counter + histogram wiring).\n- Include trace \u0026 span IDs in console JSON output for easier correlation.\n- Structured error logging with span status set to error.\n- Graceful shutdown improvements: ensure batch processors flush within timeout.\n\n### Shutdown Notes\n\nTelemetry is initialized and shutdown inside the Tokio runtime (`#[tokio::main]`). The batch processors use a blocking HTTP client to ensure final flushes succeed in their worker threads without depending on the Tokio reactor; ensure your runtime awaits shutdown so processors can complete their flush interval.\n\n## License\n\nThis template is provided as-is for educational and development purposes.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdefigli%2Frust-otel-template","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdefigli%2Frust-otel-template","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdefigli%2Frust-otel-template/lists"}