dash0-opentelemetry 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +70 -0
- data/CONTRIBUTING.md +57 -0
- data/LICENSE +202 -0
- data/README.md +128 -0
- data/RELEASING.md +53 -0
- data/lib/dash0/opentelemetry/boot.rb +99 -0
- data/lib/dash0/opentelemetry/environment.rb +53 -0
- data/lib/dash0/opentelemetry/instrumentation_installer.rb +143 -0
- data/lib/dash0/opentelemetry/lifecycle.rb +124 -0
- data/lib/dash0/opentelemetry/resource/distribution.rb +28 -0
- data/lib/dash0/opentelemetry/resource/kubernetes_pod.rb +120 -0
- data/lib/dash0/opentelemetry/resource/service_name_fallback.rb +189 -0
- data/lib/dash0/opentelemetry/sdk_configuration.rb +130 -0
- data/lib/dash0/opentelemetry/version.rb +15 -0
- data/lib/dash0/opentelemetry.rb +45 -0
- data/lib/dash0-opentelemetry.rb +43 -0
- metadata +176 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 31959435154e92aa8fd18f56086f3a5d043698bcee19ec2fd1f1503f9e239f85
|
|
4
|
+
data.tar.gz: 007201e91375e5bcb6691377585a9d2217584e2676f7c6cb0cee1d864ed60cc6
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 509d4df2c0ec60723e09de63706358c3cd0a2313b127b40a343aaa9389b228b3a5a108a305d950cb070e2eb85c4199e18fab46b63af1448a1a595ec694734780
|
|
7
|
+
data.tar.gz: d38e93933f843c7066f29aad15e402eaff175d2e804df8c315af487e154e074a22b5b96fefd85dcc296945c2486a99ce57cf70884b571cbcd66900fe9acdd1ea
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the Dash0 OpenTelemetry distribution for Ruby will be
|
|
4
|
+
documented in this file.
|
|
5
|
+
|
|
6
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
7
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
8
|
+
|
|
9
|
+
## [Unreleased]
|
|
10
|
+
|
|
11
|
+
## [0.1.0] - 2026-07-31
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- Initial project scaffolding: gem layout, tooling (RuboCop, Minitest, Rake),
|
|
16
|
+
and CI.
|
|
17
|
+
- Boot sequence and gating: stands down on unsupported Ruby (parse-safe guard in
|
|
18
|
+
the entry point), when `DASH0_DISABLE=true`, when
|
|
19
|
+
`DASH0_OTEL_COLLECTOR_BASE_URL` is unset, and when OpenTelemetry is already
|
|
20
|
+
loaded (double-instrumentation guard). Fails open — if the bundled
|
|
21
|
+
OpenTelemetry gems cannot be loaded, the distribution stands down rather than
|
|
22
|
+
crashing the host application.
|
|
23
|
+
- Trace export: configures the OpenTelemetry SDK with an OTLP/HTTP-protobuf
|
|
24
|
+
exporter derived from `DASH0_OTEL_COLLECTOR_BASE_URL`, plus the
|
|
25
|
+
`telemetry.distro.{name,version}` resource attributes (`dash0-ruby`).
|
|
26
|
+
- Zero-code instrumentation install via a `TracePoint(:end)` sweep, so libraries
|
|
27
|
+
loaded after startup are instrumented — including libraries that load in
|
|
28
|
+
stages, such as `ActiveSupport` and `ActionView` in a Rails application. Honors
|
|
29
|
+
`OTEL_RUBY_ENABLED_INSTRUMENTATIONS`.
|
|
30
|
+
- Metrics and logs export over OTLP/HTTP-protobuf to `/v1/metrics` and
|
|
31
|
+
`/v1/logs` on the same collector base URL, activated by requiring the metrics
|
|
32
|
+
and logs SDK gems. Metric export interval/timeout honor
|
|
33
|
+
`OTEL_METRIC_EXPORT_INTERVAL` / `OTEL_METRIC_EXPORT_TIMEOUT` (via the upstream
|
|
34
|
+
periodic reader).
|
|
35
|
+
- `DASH0_DEBUG_PRINT_SPANS=true` prints spans to stdout via a console exporter,
|
|
36
|
+
added alongside (not in place of) the OTLP exporter.
|
|
37
|
+
- Custom resource detectors: `kubernetes-pod` (derives `k8s.pod.uid` from cgroup
|
|
38
|
+
v1/v2, gated on the `/etc/hosts` Kubernetes marker) and `service-name-fallback`,
|
|
39
|
+
which derives `service.name` from the application's own identity — the app
|
|
40
|
+
module in `config/application.rb` / `config/app.rb` (Rails, Hanami), or the
|
|
41
|
+
`run` target / app class in `config.ru` (modular Sinatra, Roda, Rack,
|
|
42
|
+
single-file Rails). Launcher wrappers (`bundle`, `puma`, `rackup`, …) are not
|
|
43
|
+
used as a name, and when none can be derived the SDK's `unknown_service`
|
|
44
|
+
default stands. Skipped when a name is already set via `OTEL_SERVICE_NAME` /
|
|
45
|
+
`OTEL_RESOURCE_ATTRIBUTES`, or opted out with `DASH0_AUTOMATIC_SERVICE_NAME=false`.
|
|
46
|
+
The upstream container detector (`container.id`) runs by default too.
|
|
47
|
+
- Lifecycle handling: `DASH0_BOOTSTRAP_SPAN` emits a named internal span at
|
|
48
|
+
startup; `DASH0_FLUSH_ON_SIGTERM_SIGINT=true` installs SIGTERM/SIGINT handlers
|
|
49
|
+
that flush telemetry (timeboxed) and re-raise the signal; an at-exit flush runs
|
|
50
|
+
by default unless disabled with `DASH0_FLUSH_ON_EXIT=false`. Provider shutdown
|
|
51
|
+
is idempotent and runs off the trap context to stay mutex-safe.
|
|
52
|
+
- Integration test harness: an in-process mock OTLP/HTTP collector (decoding the
|
|
53
|
+
exporter's protobuf payloads) and sample apps run in a separate process with
|
|
54
|
+
the distribution preloaded via `ruby -r dash0-opentelemetry`. Covers
|
|
55
|
+
end-to-end export of all three signals with the distro resource attributes,
|
|
56
|
+
zero-code `net/http` auto-instrumentation, a minimal Rails app (proving the
|
|
57
|
+
Rack/Rails stack is instrumented when loaded after preload), and the
|
|
58
|
+
unsupported-Ruby stand-down.
|
|
59
|
+
- Documentation and release tooling: a full environment-variable reference in the
|
|
60
|
+
README, `RELEASING.md`, and a RubyGems trusted-publishing (`release.yml`)
|
|
61
|
+
workflow triggered on version tags.
|
|
62
|
+
- Injection support: when preloaded by the operator/injector (no Bundler, gems
|
|
63
|
+
mounted on `OTEL_RUBY_ADDITIONAL_GEM_PATH`), the entry point puts its own `lib`
|
|
64
|
+
and the bundled OpenTelemetry gems on the load path — plus the few required
|
|
65
|
+
non-`opentelemetry-*` gems (`google-protobuf` and its protobuf deps, `logger`)
|
|
66
|
+
— so the distribution loads without relying on Bundler or gem activation.
|
|
67
|
+
- `DISALLOWED_LIB_PATH` (comma-separated) keeps named bundled gems off the load
|
|
68
|
+
path when injected. Its main use is a `google-protobuf` version clash:
|
|
69
|
+
`DISALLOWED_LIB_PATH=google-protobuf` makes the distribution defer to the
|
|
70
|
+
application's own protobuf.
|
data/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for your interest in the Dash0 OpenTelemetry distribution for Ruby.
|
|
4
|
+
|
|
5
|
+
## Development setup
|
|
6
|
+
|
|
7
|
+
Use the Ruby version declared in the gemspec (`>= 3.3`). Then:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
bundle install
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Common tasks
|
|
14
|
+
|
|
15
|
+
- `bundle exec rake` — run the full verification (unit tests + RuboCop). This is
|
|
16
|
+
what CI runs.
|
|
17
|
+
- `bundle exec rake test` — run the unit tests only.
|
|
18
|
+
- `bundle exec rake rubocop` — run the linter only.
|
|
19
|
+
- `bundle exec rubocop -a` — auto-correct lint offenses where safe.
|
|
20
|
+
|
|
21
|
+
## Conventions
|
|
22
|
+
|
|
23
|
+
- Every Ruby source file starts with the SPDX header:
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
# frozen_string_literal: true
|
|
27
|
+
|
|
28
|
+
# SPDX-FileCopyrightText: Copyright 2026 Dash0 Inc.
|
|
29
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
- Tests use Minitest and live under `test/`, named `*_test.rb`.
|
|
33
|
+
- This distribution is a **wrapper** around upstream OpenTelemetry Ruby. Prefer
|
|
34
|
+
configuring or extending upstream behavior over reimplementing it.
|
|
35
|
+
|
|
36
|
+
## Changelog
|
|
37
|
+
|
|
38
|
+
User-facing changes need a changelog fragment instead of a direct edit to
|
|
39
|
+
`CHANGELOG.md`:
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
bundle exec rake chloggen:new[short-slug]
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Fill in the generated `.chloggen/<short-slug>.yaml` and commit it alongside
|
|
46
|
+
your change. See [`.chloggen/README.md`](.chloggen/README.md) for the fragment
|
|
47
|
+
format; CI validates fragments and requires one for changes under `lib/`
|
|
48
|
+
(skippable with the `Skip changelog` label). CI also rejects PRs that edit
|
|
49
|
+
`CHANGELOG.md` directly — it is only ever updated by `rake chloggen:update` at
|
|
50
|
+
release time.
|
|
51
|
+
|
|
52
|
+
## How it is used
|
|
53
|
+
|
|
54
|
+
The distribution is designed to be injected into Ruby workloads by the Dash0
|
|
55
|
+
Kubernetes operator via `RUBYOPT="-r dash0-opentelemetry"`, the Ruby analog of
|
|
56
|
+
Node's `NODE_OPTIONS=--require`. Everything runs as a side effect of requiring
|
|
57
|
+
the gem; there is no API to call.
|
data/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
data/README.md
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Dash0 OpenTelemetry Distribution for Ruby
|
|
2
|
+
|
|
3
|
+
An opinionated, zero-code [OpenTelemetry](https://opentelemetry.io) distribution
|
|
4
|
+
for Ruby, published as the `dash0-opentelemetry` gem. It is a thin wrapper around
|
|
5
|
+
upstream OpenTelemetry Ruby that ships a batteries-included auto-instrumentation
|
|
6
|
+
setup for [Dash0](https://www.dash0.com).
|
|
7
|
+
|
|
8
|
+
## Intended use
|
|
9
|
+
|
|
10
|
+
This distribution is primarily intended to be injected into Ruby workloads by the
|
|
11
|
+
[Dash0 Kubernetes operator](https://github.com/dash0hq/dash0-operator), which
|
|
12
|
+
instruments them without code changes. It is required at process startup via
|
|
13
|
+
`RUBYOPT`, the Ruby analog of Node.js's `NODE_OPTIONS=--require`:
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
RUBYOPT="-r dash0-opentelemetry" ruby your_app.rb
|
|
17
|
+
RUBYOPT="-r dash0-opentelemetry" rails server
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Everything runs as a side effect of requiring the gem — there is no API to call.
|
|
21
|
+
Because it is loaded before the application's own libraries, instrumentation is
|
|
22
|
+
installed lazily (via a `TracePoint`) as each supported library is loaded, so no
|
|
23
|
+
particular load order is required.
|
|
24
|
+
|
|
25
|
+
## Requirements
|
|
26
|
+
|
|
27
|
+
- Ruby **>= 3.3**. On an unsupported Ruby the distribution stands down cleanly
|
|
28
|
+
(it logs a message and does nothing) rather than interfering with the app.
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
gem install dash0-opentelemetry
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Install it **outside** the application's bundle (it loads OpenTelemetry from its
|
|
37
|
+
own gem path, not from the app's `Gemfile`).
|
|
38
|
+
|
|
39
|
+
## What it does
|
|
40
|
+
|
|
41
|
+
On startup the distribution:
|
|
42
|
+
|
|
43
|
+
- exports **traces, metrics, and logs** over OTLP (HTTP/protobuf) to the Dash0
|
|
44
|
+
collector at `${DASH0_OTEL_COLLECTOR_BASE_URL}/v1/{traces,metrics,logs}`;
|
|
45
|
+
- enables all instrumentations from
|
|
46
|
+
[`opentelemetry-instrumentation-all`](https://github.com/open-telemetry/opentelemetry-ruby-contrib),
|
|
47
|
+
installed lazily as their target libraries load;
|
|
48
|
+
- adds resource attributes: the upstream defaults (process, SDK, and any
|
|
49
|
+
`OTEL_SERVICE_NAME` / `OTEL_RESOURCE_ATTRIBUTES`), the container detector
|
|
50
|
+
(`container.id`), a Kubernetes pod-uid detector (`k8s.pod.uid`), a service-name
|
|
51
|
+
fallback (`service.name`), and the distribution identity
|
|
52
|
+
(`telemetry.distro.name = dash0-ruby`, `telemetry.distro.version`).
|
|
53
|
+
|
|
54
|
+
## Configuration
|
|
55
|
+
|
|
56
|
+
Behavior is driven entirely by environment variables: `DASH0_*` for
|
|
57
|
+
distribution-specific switches and standard `OTEL_*` for the rest.
|
|
58
|
+
|
|
59
|
+
### `DASH0_*` variables
|
|
60
|
+
|
|
61
|
+
| Variable | Description |
|
|
62
|
+
| --- | --- |
|
|
63
|
+
| `DASH0_OTEL_COLLECTOR_BASE_URL` | **Required.** Base URL of the OpenTelemetry collector to export to; each signal is sent to `<base>/v1/{traces,metrics,logs}`. If unset, the distribution stands down and exports nothing. (The Dash0 operator sets this for you.) |
|
|
64
|
+
| `DASH0_DISABLE` | Set to `true` to disable the distribution entirely. |
|
|
65
|
+
| `DASH0_AUTOMATIC_SERVICE_NAME` | Set to `false` to opt out of the automatic `service.name` fallback (see below). |
|
|
66
|
+
| `DASH0_BOOTSTRAP_SPAN` | If set to a non-empty string, a single internal span with that name is emitted immediately at startup. Useful for confirming the distribution is active. |
|
|
67
|
+
| `DASH0_FLUSH_ON_SIGTERM_SIGINT` | Set to `true` to install SIGTERM/SIGINT handlers that flush pending telemetry (timeboxed) before re-raising the signal. Do not use if the application installs its own handlers for these signals. |
|
|
68
|
+
| `DASH0_FLUSH_ON_EXIT` | Telemetry is flushed on normal process exit by default (via an `at_exit` hook). Set to `false` to disable this. |
|
|
69
|
+
| `DASH0_DEBUG` | Set to `true` for additional debug logging on stderr. |
|
|
70
|
+
| `DASH0_DEBUG_PRINT_SPANS` | Set to `true` to also print every span to stdout via a console exporter (in addition to exporting over OTLP). |
|
|
71
|
+
|
|
72
|
+
### Automatic service name
|
|
73
|
+
|
|
74
|
+
If no service name has been configured, the distribution derives one from the
|
|
75
|
+
application's own identity, in order of preference:
|
|
76
|
+
|
|
77
|
+
1. the app module in `config/application.rb` or `config/app.rb` (Rails, Hanami) —
|
|
78
|
+
e.g. `module DemoApp` → `demo_app`;
|
|
79
|
+
2. `config.ru` — the `run <AppClass>` target or an inline application class
|
|
80
|
+
(modular Sinatra, Roda, plain Rack, single-file Rails);
|
|
81
|
+
3. the entry script name, when it is not a known launcher/wrapper (e.g.
|
|
82
|
+
`ruby my_worker.rb` → `my_worker`).
|
|
83
|
+
|
|
84
|
+
Because the distribution loads at interpreter startup, the entry point is usually
|
|
85
|
+
a wrapper (`bundle`, `puma`, `rackup`, …); these are deliberately ignored rather
|
|
86
|
+
than reported as the service name. When none of the above yields a name, the
|
|
87
|
+
distribution reports nothing and the SDK's `unknown_service` default stands.
|
|
88
|
+
|
|
89
|
+
This fallback is skipped entirely when a service name is already set via
|
|
90
|
+
`OTEL_SERVICE_NAME`, via a `service.name` in `OTEL_RESOURCE_ATTRIBUTES`, or when
|
|
91
|
+
`DASH0_AUTOMATIC_SERVICE_NAME=false`.
|
|
92
|
+
|
|
93
|
+
### `OTEL_*` variables
|
|
94
|
+
|
|
95
|
+
Standard OpenTelemetry variables are honored because the distribution uses the
|
|
96
|
+
stock OpenTelemetry SDK, exporters, and processors. Commonly useful ones:
|
|
97
|
+
|
|
98
|
+
| Variable | Description |
|
|
99
|
+
| --- | --- |
|
|
100
|
+
| `OTEL_SERVICE_NAME` | Sets `service.name` explicitly (and disables the automatic fallback). |
|
|
101
|
+
| `OTEL_RESOURCE_ATTRIBUTES` | Additional resource attributes as `key=value` pairs. A `service.name` here also disables the automatic fallback. |
|
|
102
|
+
| `OTEL_RUBY_ENABLED_INSTRUMENTATIONS` | Comma-separated allowlist of instrumentations to enable (by snake_case name, e.g. `net_http,rack`). When unset, all supported instrumentations are enabled. |
|
|
103
|
+
| `OTEL_EXPORTER_OTLP_ENDPOINT` | Overrides the OTLP endpoint. Defaulted from `DASH0_OTEL_COLLECTOR_BASE_URL`; set explicitly only to override. |
|
|
104
|
+
| `OTEL_EXPORTER_OTLP_PROTOCOL` | OTLP protocol; defaults to `http/protobuf` (the only protocol supported by the Ruby SDK's default path). |
|
|
105
|
+
| `OTEL_EXPORTER_OTLP_HEADERS` | Extra headers for the OTLP exporter (e.g. an authorization token when exporting directly to Dash0 rather than through the operator's collector). |
|
|
106
|
+
| `OTEL_METRIC_EXPORT_INTERVAL` / `OTEL_METRIC_EXPORT_TIMEOUT` | Periodic metric reader interval / timeout, in milliseconds. |
|
|
107
|
+
|
|
108
|
+
Standard batch-processor variables (`OTEL_BSP_*`, `OTEL_BLRP_*`) and
|
|
109
|
+
`OTEL_SDK_DISABLED` are honored by the underlying SDK as well.
|
|
110
|
+
|
|
111
|
+
### `DISALLOWED_LIB_PATH` (advanced)
|
|
112
|
+
|
|
113
|
+
A comma-separated list of bundled non-`opentelemetry-*` gems the distribution
|
|
114
|
+
should **not** put on the load path when injected. Its main use is the escape
|
|
115
|
+
hatch for a `google-protobuf` version clash: when instrumenting an app that uses
|
|
116
|
+
its own `google-protobuf` at runtime and breaks because the bundled version wins
|
|
117
|
+
set `DISALLOWED_LIB_PATH=google-protobuf` so the distribution defers to the app's
|
|
118
|
+
copy. Works while the app's protobuf is within the exporter's supported range;
|
|
119
|
+
otherwise the distribution stands down rather than crash.
|
|
120
|
+
|
|
121
|
+
## Development
|
|
122
|
+
|
|
123
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md). Releasing is documented in
|
|
124
|
+
[RELEASING.md](RELEASING.md).
|
|
125
|
+
|
|
126
|
+
## License
|
|
127
|
+
|
|
128
|
+
[Apache-2.0](LICENSE)
|
data/RELEASING.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Releasing
|
|
2
|
+
|
|
3
|
+
Releases follow [Semantic Versioning](https://semver.org). Publishing happens in
|
|
4
|
+
CI via [RubyGems Trusted Publishing](https://guides.rubygems.org/trusted-publishing/)
|
|
5
|
+
(OIDC) when a version tag is pushed — no API keys are stored.
|
|
6
|
+
|
|
7
|
+
## One-time setup
|
|
8
|
+
|
|
9
|
+
On [rubygems.org](https://rubygems.org), configure a trusted publisher for the
|
|
10
|
+
`dash0-opentelemetry` gem pointing at this GitHub repository and the
|
|
11
|
+
`.github/workflows/release.yml` workflow.
|
|
12
|
+
|
|
13
|
+
On GitHub, configure the `rubygems` [deployment environment][gh-envs] with
|
|
14
|
+
required reviewers (a maintainer must approve each publish). The release
|
|
15
|
+
workflow uses this environment to gate RubyGems Trusted Publishing; without
|
|
16
|
+
required reviewers the environment is a no-op and any triggered run publishes
|
|
17
|
+
automatically. The workflow also refuses to publish a tag that is not reachable
|
|
18
|
+
from `origin/main`, so the two gates together mean a release requires (a) a
|
|
19
|
+
tag on a merged commit and (b) an explicit human approval on the environment.
|
|
20
|
+
|
|
21
|
+
[gh-envs]: https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment#required-reviewers
|
|
22
|
+
|
|
23
|
+
## Cutting a release
|
|
24
|
+
|
|
25
|
+
1. Make sure `main` is green (`bundle exec rake`).
|
|
26
|
+
2. Bump the version in
|
|
27
|
+
[`lib/dash0/opentelemetry/version.rb`](lib/dash0/opentelemetry/version.rb).
|
|
28
|
+
3. Merge the pending changelog fragments:
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
bundle exec rake chloggen:update
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
This folds every fragment under [`.chloggen/`](.chloggen) into
|
|
35
|
+
[`CHANGELOG.md`](CHANGELOG.md)'s `Unreleased` section (grouped by change
|
|
36
|
+
type) and deletes the fragments. Then rename `Unreleased` to the new
|
|
37
|
+
version with today's date, and start a fresh `Unreleased` section.
|
|
38
|
+
4. Commit the change (e.g. `chore: release vX.Y.Z`).
|
|
39
|
+
5. Tag and push:
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
git tag vX.Y.Z
|
|
43
|
+
git push origin main --tags
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
6. The `Release` workflow runs the full verification (`bundle exec rake`) and, if
|
|
47
|
+
it passes, builds and publishes the gem to RubyGems via trusted publishing.
|
|
48
|
+
|
|
49
|
+
## After a release
|
|
50
|
+
|
|
51
|
+
Consuming the new version in the Dash0 operator is a one-line change in the
|
|
52
|
+
operator's Ruby build stage (install `dash0-opentelemetry` and point the injector
|
|
53
|
+
entry symlink at it) — tracked in the operator repository, not here.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: Copyright 2026 Dash0 Inc.
|
|
4
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
|
|
6
|
+
module Dash0
|
|
7
|
+
module OpenTelemetry
|
|
8
|
+
# Orchestrates startup: gating (disable switch, mandatory collector URL,
|
|
9
|
+
# double-instrumentation guard), SDK configuration, and instrumentation
|
|
10
|
+
# install.
|
|
11
|
+
#
|
|
12
|
+
# The Ruby-version stand-down lives in the requirable entry point
|
|
13
|
+
# (`lib/dash0-opentelemetry.rb`) so it can run before any modern-syntax file
|
|
14
|
+
# is required.
|
|
15
|
+
module Boot
|
|
16
|
+
COLLECTOR_BASE_URL_ENV = 'DASH0_OTEL_COLLECTOR_BASE_URL'
|
|
17
|
+
DISABLE_ENV = 'DASH0_DISABLE'
|
|
18
|
+
|
|
19
|
+
class << self
|
|
20
|
+
# Boots the distribution. Safe to call more than once; only the first call
|
|
21
|
+
# that passes the gates takes effect.
|
|
22
|
+
def run
|
|
23
|
+
return if @booted
|
|
24
|
+
return if disabled?
|
|
25
|
+
|
|
26
|
+
base_url = collector_base_url
|
|
27
|
+
return unless base_url
|
|
28
|
+
|
|
29
|
+
if already_instrumented?
|
|
30
|
+
Dash0::OpenTelemetry.log_error(
|
|
31
|
+
'The application already has OpenTelemetry loaded; standing down to avoid double instrumentation.'
|
|
32
|
+
)
|
|
33
|
+
return
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
@booted = true
|
|
37
|
+
configure(base_url)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Resets the one-time boot guard. Intended for tests only.
|
|
41
|
+
def reset_for_testing!
|
|
42
|
+
@booted = false
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def booted?
|
|
46
|
+
@booted == true
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def configure(base_url)
|
|
52
|
+
SdkConfiguration.apply(base_url: base_url)
|
|
53
|
+
InstrumentationInstaller.start
|
|
54
|
+
Lifecycle.install
|
|
55
|
+
Dash0::OpenTelemetry.log_debug("Distribution initialized (collector base URL: #{base_url}).")
|
|
56
|
+
rescue StandardError, ScriptError => e
|
|
57
|
+
@booted = false
|
|
58
|
+
Dash0::OpenTelemetry.log_error(
|
|
59
|
+
"Initialization failed: #{e.message}. OpenTelemetry data will not be sent to Dash0."
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def disabled?
|
|
64
|
+
return false unless Environment.opted_in?(DISABLE_ENV)
|
|
65
|
+
|
|
66
|
+
Dash0::OpenTelemetry.log_error(
|
|
67
|
+
"#{DISABLE_ENV} is set to true; the distribution is disabled. OpenTelemetry data will not be sent to Dash0."
|
|
68
|
+
)
|
|
69
|
+
true
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# The mandatory collector base URL, or nil (with an explanatory log) when
|
|
73
|
+
# unset. There is no default in code - the operator supplies it.
|
|
74
|
+
def collector_base_url
|
|
75
|
+
value = ENV.fetch(COLLECTOR_BASE_URL_ENV, nil)
|
|
76
|
+
if value.nil? || value.strip.empty?
|
|
77
|
+
Dash0::OpenTelemetry.log_error(
|
|
78
|
+
"#{COLLECTOR_BASE_URL_ENV} is not set; there is nowhere to export to. " \
|
|
79
|
+
'OpenTelemetry data will not be sent to Dash0.'
|
|
80
|
+
)
|
|
81
|
+
return nil
|
|
82
|
+
end
|
|
83
|
+
value.strip
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Detects whether OpenTelemetry's SDK was already loaded by the application
|
|
87
|
+
# (e.g. the app bundles and initializes OpenTelemetry itself). Checked
|
|
88
|
+
# before we require the SDK ourselves.
|
|
89
|
+
def already_instrumented?
|
|
90
|
+
sdk_loaded?
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def sdk_loaded?(loaded_features = $LOADED_FEATURES)
|
|
94
|
+
loaded_features.any? { |path| path.end_with?('lib/opentelemetry/sdk.rb') }
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: Copyright 2026 Dash0 Inc.
|
|
4
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
|
|
6
|
+
module Dash0
|
|
7
|
+
module OpenTelemetry
|
|
8
|
+
# Helpers for reading and defaulting environment variables. The distribution
|
|
9
|
+
# is configured entirely through the environment (`DASH0_*` switches and
|
|
10
|
+
# standard `OTEL_*` variables).
|
|
11
|
+
module Environment
|
|
12
|
+
module_function
|
|
13
|
+
|
|
14
|
+
# True when the variable is set to "true" (case-insensitive, trimmed).
|
|
15
|
+
def opted_in?(name)
|
|
16
|
+
ENV.fetch(name, '').strip.casecmp('true').zero?
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# True when the variable is set to "false" (case-insensitive, trimmed).
|
|
20
|
+
def opted_out?(name)
|
|
21
|
+
ENV.fetch(name, '').strip.casecmp('false').zero?
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# True when the variable is set to a non-empty (trimmed) value.
|
|
25
|
+
def present?(name)
|
|
26
|
+
value = ENV.fetch(name, nil)
|
|
27
|
+
!value.nil? && !value.strip.empty?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Sets +name+ to +value+ unless it already has a non-empty value. This lets
|
|
31
|
+
# the distribution provide defaults without ever overriding a value the
|
|
32
|
+
# application or operator set explicitly.
|
|
33
|
+
def set_default(name, value)
|
|
34
|
+
ENV[name] = value unless present?(name)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Parses an integer environment variable, falling back to +default+ when
|
|
38
|
+
# unset or not a valid integer.
|
|
39
|
+
def integer(name, default)
|
|
40
|
+
value = ENV.fetch(name, nil)
|
|
41
|
+
return default if value.nil? || value.strip.empty?
|
|
42
|
+
|
|
43
|
+
Integer(value.strip)
|
|
44
|
+
rescue ArgumentError
|
|
45
|
+
default
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def debug?
|
|
49
|
+
opted_in?('DASH0_DEBUG')
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|