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.
@@ -0,0 +1,130 @@
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
+ # Configures the OpenTelemetry SDK with Dash0 defaults. Leans on the Ruby
9
+ # SDK's env-driven configurator: once the endpoint/protocol defaults are set
10
+ # and the exporter gems are required, `OpenTelemetry::SDK.configure` wires
11
+ # up the OTLP exporters and processors from the environment. We own the
12
+ # resource. Traces, metrics, and logs are all exported over OTLP/HTTP-protobuf
13
+ # to the same collector base URL (`/v1/traces`, `/v1/metrics`, `/v1/logs`).
14
+ #
15
+ # Metrics and logs support activates simply by requiring their SDK gems: they
16
+ # prepend `ConfiguratorPatch` modules that fill in the otherwise-empty
17
+ # metrics/logs configuration hooks the base SDK calls during `configure`.
18
+ module SdkConfiguration
19
+ # OTLP is only supported over HTTP/protobuf in the Ruby SDK's default path.
20
+ DEFAULT_PROTOCOL = 'http/protobuf'
21
+
22
+ # DASH0_DEBUG_PRINT_SPANS=true additionally prints every span to stdout.
23
+ DEBUG_PRINT_SPANS_ENV = 'DASH0_DEBUG_PRINT_SPANS'
24
+
25
+ # When the operator/injector mounts a Bundler-less gem bundle, its path is
26
+ # exposed here and the distribution is responsible for putting the bundled
27
+ # gems on the load path itself.
28
+ ADDITIONAL_GEM_PATH_ENV = 'OTEL_RUBY_ADDITIONAL_GEM_PATH'
29
+
30
+ # Comma-separated allowlist gems to NOT put on the load path (see
31
+ # #disallowed_lib_names). Mirrors the upstream env var.
32
+ DISALLOWED_LIB_PATH_ENV = 'DISALLOWED_LIB_PATH'
33
+
34
+ # Non-`opentelemetry-*` gems from the bundle that the SDK closure requires at
35
+ # load time and must therefore be on the load path: the OTLP exporter's
36
+ # protobuf deps, plus `logger` (required by `opentelemetry-api`; a default gem
37
+ # today, but a bundled gem from Ruby 4.0, at which point requiring it without
38
+ # this entry would fail). Kept minimal to avoid shadowing the app's own gems.
39
+ ADDITIONAL_LIB_GEM_ALLOWLIST = %w[googleapis-common-protos-types google-protobuf logger].freeze
40
+
41
+ module_function
42
+
43
+ # @param base_url [String] the Dash0 collector base URL. The generic
44
+ # `OTEL_EXPORTER_OTLP_ENDPOINT` is used (not a signal-specific variable),
45
+ # so each exporter appends its own `/v1/{traces,metrics,logs}` path.
46
+ def apply(base_url:)
47
+ configure_exporter_defaults(base_url)
48
+ require_sdk_gems
49
+ resource = build_resource
50
+
51
+ ::OpenTelemetry::SDK.configure do |c|
52
+ c.resource = resource
53
+ end
54
+
55
+ install_debug_span_printer if Environment.opted_in?(DEBUG_PRINT_SPANS_ENV)
56
+ end
57
+
58
+ def configure_exporter_defaults(base_url)
59
+ Environment.set_default('OTEL_EXPORTER_OTLP_ENDPOINT', base_url)
60
+ Environment.set_default('OTEL_EXPORTER_OTLP_PROTOCOL', DEFAULT_PROTOCOL)
61
+ end
62
+
63
+ def require_sdk_gems
64
+ wire_additional_gem_path
65
+ require 'opentelemetry-sdk'
66
+ require 'opentelemetry-metrics-sdk'
67
+ require 'opentelemetry-logs-sdk'
68
+ require 'opentelemetry-exporter-otlp'
69
+ require 'opentelemetry-exporter-otlp-metrics'
70
+ require 'opentelemetry-exporter-otlp-logs'
71
+ require 'opentelemetry-resource-detector-container'
72
+ # Populate the instrumentation registry so the installer can sweep it.
73
+ require 'opentelemetry-instrumentation-all'
74
+ end
75
+
76
+ # In the injected (Bundler-less) environment the OpenTelemetry gems live only
77
+ # under OTEL_RUBY_ADDITIONAL_GEM_PATH; put their `lib` directories on the load
78
+ # path so the requires above resolve. A no-op when the variable is unset
79
+ # (e.g. running under Bundler, where the gems are already resolvable).
80
+ def wire_additional_gem_path
81
+ gem_path = ENV.fetch(ADDITIONAL_GEM_PATH_ENV, nil)
82
+ return if gem_path.nil? || !Dir.exist?(gem_path)
83
+
84
+ allowlist = ADDITIONAL_LIB_GEM_ALLOWLIST - disallowed_lib_names
85
+ Dir.glob(File.join(gem_path, 'gems', '*')).each do |gem_dir|
86
+ next unless on_load_path?(File.basename(gem_dir), allowlist)
87
+
88
+ lib = File.join(gem_dir, 'lib')
89
+ $LOAD_PATH.unshift(lib) if Dir.exist?(lib) && !$LOAD_PATH.include?(lib)
90
+ end
91
+ end
92
+
93
+ # Gems opted out of via DISALLOWED_LIB_PATH (comma-separated).
94
+ def disallowed_lib_names
95
+ ENV.fetch(DISALLOWED_LIB_PATH_ENV, '').split(',').map(&:strip).reject(&:empty?)
96
+ end
97
+
98
+ def on_load_path?(gem_dir_name, allowlist)
99
+ gem_dir_name.start_with?('opentelemetry-') ||
100
+ allowlist.any? { |name| gem_dir_name.start_with?("#{name}-") }
101
+ end
102
+
103
+ # Adds a console span exporter *after* configure. It must not be added inside
104
+ # the configure block: the SDK configurator only builds the env-driven OTLP
105
+ # exporter when no span processor was registered manually, so adding one
106
+ # there would silently disable OTLP export. Appending to the already-built
107
+ # provider keeps OTLP and adds console output alongside it.
108
+ def install_debug_span_printer
109
+ ::OpenTelemetry.tracer_provider.add_span_processor(
110
+ ::OpenTelemetry::SDK::Trace::Export::SimpleSpanProcessor.new(
111
+ ::OpenTelemetry::SDK::Trace::Export::ConsoleSpanExporter.new
112
+ )
113
+ )
114
+ end
115
+
116
+ # Builds the resource merged onto the SDK's default resource (process, SDK,
117
+ # and service-name-from-env attributes) by the configurator. Beyond the
118
+ # distro identity, this runs the upstream container detector plus the two
119
+ # custom Dash0 detectors. On key conflicts the later `merge` argument wins;
120
+ # distro attributes are applied last so they always take effect.
121
+ def build_resource
122
+ ::OpenTelemetry::SDK::Resources::Resource.create({})
123
+ .merge(::OpenTelemetry::Resource::Detector::Container.detect)
124
+ .merge(Resource::KubernetesPod.detect)
125
+ .merge(Resource::ServiceNameFallback.detect)
126
+ .merge(Resource::Distribution.detect)
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,15 @@
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
+ VERSION = '0.1.0'
9
+
10
+ # Lowest Ruby version the distribution supports. Kept here (in a file that
11
+ # must stay parse-safe on older Rubies) so the requirable entry point can
12
+ # gate on it before requiring anything that uses newer syntax.
13
+ MINIMUM_RUBY_VERSION = '3.3.0'
14
+ end
15
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: Copyright 2026 Dash0 Inc.
4
+ # SPDX-License-Identifier: Apache-2.0
5
+
6
+ require 'dash0/opentelemetry/version'
7
+ require 'dash0/opentelemetry/environment'
8
+ require 'dash0/opentelemetry/resource/distribution'
9
+ require 'dash0/opentelemetry/resource/kubernetes_pod'
10
+ require 'dash0/opentelemetry/resource/service_name_fallback'
11
+ require 'dash0/opentelemetry/instrumentation_installer'
12
+ require 'dash0/opentelemetry/lifecycle'
13
+ require 'dash0/opentelemetry/sdk_configuration'
14
+ require 'dash0/opentelemetry/boot'
15
+
16
+ module Dash0
17
+ # Namespace for the Dash0 OpenTelemetry distribution for Ruby.
18
+ #
19
+ # The distribution activates as a side effect of requiring the gem's entry
20
+ # point (`require 'dash0-opentelemetry'`), typically via
21
+ # `RUBYOPT="-r dash0-opentelemetry"`. Requiring this namespace on its own has no
22
+ # side effects; the boot sequence is triggered explicitly via {boot!}.
23
+ module OpenTelemetry
24
+ # Prefix used for all messages the distribution writes to stderr.
25
+ LOG_PREFIX = '[Dash0 OpenTelemetry Distribution]'
26
+
27
+ # Boots the distribution. This is the single entry point that the requirable
28
+ # entry file (`lib/dash0-opentelemetry.rb`) invokes.
29
+ def self.boot!
30
+ Boot.run
31
+ end
32
+
33
+ # Logs an error-level message to stderr. Always emitted.
34
+ def self.log_error(message)
35
+ warn "#{LOG_PREFIX} #{message}"
36
+ end
37
+
38
+ # Logs a debug-level message to stderr, only when DASH0_DEBUG=true.
39
+ def self.log_debug(message)
40
+ return unless Environment.debug?
41
+
42
+ warn "#{LOG_PREFIX} #{message}"
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: Copyright 2026 Dash0 Inc.
4
+ # SPDX-License-Identifier: Apache-2.0
5
+
6
+ # Requirable entry point for the Dash0 OpenTelemetry distribution for Ruby.
7
+ #
8
+ # This is the file the Dash0 operator/injector points `RUBYOPT="-r ..."` at.
9
+ # Requiring it boots the distribution as a side effect.
10
+ #
11
+ # IMPORTANT: the injector preloads this file into whatever Ruby the target app
12
+ # runs and performs no Ruby-version detection of its own. So this file must stay
13
+ # syntactically conservative (parse-safe on older Rubies) and check the runtime
14
+ # version BEFORE requiring any of the distribution's other files, which are free
15
+ # to use modern syntax. On an unsupported Ruby we stand down cleanly rather than
16
+ # crash the host application.
17
+
18
+ # When the operator/injector preloads this file via `RUBYOPT="-r <abs path>"`,
19
+ # Ruby loads it by absolute path without activating the gem, and no Bundler is
20
+ # involved, so this gem's own `lib` is not on the load path yet. Put it there so
21
+ # `require 'dash0/...'` resolves. (Under Bundler this is a harmless no-op.)
22
+ dash0_lib_dir = __dir__
23
+ $LOAD_PATH.unshift(dash0_lib_dir) unless $LOAD_PATH.include?(dash0_lib_dir)
24
+
25
+ require 'dash0/opentelemetry/version'
26
+
27
+ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new(Dash0::OpenTelemetry::MINIMUM_RUBY_VERSION)
28
+ warn "[Dash0 OpenTelemetry Distribution] Ruby #{RUBY_VERSION} is not supported " \
29
+ "(requires >= #{Dash0::OpenTelemetry::MINIMUM_RUBY_VERSION}). " \
30
+ 'OpenTelemetry data will not be sent to Dash0.'
31
+ return
32
+ end
33
+
34
+ # This rescue ensures we fail open if there is an issue loading our library.
35
+ # Unlikely but not impossible: a missing/corrupt distribution file, or a
36
+ # SyntaxError in our lib.
37
+ begin
38
+ require 'dash0/opentelemetry'
39
+ Dash0::OpenTelemetry.boot!
40
+ rescue StandardError, ScriptError => e
41
+ warn "[Dash0 OpenTelemetry Distribution] Initialization failed: #{e.message}. " \
42
+ 'OpenTelemetry data will not be sent to Dash0.'
43
+ end
metadata ADDED
@@ -0,0 +1,176 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dash0-opentelemetry
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Dash0 Inc.
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-07-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: opentelemetry-exporter-otlp
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.33.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.33.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: opentelemetry-exporter-otlp-logs
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.4.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.4.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: opentelemetry-exporter-otlp-metrics
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.8.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.8.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: opentelemetry-instrumentation-all
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.91.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.91.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: opentelemetry-logs-sdk
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.5.1
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.5.1
83
+ - !ruby/object:Gem::Dependency
84
+ name: opentelemetry-metrics-sdk
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.13.1
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.13.1
97
+ - !ruby/object:Gem::Dependency
98
+ name: opentelemetry-resource-detector-container
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 0.3.0
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 0.3.0
111
+ - !ruby/object:Gem::Dependency
112
+ name: opentelemetry-sdk
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 1.11.0
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 1.11.0
125
+ description: An opinionated, zero-code OpenTelemetry distribution for Ruby that wraps
126
+ upstream OpenTelemetry and is designed to be injected by the Dash0 Kubernetes operator.
127
+ email:
128
+ - support@dash0.com
129
+ executables: []
130
+ extensions: []
131
+ extra_rdoc_files: []
132
+ files:
133
+ - CHANGELOG.md
134
+ - CONTRIBUTING.md
135
+ - LICENSE
136
+ - README.md
137
+ - RELEASING.md
138
+ - lib/dash0-opentelemetry.rb
139
+ - lib/dash0/opentelemetry.rb
140
+ - lib/dash0/opentelemetry/boot.rb
141
+ - lib/dash0/opentelemetry/environment.rb
142
+ - lib/dash0/opentelemetry/instrumentation_installer.rb
143
+ - lib/dash0/opentelemetry/lifecycle.rb
144
+ - lib/dash0/opentelemetry/resource/distribution.rb
145
+ - lib/dash0/opentelemetry/resource/kubernetes_pod.rb
146
+ - lib/dash0/opentelemetry/resource/service_name_fallback.rb
147
+ - lib/dash0/opentelemetry/sdk_configuration.rb
148
+ - lib/dash0/opentelemetry/version.rb
149
+ homepage: https://github.com/dash0hq/opentelemetry-ruby-distribution
150
+ licenses:
151
+ - Apache-2.0
152
+ metadata:
153
+ changelog_uri: https://github.com/dash0hq/opentelemetry-ruby-distribution/blob/main/CHANGELOG.md
154
+ source_code_uri: https://github.com/dash0hq/opentelemetry-ruby-distribution
155
+ bug_tracker_uri: https://github.com/dash0hq/opentelemetry-ruby-distribution/issues
156
+ rubygems_mfa_required: 'true'
157
+ post_install_message:
158
+ rdoc_options: []
159
+ require_paths:
160
+ - lib
161
+ required_ruby_version: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ version: '3.3'
166
+ required_rubygems_version: !ruby/object:Gem::Requirement
167
+ requirements:
168
+ - - ">="
169
+ - !ruby/object:Gem::Version
170
+ version: '0'
171
+ requirements: []
172
+ rubygems_version: 3.5.22
173
+ signing_key:
174
+ specification_version: 4
175
+ summary: Dash0 OpenTelemetry distribution for Ruby
176
+ test_files: []