{"id":47600271,"url":"https://github.com/graphaelli/execreceiver","last_synced_at":"2026-04-01T18:47:21.993Z","repository":{"id":342642741,"uuid":"1173914421","full_name":"graphaelli/execreceiver","owner":"graphaelli","description":"Collect output from running a subprocess under OpenTelemetry Collector","archived":false,"fork":false,"pushed_at":"2026-03-06T20:45:13.000Z","size":124,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-06T20:51:33.083Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/graphaelli.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"2026-03-05T22:05:33.000Z","updated_at":"2026-03-06T20:38:38.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/graphaelli/execreceiver","commit_stats":null,"previous_names":["graphaelli/execreceiver"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/graphaelli/execreceiver","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/graphaelli%2Fexecreceiver","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/graphaelli%2Fexecreceiver/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/graphaelli%2Fexecreceiver/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/graphaelli%2Fexecreceiver/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/graphaelli","download_url":"https://codeload.github.com/graphaelli/execreceiver/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/graphaelli%2Fexecreceiver/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31290947,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T13:12:26.723Z","status":"ssl_error","status_checked_at":"2026-04-01T13:12:25.102Z","response_time":53,"last_error":"SSL_read: 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":[],"created_at":"2026-04-01T18:47:21.016Z","updated_at":"2026-04-01T18:47:21.986Z","avatar_url":"https://github.com/graphaelli.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Exec Receiver\n\n| Status        |           |\n| ------------- |-----------|\n| Stability     | [alpha]: logs |\n| Distributions | |\n| Issues        | [![Open issues](https://img.shields.io/github/issues-search/graphaelli/execreceiver?query=is%3Aissue%20is%3Aopen\u0026label=open\u0026color=orange\u0026logo=opentelemetry)](https://github.com/graphaelli/execreceiver/issues) |\n\n[alpha]: https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/component-stability.md#alpha\n\nAn OpenTelemetry Collector receiver that runs external commands and captures their stdout/stderr output as log records.\n\n## Security Considerations\n\nThis receiver executes arbitrary commands with the same privileges as the collector process. Keep the following in mind:\n\n- **Restrict access to the collector configuration.** Anyone who can modify the config can execute commands on the host.\n- **Environment is clean by default.** Commands start with an empty environment unless `inherit_environment: true` is set. Avoid inheriting the collector's environment when it may contain secrets (API keys, tokens, credentials).\n- **Be mindful of command output.** Output flows into the telemetry pipeline and may contain sensitive data (PII, credentials, internal paths). Use processors to filter or redact as needed.\n- **Avoid passing secrets as command arguments.** The full command string is recorded in the `exec.command` log attribute on every record.\n- **Set `exec_timeout` in scheduled mode** to prevent runaway processes from accumulating.\n\n## Modes\n\n### Scheduled (default)\n\nRuns the command on a configurable interval.\nEach execution produces a batch of log records from the command's output. If the command is still running when the next interval fires and the `max_concurrent` limit has been reached, that tick is skipped and a warning is logged.\n\n### Streaming\n\nRuns the command continuously and reads output line-by-line as it is produced.\nIf the command exits, it is restarted after a configurable delay with exponential backoff.\nThe backoff starts at `restart_delay`, doubles on each consecutive failure, and caps at `max_restart_delay`.\nIt resets when the command runs successfully for longer than `restart_delay`.\n\n## Configuration\n\n| Field | Type | Default | Description |\n|-------|------|---------|-------------|\n| `command` | `[]string` | *(required)* | Command and arguments to execute. First element is the program. |\n| `mode` | `string` | `scheduled` | Execution mode: `scheduled` or `streaming`. |\n| `interval` | `duration` | `60s` | Time between scheduled executions. Only used in scheduled mode. |\n| `exec_timeout` | `duration` | `0` (none) | Maximum duration for a scheduled execution. Process is killed if exceeded. Only used in scheduled mode. |\n| `max_concurrent` | `int` | `1` | Maximum number of concurrent command executions in scheduled mode. If the limit is reached when a tick fires, that execution is skipped and a warning is logged. Only used in scheduled mode. |\n| `include_stderr` | `bool` | `true` | Whether to capture stderr output alongside stdout. |\n| `max_buffer_size` | `int` | `1048576` (1MB) | Maximum buffer size in bytes for reading a single line of output. |\n| `max_output_size` | `int` | `10485760` (10MB) | Maximum total bytes buffered per scheduled execution. Output is truncated when exceeded. `0` means no limit. Must be \u003e= `max_buffer_size` when set. Only used in scheduled mode. |\n| `environment` | `map[string]string` | `{}` | Environment variables to set for the command. |\n| `working_directory` | `string` | *(inherit)* | Working directory for the command. |\n| `inherit_environment` | `bool` | `false` | If true, inherits the collector's environment as a base. When false (default), starts with a clean environment. `environment` entries are always added. |\n| `restart_delay` | `duration` | `1s` | Initial delay before restarting a streaming command that has exited. Doubles on each consecutive failure (exponential backoff), capped at `max_restart_delay`. Resets after the command runs longer than `restart_delay`. Only used in streaming mode. |\n| `max_restart_delay` | `duration` | `5m` | Maximum backoff delay for streaming command restarts. Must be \u003e= `restart_delay`. Only used in streaming mode. |\n\n### Example: Scheduled\n\n```yaml\nreceivers:\n  exec:\n    command: [\"df\", \"-h\"]\n    mode: scheduled\n    interval: 30s\n```\n\nOn Windows:\n\n```yaml\nreceivers:\n  exec:\n    command: [\"powershell\", \"-Command\", \"Invoke-WebRequest -Uri http://localhost:8080/health -UseBasicParsing | Select-Object -ExpandProperty Content\"]\n    mode: scheduled\n    interval: 30s\n    exec_timeout: 10s\n```\n\n### Example: Streaming\n\n```yaml\nreceivers:\n  exec:\n    command: [\"tail\", \"-f\", \"/var/log/syslog\"]\n    mode: streaming\n    restart_delay: 5s\n    include_stderr: false\n```\n\nOn Windows:\n\n```yaml\nreceivers:\n  exec:\n    command: [\"powershell\", \"-Command\", \"Get-Content -Path C:\\\\logs\\\\app.log -Wait -Tail 0\"]\n    mode: streaming\n    restart_delay: 5s\n    include_stderr: false\n```\n\n### Example: Multiple Receivers\n\n```yaml\nreceivers:\n  exec/disk:\n    command: [\"df\", \"-h\"]\n    mode: scheduled\n    interval: 60s\n  exec/uptime:\n    command: [\"uptime\"]\n    mode: scheduled\n    interval: 300s\n  exec/journal:\n    command: [\"journalctl\", \"-f\", \"-o\", \"json\"]\n    mode: streaming\n\nservice:\n  pipelines:\n    logs:\n      receivers: [exec/disk, exec/uptime, exec/journal]\n      processors: [batch]\n      exporters: [debug]\n```\n\nSee the [examples/](examples/) directory for complete collector configurations.\n\n## Log Record Schema\n\nEach line of output produces a log record with the following structure:\n\n| Field | Value |\n|-------|-------|\n| **Body** | Raw output line text |\n| **Timestamp** | Time the line was read |\n| **ObservedTimestamp** | Time the log record was created |\n| **SeverityNumber** | `INFO` (stdout) or `WARN` (stderr) |\n| **SeverityText** | `INFO` or `WARN` |\n\n### Attributes\n\n| Attribute | Type | Description |\n|-----------|------|-------------|\n| `exec.command` | string | The full command string (space-joined) |\n| `exec.pid` | int64 | Process ID of the executed command |\n| `exec.stream` | string | `stdout` or `stderr` |\n\n### Resource Attributes\n\n| Attribute | Type | Description |\n|-----------|------|-------------|\n| `host.name` | string | Hostname of the collector host |\n\n## Self-Observability\n\nThe receiver exposes internal metrics for monitoring its own operation:\n\n| Metric | Type | Description |\n|--------|------|-------------|\n| `otelcol_exec_receiver_errors` | Counter | Execution errors (start failures, non-zero exits) |\n| `otelcol_exec_receiver_executions` | Counter | Total command executions started |\n| `otelcol_exec_receiver_executions_skipped` | Counter | Scheduled executions skipped due to concurrency limit |\n| `otelcol_exec_receiver_execution_duration` | Histogram | Duration of command executions (seconds) |\n| `otelcol_exec_receiver_log_records` | Counter | Total log records produced from command output |\n| `otelcol_exec_receiver_restarts` | Counter | Streaming mode command restarts |\n\nStandard receiver metrics (`otelcol_receiver_accepted_log_records`, `otelcol_receiver_refused_log_records`)\nare also available via the built-in `ObsReport` instrumentation.\n\n## Process Management\n\n- **Graceful shutdown**: On shutdown, the receiver sends `SIGINT` to running processes, then `SIGKILL` after 5 seconds if the process hasn't exited.\n- **Scheduled mode**: Each execution runs independently. If `exec_timeout` is set, the process is killed if it exceeds the timeout.\n- **Streaming mode**: The command runs continuously. If it exits, the receiver waits before restarting with exponential backoff (starting at `restart_delay`, doubling on each consecutive failure, capped at `max_restart_delay`). The backoff resets when the command runs longer than `restart_delay`. The restart counter metric tracks how many times a streaming command has been restarted.\n- **Environment**: By default, commands start with a clean environment. Use `environment` to set variables, or `inherit_environment: true` to inherit the collector's environment as a base.\n\n### Audit Logging\n\nThe receiver emits structured INFO-level log messages for each command execution to provide an audit trail. These logs are written to the collector's own log output (not the telemetry pipeline) and include the following fields:\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `command` | string | The full command string (space-joined) |\n| `pid` | int | Process ID of the executed command |\n| `exit_code` | int | Exit code (0 for success, \u003e0 for failure, -1 if unavailable) |\n| `duration` | duration | Wall-clock duration of the execution |\n| `mode` | string | `scheduled` or `streaming` |\n| `receiver_id` | string | The receiver's component ID (e.g. `exec/myname`) |\n\nIn **scheduled mode**, a `\"Command exited\"` message is logged after each execution completes.\n\nIn **streaming mode**, a `\"Command started\"` message is logged when the process starts (with `command`, `pid`, `mode`, and `receiver_id` fields), and a `\"Command exited\"` message is logged when the process exits (with all fields including `exit_code` and `duration`).\n\n## Building a Custom Collector\n\nUse the [OpenTelemetry Collector Builder (ocb)](https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/cmd/builder) to include this receiver in a custom collector distribution:\n\n```yaml\n# builder-config.yaml\ndist:\n  name: custom-otelcol\n  output_path: ./dist\n\nreceivers:\n  - gomod: github.com/graphaelli/execreceiver v0.1.0\n\nexporters:\n  - gomod: go.opentelemetry.io/collector/exporter/debugexporter v0.147.0\n\nprocessors:\n  - gomod: go.opentelemetry.io/collector/processor/batchprocessor v0.147.0\n```\n\nThen build:\n\n```sh\nocb --config builder-config.yaml\n```\n\n## Benchmarks\n\nRun benchmarks with:\n\n```sh\ngo test -bench=. -benchtime=3s ./...\n```\n\nExample results (Apple M4 Pro):\n\n```\nBenchmarkLogRecordCreation/1-lines      994836     1188 ns/op     848 B/op    21 allocs/op\nBenchmarkLogRecordCreation/10-lines     519051     2372 ns/op    4768 B/op    97 allocs/op\nBenchmarkLogRecordCreation/100-lines     88672    13651 ns/op   43408 B/op   820 allocs/op\nBenchmarkLogRecordCreation/1000-lines     8958   136036 ns/op  425973 B/op  8023 allocs/op\nBenchmarkScheduledExecution/1-lines       331  3718713 ns/op\nBenchmarkScheduledExecution/1000-lines    334  3587836 ns/op\nBenchmarkReceiverLifecycle              38466    27741 ns/op    8470 B/op   112 allocs/op\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgraphaelli%2Fexecreceiver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgraphaelli%2Fexecreceiver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgraphaelli%2Fexecreceiver/lists"}