{"id":41424076,"url":"https://github.com/domstolene/da-otel-agent","last_synced_at":"2026-04-08T13:01:00.931Z","repository":{"id":187222006,"uuid":"653632340","full_name":"domstolene/da-otel-agent","owner":"domstolene","description":"This project delivers an OpenTelemetry Java Agent extension with a dynamically configurable sampler, along with the optional REST service for configuring it.","archived":false,"fork":false,"pushed_at":"2026-03-11T10:55:17.000Z","size":2640,"stargazers_count":13,"open_issues_count":13,"forks_count":3,"subscribers_count":10,"default_branch":"main","last_synced_at":"2026-03-11T16:21:24.669Z","etag":null,"topics":["java","observability","opentelemetry","otel","trace"],"latest_commit_sha":null,"homepage":"","language":"Java","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/domstolene.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":"2023-06-14T12:27:00.000Z","updated_at":"2026-03-11T10:53:51.000Z","dependencies_parsed_at":"2023-08-09T13:12:56.455Z","dependency_job_id":"afbe36cf-fdb6-4f9f-a784-d3304f0ed0e6","html_url":"https://github.com/domstolene/da-otel-agent","commit_stats":null,"previous_names":["domstolene/da-otel-agent"],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/domstolene/da-otel-agent","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/domstolene%2Fda-otel-agent","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/domstolene%2Fda-otel-agent/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/domstolene%2Fda-otel-agent/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/domstolene%2Fda-otel-agent/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/domstolene","download_url":"https://codeload.github.com/domstolene/da-otel-agent/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/domstolene%2Fda-otel-agent/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31556239,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T10:21:54.569Z","status":"ssl_error","status_checked_at":"2026-04-08T10:21:38.171Z","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":["java","observability","opentelemetry","otel","trace"],"created_at":"2026-01-23T14:12:31.552Z","updated_at":"2026-04-08T13:01:00.924Z","avatar_url":"https://github.com/domstolene.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# DA OpenTelemetry Agent \u0026 Service\n\nThis project delivers an [OpenTelemetry Java Agent](https://opentelemetry.io/docs/instrumentation/java/automatic/) including a remotely configurable [sampler](https://opentelemetry.io/docs/concepts/sampling/), along with the accompanying REST service (and optional front-end) for configuring it. \n\n![](system.png)\n\nBy utilizing a remotely controlled sampler, we can dynamically change its behavior based on the current needs. For example, we could adjust sampling rates or rules, all without having to redeploy or restart our applications – which would normally be the case. If remote control is not desirable, the agent configuration can be set to _read only_. This will still expose the configuration to the service along with any metrics collected. This allowing you to for example. get an idea of how many spans _would be_ sampled if the sampler was set to `always_on`.\n\nThe sampler is implemented as an [extension](https://opentelemetry.io/docs/instrumentation/java/automatic/extensions/) to the agent and can be made use of in a way that allows you to simply replace the existing `opentelemetry-javaagent.jar` with `da-opentelemetry-javaagent.jar`. Existing configurations can be used as is, and enabling the `dynamic` sampler is optional.\n\n## The Java Agent Extension\n\nThe agent extension enhances the standard OTEL Java agent's capabilities by allowing it to switch between different [sampler implementations](https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk-extensions/autoconfigure/README.md#sampler) and their configurations on the fly. This is enabled by making use of the `dynamic` sampler which delegates the sampling decisions to one of the available implementations. In addition the dynamic sampler includes a basic filtering mechanism that allows spans to be created or discarded based on a simple set of rules.\n\nThis means that you can adjust your sampling strategy during runtime without having to stop and restart your application. It adds a great deal of flexibility to your observability strategy and can help you adapt to changing diagnostic needs.\n\n### Filtering\n\nThe filtering mechanism is applied *before* the underlying sampler recieves its data. It can be used to determine whether or not a span should be created. Note that inclusion takes precedence.\n\nOTEL can add certain pieces of metadata, or [attributes](https://opentelemetry.io/docs/concepts/signals/traces/#attributes), to each [span](https://opentelemetry.io/docs/concepts/signals/traces/#spans) it collects. For HTTP requests, this might include things like the target URL of the request (`http.target`), or the HTTP method used (`http.method`). These attributes are what is used for the filtering. For example:\n\n```yaml\nrules:\n  - exclude:\n    - http.target: \"/health/.+\"\n      http.method: \"GET\"\n    - http.target: \"/metrics\"\n      http.method: \"GET\"\n  - include:\n    - http.method: \"POST\"\n```\n\nIn this example all HTTP `GET` calls to the health and metrics endpoints are ignored, while all `POST` calls are sampled, regardless of what the underlying sampler decides should be sampled or not. If the attributes does not match any of these rules, it is up to the underlying sampler to determine whether or not the span should be created.\n\nMore than one attribute can be specified in each rule, and all must match for the rule to trigger. Also notice that Java regular expressions can be used.\n\n## The Agent Configuration Service\n\nThe _OpenTelemetry Agent Configuration Service_ is a component of this project that keeps track of different agent configurations. This service exposes a RESTful API that allows clients to interact with it. The API supports all the common REST verbs. The endpoints are as follows:\n\n* `POST /agent-configuration` – Posts _new_ agent configuration(s). The configuration must be in the payload as a single object or an array if posting multiple configurations.\n* `PATCH /agent-configuration/\u003cid\u003e` – Updates the agent configuration with any changes. Only _changed_ values will be updated and the payload does not have to be complete.\n* `GET /agent-configuration/\u003cid\u003e` – Returns an agent configuration or 404 if not found.\n* `PUT /agent-configuration/\u003cid\u003e` – Updates an existing configuration, returns 404 if not found, or 403 if it is set to be _read only_. The configuration must be in the payload.\n* `DELETE /agent-configuration/\u003cid\u003e` – Deletes the agent configuration.\n* `GET /agent-configuration` – Returns all agent configurations.\n\nWhile the dynamic sampler is working, the following metrics are collected and exposed on the [Prometheus](https://prometheus.io) compatible endpoint `/metrics`. For each of the configured services, the following metrics are collected:\n\n* the total number of samples processed\n* the toal number of samples recorded\n* the total number of samples dropped\n* the number of samples excluded by filtering rules\n* the number of samples included by filtering rules\n* the number of samples excluded by sampling rules\n* the number of samples included by sampling rules\n\nNote that the current implementation of the service does _not_ persist agent configurations or metrics. If the service is restarted everything will be lost, however data should be available again once the agents report their configurations and metrics. This may take up to 30 seconds.\n\n### Configuration\n\nThe service is delivered as a Docker image, ready to be deployed. The service is itself configured to be instrumented with the agent. However no exporters are defined, so traces are not submitted to any collectors. The default values can be overridden by specifying these in the environment variable `$JAVA_OPTS`.\n\n### Security\n\nThe configuration service can be secured using a API key. This is enabled by specifying `-Dotel.configuration.service.api.key=\"\u003ckey\u003e\"` as a JVM option when starting the service. The same property must be used when configuring the agent.\n\n## The Agent Configuration Frontend\n\n![](frontend-overview.png)\n\nThe frontend is a basic Spring Boot, Thymeleaf and Bootstrap based service that can be used to get a quick overview of the sampler configurations while making it a bit easier to do the actual configuration. It also gives you quick access to the Jaeger interface for the service. It can be configured setting the `JAVA_OPTS` environment variable to something like this:\n\n```shell\n-Dotel.configuration.service.url=http://localhost:8080 \\\n-Dotel.configuration.public.url=https://otelconfig.test \\\n-Dotel.configuration.jaeger.url=https://jaeger.test \\\n```\n\nAs it is using JavaScript to access the backend service we need the public URL (`Route` on OpenShift).\n\nIn order to secure it (if need be), we suggest using an _oauth proxy_ in front of the frontend.\n\n## Usage\n\nThere are basically three ways of configuring the agent. Either you use a file-based configuration, a service-based, or both. \n\nA typical use case would be to set up a file based configuration while pointing to the service. In this case the agent will load and use the configuration from the file. It will connect to the service, and if the the agent is not registered there, upload the current configuration. If the configuration is changed on the service, the agent will update and use this version, unless the `readOnly` flag is set to true. The configuration file will automatically be reloaded if changed.\n\n### Example agent configuration\n\n```shell\n-javaagent:da-opentelemetry-javaagent.jar \\\n-Dotel.metrics.exporter=none \\\n-Dotel.service.name=my-service \\\n-Dotel.traces.sampler=dynamic \\\n-Dotel.configuration.readOnly=false \\\n-Dotel.configuration.service.file=otel-configuration-file.yaml \\\n-Dotel.configuration.service.url=http://otel-configuration-service.test \\\n-Dotel.exporter.otlp.endpoint=http://otel-collector.test:4317 \\\n-Dotel.traces.exporter=otlp\n```\n\n```yaml\nserviceName: my-service\nsampler: parentbased_traceidratio\nsampleRatio: 0.1\nreadOnly: false\nrules:\n  - exclude:\n    - http.target: \"/agent-configuration/.+\"\n      http.method: \"GET\"\n  - include:\n    - http.method: \"POST\"\n```\n\nNotice that `otel.traces.sampler` must be set to `dynamic`, while the `sampler` entry in the configuration file points to the actual implementation. By default, `readOnly` is set to `false`. The configuration must explicitly set to `readOnly: true` in order for the service to disable the configuration.\n\n## Building and testing\n\nIn order to test this setup, first start Jaeger and Prometheus by calling `docker compose up` found in the root folder. This will start a new Jaeger instance in Docker and expose port 4317 for tracing and \u003ca href=\"http://localhost:16686\"\u003ehttp://localhost:16686\u003c/a\u003e for the UI. The Prometheus UI will be available at \u003ca href=\"http://localhost:9090\"\u003ehttp://localhost:9090\u003c/a\u003e\n\nNow run `build-and-test.sh`. This will build the agent, run the tests, start the service instrumented using the built agent and launch the frontend on \u003ca href=\"http://localhost:8081\"\u003ehttp://localhost:8081\u003c/a\u003e.\n\nSince the configuration service is not started when it's being instrumented, obtaining the remote configuration will fail and defaults will be used. You will see something like this in the log:\n\n```\nopentelemetry-javaagent - version: 1.29.0\nCould not connect to OTEL Configuration Service at http://localhost:8080,\nusing sampler \"parentbased_always_off\".\n```\n\nIf the OpenTelemetry Configuration Service is available, but does not contain a configuration for the agent, the agent will register itself. You can see this in the log as:\n\n```\nSelf-registered as \"da-otel-agent-service\" at the OTEL Configuration Service\n```\n\nNow getting the available configurations from the service will a JSON array with all the agent configurations:\n\n```\ncurl -s -X GET http://localhost:8080/agent-configuration | jq\n[\n  {\n    \"serviceName\": \"da-otel-agent-service\",\n    \"sampleRatio\": 0.0,\n    \"sampler\": \"parentbased_always_off\"\n  }\n]\n```\n\nIn order to test the agent and the service you can execute the following to turn on sampling:\n\n```bash\ncurl -X POST http://localhost:8080/agent-configuration \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"serviceName\":\"da-otel-agent-service\", \"sampler\":\"always_on\"}'\n```\n\nThe `DynamicSamplerProvider` in the _OpenTelemetry Configuration Service_ will now respond with the following log message:\n\n```\nChanging sampler to AlwaysOnSampler\n```\n\nThe `DynamicSamplerProvider` will poll the service at regular intervals (currently each 30s) to check for updated configurations. You should be able to see this in the Jaeger UI.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdomstolene%2Fda-otel-agent","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdomstolene%2Fda-otel-agent","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdomstolene%2Fda-otel-agent/lists"}