From 9d13c5ade08cc3fd7906e671900c66132e9b5564 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Wed, 29 Jan 2025 14:59:33 -0500 Subject: [PATCH 01/68] feat: init commit for zero-code instrumentation --- .github/workflows/ci-contrib.yml | 37 ++++ zero-code-instrumentation/CHANGELOG.md | 5 + zero-code-instrumentation/Gemfile | 23 ++ zero-code-instrumentation/LICENSE | 201 ++++++++++++++++++ zero-code-instrumentation/README.md | 151 +++++++++++++ zero-code-instrumentation/Rakefile | 28 +++ zero-code-instrumentation/example/Gemfile | 3 + zero-code-instrumentation/example/app.rb | 14 ++ zero-code-instrumentation/lib/version.rb | 7 + .../lib/zero-code-instrumentation.rb | 144 +++++++++++++ zero-code-instrumentation/test/test_helper.rb | 14 ++ .../test/zero-code-instrumentation_test.rb | 88 ++++++++ .../zero-code-instrumentation.gemspec | 54 +++++ 13 files changed, 769 insertions(+) create mode 100644 zero-code-instrumentation/CHANGELOG.md create mode 100644 zero-code-instrumentation/Gemfile create mode 100644 zero-code-instrumentation/LICENSE create mode 100644 zero-code-instrumentation/README.md create mode 100644 zero-code-instrumentation/Rakefile create mode 100644 zero-code-instrumentation/example/Gemfile create mode 100644 zero-code-instrumentation/example/app.rb create mode 100644 zero-code-instrumentation/lib/version.rb create mode 100644 zero-code-instrumentation/lib/zero-code-instrumentation.rb create mode 100644 zero-code-instrumentation/test/test_helper.rb create mode 100644 zero-code-instrumentation/test/zero-code-instrumentation_test.rb create mode 100644 zero-code-instrumentation/zero-code-instrumentation.gemspec diff --git a/.github/workflows/ci-contrib.yml b/.github/workflows/ci-contrib.yml index 39ee9e9e21..7cafbfcbce 100644 --- a/.github/workflows/ci-contrib.yml +++ b/.github/workflows/ci-contrib.yml @@ -168,3 +168,40 @@ jobs: with: gem: "opentelemetry-processor-${{ matrix.gem }}" ruby: "jruby-9.4.9.0" + + zero-code-instrumentation: + strategy: + fail-fast: false + matrix: + gem: + - zero-code-instrumentation + os: + - ubuntu-latest + name: "${{ matrix.gem }} / ${{ matrix.os }}" + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - name: "Test Ruby 3.3" + uses: ./.github/actions/test_gem + with: + gem: "${{ matrix.gem }}" + ruby: "3.3" + - name: "Test Ruby 3.2" + uses: ./.github/actions/test_gem + with: + gem: "${{ matrix.gem }}" + ruby: "3.2" + - name: "Test Ruby 3.1" + uses: ./.github/actions/test_gem + with: + gem: "${{ matrix.gem }}" + ruby: "3.1" + yard: true + rubocop: true + build: true + - name: "Test JRuby" + if: "${{ matrix.os == 'ubuntu-latest'" + uses: ./.github/actions/test_gem + with: + gem: "${{ matrix.gem }}" + ruby: "jruby-9.4.9.0" diff --git a/zero-code-instrumentation/CHANGELOG.md b/zero-code-instrumentation/CHANGELOG.md new file mode 100644 index 0000000000..de43fbf3d1 --- /dev/null +++ b/zero-code-instrumentation/CHANGELOG.md @@ -0,0 +1,5 @@ +# Release History: zero-code-instrumentation + +### v0.1.0 / 2025-01-21 + +* Initial release diff --git a/zero-code-instrumentation/Gemfile b/zero-code-instrumentation/Gemfile new file mode 100644 index 0000000000..2b7a43e677 --- /dev/null +++ b/zero-code-instrumentation/Gemfile @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +source 'https://rubygems.org' + +# avoid use gemspec to avoid circular load + +gem 'opentelemetry-sdk' +gem 'opentelemetry-instrumentation-all' +gem 'opentelemetry-exporter-otlp' +gem 'opentelemetry-test-helpers' +gem 'opentelemetry-resource-detector-container', path: '../resources/container' + +gem 'bundler' +gem 'minitest' +gem 'rake' +gem 'rubocop' +gem 'rubocop-performance' +gem 'simplecov' +gem 'yard' diff --git a/zero-code-instrumentation/LICENSE b/zero-code-instrumentation/LICENSE new file mode 100644 index 0000000000..1ef7dad2c5 --- /dev/null +++ b/zero-code-instrumentation/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright The OpenTelemetry Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/zero-code-instrumentation/README.md b/zero-code-instrumentation/README.md new file mode 100644 index 0000000000..1263908ef5 --- /dev/null +++ b/zero-code-instrumentation/README.md @@ -0,0 +1,151 @@ +# Zero-code Instrumentation + +The `zero-code-instrumentation` gem provides an easy way to load and initialize opentelemetry-ruby for zero-code instrumentation. + +## What is OpenTelemetry? + +OpenTelemetry is an open source observability framework, providing a general-purpose API, SDK, and related tools required for the instrumentation of cloud-native software, frameworks, and libraries. + +OpenTelemetry provides a single set of APIs, libraries, agents, and collector services to capture distributed traces and metrics from your application. You can analyze them using Prometheus, Jaeger, and other observability tools. + +## How does this gem fit in? + +The `zero-code-instrumentation` gem provides an easy way to load and initialize the OpenTelemetry Ruby SDK. This gem is particularly used for the [OpenTelemetry Operator][opentelemetry-operator]. + +## How do I get started? + +It's recommended to install this gem through gem install rather than Bundler since it doesn't require modifying any user's codebase (including the Gemfile). + +Install the gem using: + +```console +gem install zero-code-instrumentation +``` + +Install zero-code-instrumentation will automatically install following gems: + + opentelemetry-sdk + opentelemetry-api + opentelemetry-instrumentation-all + opentelemetry-exporter-otlp + opentelemetry-helpers-mysql + opentelemetry-helpers-sql-obfuscation + opentelemetry-resource-detector-google_cloud_platform + opentelemetry-resource-detector-azure + opentelemetry-resource-detector-container + +Instrument your application: + +```console +RUBYOPT="-r zero-code-instrumentation" ruby application.rb +``` + +Instrument your application with additional environment variables: + +```console +export OTEL_TRACES_EXPORTER="otlp" +export OTEL_EXPORTER_OTLP_ENDPOINT="your-endpoint" +export OTEL_RUBY_RESOURCE_DETECTORS="container" +export OTEL_SERVICE_NAME="your-service-name" + +RUBYOPT="-r zero-code-instrumentation" ruby application.rb +``` + +Instrument your application with disabling certain instrumentation (e.g. sinatra): + +```console +export OTEL_RUBY_INSTRUMENTATION_SINATRA_ENABLED='false' +RUBYOPT="-r zero-code-instrumentation" ruby application.rb +``` + +Instrument your application with only certain instrumentation installed (e.g. mysql, redis): + +```console +export OTEL_RUBY_ENABLED_INSTRUMENTATIONS='mysql2,redis' +RUBYOPT="-r zero-code-instrumentation" ruby application.rb +``` + +Instrument your application with only redis and configure its options: + +```console +export OTEL_RUBY_ENABLED_INSTRUMENTATIONS='redis' +export OTEL_RUBY_INSTRUMENTATION_REDIS_CONFIG_OPTS='peer_service=new_service;db_statement=omit' +RUBYOPT="-r zero-code-instrumentation" ruby application.rb +``` + +Instrument Rails application: + +```console +RUBYOPT="-r zero-code-instrumentation" rails server +``` + +Instrument Rails application with `bundle exec` + +Since installing the `zero-code-instrumentation` gem through `gem install`, anything related to the OpenTelemetry gem won't be stored in Bundler's gem path. Therefore, users need to add an additional gem path that contains these gems prior to initialization. + +```console +export GEM_PATH= +RUBYOPT="-r {PUT YOUR GEM PATH}/gems/zero-code-instrumentation-0.1.0/lib/zero-code-instrumentation" bundle exec rails server +``` + +Instrument Sinatra application with rackup: + +If you are using a Gemfile to install the required gems but without `Bundler.require`, set `REQUIRE_BUNDLER` to true. This way, `zero-code-instrumentation` will call `Bundler.require` to initialize the required gems prior to SDK initialization. + +```console +export REQUIRE_BUNDLER=true +RUBYOPT="-r zero-code-instrumentation" rackup config.ru +``` + +If wish to load some gem outside the Gemfile, then it needs to be placed in front of zero-code-instrumentation: + +```console +export BUNDLE_WITHOUT=development,test +gem install mysql2 +RUBYOPT="-r mysql2 -r zero-code-instrumentation" ruby application.rb +``` + +## Example + +In example folder, execute the following commands should see the trace output. + +```console +# if user don't want to install zero-code-instrumentation from rubygems.org +# user can build the gem and install it with gem install *.gem + +gem install zero-code-instrumentation +bundle install +export REQUIRE_BUNDLER=true +export OTEL_TRACES_EXPORTER=console +RUBYOPT="-r zero-code-instrumentation" ruby app.rb +``` + +## Configuration + +These environment variables are not standard OpenTelemetry environment variables; they are only feature flags for this gem. + +| Environment Variable | Description | Default | Example | +|----------------------|-------------|---------|---------| +| `REQUIRE_BUNDLER` | Set to `true` if you are using Bundler to install gems but without `Bundler.require` in your script during initialization. | nil | N/A | +| `USE_BUNDLE_EXEC` | Set to `true` if initializing through `bundle exec`. | nil | N/A | +| `ADDITIONAL_GEM_PATH` | Intended to be used for the OpenTelemetry Operator environment if you install `zero-code-instrumentation` to a customized path. | nil | N/A | +| `OTEL_OPERATOR` | Set to `true` to set the binding path for the OpenTelemetry Operator. | `/otel-auto-instrumentation-ruby` | N/A | +| `OTEL_RUBY_RESOURCE_DETECTORS` | Determine what kind of resource detector is needed. Currently supports `container`, `azure`, and `google_cloud_platform`. Use commas to separate multiple detectors. | nil | `container,azure` | +| `OTEL_RUBY_ENABLED_INSTRUMENTATIONS` | Shortcut used when you only want to instrument one or a couple of particular libraries. | nil | `redis,active_record` | + +## How can I get involved? + +The `zero-code-instrumentation` gem source is on GitHub, along with related gems. + +The OpenTelemetry Ruby gems are maintained by the OpenTelemetry Ruby special interest group (SIG). You can get involved by joining us on our [GitHub Discussions][discussions-url], [Slack Channel][slack-channel] or attending our weekly meeting. See the [meeting calendar][community-meetings] for dates and times. For more information on this and other language SIGs, see the OpenTelemetry [community page][ruby-sig]. + +## License + +The `zero-code-instrumentation` gem is distributed under the Apache 2.0 license. See LICENSE for more information. + +[ruby-sig]: https://github.com/open-telemetry/community#ruby-sig +[community-meetings]: https://github.com/open-telemetry/community#community-meetings +[slack-channel]: https://cloud-native.slack.com/archives/C01NWKKMKMY +[discussions-url]: https://github.com/open-telemetry/opentelemetry-ruby/discussions +[k8sattributesprocessor-url]: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/k8sattributesprocessor/README.md +[opentelemetry-operator]: https://github.com/open-telemetry/opentelemetry-operator diff --git a/zero-code-instrumentation/Rakefile b/zero-code-instrumentation/Rakefile new file mode 100644 index 0000000000..1a64ba842e --- /dev/null +++ b/zero-code-instrumentation/Rakefile @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'bundler/gem_tasks' +require 'rake/testtask' +require 'yard' +require 'rubocop/rake_task' + +RuboCop::RakeTask.new + +Rake::TestTask.new :test do |t| + t.libs << 'test' + t.libs << 'lib' + t.test_files = FileList['test/**/*_test.rb'] +end + +YARD::Rake::YardocTask.new do |t| + t.stats_options = ['--list-undoc'] +end + +if RUBY_ENGINE == 'truffleruby' + task default: %i[test] +else + task default: %i[test rubocop yard] +end diff --git a/zero-code-instrumentation/example/Gemfile b/zero-code-instrumentation/example/Gemfile new file mode 100644 index 0000000000..a8b0038780 --- /dev/null +++ b/zero-code-instrumentation/example/Gemfile @@ -0,0 +1,3 @@ +source 'https://rubygems.org' + +gem 'net-http' diff --git a/zero-code-instrumentation/example/app.rb b/zero-code-instrumentation/example/app.rb new file mode 100644 index 0000000000..f895369eda --- /dev/null +++ b/zero-code-instrumentation/example/app.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'uri' +require 'net/http' + +url = URI.parse("http://catfact.ninja/fact") +req = Net::HTTP::Get.new(url.to_s) +res = Net::HTTP.start(url.host, url.port) {|http| + http.request(req) +} diff --git a/zero-code-instrumentation/lib/version.rb b/zero-code-instrumentation/lib/version.rb new file mode 100644 index 0000000000..001acd6202 --- /dev/null +++ b/zero-code-instrumentation/lib/version.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +VERSION = '0.1.0' diff --git a/zero-code-instrumentation/lib/zero-code-instrumentation.rb b/zero-code-instrumentation/lib/zero-code-instrumentation.rb new file mode 100644 index 0000000000..b897c4b26c --- /dev/null +++ b/zero-code-instrumentation/lib/zero-code-instrumentation.rb @@ -0,0 +1,144 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OTelBundlerPatch + + # ref: https://github.com/newrelic/newrelic-ruby-agent/blob/dev/lib/boot/strap.rb + def require(*_groups) + super + require_otel + end + + # this is used for case when user particularly want to enable single instrumentation + OTEL_INSTRUMENTATION_MAP = { + "gruf" => "OpenTelemetry::Instrumentation::Gruf", + "trilogy" => "OpenTelemetry::Instrumentation::Trilogy", + "active_support" => "OpenTelemetry::Instrumentation::ActiveSupport", + "action_pack" => "OpenTelemetry::Instrumentation::ActionPack", + "active_job" => "OpenTelemetry::Instrumentation::ActiveJob", + "active_record" => "OpenTelemetry::Instrumentation::ActiveRecord", + "action_view" => "OpenTelemetry::Instrumentation::ActionView", + "action_mailer" => "OpenTelemetry::Instrumentation::ActionMailer", + "aws_sdk" => "OpenTelemetry::Instrumentation::AwsSdk", + "aws_lambda" => "OpenTelemetry::Instrumentation::AwsLambda", + "bunny" => "OpenTelemetry::Instrumentation::Bunny", + "lmdb" => "OpenTelemetry::Instrumentation::LMDB", + "http" => "OpenTelemetry::Instrumentation::HTTP", + "koala" => "OpenTelemetry::Instrumentation::Koala", + "active_model_serializers" => "OpenTelemetry::Instrumentation::ActiveModelSerializers", + "concurrent_ruby" => "OpenTelemetry::Instrumentation::ConcurrentRuby", + "dalli" => "OpenTelemetry::Instrumentation::Dalli", + "delayed_job" => "OpenTelemetry::Instrumentation::DelayedJob", + "ethon" => "OpenTelemetry::Instrumentation::Ethon", + "excon" => "OpenTelemetry::Instrumentation::Excon", + "faraday" => "OpenTelemetry::Instrumentation::Faraday", + "grape" => "OpenTelemetry::Instrumentation::Grape", + "graphql" => "OpenTelemetry::Instrumentation::GraphQL", + "http_client" => "OpenTelemetry::Instrumentation::HttpClient", + "mongo" => "OpenTelemetry::Instrumentation::Mongo", + "mysql2" => "OpenTelemetry::Instrumentation::Mysql2", + "net_http" => "OpenTelemetry::Instrumentation::Net::HTTP", + "pg" => "OpenTelemetry::Instrumentation::PG", + "que" => "OpenTelemetry::Instrumentation::Que", + "racecar" => "OpenTelemetry::Instrumentation::Racecar", + "rack" => "OpenTelemetry::Instrumentation::Rack", + "rails" => "OpenTelemetry::Instrumentation::Rails", + "rake" => "OpenTelemetry::Instrumentation::Rake", + "rdkafka" => "OpenTelemetry::Instrumentation::Rdkafka", + "redis" => "OpenTelemetry::Instrumentation::Redis", + "restclient" => "OpenTelemetry::Instrumentation::RestClient", + "resque" => "OpenTelemetry::Instrumentation::Resque", + "ruby_kafka" => "OpenTelemetry::Instrumentation::RubyKafka", + "sidekiq" => "OpenTelemetry::Instrumentation::Sidekiq", + "sinatra" => "OpenTelemetry::Instrumentation::Sinatra" + } + + def detect_resource_from_env + env = ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s + additional_resource = ::OpenTelemetry::SDK::Resources::Resource.create({}) + + env.split(',').each do |detector| + case detector + when 'container' + additional_resource = additional_resource.merge(::OpenTelemetry::Resource::Detector::Container.detect) if defined? (::OpenTelemetry::Resource::Detector::Container) + when 'google_cloud_platform' + additional_resource = additional_resource.merge(::OpenTelemetry::Resource::Detector::GoogleCloudPlatform.detect) if defined? (::OpenTelemetry::Resource::Detector::GoogleCloudPlatform) + when 'azure' + additional_resource = additional_resource.merge(::OpenTelemetry::Resource::Detector::Azure.detect) if defined? (::OpenTelemetry::Resource::Detector::Azure) + end + end + + additional_resource + end + + def determine_enabled_instrumentation + env = ENV['OTEL_RUBY_ENABLED_INSTRUMENTATIONS'].to_s + + enabled_instrumentation = [] + env.split(',').each { |instrumentation| enabled_instrumentation.push(OTEL_INSTRUMENTATION_MAP[instrumentation]) } + enabled_instrumentation + end + + def require_otel + lib = File.expand_path('../..', __FILE__) + $LOAD_PATH.reject! { |path| path.include?('zero-code-instrumentation') } + $LOAD_PATH.unshift(lib) + + begin + required_instrumentation = determine_enabled_instrumentation + + OpenTelemetry::SDK.configure do |c| + c.resource = detect_resource_from_env + if required_instrumentation.size == 0 + c.use_all # enables all instrumentation! + else + required_instrumentation.each do |instrumentation| + c.use instrumentation + end + end + end + OpenTelemetry.logger.info {"Auto-instrumentation initialized"} + rescue StandardError => e + OpenTelemetry.logger.info {"Auto-instrumentation failed to initialize. Error: #{e.message}"} + end + end +end + +require 'bundler' + +container = ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s.include?('container') +google_cloud_platform = ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s.include?('google_cloud_platform') +azure = ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s.include?('azure') + +if ENV['USE_BUNDLE_EXEC'].to_s == 'true' + + # assume the operator ruby image will amount the additional otel gem to this folder + operator_gem_path = ENV['OTEL_OPERATOR'].to_s == 'true' ? '/otel-auto-instrumentation-ruby' : nil + additional_gem_path = operator_gem_path || ENV['ADDITIONAL_GEM_PATH'] || Gem.dir + OpenTelemetry.logger.info { "Loading the additional gem path from #{additional_gem_path}"} + + # google-protobuf is used for otel trace exporter + Dir.glob("#{additional_gem_path}/gems/*").each do |file| + if file.include?('opentelemetry') || file.include?('google') + puts file.inspect + $LOAD_PATH.unshift("#{file}/lib") + end + end +end + +require 'opentelemetry-sdk' +require 'opentelemetry-instrumentation-all' +require 'opentelemetry-helpers-mysql' +require 'opentelemetry-helpers-sql-obfuscation' +require 'opentelemetry-exporter-otlp' + +require 'opentelemetry-resource-detector-container' if container +require 'opentelemetry-resource-detector-google_cloud_platform' if google_cloud_platform +require 'opentelemetry-resource-detector-azure' if azure + +Bundler::Runtime.prepend(OTelBundlerPatch) + +Bundler.require if ENV['REQUIRE_BUNDLER'].to_s == 'true' diff --git a/zero-code-instrumentation/test/test_helper.rb b/zero-code-instrumentation/test/test_helper.rb new file mode 100644 index 0000000000..9798aead57 --- /dev/null +++ b/zero-code-instrumentation/test/test_helper.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'rake' +require 'minitest' +require 'minitest/autorun' +require 'opentelemetry-test-helpers' +require 'opentelemetry/resource/detector' +require 'net/http' +require 'opentelemetry-sdk' +require 'opentelemetry-instrumentation-all' diff --git a/zero-code-instrumentation/test/zero-code-instrumentation_test.rb b/zero-code-instrumentation/test/zero-code-instrumentation_test.rb new file mode 100644 index 0000000000..f32f92c7fc --- /dev/null +++ b/zero-code-instrumentation/test/zero-code-instrumentation_test.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'test_helper' + +describe 'ZeroCodeInstrumentation' do + + before do + OpenTelemetry::TestHelpers.reset_opentelemetry + end + + after do + OTelBundlerPatch.send(:remove_const, :OTEL_INSTRUMENTATION_MAP) + OTelBundlerPatch.send(:undef_method, :detect_resource_from_env) + OTelBundlerPatch.send(:undef_method, :determine_enabled_instrumentation) + OTelBundlerPatch.send(:undef_method, :require_otel) + OTelBundlerPatch.send(:undef_method, :require) + OpenTelemetry::Instrumentation::Net::HTTP::Instrumentation.instance_variable_get(:@instance).instance_variable_set(:@installed, false) + OpenTelemetry::Instrumentation::Rake::Instrumentation.instance_variable_get(:@instance).instance_variable_set(:@installed, false) + end + + it 'simple_load_test' do + + load './lib/zero-code-instrumentation.rb' + Bundler.require + + _(OpenTelemetry.tracer_provider.class).must_equal OpenTelemetry::SDK::Trace::TracerProvider + + resource_attributes = OpenTelemetry.tracer_provider.instance_variable_get(:@resource).instance_variable_get(:@attributes) + + _(resource_attributes['service.name']).must_equal 'unknown_service' + _(resource_attributes['telemetry.sdk.name']).must_equal 'opentelemetry' + _(resource_attributes['telemetry.sdk.language']).must_equal 'ruby' + _(resource_attributes.key?('container.id')).must_equal false + + registry = OpenTelemetry.tracer_provider.instance_variable_get(:@registry) + + _(registry.size).must_equal 2 + end + + it 'simple_load_with_net_http_disabled' do + + ENV['OTEL_RUBY_INSTRUMENTATION_NET_HTTP_ENABLED'] = 'false' + + load './lib/zero-code-instrumentation.rb' + Bundler.require + + registry = OpenTelemetry.tracer_provider.instance_variable_get(:@registry) + + _(registry.size).must_equal 1 + _(registry.first.first.name).must_equal 'OpenTelemetry::Instrumentation::Rake' + + ENV['OTEL_RUBY_INSTRUMENTATION_NET_HTTP_ENABLED'] = nil + end + + it 'simple_load_with_desired_instrument_only' do + + ENV['OTEL_RUBY_ENABLED_INSTRUMENTATIONS'] = 'net_http' + + load './lib/zero-code-instrumentation.rb' + Bundler.require + + registry = OpenTelemetry.tracer_provider.instance_variable_get(:@registry) + + _(registry.size).must_equal 1 + _(registry.first.first.name).must_equal 'OpenTelemetry::Instrumentation::Net::HTTP' + + ENV['OTEL_RUBY_ENABLED_INSTRUMENTATIONS'] = nil + end + + it 'simple_load_with_additional_resource' do + + ENV['OTEL_RUBY_RESOURCE_DETECTORS'] = 'container' + + load './lib/zero-code-instrumentation.rb' + Bundler.require + + resource_attributes = OpenTelemetry.tracer_provider.instance_variable_get(:@resource).instance_variable_get(:@attributes) + _(resource_attributes.key?('container.id')).must_equal true + _(resource_attributes['telemetry.sdk.name']).must_equal 'opentelemetry' + _(resource_attributes['telemetry.sdk.language']).must_equal 'ruby' + + ENV['OTEL_RUBY_RESOURCE_DETECTORS'] = nil + end +end diff --git a/zero-code-instrumentation/zero-code-instrumentation.gemspec b/zero-code-instrumentation/zero-code-instrumentation.gemspec new file mode 100644 index 0000000000..5808aae632 --- /dev/null +++ b/zero-code-instrumentation/zero-code-instrumentation.gemspec @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +lib = File.expand_path('lib', __dir__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +require 'version' + +Gem::Specification.new do |spec| + spec.name = 'zero-code-instrumentation' + spec.version = VERSION + spec.authors = ['OpenTelemetry Authors'] + spec.email = ['cncf-opentelemetry-contributors@lists.cncf.io'] + + spec.summary = 'Zero-code instrumentation for opentelemetry ruby' + spec.description = 'Zero-code instrumentation for opentelemetry ruby' + spec.homepage = 'https://github.com/open-telemetry/opentelemetry-ruby-contrib' + spec.license = 'Apache-2.0' + + spec.files = Dir.glob('lib/**/*.rb') + + Dir.glob('*.md') + + ['LICENSE'] + + spec.require_paths = ['lib'] + spec.required_ruby_version = '>= 3.1' + + spec.add_dependency 'opentelemetry-sdk', '~> 1.0' + spec.add_dependency 'opentelemetry-instrumentation-all', '~> 0.72.0' + spec.add_dependency 'opentelemetry-exporter-otlp', '~> 0.29.1' + spec.add_dependency 'opentelemetry-helpers-mysql', '~> 0.2.0' + spec.add_dependency 'opentelemetry-helpers-sql-obfuscation', '~> 0.3.0' + spec.add_dependency 'opentelemetry-resource-detector-google_cloud_platform', '~> 0.2.0' + spec.add_dependency 'opentelemetry-resource-detector-azure', '~> 0.2.0' + spec.add_dependency 'opentelemetry-resource-detector-container', '~> 0.2.0' + + spec.add_development_dependency 'bundler', '~> 2.4' + spec.add_development_dependency 'minitest', '~> 5.0' + spec.add_development_dependency 'rake', '~> 13.0' + spec.add_development_dependency 'rubocop', '~> 1.69.1' + spec.add_development_dependency 'rubocop-performance', '~> 1.23.0' + spec.add_development_dependency 'simplecov', '~> 0.17' + spec.add_development_dependency 'yard', '~> 0.9' + + if spec.respond_to?(:metadata) + spec.metadata['changelog_uri'] = "https://rubydoc.info/gems/#{spec.name}/#{spec.version}/file/CHANGELOG.md" + spec.metadata['source_code_uri'] = 'https://github.com/open-telemetry/opentelemetry-ruby-contrib/zero-code-instrumentation' + spec.metadata['bug_tracker_uri'] = 'https://github.com/open-telemetry/opentelemetry-ruby-contrib/issues' + spec.metadata['documentation_uri'] = "https://rubydoc.info/gems/#{spec.name}/#{spec.version}" + end + + spec.post_install_message = File.read(File.expand_path('../POST_INSTALL_MESSAGE', __dir__)) +end From 444959aad3e85ac7343b5a6cb81eb236f9d822a1 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Wed, 29 Jan 2025 15:01:28 -0500 Subject: [PATCH 02/68] lint --- zero-code-instrumentation/Gemfile | 4 +- .../lib/zero-code-instrumentation.rb | 104 +++++++++--------- .../test/zero-code-instrumentation_test.rb | 5 - .../zero-code-instrumentation.gemspec | 6 +- 4 files changed, 56 insertions(+), 63 deletions(-) diff --git a/zero-code-instrumentation/Gemfile b/zero-code-instrumentation/Gemfile index 2b7a43e677..793db3cb15 100644 --- a/zero-code-instrumentation/Gemfile +++ b/zero-code-instrumentation/Gemfile @@ -16,8 +16,8 @@ gem 'opentelemetry-resource-detector-container', path: '../resources/container' gem 'bundler' gem 'minitest' -gem 'rake' +gem 'rake' gem 'rubocop' gem 'rubocop-performance' -gem 'simplecov' +gem 'simplecov' gem 'yard' diff --git a/zero-code-instrumentation/lib/zero-code-instrumentation.rb b/zero-code-instrumentation/lib/zero-code-instrumentation.rb index b897c4b26c..1ff6156cd9 100644 --- a/zero-code-instrumentation/lib/zero-code-instrumentation.rb +++ b/zero-code-instrumentation/lib/zero-code-instrumentation.rb @@ -4,8 +4,8 @@ # # SPDX-License-Identifier: Apache-2.0 +# OTelBundlerPatch module OTelBundlerPatch - # ref: https://github.com/newrelic/newrelic-ruby-agent/blob/dev/lib/boot/strap.rb def require(*_groups) super @@ -14,47 +14,47 @@ def require(*_groups) # this is used for case when user particularly want to enable single instrumentation OTEL_INSTRUMENTATION_MAP = { - "gruf" => "OpenTelemetry::Instrumentation::Gruf", - "trilogy" => "OpenTelemetry::Instrumentation::Trilogy", - "active_support" => "OpenTelemetry::Instrumentation::ActiveSupport", - "action_pack" => "OpenTelemetry::Instrumentation::ActionPack", - "active_job" => "OpenTelemetry::Instrumentation::ActiveJob", - "active_record" => "OpenTelemetry::Instrumentation::ActiveRecord", - "action_view" => "OpenTelemetry::Instrumentation::ActionView", - "action_mailer" => "OpenTelemetry::Instrumentation::ActionMailer", - "aws_sdk" => "OpenTelemetry::Instrumentation::AwsSdk", - "aws_lambda" => "OpenTelemetry::Instrumentation::AwsLambda", - "bunny" => "OpenTelemetry::Instrumentation::Bunny", - "lmdb" => "OpenTelemetry::Instrumentation::LMDB", - "http" => "OpenTelemetry::Instrumentation::HTTP", - "koala" => "OpenTelemetry::Instrumentation::Koala", - "active_model_serializers" => "OpenTelemetry::Instrumentation::ActiveModelSerializers", - "concurrent_ruby" => "OpenTelemetry::Instrumentation::ConcurrentRuby", - "dalli" => "OpenTelemetry::Instrumentation::Dalli", - "delayed_job" => "OpenTelemetry::Instrumentation::DelayedJob", - "ethon" => "OpenTelemetry::Instrumentation::Ethon", - "excon" => "OpenTelemetry::Instrumentation::Excon", - "faraday" => "OpenTelemetry::Instrumentation::Faraday", - "grape" => "OpenTelemetry::Instrumentation::Grape", - "graphql" => "OpenTelemetry::Instrumentation::GraphQL", - "http_client" => "OpenTelemetry::Instrumentation::HttpClient", - "mongo" => "OpenTelemetry::Instrumentation::Mongo", - "mysql2" => "OpenTelemetry::Instrumentation::Mysql2", - "net_http" => "OpenTelemetry::Instrumentation::Net::HTTP", - "pg" => "OpenTelemetry::Instrumentation::PG", - "que" => "OpenTelemetry::Instrumentation::Que", - "racecar" => "OpenTelemetry::Instrumentation::Racecar", - "rack" => "OpenTelemetry::Instrumentation::Rack", - "rails" => "OpenTelemetry::Instrumentation::Rails", - "rake" => "OpenTelemetry::Instrumentation::Rake", - "rdkafka" => "OpenTelemetry::Instrumentation::Rdkafka", - "redis" => "OpenTelemetry::Instrumentation::Redis", - "restclient" => "OpenTelemetry::Instrumentation::RestClient", - "resque" => "OpenTelemetry::Instrumentation::Resque", - "ruby_kafka" => "OpenTelemetry::Instrumentation::RubyKafka", - "sidekiq" => "OpenTelemetry::Instrumentation::Sidekiq", - "sinatra" => "OpenTelemetry::Instrumentation::Sinatra" - } + 'gruf' => 'OpenTelemetry::Instrumentation::Gruf', + 'trilogy' => 'OpenTelemetry::Instrumentation::Trilogy', + 'active_support' => 'OpenTelemetry::Instrumentation::ActiveSupport', + 'action_pack' => 'OpenTelemetry::Instrumentation::ActionPack', + 'active_job' => 'OpenTelemetry::Instrumentation::ActiveJob', + 'active_record' => 'OpenTelemetry::Instrumentation::ActiveRecord', + 'action_view' => 'OpenTelemetry::Instrumentation::ActionView', + 'action_mailer' => 'OpenTelemetry::Instrumentation::ActionMailer', + 'aws_sdk' => 'OpenTelemetry::Instrumentation::AwsSdk', + 'aws_lambda' => 'OpenTelemetry::Instrumentation::AwsLambda', + 'bunny' => 'OpenTelemetry::Instrumentation::Bunny', + 'lmdb' => 'OpenTelemetry::Instrumentation::LMDB', + 'http' => 'OpenTelemetry::Instrumentation::HTTP', + 'koala' => 'OpenTelemetry::Instrumentation::Koala', + 'active_model_serializers' => 'OpenTelemetry::Instrumentation::ActiveModelSerializers', + 'concurrent_ruby' => 'OpenTelemetry::Instrumentation::ConcurrentRuby', + 'dalli' => 'OpenTelemetry::Instrumentation::Dalli', + 'delayed_job' => 'OpenTelemetry::Instrumentation::DelayedJob', + 'ethon' => 'OpenTelemetry::Instrumentation::Ethon', + 'excon' => 'OpenTelemetry::Instrumentation::Excon', + 'faraday' => 'OpenTelemetry::Instrumentation::Faraday', + 'grape' => 'OpenTelemetry::Instrumentation::Grape', + 'graphql' => 'OpenTelemetry::Instrumentation::GraphQL', + 'http_client' => 'OpenTelemetry::Instrumentation::HttpClient', + 'mongo' => 'OpenTelemetry::Instrumentation::Mongo', + 'mysql2' => 'OpenTelemetry::Instrumentation::Mysql2', + 'net_http' => 'OpenTelemetry::Instrumentation::Net::HTTP', + 'pg' => 'OpenTelemetry::Instrumentation::PG', + 'que' => 'OpenTelemetry::Instrumentation::Que', + 'racecar' => 'OpenTelemetry::Instrumentation::Racecar', + 'rack' => 'OpenTelemetry::Instrumentation::Rack', + 'rails' => 'OpenTelemetry::Instrumentation::Rails', + 'rake' => 'OpenTelemetry::Instrumentation::Rake', + 'rdkafka' => 'OpenTelemetry::Instrumentation::Rdkafka', + 'redis' => 'OpenTelemetry::Instrumentation::Redis', + 'restclient' => 'OpenTelemetry::Instrumentation::RestClient', + 'resque' => 'OpenTelemetry::Instrumentation::Resque', + 'ruby_kafka' => 'OpenTelemetry::Instrumentation::RubyKafka', + 'sidekiq' => 'OpenTelemetry::Instrumentation::Sidekiq', + 'sinatra' => 'OpenTelemetry::Instrumentation::Sinatra' + }.freeze def detect_resource_from_env env = ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s @@ -63,11 +63,11 @@ def detect_resource_from_env env.split(',').each do |detector| case detector when 'container' - additional_resource = additional_resource.merge(::OpenTelemetry::Resource::Detector::Container.detect) if defined? (::OpenTelemetry::Resource::Detector::Container) + additional_resource = additional_resource.merge(::OpenTelemetry::Resource::Detector::Container.detect) if defined? ::OpenTelemetry::Resource::Detector::Container when 'google_cloud_platform' - additional_resource = additional_resource.merge(::OpenTelemetry::Resource::Detector::GoogleCloudPlatform.detect) if defined? (::OpenTelemetry::Resource::Detector::GoogleCloudPlatform) + additional_resource = additional_resource.merge(::OpenTelemetry::Resource::Detector::GoogleCloudPlatform.detect) if defined? ::OpenTelemetry::Resource::Detector::GoogleCloudPlatform when 'azure' - additional_resource = additional_resource.merge(::OpenTelemetry::Resource::Detector::Azure.detect) if defined? (::OpenTelemetry::Resource::Detector::Azure) + additional_resource = additional_resource.merge(::OpenTelemetry::Resource::Detector::Azure.detect) if defined? ::OpenTelemetry::Resource::Detector::Azure end end @@ -77,13 +77,11 @@ def detect_resource_from_env def determine_enabled_instrumentation env = ENV['OTEL_RUBY_ENABLED_INSTRUMENTATIONS'].to_s - enabled_instrumentation = [] - env.split(',').each { |instrumentation| enabled_instrumentation.push(OTEL_INSTRUMENTATION_MAP[instrumentation]) } - enabled_instrumentation + env.split(',').map { |instrumentation| OTEL_INSTRUMENTATION_MAP[instrumentation] } end def require_otel - lib = File.expand_path('../..', __FILE__) + lib = File.expand_path('..', __dir__) $LOAD_PATH.reject! { |path| path.include?('zero-code-instrumentation') } $LOAD_PATH.unshift(lib) @@ -92,7 +90,7 @@ def require_otel OpenTelemetry::SDK.configure do |c| c.resource = detect_resource_from_env - if required_instrumentation.size == 0 + if required_instrumentation.empty? c.use_all # enables all instrumentation! else required_instrumentation.each do |instrumentation| @@ -100,9 +98,9 @@ def require_otel end end end - OpenTelemetry.logger.info {"Auto-instrumentation initialized"} + OpenTelemetry.logger.info { 'Auto-instrumentation initialized' } rescue StandardError => e - OpenTelemetry.logger.info {"Auto-instrumentation failed to initialize. Error: #{e.message}"} + OpenTelemetry.logger.info { "Auto-instrumentation failed to initialize. Error: #{e.message}" } end end end @@ -118,7 +116,7 @@ def require_otel # assume the operator ruby image will amount the additional otel gem to this folder operator_gem_path = ENV['OTEL_OPERATOR'].to_s == 'true' ? '/otel-auto-instrumentation-ruby' : nil additional_gem_path = operator_gem_path || ENV['ADDITIONAL_GEM_PATH'] || Gem.dir - OpenTelemetry.logger.info { "Loading the additional gem path from #{additional_gem_path}"} + OpenTelemetry.logger.info { "Loading the additional gem path from #{additional_gem_path}" } # google-protobuf is used for otel trace exporter Dir.glob("#{additional_gem_path}/gems/*").each do |file| diff --git a/zero-code-instrumentation/test/zero-code-instrumentation_test.rb b/zero-code-instrumentation/test/zero-code-instrumentation_test.rb index f32f92c7fc..f49473370d 100644 --- a/zero-code-instrumentation/test/zero-code-instrumentation_test.rb +++ b/zero-code-instrumentation/test/zero-code-instrumentation_test.rb @@ -7,7 +7,6 @@ require 'test_helper' describe 'ZeroCodeInstrumentation' do - before do OpenTelemetry::TestHelpers.reset_opentelemetry end @@ -23,7 +22,6 @@ end it 'simple_load_test' do - load './lib/zero-code-instrumentation.rb' Bundler.require @@ -42,7 +40,6 @@ end it 'simple_load_with_net_http_disabled' do - ENV['OTEL_RUBY_INSTRUMENTATION_NET_HTTP_ENABLED'] = 'false' load './lib/zero-code-instrumentation.rb' @@ -57,7 +54,6 @@ end it 'simple_load_with_desired_instrument_only' do - ENV['OTEL_RUBY_ENABLED_INSTRUMENTATIONS'] = 'net_http' load './lib/zero-code-instrumentation.rb' @@ -72,7 +68,6 @@ end it 'simple_load_with_additional_resource' do - ENV['OTEL_RUBY_RESOURCE_DETECTORS'] = 'container' load './lib/zero-code-instrumentation.rb' diff --git a/zero-code-instrumentation/zero-code-instrumentation.gemspec b/zero-code-instrumentation/zero-code-instrumentation.gemspec index 5808aae632..9be5a995fe 100644 --- a/zero-code-instrumentation/zero-code-instrumentation.gemspec +++ b/zero-code-instrumentation/zero-code-instrumentation.gemspec @@ -26,14 +26,14 @@ Gem::Specification.new do |spec| spec.require_paths = ['lib'] spec.required_ruby_version = '>= 3.1' - spec.add_dependency 'opentelemetry-sdk', '~> 1.0' - spec.add_dependency 'opentelemetry-instrumentation-all', '~> 0.72.0' spec.add_dependency 'opentelemetry-exporter-otlp', '~> 0.29.1' spec.add_dependency 'opentelemetry-helpers-mysql', '~> 0.2.0' spec.add_dependency 'opentelemetry-helpers-sql-obfuscation', '~> 0.3.0' - spec.add_dependency 'opentelemetry-resource-detector-google_cloud_platform', '~> 0.2.0' + spec.add_dependency 'opentelemetry-instrumentation-all', '~> 0.72.0' spec.add_dependency 'opentelemetry-resource-detector-azure', '~> 0.2.0' spec.add_dependency 'opentelemetry-resource-detector-container', '~> 0.2.0' + spec.add_dependency 'opentelemetry-resource-detector-google_cloud_platform', '~> 0.2.0' + spec.add_dependency 'opentelemetry-sdk', '~> 1.0' spec.add_development_dependency 'bundler', '~> 2.4' spec.add_development_dependency 'minitest', '~> 5.0' From 4c0c97b01331efdd0c2766f04c65e24b2b53f6a0 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Wed, 29 Jan 2025 15:06:05 -0500 Subject: [PATCH 03/68] rubocop --- zero-code-instrumentation/.rubocop.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 zero-code-instrumentation/.rubocop.yml diff --git a/zero-code-instrumentation/.rubocop.yml b/zero-code-instrumentation/.rubocop.yml new file mode 100644 index 0000000000..92a2a4f69e --- /dev/null +++ b/zero-code-instrumentation/.rubocop.yml @@ -0,0 +1,3 @@ +inherit_from: ../.rubocop.yml +Naming/FileName: + Enabled: false From 8a2fadd4f5d7f5695f837ed605f5f67ee1ab01d7 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Thu, 30 Jan 2025 13:42:40 -0500 Subject: [PATCH 04/68] enforce the dependencies version --- .../zero-code-instrumentation.gemspec | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/zero-code-instrumentation/zero-code-instrumentation.gemspec b/zero-code-instrumentation/zero-code-instrumentation.gemspec index 9be5a995fe..d7cfe61d95 100644 --- a/zero-code-instrumentation/zero-code-instrumentation.gemspec +++ b/zero-code-instrumentation/zero-code-instrumentation.gemspec @@ -26,14 +26,15 @@ Gem::Specification.new do |spec| spec.require_paths = ['lib'] spec.required_ruby_version = '>= 3.1' - spec.add_dependency 'opentelemetry-exporter-otlp', '~> 0.29.1' - spec.add_dependency 'opentelemetry-helpers-mysql', '~> 0.2.0' - spec.add_dependency 'opentelemetry-helpers-sql-obfuscation', '~> 0.3.0' - spec.add_dependency 'opentelemetry-instrumentation-all', '~> 0.72.0' - spec.add_dependency 'opentelemetry-resource-detector-azure', '~> 0.2.0' - spec.add_dependency 'opentelemetry-resource-detector-container', '~> 0.2.0' - spec.add_dependency 'opentelemetry-resource-detector-google_cloud_platform', '~> 0.2.0' - spec.add_dependency 'opentelemetry-sdk', '~> 1.0' + spec.add_dependency 'opentelemetry-api', '1.4.0' + spec.add_dependency 'opentelemetry-exporter-otlp', '0.29.1' + spec.add_dependency 'opentelemetry-helpers-mysql', '0.2.0' + spec.add_dependency 'opentelemetry-helpers-sql-obfuscation', '0.3.0' + spec.add_dependency 'opentelemetry-instrumentation-all', '0.72.0' + spec.add_dependency 'opentelemetry-resource-detector-azure', '0.2.0' + spec.add_dependency 'opentelemetry-resource-detector-container', '0.2.0' + spec.add_dependency 'opentelemetry-resource-detector-google_cloud_platform', '0.2.0' + spec.add_dependency 'opentelemetry-sdk', '1.6.0' spec.add_development_dependency 'bundler', '~> 2.4' spec.add_development_dependency 'minitest', '~> 5.0' From f96a637fb0de32b0d2a517fee673a53f25c02cf3 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Fri, 14 Feb 2025 10:31:35 -0500 Subject: [PATCH 05/68] assume use bundle exec all the time --- zero-code-instrumentation/README.md | 1 - .../lib/zero-code-instrumentation.rb | 24 +++++++++---------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/zero-code-instrumentation/README.md b/zero-code-instrumentation/README.md index 1263908ef5..d252f39529 100644 --- a/zero-code-instrumentation/README.md +++ b/zero-code-instrumentation/README.md @@ -84,7 +84,6 @@ Instrument Rails application with `bundle exec` Since installing the `zero-code-instrumentation` gem through `gem install`, anything related to the OpenTelemetry gem won't be stored in Bundler's gem path. Therefore, users need to add an additional gem path that contains these gems prior to initialization. ```console -export GEM_PATH= RUBYOPT="-r {PUT YOUR GEM PATH}/gems/zero-code-instrumentation-0.1.0/lib/zero-code-instrumentation" bundle exec rails server ``` diff --git a/zero-code-instrumentation/lib/zero-code-instrumentation.rb b/zero-code-instrumentation/lib/zero-code-instrumentation.rb index 1ff6156cd9..04c7f1dc73 100644 --- a/zero-code-instrumentation/lib/zero-code-instrumentation.rb +++ b/zero-code-instrumentation/lib/zero-code-instrumentation.rb @@ -111,19 +111,17 @@ def require_otel google_cloud_platform = ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s.include?('google_cloud_platform') azure = ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s.include?('azure') -if ENV['USE_BUNDLE_EXEC'].to_s == 'true' - - # assume the operator ruby image will amount the additional otel gem to this folder - operator_gem_path = ENV['OTEL_OPERATOR'].to_s == 'true' ? '/otel-auto-instrumentation-ruby' : nil - additional_gem_path = operator_gem_path || ENV['ADDITIONAL_GEM_PATH'] || Gem.dir - OpenTelemetry.logger.info { "Loading the additional gem path from #{additional_gem_path}" } - - # google-protobuf is used for otel trace exporter - Dir.glob("#{additional_gem_path}/gems/*").each do |file| - if file.include?('opentelemetry') || file.include?('google') - puts file.inspect - $LOAD_PATH.unshift("#{file}/lib") - end +# set OTEL_OPERATOR to true if in autoinstrumentation-ruby image +# /otel-auto-instrumentation-ruby is set in operator ruby.go +operator_gem_path = ENV['OTEL_OPERATOR'].to_s == 'true' ? '/otel-auto-instrumentation-ruby' : nil +additional_gem_path = operator_gem_path || ENV['ADDITIONAL_GEM_PATH'] || Gem.dir +puts "Loading the additional gem path from #{additional_gem_path}" + +# google-protobuf is used for otel trace exporter +Dir.glob("#{additional_gem_path}/gems/*").each do |file| + if file.include?('opentelemetry') || file.include?('google') + puts "Unshift #{file.inspect}" + $LOAD_PATH.unshift("#{file}/lib") end end From 60a3ab2af642be7c302391c5d483be67e343522d Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Wed, 26 Feb 2025 13:51:11 -0500 Subject: [PATCH 06/68] update readme --- zero-code-instrumentation/README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/zero-code-instrumentation/README.md b/zero-code-instrumentation/README.md index 83f0bb0082..8f8d8928e8 100644 --- a/zero-code-instrumentation/README.md +++ b/zero-code-instrumentation/README.md @@ -23,16 +23,17 @@ gem install zero-code-instrumentation ``` Install zero-code-instrumentation will automatically install following gems: - - opentelemetry-sdk - opentelemetry-api - opentelemetry-instrumentation-all - opentelemetry-exporter-otlp - opentelemetry-helpers-mysql - opentelemetry-helpers-sql-obfuscation - opentelemetry-resource-detector-google_cloud_platform - opentelemetry-resource-detector-azure - opentelemetry-resource-detector-container +```console +opentelemetry-sdk +opentelemetry-api +opentelemetry-instrumentation-all +opentelemetry-exporter-otlp +opentelemetry-helpers-mysql +opentelemetry-helpers-sql-obfuscation +opentelemetry-resource-detector-google_cloud_platform +opentelemetry-resource-detector-azure +opentelemetry-resource-detector-container +``` Instrument your application: @@ -87,7 +88,7 @@ Since installing the `zero-code-instrumentation` gem through `gem install`, anyt RUBYOPT="-r {PUT YOUR GEM PATH}/gems/zero-code-instrumentation-0.1.0/lib/zero-code-instrumentation" bundle exec rails server ``` -Instrument Sinatra application with rackup: +Instrument Sinatra application with `rackup`: If you are using a Gemfile to install the required gems but without `Bundler.require`, set `REQUIRE_BUNDLER` to true. This way, `zero-code-instrumentation` will call `Bundler.require` to initialize the required gems prior to SDK initialization. @@ -147,5 +148,4 @@ The `zero-code-instrumentation` gem is distributed under the Apache 2.0 license. [community-meetings]: https://github.com/open-telemetry/community#community-meetings [slack-channel]: https://cloud-native.slack.com/archives/C01NWKKMKMY [discussions-url]: https://github.com/open-telemetry/opentelemetry-ruby/discussions -[k8sattributesprocessor-url]: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/k8sattributesprocessor/README.md [opentelemetry-operator]: https://github.com/open-telemetry/opentelemetry-operator From 35122254425ecd59c49580606d88929f1fcc8a5e Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Wed, 26 Feb 2025 14:00:12 -0500 Subject: [PATCH 07/68] cspell --- .cspell.yml | 1 + zero-code-instrumentation/README.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.cspell.yml b/.cspell.yml index c4217065cd..fc59c3d224 100644 --- a/.cspell.yml +++ b/.cspell.yml @@ -81,3 +81,4 @@ words: - webmocks - Xuan - yardoc + - rackup diff --git a/zero-code-instrumentation/README.md b/zero-code-instrumentation/README.md index 8f8d8928e8..abbab04976 100644 --- a/zero-code-instrumentation/README.md +++ b/zero-code-instrumentation/README.md @@ -88,7 +88,7 @@ Since installing the `zero-code-instrumentation` gem through `gem install`, anyt RUBYOPT="-r {PUT YOUR GEM PATH}/gems/zero-code-instrumentation-0.1.0/lib/zero-code-instrumentation" bundle exec rails server ``` -Instrument Sinatra application with `rackup`: +Instrument Sinatra application with rackup: If you are using a Gemfile to install the required gems but without `Bundler.require`, set `REQUIRE_BUNDLER` to true. This way, `zero-code-instrumentation` will call `Bundler.require` to initialize the required gems prior to SDK initialization. From 2c880da118156d9c266eda66d23c449245299847 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Wed, 16 Apr 2025 12:48:16 -0400 Subject: [PATCH 08/68] update the script --- .../lib/zero-code-instrumentation.rb | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/zero-code-instrumentation/lib/zero-code-instrumentation.rb b/zero-code-instrumentation/lib/zero-code-instrumentation.rb index f03f1bd6ac..40dd2bd138 100644 --- a/zero-code-instrumentation/lib/zero-code-instrumentation.rb +++ b/zero-code-instrumentation/lib/zero-code-instrumentation.rb @@ -100,7 +100,7 @@ def require_otel end OpenTelemetry.logger.info { 'Auto-instrumentation initialized' } rescue StandardError => e - OpenTelemetry.logger.info { "Auto-instrumentation failed to initialize. Error: #{e.message}" } + puts "Auto-instrumentation failed to initialize. Error: #{e.message}" end end end @@ -111,18 +111,27 @@ def require_otel google_cloud_platform = ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s.include?('google_cloud_platform') azure = ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s.include?('azure') -# set OTEL_OPERATOR to true if in autoinstrumentation-ruby image -# /otel-auto-instrumentation-ruby is set in operator ruby.go -operator_gem_path = ENV['OTEL_OPERATOR'].to_s == 'true' ? '/otel-auto-instrumentation-ruby' : nil +# set OTEL_OPERATOR to false if not in autoinstrumentation-ruby image, default to /otel-auto-instrumentation-ruby +# /otel-auto-instrumentation-ruby is set in opentelemetry-operator ruby.go +operator_gem_path = (ENV['OTEL_OPERATOR'].nil? || ENV['OTEL_OPERATOR'] == 'true') ? '/otel-auto-instrumentation-ruby' : nil additional_gem_path = operator_gem_path || ENV['ADDITIONAL_GEM_PATH'] || Gem.dir puts "Loading the additional gem path from #{additional_gem_path}" if ENV['ZERO_CODE_DEBUG'] == 'true' -# google-protobuf is used for otel trace exporter -Dir.glob("#{additional_gem_path}/gems/*").each do |file| - if file.include?('opentelemetry') || file.include?('google') - puts "Unshift #{file.inspect}" if ENV['ZERO_CODE_DEBUG'] == 'true' - $LOAD_PATH.unshift("#{file}/lib") - end +# use UNLOAD_LIBRARY to avoid certain gem preload (esp. google protobuf) +# e.g export UNLOAD_LIBRARY=google-protobuf;googleapis-common-protos-types +unload_libraries = ENV['UNLOAD_LIBRARY'].to_s.split(';') +loaded_library_file_path = Array.new + +Dir.glob("#{additional_gem_path}/gems/*").each do |file_path| + gem_name = file_path.match(/\/gems\/([^\/]+)-\d/)[1] + include_file = (file_path.include?('opentelemetry') || file_path.include?('google')) && !unload_libraries.include? gem_name + loaded_library_file_path << file_path if include_file +end + +puts loaded_library_file_path.to_s if ENV['ZERO_CODE_DEBUG'] == 'true' + +loaded_library_file_path.each do |file_path| + $LOAD_PATH.unshift("#{file_path}/lib") end require 'opentelemetry-sdk' From 76f6d0ef75e3319fb33352c0d3231c5081e2ce31 Mon Sep 17 00:00:00 2001 From: Xuan <112967240+xuan-cao-swi@users.noreply.github.com> Date: Wed, 4 Jun 2025 11:22:10 -0400 Subject: [PATCH 09/68] Update zero-code-instrumentation/lib/version.rb Co-authored-by: Kayla Reopelle <87386821+kaylareopelle@users.noreply.github.com> --- zero-code-instrumentation/lib/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zero-code-instrumentation/lib/version.rb b/zero-code-instrumentation/lib/version.rb index 001acd6202..57bc2eb570 100644 --- a/zero-code-instrumentation/lib/version.rb +++ b/zero-code-instrumentation/lib/version.rb @@ -4,4 +4,4 @@ # # SPDX-License-Identifier: Apache-2.0 -VERSION = '0.1.0' +VERSION = '0.0.0' From 90995f7fbb84b2d57d0d09c41b9847b3691fce35 Mon Sep 17 00:00:00 2001 From: Xuan <112967240+xuan-cao-swi@users.noreply.github.com> Date: Wed, 4 Jun 2025 11:22:19 -0400 Subject: [PATCH 10/68] Update zero-code-instrumentation/CHANGELOG.md Co-authored-by: Kayla Reopelle <87386821+kaylareopelle@users.noreply.github.com> --- zero-code-instrumentation/CHANGELOG.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/zero-code-instrumentation/CHANGELOG.md b/zero-code-instrumentation/CHANGELOG.md index de43fbf3d1..cba9d81adf 100644 --- a/zero-code-instrumentation/CHANGELOG.md +++ b/zero-code-instrumentation/CHANGELOG.md @@ -1,5 +1 @@ # Release History: zero-code-instrumentation - -### v0.1.0 / 2025-01-21 - -* Initial release From 917cb33245fc96fe102c8a6a7a026d66a0a78d8e Mon Sep 17 00:00:00 2001 From: Xuan <112967240+xuan-cao-swi@users.noreply.github.com> Date: Wed, 4 Jun 2025 11:24:30 -0400 Subject: [PATCH 11/68] Update zero-code-instrumentation/README.md Co-authored-by: Kayla Reopelle <87386821+kaylareopelle@users.noreply.github.com> --- zero-code-instrumentation/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zero-code-instrumentation/README.md b/zero-code-instrumentation/README.md index abbab04976..bf5607111d 100644 --- a/zero-code-instrumentation/README.md +++ b/zero-code-instrumentation/README.md @@ -10,7 +10,7 @@ OpenTelemetry provides a single set of APIs, libraries, agents, and collector se ## How does this gem fit in? -The `zero-code-instrumentation` gem provides an easy way to load and initialize the OpenTelemetry Ruby SDK. This gem is particularly used for the [OpenTelemetry Operator][opentelemetry-operator]. +The `zero-code-instrumentation` gem provides an easy way to load and initialize the OpenTelemetry Ruby SDK without changing your code initialize the SDK. This gem is particularly used with the [OpenTelemetry Operator][opentelemetry-operator]. ## How do I get started? From 072c44aff21c09115b252805f6a8344d6e26873f Mon Sep 17 00:00:00 2001 From: Xuan <112967240+xuan-cao-swi@users.noreply.github.com> Date: Wed, 4 Jun 2025 11:24:36 -0400 Subject: [PATCH 12/68] Update zero-code-instrumentation/README.md Co-authored-by: Kayla Reopelle <87386821+kaylareopelle@users.noreply.github.com> --- zero-code-instrumentation/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zero-code-instrumentation/README.md b/zero-code-instrumentation/README.md index bf5607111d..5881ec2f19 100644 --- a/zero-code-instrumentation/README.md +++ b/zero-code-instrumentation/README.md @@ -97,7 +97,7 @@ export REQUIRE_BUNDLER=true RUBYOPT="-r zero-code-instrumentation" rackup config.ru ``` -If wish to load some gem outside the Gemfile, then it needs to be placed in front of zero-code-instrumentation: +If you wish to load some gems outside the Gemfile, then they need to be placed in front of zero-code-instrumentation: ```console export BUNDLE_WITHOUT=development,test From dd16883242dcfb294fa60b60520ca4af8cbaebec Mon Sep 17 00:00:00 2001 From: Xuan <112967240+xuan-cao-swi@users.noreply.github.com> Date: Wed, 4 Jun 2025 11:24:42 -0400 Subject: [PATCH 13/68] Update zero-code-instrumentation/README.md Co-authored-by: Kayla Reopelle <87386821+kaylareopelle@users.noreply.github.com> --- zero-code-instrumentation/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zero-code-instrumentation/README.md b/zero-code-instrumentation/README.md index 5881ec2f19..1727c48c39 100644 --- a/zero-code-instrumentation/README.md +++ b/zero-code-instrumentation/README.md @@ -107,7 +107,7 @@ RUBYOPT="-r mysql2 -r zero-code-instrumentation" ruby application.rb ## Example -In example folder, execute the following commands should see the trace output. +In example folder, executing the following commands should result in trace output. ```console # if user don't want to install zero-code-instrumentation from rubygems.org From d4de6d032336e44b01b4e5f294871a35e9a6e279 Mon Sep 17 00:00:00 2001 From: Xuan <112967240+xuan-cao-swi@users.noreply.github.com> Date: Wed, 4 Jun 2025 11:24:48 -0400 Subject: [PATCH 14/68] Update zero-code-instrumentation/README.md Co-authored-by: Kayla Reopelle <87386821+kaylareopelle@users.noreply.github.com> --- zero-code-instrumentation/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zero-code-instrumentation/README.md b/zero-code-instrumentation/README.md index 1727c48c39..e977433419 100644 --- a/zero-code-instrumentation/README.md +++ b/zero-code-instrumentation/README.md @@ -110,7 +110,7 @@ RUBYOPT="-r mysql2 -r zero-code-instrumentation" ruby application.rb In example folder, executing the following commands should result in trace output. ```console -# if user don't want to install zero-code-instrumentation from rubygems.org +# if the user doesn't want to install zero-code-instrumentation from rubygems.org # user can build the gem and install it with gem install *.gem gem install zero-code-instrumentation From 342084f946c16eda93e1d197f65b17287a33658b Mon Sep 17 00:00:00 2001 From: Xuan <112967240+xuan-cao-swi@users.noreply.github.com> Date: Wed, 4 Jun 2025 11:24:54 -0400 Subject: [PATCH 15/68] Update zero-code-instrumentation/README.md Co-authored-by: Kayla Reopelle <87386821+kaylareopelle@users.noreply.github.com> --- zero-code-instrumentation/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zero-code-instrumentation/README.md b/zero-code-instrumentation/README.md index e977433419..893333658c 100644 --- a/zero-code-instrumentation/README.md +++ b/zero-code-instrumentation/README.md @@ -111,7 +111,7 @@ In example folder, executing the following commands should result in trace outpu ```console # if the user doesn't want to install zero-code-instrumentation from rubygems.org -# user can build the gem and install it with gem install *.gem +# the user can build the gem and install it with gem install *.gem gem install zero-code-instrumentation bundle install From e1e4d20868a094c1a6306718bdcc6467252b881e Mon Sep 17 00:00:00 2001 From: Xuan <112967240+xuan-cao-swi@users.noreply.github.com> Date: Wed, 4 Jun 2025 11:37:59 -0400 Subject: [PATCH 16/68] Update zero-code-instrumentation/lib/zero-code-instrumentation.rb Co-authored-by: Ariel Valentin --- zero-code-instrumentation/lib/zero-code-instrumentation.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zero-code-instrumentation/lib/zero-code-instrumentation.rb b/zero-code-instrumentation/lib/zero-code-instrumentation.rb index 40dd2bd138..5a2c1e5a36 100644 --- a/zero-code-instrumentation/lib/zero-code-instrumentation.rb +++ b/zero-code-instrumentation/lib/zero-code-instrumentation.rb @@ -128,7 +128,7 @@ def require_otel loaded_library_file_path << file_path if include_file end -puts loaded_library_file_path.to_s if ENV['ZERO_CODE_DEBUG'] == 'true' +puts "Loaded Library File Paths " + loaded_library_file_path.join(",") if ENV['ZERO_CODE_DEBUG'] == 'true' loaded_library_file_path.each do |file_path| $LOAD_PATH.unshift("#{file_path}/lib") From 2c2b9f8cbd63ee3ab9f47137982b1193936be1c5 Mon Sep 17 00:00:00 2001 From: Xuan <112967240+xuan-cao-swi@users.noreply.github.com> Date: Wed, 4 Jun 2025 11:39:16 -0400 Subject: [PATCH 17/68] Update zero-code-instrumentation/lib/zero-code-instrumentation.rb Co-authored-by: Ariel Valentin --- zero-code-instrumentation/lib/zero-code-instrumentation.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/zero-code-instrumentation/lib/zero-code-instrumentation.rb b/zero-code-instrumentation/lib/zero-code-instrumentation.rb index 5a2c1e5a36..82d3680d65 100644 --- a/zero-code-instrumentation/lib/zero-code-instrumentation.rb +++ b/zero-code-instrumentation/lib/zero-code-instrumentation.rb @@ -122,10 +122,9 @@ def require_otel unload_libraries = ENV['UNLOAD_LIBRARY'].to_s.split(';') loaded_library_file_path = Array.new -Dir.glob("#{additional_gem_path}/gems/*").each do |file_path| +loaded_library_file_path = Dir.glob("#{additional_gem_path}/gems/*").select do |file_path| gem_name = file_path.match(/\/gems\/([^\/]+)-\d/)[1] - include_file = (file_path.include?('opentelemetry') || file_path.include?('google')) && !unload_libraries.include? gem_name - loaded_library_file_path << file_path if include_file + (file_path.include?('opentelemetry') || file_path.include?('google')) && !unload_libraries.include? gem_name end puts "Loaded Library File Paths " + loaded_library_file_path.join(",") if ENV['ZERO_CODE_DEBUG'] == 'true' From 01a8779d732aac2d832785ae1ebda5db16ed928b Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Wed, 4 Jun 2025 11:55:53 -0400 Subject: [PATCH 18/68] revision --- zero-code-instrumentation/README.md | 10 +++--- .../lib/zero-code-instrumentation.rb | 33 +++++++++---------- .../test/zero-code-instrumentation_test.rb | 3 +- .../zero-code-instrumentation.gemspec | 20 +++++------ 4 files changed, 32 insertions(+), 34 deletions(-) diff --git a/zero-code-instrumentation/README.md b/zero-code-instrumentation/README.md index abbab04976..3a82e0900c 100644 --- a/zero-code-instrumentation/README.md +++ b/zero-code-instrumentation/README.md @@ -126,13 +126,13 @@ These environment variables are not standard OpenTelemetry environment variables | Environment Variable | Description | Default | Example | |----------------------|-------------|---------|---------| -| `REQUIRE_BUNDLER` | Set to `true` if you are using Bundler to install gems but without `Bundler.require` in your script during initialization. | nil | N/A | -| `USE_BUNDLE_EXEC` | Set to `true` if initializing through `bundle exec`. | nil | N/A | -| `ADDITIONAL_GEM_PATH` | Intended to be used for the OpenTelemetry Operator environment if you install `zero-code-instrumentation` to a customized path. | nil | N/A | -| `OTEL_OPERATOR` | Set to `true` to set the binding path for the OpenTelemetry Operator. | `/otel-auto-instrumentation-ruby` | N/A | +| `OTEL_RUBY_REQUIRE_BUNDLER` | Set to `true` if you are using Bundler to install gems but without `Bundler.require` in your script during initialization. | nil | N/A | +| `OTEL_RUBY_ADDITIONAL_GEM_PATH` | Intended to be used for the OpenTelemetry Operator environment if you install `zero-code-instrumentation` to a customized path. | nil | N/A | +| `OTEL_RUBY_OPERATOR` | Set to `true` to set the binding path for the OpenTelemetry Operator. | `/otel-auto-instrumentation-ruby` | N/A | | `OTEL_RUBY_RESOURCE_DETECTORS` | Determine what kind of resource detector is needed. Currently supports `container`, `azure`, and `google_cloud_platform`. Use commas to separate multiple detectors. | nil | `container,azure` | | `OTEL_RUBY_ENABLED_INSTRUMENTATIONS` | Shortcut used when you only want to instrument one or a couple of particular libraries. | nil | `redis,active_record` | -| `ZERO_CODE_DEBUG` | Set to `true` if want to see some debug information. This to avoid preload logger gem | nil | N/A | +| `OTEL_RUBY_ZERO_CODE_DEBUG` | Set to `true` if want to see some debug information. This to avoid preload logger gem | nil | N/A | +| `OTEL_RUBY_UNLOAD_LIBRARY` | Use OTEL_RUBY_UNLOAD_LIBRARY to avoid certain gem preload (esp. google protobuf) | nil | N/A | ## How can I get involved? diff --git a/zero-code-instrumentation/lib/zero-code-instrumentation.rb b/zero-code-instrumentation/lib/zero-code-instrumentation.rb index 40dd2bd138..abd7122cb1 100644 --- a/zero-code-instrumentation/lib/zero-code-instrumentation.rb +++ b/zero-code-instrumentation/lib/zero-code-instrumentation.rb @@ -77,7 +77,7 @@ def detect_resource_from_env def determine_enabled_instrumentation env = ENV['OTEL_RUBY_ENABLED_INSTRUMENTATIONS'].to_s - env.split(',').map { |instrumentation| OTEL_INSTRUMENTATION_MAP[instrumentation] } + env.split(',').each_with_object({}) { |instrumentation, config| config[OTEL_INSTRUMENTATION_MAP[instrumentation]] = {} } end def require_otel @@ -90,13 +90,7 @@ def require_otel OpenTelemetry::SDK.configure do |c| c.resource = detect_resource_from_env - if required_instrumentation.empty? - c.use_all # enables all instrumentation! - else - required_instrumentation.each do |instrumentation| - c.use instrumentation - end - end + c.use_all(required_instrumentation) end OpenTelemetry.logger.info { 'Auto-instrumentation initialized' } rescue StandardError => e @@ -113,27 +107,30 @@ def require_otel # set OTEL_OPERATOR to false if not in autoinstrumentation-ruby image, default to /otel-auto-instrumentation-ruby # /otel-auto-instrumentation-ruby is set in opentelemetry-operator ruby.go -operator_gem_path = (ENV['OTEL_OPERATOR'].nil? || ENV['OTEL_OPERATOR'] == 'true') ? '/otel-auto-instrumentation-ruby' : nil -additional_gem_path = operator_gem_path || ENV['ADDITIONAL_GEM_PATH'] || Gem.dir -puts "Loading the additional gem path from #{additional_gem_path}" if ENV['ZERO_CODE_DEBUG'] == 'true' +operator_gem_path = (ENV['OTEL_RUBY_OPERATOR'].nil? || ENV['OTEL_RUBY_OPERATOR'] == 'true') ? '/otel-auto-instrumentation-ruby' : nil +additional_gem_path = operator_gem_path || ENV['OTEL_RUBY_ADDITIONAL_GEM_PATH'] || Gem.dir +puts "Loading the additional gem path from #{additional_gem_path}" if ENV['OTEL_RUBY_ZERO_CODE_DEBUG'] == 'true' -# use UNLOAD_LIBRARY to avoid certain gem preload (esp. google protobuf) -# e.g export UNLOAD_LIBRARY=google-protobuf;googleapis-common-protos-types -unload_libraries = ENV['UNLOAD_LIBRARY'].to_s.split(';') +# use OTEL_RUBY_UNLOAD_LIBRARY to avoid certain gem preload (esp. google protobuf) +# e.g export OTEL_RUBY_UNLOAD_LIBRARY=google-protobuf;googleapis-common-protos-types +unload_libraries = ENV['OTEL_RUBY_UNLOAD_LIBRARY'].to_s.split(';') loaded_library_file_path = Array.new Dir.glob("#{additional_gem_path}/gems/*").each do |file_path| gem_name = file_path.match(/\/gems\/([^\/]+)-\d/)[1] - include_file = (file_path.include?('opentelemetry') || file_path.include?('google')) && !unload_libraries.include? gem_name + include_file = (file_path.include?('opentelemetry') || file_path.include?('google')) && !unload_libraries.include?(gem_name) loaded_library_file_path << file_path if include_file end -puts loaded_library_file_path.to_s if ENV['ZERO_CODE_DEBUG'] == 'true' - loaded_library_file_path.each do |file_path| $LOAD_PATH.unshift("#{file_path}/lib") end +if ENV['OTEL_RUBY_ZERO_CODE_DEBUG'] == 'true' + puts loaded_library_file_path.to_s + puts "$LOAD_PATH #{$LOAD_PATH}" +end + require 'opentelemetry-sdk' require 'opentelemetry-instrumentation-all' require 'opentelemetry-helpers-mysql' @@ -146,4 +143,4 @@ def require_otel Bundler::Runtime.prepend(OTelBundlerPatch) -Bundler.require if ENV['REQUIRE_BUNDLER'].to_s == 'true' +Bundler.require if ENV['OTEL_RUBY_REQUIRE_BUNDLER'].to_s == 'true' diff --git a/zero-code-instrumentation/test/zero-code-instrumentation_test.rb b/zero-code-instrumentation/test/zero-code-instrumentation_test.rb index f49473370d..4825183d6e 100644 --- a/zero-code-instrumentation/test/zero-code-instrumentation_test.rb +++ b/zero-code-instrumentation/test/zero-code-instrumentation_test.rb @@ -19,6 +19,8 @@ OTelBundlerPatch.send(:undef_method, :require) OpenTelemetry::Instrumentation::Net::HTTP::Instrumentation.instance_variable_get(:@instance).instance_variable_set(:@installed, false) OpenTelemetry::Instrumentation::Rake::Instrumentation.instance_variable_get(:@instance).instance_variable_set(:@installed, false) + + ENV['OTEL_RUBY_ENABLED_INSTRUMENTATIONS'] = nil end it 'simple_load_test' do @@ -64,7 +66,6 @@ _(registry.size).must_equal 1 _(registry.first.first.name).must_equal 'OpenTelemetry::Instrumentation::Net::HTTP' - ENV['OTEL_RUBY_ENABLED_INSTRUMENTATIONS'] = nil end it 'simple_load_with_additional_resource' do diff --git a/zero-code-instrumentation/zero-code-instrumentation.gemspec b/zero-code-instrumentation/zero-code-instrumentation.gemspec index d7cfe61d95..7c057428d9 100644 --- a/zero-code-instrumentation/zero-code-instrumentation.gemspec +++ b/zero-code-instrumentation/zero-code-instrumentation.gemspec @@ -26,15 +26,15 @@ Gem::Specification.new do |spec| spec.require_paths = ['lib'] spec.required_ruby_version = '>= 3.1' - spec.add_dependency 'opentelemetry-api', '1.4.0' - spec.add_dependency 'opentelemetry-exporter-otlp', '0.29.1' - spec.add_dependency 'opentelemetry-helpers-mysql', '0.2.0' - spec.add_dependency 'opentelemetry-helpers-sql-obfuscation', '0.3.0' - spec.add_dependency 'opentelemetry-instrumentation-all', '0.72.0' - spec.add_dependency 'opentelemetry-resource-detector-azure', '0.2.0' - spec.add_dependency 'opentelemetry-resource-detector-container', '0.2.0' - spec.add_dependency 'opentelemetry-resource-detector-google_cloud_platform', '0.2.0' - spec.add_dependency 'opentelemetry-sdk', '1.6.0' + spec.add_dependency 'opentelemetry-api', '~> 1.4.0' + spec.add_dependency 'opentelemetry-exporter-otlp', '~> 0.29.1' + spec.add_dependency 'opentelemetry-helpers-mysql', '~> 0.2.0' + spec.add_dependency 'opentelemetry-helpers-sql-obfuscation', '~> 0.3.0' + spec.add_dependency 'opentelemetry-instrumentation-all', '~> 0.72.0' + spec.add_dependency 'opentelemetry-resource-detector-azure', '~> 0.2.0' + spec.add_dependency 'opentelemetry-resource-detector-container', '~> 0.2.0' + spec.add_dependency 'opentelemetry-resource-detector-google_cloud_platform', '~> 0.2.0' + spec.add_dependency 'opentelemetry-sdk', '~> 1.6.0' spec.add_development_dependency 'bundler', '~> 2.4' spec.add_development_dependency 'minitest', '~> 5.0' @@ -51,5 +51,5 @@ Gem::Specification.new do |spec| spec.metadata['documentation_uri'] = "https://rubydoc.info/gems/#{spec.name}/#{spec.version}" end - spec.post_install_message = File.read(File.expand_path('../POST_INSTALL_MESSAGE', __dir__)) + # spec.post_install_message = File.read(File.expand_path('../POST_INSTALL_MESSAGE', __dir__)) end From 5104dd874451556f74aad9b25bb1612fede2e8c8 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Tue, 10 Jun 2025 14:51:57 -0400 Subject: [PATCH 19/68] change gem name --- .../.rubocop.yml | 0 .../CHANGELOG.md | 1 + .../Gemfile | 0 .../LICENSE | 0 .../README.md | 50 +++++++++---------- .../Rakefile | 0 .../example/Gemfile | 0 .../example/app.rb | 0 .../lib/opentelemetry-auto-instrumentation.rb | 0 .../lib/version.rb | 0 ...opentelemetry-auto-instrumentation.gemspec | 2 +- ...opentelemetry-auto-instrumentation_test.rb | 8 +-- .../test/test_helper.rb | 0 zero-code-instrumentation/CHANGELOG.md | 1 - 14 files changed, 31 insertions(+), 31 deletions(-) rename {zero-code-instrumentation => opentelemetry-auto-instrumentation}/.rubocop.yml (100%) create mode 100644 opentelemetry-auto-instrumentation/CHANGELOG.md rename {zero-code-instrumentation => opentelemetry-auto-instrumentation}/Gemfile (100%) rename {zero-code-instrumentation => opentelemetry-auto-instrumentation}/LICENSE (100%) rename {zero-code-instrumentation => opentelemetry-auto-instrumentation}/README.md (65%) rename {zero-code-instrumentation => opentelemetry-auto-instrumentation}/Rakefile (100%) rename {zero-code-instrumentation => opentelemetry-auto-instrumentation}/example/Gemfile (100%) rename {zero-code-instrumentation => opentelemetry-auto-instrumentation}/example/app.rb (100%) rename zero-code-instrumentation/lib/zero-code-instrumentation.rb => opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb (100%) rename {zero-code-instrumentation => opentelemetry-auto-instrumentation}/lib/version.rb (100%) rename zero-code-instrumentation/zero-code-instrumentation.gemspec => opentelemetry-auto-instrumentation/opentelemetry-auto-instrumentation.gemspec (97%) rename zero-code-instrumentation/test/zero-code-instrumentation_test.rb => opentelemetry-auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb (92%) rename {zero-code-instrumentation => opentelemetry-auto-instrumentation}/test/test_helper.rb (100%) delete mode 100644 zero-code-instrumentation/CHANGELOG.md diff --git a/zero-code-instrumentation/.rubocop.yml b/opentelemetry-auto-instrumentation/.rubocop.yml similarity index 100% rename from zero-code-instrumentation/.rubocop.yml rename to opentelemetry-auto-instrumentation/.rubocop.yml diff --git a/opentelemetry-auto-instrumentation/CHANGELOG.md b/opentelemetry-auto-instrumentation/CHANGELOG.md new file mode 100644 index 0000000000..d068b6d9e8 --- /dev/null +++ b/opentelemetry-auto-instrumentation/CHANGELOG.md @@ -0,0 +1 @@ +# Release History: opentelemetry-auto-instrumentation diff --git a/zero-code-instrumentation/Gemfile b/opentelemetry-auto-instrumentation/Gemfile similarity index 100% rename from zero-code-instrumentation/Gemfile rename to opentelemetry-auto-instrumentation/Gemfile diff --git a/zero-code-instrumentation/LICENSE b/opentelemetry-auto-instrumentation/LICENSE similarity index 100% rename from zero-code-instrumentation/LICENSE rename to opentelemetry-auto-instrumentation/LICENSE diff --git a/zero-code-instrumentation/README.md b/opentelemetry-auto-instrumentation/README.md similarity index 65% rename from zero-code-instrumentation/README.md rename to opentelemetry-auto-instrumentation/README.md index a32cf45557..365ffe14af 100644 --- a/zero-code-instrumentation/README.md +++ b/opentelemetry-auto-instrumentation/README.md @@ -1,6 +1,6 @@ -# Zero-code Instrumentation +# Opentelemetry Auto Instrumentation -The `zero-code-instrumentation` gem provides an easy way to load and initialize opentelemetry-ruby for zero-code instrumentation. +The `opentelemetry-auto-instrumentation` gem provides an easy way to load and initialize opentelemetry-ruby for zero-code instrumentation. ## What is OpenTelemetry? @@ -10,19 +10,19 @@ OpenTelemetry provides a single set of APIs, libraries, agents, and collector se ## How does this gem fit in? -The `zero-code-instrumentation` gem provides an easy way to load and initialize the OpenTelemetry Ruby SDK without changing your code initialize the SDK. This gem is particularly used with the [OpenTelemetry Operator][opentelemetry-operator]. +The `opentelemetry-auto-instrumentation` gem provides an easy way to load and initialize the OpenTelemetry Ruby SDK without changing your code initialize the SDK. This gem is particularly used with the [OpenTelemetry Operator][opentelemetry-operator]. ## How do I get started? -It's recommended to install this gem through gem install rather than Bundler since it doesn't require modifying any user's codebase (including the Gemfile). +It's recommended to install this gem through `gem install` rather than Bundler since it doesn't require modifying your codebase (including the Gemfile). Install the gem using: ```console -gem install zero-code-instrumentation +gem install opentelemetry-auto-instrumentation ``` -Install zero-code-instrumentation will automatically install following gems: +Installing opentelemetry-auto-instrumentation will automatically install following gems: ```console opentelemetry-sdk opentelemetry-api @@ -38,7 +38,7 @@ opentelemetry-resource-detector-container Instrument your application: ```console -RUBYOPT="-r zero-code-instrumentation" ruby application.rb +RUBYOPT="-r opentelemetry-auto-instrumentation" ruby application.rb ``` Instrument your application with additional environment variables: @@ -49,21 +49,21 @@ export OTEL_EXPORTER_OTLP_ENDPOINT="your-endpoint" export OTEL_RUBY_RESOURCE_DETECTORS="container" export OTEL_SERVICE_NAME="your-service-name" -RUBYOPT="-r zero-code-instrumentation" ruby application.rb +RUBYOPT="-r opentelemetry-auto-instrumentation" ruby application.rb ``` Instrument your application with disabling certain instrumentation (e.g. sinatra): ```console export OTEL_RUBY_INSTRUMENTATION_SINATRA_ENABLED='false' -RUBYOPT="-r zero-code-instrumentation" ruby application.rb +RUBYOPT="-r opentelemetry-auto-instrumentation" ruby application.rb ``` Instrument your application with only certain instrumentation installed (e.g. mysql, redis): ```console export OTEL_RUBY_ENABLED_INSTRUMENTATIONS='mysql2,redis' -RUBYOPT="-r zero-code-instrumentation" ruby application.rb +RUBYOPT="-r opentelemetry-auto-instrumentation" ruby application.rb ``` Instrument your application with only redis and configure its options: @@ -71,38 +71,38 @@ Instrument your application with only redis and configure its options: ```console export OTEL_RUBY_ENABLED_INSTRUMENTATIONS='redis' export OTEL_RUBY_INSTRUMENTATION_REDIS_CONFIG_OPTS='peer_service=new_service;db_statement=omit' -RUBYOPT="-r zero-code-instrumentation" ruby application.rb +RUBYOPT="-r opentelemetry-auto-instrumentation" ruby application.rb ``` Instrument Rails application: ```console -RUBYOPT="-r zero-code-instrumentation" rails server +RUBYOPT="-r opentelemetry-auto-instrumentation" rails server ``` -Instrument Rails application with `bundle exec` +Instrument Rails application with `bundle exec`: -Since installing the `zero-code-instrumentation` gem through `gem install`, anything related to the OpenTelemetry gem won't be stored in Bundler's gem path. Therefore, users need to add an additional gem path that contains these gems prior to initialization. +Since installing the `opentelemetry-auto-instrumentation` gem through `gem install`, anything related to the OpenTelemetry gem won't be stored in Bundler's gem path. Therefore, users need to add an additional gem path that contains these gems prior to initialization. ```console -RUBYOPT="-r {PUT YOUR GEM PATH}/gems/zero-code-instrumentation-0.1.0/lib/zero-code-instrumentation" bundle exec rails server +RUBYOPT="-r {PUT YOUR GEM PATH}/gems/opentelemetry-auto-instrumentation-0.1.0/lib/opentelemetry-auto-instrumentation" bundle exec rails server ``` Instrument Sinatra application with rackup: -If you are using a Gemfile to install the required gems but without `Bundler.require`, set `REQUIRE_BUNDLER` to true. This way, `zero-code-instrumentation` will call `Bundler.require` to initialize the required gems prior to SDK initialization. +If you are using a Gemfile to install the required gems but without `Bundler.require`, set `REQUIRE_BUNDLER` to true. This way, `opentelemetry-auto-instrumentation` will call `Bundler.require` to initialize the required gems prior to SDK initialization. ```console export REQUIRE_BUNDLER=true -RUBYOPT="-r zero-code-instrumentation" rackup config.ru +RUBYOPT="-r opentelemetry-auto-instrumentation" rackup config.ru ``` -If you wish to load some gems outside the Gemfile, then they need to be placed in front of zero-code-instrumentation: +If you wish to load some gems outside the Gemfile, then they need to be placed in front of opentelemetry-auto-instrumentation: ```console export BUNDLE_WITHOUT=development,test gem install mysql2 -RUBYOPT="-r mysql2 -r zero-code-instrumentation" ruby application.rb +RUBYOPT="-r mysql2 -r opentelemetry-auto-instrumentation" ruby application.rb ``` ## Example @@ -110,14 +110,14 @@ RUBYOPT="-r mysql2 -r zero-code-instrumentation" ruby application.rb In example folder, executing the following commands should result in trace output. ```console -# if the user doesn't want to install zero-code-instrumentation from rubygems.org +# if the user doesn't want to install opentelemetry-auto-instrumentation from rubygems.org # the user can build the gem and install it with gem install *.gem -gem install zero-code-instrumentation +gem install opentelemetry-auto-instrumentation bundle install export REQUIRE_BUNDLER=true export OTEL_TRACES_EXPORTER=console -RUBYOPT="-r zero-code-instrumentation" ruby app.rb +RUBYOPT="-r opentelemetry-auto-instrumentation" ruby app.rb ``` ## Configuration @@ -127,7 +127,7 @@ These environment variables are not standard OpenTelemetry environment variables | Environment Variable | Description | Default | Example | |----------------------|-------------|---------|---------| | `OTEL_RUBY_REQUIRE_BUNDLER` | Set to `true` if you are using Bundler to install gems but without `Bundler.require` in your script during initialization. | nil | N/A | -| `OTEL_RUBY_ADDITIONAL_GEM_PATH` | Intended to be used for the OpenTelemetry Operator environment if you install `zero-code-instrumentation` to a customized path. | nil | N/A | +| `OTEL_RUBY_ADDITIONAL_GEM_PATH` | Intended to be used for the OpenTelemetry Operator environment if you install `opentelemetry-auto-instrumentation` to a customized path. | nil | N/A | | `OTEL_RUBY_OPERATOR` | Set to `true` to set the binding path for the OpenTelemetry Operator. | `/otel-auto-instrumentation-ruby` | N/A | | `OTEL_RUBY_RESOURCE_DETECTORS` | Determine what kind of resource detector is needed. Currently supports `container`, `azure`, and `google_cloud_platform`. Use commas to separate multiple detectors. | nil | `container,azure` | | `OTEL_RUBY_ENABLED_INSTRUMENTATIONS` | Shortcut used when you only want to instrument one or a couple of particular libraries. | nil | `redis,active_record` | @@ -136,13 +136,13 @@ These environment variables are not standard OpenTelemetry environment variables ## How can I get involved? -The `zero-code-instrumentation` gem source is on GitHub, along with related gems. +The `opentelemetry-auto-instrumentation` gem source is on GitHub, along with related gems. The OpenTelemetry Ruby gems are maintained by the OpenTelemetry Ruby special interest group (SIG). You can get involved by joining us on our [GitHub Discussions][discussions-url], [Slack Channel][slack-channel] or attending our weekly meeting. See the [meeting calendar][community-meetings] for dates and times. For more information on this and other language SIGs, see the OpenTelemetry [community page][ruby-sig]. ## License -The `zero-code-instrumentation` gem is distributed under the Apache 2.0 license. See LICENSE for more information. +The `opentelemetry-auto-instrumentation` gem is distributed under the Apache 2.0 license. See LICENSE for more information. [ruby-sig]: https://github.com/open-telemetry/community#ruby-sig [community-meetings]: https://github.com/open-telemetry/community#community-meetings diff --git a/zero-code-instrumentation/Rakefile b/opentelemetry-auto-instrumentation/Rakefile similarity index 100% rename from zero-code-instrumentation/Rakefile rename to opentelemetry-auto-instrumentation/Rakefile diff --git a/zero-code-instrumentation/example/Gemfile b/opentelemetry-auto-instrumentation/example/Gemfile similarity index 100% rename from zero-code-instrumentation/example/Gemfile rename to opentelemetry-auto-instrumentation/example/Gemfile diff --git a/zero-code-instrumentation/example/app.rb b/opentelemetry-auto-instrumentation/example/app.rb similarity index 100% rename from zero-code-instrumentation/example/app.rb rename to opentelemetry-auto-instrumentation/example/app.rb diff --git a/zero-code-instrumentation/lib/zero-code-instrumentation.rb b/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb similarity index 100% rename from zero-code-instrumentation/lib/zero-code-instrumentation.rb rename to opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb diff --git a/zero-code-instrumentation/lib/version.rb b/opentelemetry-auto-instrumentation/lib/version.rb similarity index 100% rename from zero-code-instrumentation/lib/version.rb rename to opentelemetry-auto-instrumentation/lib/version.rb diff --git a/zero-code-instrumentation/zero-code-instrumentation.gemspec b/opentelemetry-auto-instrumentation/opentelemetry-auto-instrumentation.gemspec similarity index 97% rename from zero-code-instrumentation/zero-code-instrumentation.gemspec rename to opentelemetry-auto-instrumentation/opentelemetry-auto-instrumentation.gemspec index 7c057428d9..df8e5a22f6 100644 --- a/zero-code-instrumentation/zero-code-instrumentation.gemspec +++ b/opentelemetry-auto-instrumentation/opentelemetry-auto-instrumentation.gemspec @@ -9,7 +9,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'version' Gem::Specification.new do |spec| - spec.name = 'zero-code-instrumentation' + spec.name = 'opentelemetry-auto-instrumentation' spec.version = VERSION spec.authors = ['OpenTelemetry Authors'] spec.email = ['cncf-opentelemetry-contributors@lists.cncf.io'] diff --git a/zero-code-instrumentation/test/zero-code-instrumentation_test.rb b/opentelemetry-auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb similarity index 92% rename from zero-code-instrumentation/test/zero-code-instrumentation_test.rb rename to opentelemetry-auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb index 12f00cd74c..d7ff916488 100644 --- a/zero-code-instrumentation/test/zero-code-instrumentation_test.rb +++ b/opentelemetry-auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb @@ -24,7 +24,7 @@ end it 'simple_load_test' do - load './lib/zero-code-instrumentation.rb' + load './lib/opentelemetry-auto-instrumentation.rb' Bundler.require _(OpenTelemetry.tracer_provider.class).must_equal OpenTelemetry::SDK::Trace::TracerProvider @@ -44,7 +44,7 @@ it 'simple_load_with_net_http_disabled' do ENV['OTEL_RUBY_INSTRUMENTATION_NET_HTTP_ENABLED'] = 'false' - load './lib/zero-code-instrumentation.rb' + load './lib/opentelemetry-auto-instrumentation.rb' Bundler.require registry = OpenTelemetry.tracer_provider.instance_variable_get(:@registry) @@ -58,7 +58,7 @@ it 'simple_load_with_desired_instrument_only' do ENV['OTEL_RUBY_ENABLED_INSTRUMENTATIONS'] = 'net_http' - load './lib/zero-code-instrumentation.rb' + load './lib/opentelemetry-auto-instrumentation.rb' Bundler.require registry = OpenTelemetry.tracer_provider.instance_variable_get(:@registry) @@ -70,7 +70,7 @@ it 'simple_load_with_additional_resource' do ENV['OTEL_RUBY_RESOURCE_DETECTORS'] = 'container' - load './lib/zero-code-instrumentation.rb' + load './lib/opentelemetry-auto-instrumentation.rb' Bundler.require resource_attributes = OpenTelemetry.tracer_provider.instance_variable_get(:@resource).instance_variable_get(:@attributes) diff --git a/zero-code-instrumentation/test/test_helper.rb b/opentelemetry-auto-instrumentation/test/test_helper.rb similarity index 100% rename from zero-code-instrumentation/test/test_helper.rb rename to opentelemetry-auto-instrumentation/test/test_helper.rb diff --git a/zero-code-instrumentation/CHANGELOG.md b/zero-code-instrumentation/CHANGELOG.md deleted file mode 100644 index cba9d81adf..0000000000 --- a/zero-code-instrumentation/CHANGELOG.md +++ /dev/null @@ -1 +0,0 @@ -# Release History: zero-code-instrumentation From 1e8c2b250becb3f87091887b346479139cae014f Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Tue, 17 Jun 2025 12:19:29 -0400 Subject: [PATCH 20/68] remove zero as reference name --- opentelemetry-auto-instrumentation/README.md | 4 ++-- .../lib/opentelemetry-auto-instrumentation.rb | 8 ++++---- .../opentelemetry-auto-instrumentation.gemspec | 6 +++--- .../test/opentelemetry-auto-instrumentation_test.rb | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/opentelemetry-auto-instrumentation/README.md b/opentelemetry-auto-instrumentation/README.md index 365ffe14af..f46cf23b8b 100644 --- a/opentelemetry-auto-instrumentation/README.md +++ b/opentelemetry-auto-instrumentation/README.md @@ -1,6 +1,6 @@ # Opentelemetry Auto Instrumentation -The `opentelemetry-auto-instrumentation` gem provides an easy way to load and initialize opentelemetry-ruby for zero-code instrumentation. +The `opentelemetry-auto-instrumentation` gem provides an easy way to load and initialize opentelemetry-ruby for auto instrumentation. ## What is OpenTelemetry? @@ -131,7 +131,7 @@ These environment variables are not standard OpenTelemetry environment variables | `OTEL_RUBY_OPERATOR` | Set to `true` to set the binding path for the OpenTelemetry Operator. | `/otel-auto-instrumentation-ruby` | N/A | | `OTEL_RUBY_RESOURCE_DETECTORS` | Determine what kind of resource detector is needed. Currently supports `container`, `azure`, and `google_cloud_platform`. Use commas to separate multiple detectors. | nil | `container,azure` | | `OTEL_RUBY_ENABLED_INSTRUMENTATIONS` | Shortcut used when you only want to instrument one or a couple of particular libraries. | nil | `redis,active_record` | -| `OTEL_RUBY_ZERO_CODE_DEBUG` | Set to `true` if want to see some debug information. This to avoid preload logger gem | nil | N/A | +| `OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG` | Set to `true` if want to see some debug information. This to avoid preload logger gem | nil | N/A | | `OTEL_RUBY_UNLOAD_LIBRARY` | Use OTEL_RUBY_UNLOAD_LIBRARY to avoid certain gem preload (esp. google protobuf) | nil | N/A | ## How can I get involved? diff --git a/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb b/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb index 16d22ca91c..40f8def965 100644 --- a/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb +++ b/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb @@ -82,7 +82,7 @@ def determine_enabled_instrumentation def require_otel lib = File.expand_path('..', __dir__) - $LOAD_PATH.reject! { |path| path.include?('zero-code-instrumentation') } + $LOAD_PATH.reject! { |path| path.include?('opentelemetry-auto-instrumentation') } $LOAD_PATH.unshift(lib) begin @@ -115,7 +115,7 @@ def require_otel # /otel-auto-instrumentation-ruby is set in opentelemetry-operator ruby.go operator_gem_path = ENV['OTEL_RUBY_OPERATOR'].nil? || ENV['OTEL_RUBY_OPERATOR'] == 'true' ? '/otel-auto-instrumentation-ruby' : nil additional_gem_path = operator_gem_path || ENV['OTEL_RUBY_ADDITIONAL_GEM_PATH'] || Gem.dir -puts "Loading the additional gem path from #{additional_gem_path}" if ENV['OTEL_RUBY_ZERO_CODE_DEBUG'] == 'true' +puts "Loading the additional gem path from #{additional_gem_path}" if ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] == 'true' # use OTEL_RUBY_UNLOAD_LIBRARY to avoid certain gem preload (esp. google protobuf) # e.g export OTEL_RUBY_UNLOAD_LIBRARY=google-protobuf;googleapis-common-protos-types @@ -126,13 +126,13 @@ def require_otel (file_path.include?('opentelemetry') || file_path.include?('google')) && !unload_libraries.include?(gem_name) end -puts "Loaded Library File Paths #{loaded_library_file_path.join(',')}" if ENV['ZERO_CODE_DEBUG'] == 'true' +puts "Loaded Library File Paths #{loaded_library_file_path.join(',')}" if ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] == 'true' loaded_library_file_path.each do |file_path| $LOAD_PATH.unshift("#{file_path}/lib") end -puts "$LOAD_PATH after unshift: #{$LOAD_PATH.join(',')}" if ENV['OTEL_RUBY_ZERO_CODE_DEBUG'] == 'true' +puts "$LOAD_PATH after unshift: #{$LOAD_PATH.join(',')}" if ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] == 'true' require 'opentelemetry-sdk' require 'opentelemetry-instrumentation-all' diff --git a/opentelemetry-auto-instrumentation/opentelemetry-auto-instrumentation.gemspec b/opentelemetry-auto-instrumentation/opentelemetry-auto-instrumentation.gemspec index df8e5a22f6..418eae9673 100644 --- a/opentelemetry-auto-instrumentation/opentelemetry-auto-instrumentation.gemspec +++ b/opentelemetry-auto-instrumentation/opentelemetry-auto-instrumentation.gemspec @@ -14,8 +14,8 @@ Gem::Specification.new do |spec| spec.authors = ['OpenTelemetry Authors'] spec.email = ['cncf-opentelemetry-contributors@lists.cncf.io'] - spec.summary = 'Zero-code instrumentation for opentelemetry ruby' - spec.description = 'Zero-code instrumentation for opentelemetry ruby' + spec.summary = 'auto-instrumentation for opentelemetry ruby' + spec.description = 'auto-instrumentation for opentelemetry ruby' spec.homepage = 'https://github.com/open-telemetry/opentelemetry-ruby-contrib' spec.license = 'Apache-2.0' @@ -46,7 +46,7 @@ Gem::Specification.new do |spec| if spec.respond_to?(:metadata) spec.metadata['changelog_uri'] = "https://rubydoc.info/gems/#{spec.name}/#{spec.version}/file/CHANGELOG.md" - spec.metadata['source_code_uri'] = 'https://github.com/open-telemetry/opentelemetry-ruby-contrib/zero-code-instrumentation' + spec.metadata['source_code_uri'] = 'https://github.com/open-telemetry/opentelemetry-ruby-contrib/opentelemetry-auto-instrumentation' spec.metadata['bug_tracker_uri'] = 'https://github.com/open-telemetry/opentelemetry-ruby-contrib/issues' spec.metadata['documentation_uri'] = "https://rubydoc.info/gems/#{spec.name}/#{spec.version}" end diff --git a/opentelemetry-auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb b/opentelemetry-auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb index d7ff916488..06824b8381 100644 --- a/opentelemetry-auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb +++ b/opentelemetry-auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb @@ -6,7 +6,7 @@ require 'test_helper' -describe 'ZeroCodeInstrumentation' do +describe 'AutoInstrumentation' do before do OpenTelemetry::TestHelpers.reset_opentelemetry end From 1080edc4244db86dd1a4d04b33e8a23e91aeee1f Mon Sep 17 00:00:00 2001 From: Xuan <112967240+xuan-cao-swi@users.noreply.github.com> Date: Mon, 21 Jul 2025 14:12:05 -0400 Subject: [PATCH 21/68] Update opentelemetry-auto-instrumentation/README.md Co-authored-by: Kayla Reopelle <87386821+kaylareopelle@users.noreply.github.com> --- opentelemetry-auto-instrumentation/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-auto-instrumentation/README.md b/opentelemetry-auto-instrumentation/README.md index f46cf23b8b..714b648f91 100644 --- a/opentelemetry-auto-instrumentation/README.md +++ b/opentelemetry-auto-instrumentation/README.md @@ -130,7 +130,7 @@ These environment variables are not standard OpenTelemetry environment variables | `OTEL_RUBY_ADDITIONAL_GEM_PATH` | Intended to be used for the OpenTelemetry Operator environment if you install `opentelemetry-auto-instrumentation` to a customized path. | nil | N/A | | `OTEL_RUBY_OPERATOR` | Set to `true` to set the binding path for the OpenTelemetry Operator. | `/otel-auto-instrumentation-ruby` | N/A | | `OTEL_RUBY_RESOURCE_DETECTORS` | Determine what kind of resource detector is needed. Currently supports `container`, `azure`, and `google_cloud_platform`. Use commas to separate multiple detectors. | nil | `container,azure` | -| `OTEL_RUBY_ENABLED_INSTRUMENTATIONS` | Shortcut used when you only want to instrument one or a couple of particular libraries. | nil | `redis,active_record` | +| `OTEL_RUBY_ENABLED_INSTRUMENTATIONS` | Configuration used when you only want to install instrumentation for specific libraries. | nil | `redis,active_record` | | `OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG` | Set to `true` if want to see some debug information. This to avoid preload logger gem | nil | N/A | | `OTEL_RUBY_UNLOAD_LIBRARY` | Use OTEL_RUBY_UNLOAD_LIBRARY to avoid certain gem preload (esp. google protobuf) | nil | N/A | From 74c73f72ae92fcbf6af656c8ec8c1a0d56be725d Mon Sep 17 00:00:00 2001 From: Xuan <112967240+xuan-cao-swi@users.noreply.github.com> Date: Mon, 21 Jul 2025 14:12:13 -0400 Subject: [PATCH 22/68] Update opentelemetry-auto-instrumentation/README.md Co-authored-by: Kayla Reopelle <87386821+kaylareopelle@users.noreply.github.com> --- opentelemetry-auto-instrumentation/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/opentelemetry-auto-instrumentation/README.md b/opentelemetry-auto-instrumentation/README.md index 714b648f91..33a5e875d1 100644 --- a/opentelemetry-auto-instrumentation/README.md +++ b/opentelemetry-auto-instrumentation/README.md @@ -35,6 +35,8 @@ opentelemetry-resource-detector-azure opentelemetry-resource-detector-container ``` +### Example installation strategies + Instrument your application: ```console From f024787ce303acea3acc7fe82f6fe84a7ad2538d Mon Sep 17 00:00:00 2001 From: Xuan <112967240+xuan-cao-swi@users.noreply.github.com> Date: Mon, 21 Jul 2025 14:12:28 -0400 Subject: [PATCH 23/68] Update .github/workflows/ci-contrib.yml Co-authored-by: Kayla Reopelle <87386821+kaylareopelle@users.noreply.github.com> --- .github/workflows/ci-contrib.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-contrib.yml b/.github/workflows/ci-contrib.yml index f5523c00a3..19571a009c 100644 --- a/.github/workflows/ci-contrib.yml +++ b/.github/workflows/ci-contrib.yml @@ -238,7 +238,7 @@ jobs: gem: "opentelemetry-sampler-${{ matrix.gem }}" ruby: "jruby-9.4.12.0" - zero-code-instrumentation: + auto-instrumentation: strategy: fail-fast: false matrix: From 395ce32aac82247cb53402140c722d639b8a653b Mon Sep 17 00:00:00 2001 From: Xuan <112967240+xuan-cao-swi@users.noreply.github.com> Date: Mon, 21 Jul 2025 14:12:37 -0400 Subject: [PATCH 24/68] Update .github/workflows/ci-contrib.yml Co-authored-by: Kayla Reopelle <87386821+kaylareopelle@users.noreply.github.com> --- .github/workflows/ci-contrib.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-contrib.yml b/.github/workflows/ci-contrib.yml index 19571a009c..94b7cdc8e5 100644 --- a/.github/workflows/ci-contrib.yml +++ b/.github/workflows/ci-contrib.yml @@ -243,7 +243,7 @@ jobs: fail-fast: false matrix: gem: - - zero-code-instrumentation + - auto-instrumentation os: - ubuntu-latest name: "${{ matrix.gem }} / ${{ matrix.os }}" From 5fe841952eb0a5112171ad292fc3302d2fcc142b Mon Sep 17 00:00:00 2001 From: Xuan <112967240+xuan-cao-swi@users.noreply.github.com> Date: Mon, 21 Jul 2025 14:12:45 -0400 Subject: [PATCH 25/68] Update opentelemetry-auto-instrumentation/README.md Co-authored-by: Kayla Reopelle <87386821+kaylareopelle@users.noreply.github.com> --- opentelemetry-auto-instrumentation/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-auto-instrumentation/README.md b/opentelemetry-auto-instrumentation/README.md index 33a5e875d1..715866a9c1 100644 --- a/opentelemetry-auto-instrumentation/README.md +++ b/opentelemetry-auto-instrumentation/README.md @@ -84,7 +84,7 @@ RUBYOPT="-r opentelemetry-auto-instrumentation" rails server Instrument Rails application with `bundle exec`: -Since installing the `opentelemetry-auto-instrumentation` gem through `gem install`, anything related to the OpenTelemetry gem won't be stored in Bundler's gem path. Therefore, users need to add an additional gem path that contains these gems prior to initialization. +Since the `opentelemetry-auto-instrumentation` gem should be installed through `gem install`, anything related to the OpenTelemetry gem won't be stored in Bundler's gem path. Therefore, users need to add an additional gem path that contains these gems prior to initialization. ```console RUBYOPT="-r {PUT YOUR GEM PATH}/gems/opentelemetry-auto-instrumentation-0.1.0/lib/opentelemetry-auto-instrumentation" bundle exec rails server From 56d7ba59d584292a4f43d509b111376c41000771 Mon Sep 17 00:00:00 2001 From: Xuan <112967240+xuan-cao-swi@users.noreply.github.com> Date: Mon, 21 Jul 2025 14:12:54 -0400 Subject: [PATCH 26/68] Update opentelemetry-auto-instrumentation/README.md Co-authored-by: Kayla Reopelle <87386821+kaylareopelle@users.noreply.github.com> --- opentelemetry-auto-instrumentation/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-auto-instrumentation/README.md b/opentelemetry-auto-instrumentation/README.md index 715866a9c1..8b5a6c4a1c 100644 --- a/opentelemetry-auto-instrumentation/README.md +++ b/opentelemetry-auto-instrumentation/README.md @@ -133,7 +133,7 @@ These environment variables are not standard OpenTelemetry environment variables | `OTEL_RUBY_OPERATOR` | Set to `true` to set the binding path for the OpenTelemetry Operator. | `/otel-auto-instrumentation-ruby` | N/A | | `OTEL_RUBY_RESOURCE_DETECTORS` | Determine what kind of resource detector is needed. Currently supports `container`, `azure`, and `google_cloud_platform`. Use commas to separate multiple detectors. | nil | `container,azure` | | `OTEL_RUBY_ENABLED_INSTRUMENTATIONS` | Configuration used when you only want to install instrumentation for specific libraries. | nil | `redis,active_record` | -| `OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG` | Set to `true` if want to see some debug information. This to avoid preload logger gem | nil | N/A | +| `OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG` | Set to `true` if want to see some debug information. This to avoid preloading the logger gem | nil | N/A | | `OTEL_RUBY_UNLOAD_LIBRARY` | Use OTEL_RUBY_UNLOAD_LIBRARY to avoid certain gem preload (esp. google protobuf) | nil | N/A | ## How can I get involved? From cd42f0b67fbf02c8bee3442bc41a371bb8fedde7 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Tue, 22 Jul 2025 15:52:15 -0400 Subject: [PATCH 27/68] update --- .github/workflows/ci-contrib.yml | 5 +++++ .../base/lib/opentelemetry/instrumentation/base.rb | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-contrib.yml b/.github/workflows/ci-contrib.yml index a8b7ccf57a..85d9018a40 100644 --- a/.github/workflows/ci-contrib.yml +++ b/.github/workflows/ci-contrib.yml @@ -253,6 +253,11 @@ jobs: runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 + - name: "Test Ruby 3.4" + uses: ./.github/actions/test_gem + with: + gem: "${{ matrix.gem }}" + ruby: "3.4" - name: "Test Ruby 3.3" uses: ./.github/actions/test_gem with: diff --git a/instrumentation/base/lib/opentelemetry/instrumentation/base.rb b/instrumentation/base/lib/opentelemetry/instrumentation/base.rb index 7a13fe3f64..ddf8289551 100644 --- a/instrumentation/base/lib/opentelemetry/instrumentation/base.rb +++ b/instrumentation/base/lib/opentelemetry/instrumentation/base.rb @@ -214,7 +214,6 @@ def initialize(name, version, install_blk, present_blk, # # @param [Hash] config The config for this instrumentation def install(config = {}) - puts "name: #{@name}" return true if installed? @config = config_options(config) From c03917fdb291e50d86065bfcf3f4ff02e39b5998 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Tue, 22 Jul 2025 16:56:18 -0400 Subject: [PATCH 28/68] update workflow file --- .github/workflows/ci-contrib.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-contrib.yml b/.github/workflows/ci-contrib.yml index 85d9018a40..42cd3b3ede 100644 --- a/.github/workflows/ci-contrib.yml +++ b/.github/workflows/ci-contrib.yml @@ -277,7 +277,7 @@ jobs: rubocop: true build: true - name: "Test JRuby" - if: "${{ matrix.os == 'ubuntu-latest'" + if: "${{ matrix.os == 'ubuntu-latest' }}" uses: ./.github/actions/test_gem with: gem: "${{ matrix.gem }}" From 501918bf1da46e89f4dfb9cfeb08e6208a328f21 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Tue, 22 Jul 2025 17:13:51 -0400 Subject: [PATCH 29/68] update ci --- .github/workflows/ci-contrib.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-contrib.yml b/.github/workflows/ci-contrib.yml index 42cd3b3ede..3082a903b4 100644 --- a/.github/workflows/ci-contrib.yml +++ b/.github/workflows/ci-contrib.yml @@ -246,7 +246,7 @@ jobs: fail-fast: false matrix: gem: - - auto-instrumentation + - opentelemetry-auto-instrumentation os: - ubuntu-latest name: "${{ matrix.gem }} / ${{ matrix.os }}" From 5bd69ee80f42e5d18d8e5efd3b50a14059d59bee Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Wed, 23 Jul 2025 11:45:05 -0400 Subject: [PATCH 30/68] use custom ci for auto-instrumentation --- .github/workflows/ci-contrib.yml | 95 ++++++++++++++++++++++---------- 1 file changed, 65 insertions(+), 30 deletions(-) diff --git a/.github/workflows/ci-contrib.yml b/.github/workflows/ci-contrib.yml index 3082a903b4..4fffad60ec 100644 --- a/.github/workflows/ci-contrib.yml +++ b/.github/workflows/ci-contrib.yml @@ -242,43 +242,78 @@ jobs: ruby: "jruby-9.4.12.0" auto-instrumentation: - strategy: - fail-fast: false - matrix: - gem: - - opentelemetry-auto-instrumentation - os: - - ubuntu-latest - name: "${{ matrix.gem }} / ${{ matrix.os }}" - runs-on: ${{ matrix.os }} + name: "auto-instrumentation / ubuntu-latest" + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: "Test Ruby 3.4" - uses: ./.github/actions/test_gem + uses: ruby/setup-ruby@v1.247.0 with: - gem: "${{ matrix.gem }}" - ruby: "3.4" + ruby-version: "3.4" + - name: Test Gem + shell: bash + run: | + bundle install --quiet --jobs=3 --retry=4 + bundle exec rake test + rm -f Gemfile.lock + working-directory: './opentelemetry-auto-instrumentation' - name: "Test Ruby 3.3" - uses: ./.github/actions/test_gem + uses: ruby/setup-ruby@v1.247.0 with: - gem: "${{ matrix.gem }}" - ruby: "3.3" + ruby-version: "3.3" + - name: Test Gem + shell: bash + run: | + bundle install --quiet --jobs=3 --retry=4 + bundle exec rake test + rm -f Gemfile.lock + working-directory: './opentelemetry-auto-instrumentation' - name: "Test Ruby 3.2" - uses: ./.github/actions/test_gem + uses: ruby/setup-ruby@v1.247.0 with: - gem: "${{ matrix.gem }}" - ruby: "3.2" + ruby-version: "3.2" + - name: Test Gem + shell: bash + run: | + bundle install --quiet --jobs=3 --retry=4 + bundle exec rake test + rm -f Gemfile.lock + working-directory: './opentelemetry-auto-instrumentation' - name: "Test Ruby 3.1" - uses: ./.github/actions/test_gem + uses: ruby/setup-ruby@v1.247.0 with: - gem: "${{ matrix.gem }}" - ruby: "3.1" - yard: true - rubocop: true - build: true - - name: "Test JRuby" - if: "${{ matrix.os == 'ubuntu-latest' }}" - uses: ./.github/actions/test_gem - with: - gem: "${{ matrix.gem }}" - ruby: "jruby-9.4.12.0" + ruby-version: "3.1" + - name: Test Gem + shell: bash + run: | + bundle install --quiet --jobs=3 --retry=4 + bundle exec rake test + rm -f Gemfile.lock + working-directory: './opentelemetry-auto-instrumentation' + + - name: YARD + shell: bash + run: | + # 📄 Yard Docs 📄 + bundle exec rake yard + working-directory: './opentelemetry-auto-instrumentation' + + - name: Rubocop + shell: bash + run: | + # 🤖 Rubocop 🤖 + bundle exec rake rubocop + working-directory: './opentelemetry-auto-instrumentation' + + - name: Coverage + shell: bash + run: | + bundle exec ruby -e 'require "simplecov"; SimpleCov.minimum_coverage(85)' + working-directory: './opentelemetry-auto-instrumentation' + + - name: Build Gem + shell: bash + run: | + # 📦 Build Gem 📦 + gem build opentelemetry-auto-instrumentation.gemspec + working-directory: './opentelemetry-auto-instrumentation' From e625a8094453765bca36d561c9098475b884c531 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Tue, 29 Jul 2025 14:46:46 -0400 Subject: [PATCH 31/68] revision and fix ci --- .github/workflows/ci-contrib.yml | 92 +++----- opentelemetry-auto-instrumentation/Gemfile | 30 +-- opentelemetry-auto-instrumentation/README.md | 4 +- .../example/Gemfile | 1 - .../lib/opentelemetry-auto-instrumentation.rb | 199 ++++++++++-------- ...opentelemetry-auto-instrumentation.gemspec | 13 +- ...opentelemetry-auto-instrumentation_test.rb | 47 +++-- .../test/test_helper.rb | 6 +- 8 files changed, 189 insertions(+), 203 deletions(-) diff --git a/.github/workflows/ci-contrib.yml b/.github/workflows/ci-contrib.yml index 4fffad60ec..2755535b21 100644 --- a/.github/workflows/ci-contrib.yml +++ b/.github/workflows/ci-contrib.yml @@ -242,78 +242,38 @@ jobs: ruby: "jruby-9.4.12.0" auto-instrumentation: - name: "auto-instrumentation / ubuntu-latest" - runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + gem: + - opentelemetry-auto-instrumentation + os: + - ubuntu-latest + name: "${{ matrix.gem }} / ${{ matrix.os }}" + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - name: "Test Ruby 3.4" - uses: ruby/setup-ruby@v1.247.0 + uses: ./.github/actions/test_gem with: - ruby-version: "3.4" - - name: Test Gem - shell: bash - run: | - bundle install --quiet --jobs=3 --retry=4 - bundle exec rake test - rm -f Gemfile.lock - working-directory: './opentelemetry-auto-instrumentation' + gem: "${{ matrix.gem }}" + ruby: "3.4" - name: "Test Ruby 3.3" - uses: ruby/setup-ruby@v1.247.0 + uses: ./.github/actions/test_gem with: - ruby-version: "3.3" - - name: Test Gem - shell: bash - run: | - bundle install --quiet --jobs=3 --retry=4 - bundle exec rake test - rm -f Gemfile.lock - working-directory: './opentelemetry-auto-instrumentation' + gem: "${{ matrix.gem }}" + ruby: "3.3" - name: "Test Ruby 3.2" - uses: ruby/setup-ruby@v1.247.0 + uses: ./.github/actions/test_gem with: - ruby-version: "3.2" - - name: Test Gem - shell: bash - run: | - bundle install --quiet --jobs=3 --retry=4 - bundle exec rake test - rm -f Gemfile.lock - working-directory: './opentelemetry-auto-instrumentation' - - name: "Test Ruby 3.1" - uses: ruby/setup-ruby@v1.247.0 + gem: "${{ matrix.gem }}" + ruby: "3.2" + yard: true + rubocop: true + build: true + - name: "Test JRuby" + if: "${{ matrix.os == 'ubuntu-latest' }}" + uses: ./.github/actions/test_gem with: - ruby-version: "3.1" - - name: Test Gem - shell: bash - run: | - bundle install --quiet --jobs=3 --retry=4 - bundle exec rake test - rm -f Gemfile.lock - working-directory: './opentelemetry-auto-instrumentation' - - - name: YARD - shell: bash - run: | - # 📄 Yard Docs 📄 - bundle exec rake yard - working-directory: './opentelemetry-auto-instrumentation' - - - name: Rubocop - shell: bash - run: | - # 🤖 Rubocop 🤖 - bundle exec rake rubocop - working-directory: './opentelemetry-auto-instrumentation' - - - name: Coverage - shell: bash - run: | - bundle exec ruby -e 'require "simplecov"; SimpleCov.minimum_coverage(85)' - working-directory: './opentelemetry-auto-instrumentation' - - - name: Build Gem - shell: bash - run: | - # 📦 Build Gem 📦 - gem build opentelemetry-auto-instrumentation.gemspec - working-directory: './opentelemetry-auto-instrumentation' + gem: "${{ matrix.gem }}" + ruby: "jruby-9.4.12.0" diff --git a/opentelemetry-auto-instrumentation/Gemfile b/opentelemetry-auto-instrumentation/Gemfile index 793db3cb15..2e60a70be6 100644 --- a/opentelemetry-auto-instrumentation/Gemfile +++ b/opentelemetry-auto-instrumentation/Gemfile @@ -6,18 +6,20 @@ source 'https://rubygems.org' -# avoid use gemspec to avoid circular load +gemspec -gem 'opentelemetry-sdk' -gem 'opentelemetry-instrumentation-all' -gem 'opentelemetry-exporter-otlp' -gem 'opentelemetry-test-helpers' -gem 'opentelemetry-resource-detector-container', path: '../resources/container' - -gem 'bundler' -gem 'minitest' -gem 'rake' -gem 'rubocop' -gem 'rubocop-performance' -gem 'simplecov' -gem 'yard' +group :test do + gem 'bundler' + gem 'minitest' + gem 'rake' + gem 'rubocop' + gem 'rubocop-performance' + gem 'simplecov' + gem 'yard' + gem 'opentelemetry-test-helpers' + if RUBY_VERSION >= '3.4' + gem 'base64' + gem 'bigdecimal' + gem 'mutex_m' + end +end \ No newline at end of file diff --git a/opentelemetry-auto-instrumentation/README.md b/opentelemetry-auto-instrumentation/README.md index 8b5a6c4a1c..52cc13b6e2 100644 --- a/opentelemetry-auto-instrumentation/README.md +++ b/opentelemetry-auto-instrumentation/README.md @@ -117,9 +117,7 @@ In example folder, executing the following commands should result in trace outpu gem install opentelemetry-auto-instrumentation bundle install -export REQUIRE_BUNDLER=true -export OTEL_TRACES_EXPORTER=console -RUBYOPT="-r opentelemetry-auto-instrumentation" ruby app.rb +OTEL_RUBY_REQUIRE_BUNDLER=true OTEL_TRACES_EXPORTER=console RUBYOPT="-r opentelemetry-auto-instrumentation" OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG=false OTEL_RUBY_OPERATOR=false ruby app.rb ``` ## Configuration diff --git a/opentelemetry-auto-instrumentation/example/Gemfile b/opentelemetry-auto-instrumentation/example/Gemfile index a8b0038780..c9721cb670 100644 --- a/opentelemetry-auto-instrumentation/example/Gemfile +++ b/opentelemetry-auto-instrumentation/example/Gemfile @@ -1,3 +1,2 @@ source 'https://rubygems.org' -gem 'net-http' diff --git a/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb b/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb index 40f8def965..61116b7cc5 100644 --- a/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb +++ b/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb @@ -6,107 +6,123 @@ # OTelBundlerPatch module OTelBundlerPatch - # ref: https://github.com/newrelic/newrelic-ruby-agent/blob/dev/lib/boot/strap.rb - def require(*_groups) - super - require_otel - end + # Nested module to handle OpenTelemetry initialization + module Initializer + # this is used for case when user particularly want to enable single instrumentation + unless defined?(OTEL_INSTRUMENTATION_MAP) + OTEL_INSTRUMENTATION_MAP = { + 'gruf' => 'OpenTelemetry::Instrumentation::Gruf', + 'trilogy' => 'OpenTelemetry::Instrumentation::Trilogy', + 'active_support' => 'OpenTelemetry::Instrumentation::ActiveSupport', + 'action_pack' => 'OpenTelemetry::Instrumentation::ActionPack', + 'active_job' => 'OpenTelemetry::Instrumentation::ActiveJob', + 'active_record' => 'OpenTelemetry::Instrumentation::ActiveRecord', + 'action_view' => 'OpenTelemetry::Instrumentation::ActionView', + 'action_mailer' => 'OpenTelemetry::Instrumentation::ActionMailer', + 'aws_sdk' => 'OpenTelemetry::Instrumentation::AwsSdk', + 'aws_lambda' => 'OpenTelemetry::Instrumentation::AwsLambda', + 'bunny' => 'OpenTelemetry::Instrumentation::Bunny', + 'lmdb' => 'OpenTelemetry::Instrumentation::LMDB', + 'http' => 'OpenTelemetry::Instrumentation::HTTP', + 'koala' => 'OpenTelemetry::Instrumentation::Koala', + 'active_model_serializers' => 'OpenTelemetry::Instrumentation::ActiveModelSerializers', + 'concurrent_ruby' => 'OpenTelemetry::Instrumentation::ConcurrentRuby', + 'dalli' => 'OpenTelemetry::Instrumentation::Dalli', + 'delayed_job' => 'OpenTelemetry::Instrumentation::DelayedJob', + 'ethon' => 'OpenTelemetry::Instrumentation::Ethon', + 'excon' => 'OpenTelemetry::Instrumentation::Excon', + 'faraday' => 'OpenTelemetry::Instrumentation::Faraday', + 'grape' => 'OpenTelemetry::Instrumentation::Grape', + 'graphql' => 'OpenTelemetry::Instrumentation::GraphQL', + 'http_client' => 'OpenTelemetry::Instrumentation::HttpClient', + 'mongo' => 'OpenTelemetry::Instrumentation::Mongo', + 'mysql2' => 'OpenTelemetry::Instrumentation::Mysql2', + 'net_http' => 'OpenTelemetry::Instrumentation::Net::HTTP', + 'pg' => 'OpenTelemetry::Instrumentation::PG', + 'que' => 'OpenTelemetry::Instrumentation::Que', + 'racecar' => 'OpenTelemetry::Instrumentation::Racecar', + 'rack' => 'OpenTelemetry::Instrumentation::Rack', + 'rails' => 'OpenTelemetry::Instrumentation::Rails', + 'rake' => 'OpenTelemetry::Instrumentation::Rake', + 'rdkafka' => 'OpenTelemetry::Instrumentation::Rdkafka', + 'redis' => 'OpenTelemetry::Instrumentation::Redis', + 'restclient' => 'OpenTelemetry::Instrumentation::RestClient', + 'resque' => 'OpenTelemetry::Instrumentation::Resque', + 'ruby_kafka' => 'OpenTelemetry::Instrumentation::RubyKafka', + 'sidekiq' => 'OpenTelemetry::Instrumentation::Sidekiq', + 'sinatra' => 'OpenTelemetry::Instrumentation::Sinatra' + }.freeze + end + + class << self + def detect_resource_from_env + env = ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s + additional_resource = ::OpenTelemetry::SDK::Resources::Resource.create({}) + + env.split(',').each do |detector| + case detector + when 'container' + additional_resource = additional_resource.merge(::OpenTelemetry::Resource::Detector::Container.detect) if defined? ::OpenTelemetry::Resource::Detector::Container + when 'google_cloud_platform' + additional_resource = additional_resource.merge(::OpenTelemetry::Resource::Detector::GoogleCloudPlatform.detect) if defined? ::OpenTelemetry::Resource::Detector::GoogleCloudPlatform + when 'azure' + additional_resource = additional_resource.merge(::OpenTelemetry::Resource::Detector::Azure.detect) if defined? ::OpenTelemetry::Resource::Detector::Azure + when 'aws' + additional_resource = additional_resource.merge(::OpenTelemetry::Resource::Detector::AWS.detect([:ec2, :ecs, :eks, :lambda])) if defined? ::OpenTelemetry::Resource::Detector::AWS + end + end - # this is used for case when user particularly want to enable single instrumentation - OTEL_INSTRUMENTATION_MAP = { - 'gruf' => 'OpenTelemetry::Instrumentation::Gruf', - 'trilogy' => 'OpenTelemetry::Instrumentation::Trilogy', - 'active_support' => 'OpenTelemetry::Instrumentation::ActiveSupport', - 'action_pack' => 'OpenTelemetry::Instrumentation::ActionPack', - 'active_job' => 'OpenTelemetry::Instrumentation::ActiveJob', - 'active_record' => 'OpenTelemetry::Instrumentation::ActiveRecord', - 'action_view' => 'OpenTelemetry::Instrumentation::ActionView', - 'action_mailer' => 'OpenTelemetry::Instrumentation::ActionMailer', - 'aws_sdk' => 'OpenTelemetry::Instrumentation::AwsSdk', - 'aws_lambda' => 'OpenTelemetry::Instrumentation::AwsLambda', - 'bunny' => 'OpenTelemetry::Instrumentation::Bunny', - 'lmdb' => 'OpenTelemetry::Instrumentation::LMDB', - 'http' => 'OpenTelemetry::Instrumentation::HTTP', - 'koala' => 'OpenTelemetry::Instrumentation::Koala', - 'active_model_serializers' => 'OpenTelemetry::Instrumentation::ActiveModelSerializers', - 'concurrent_ruby' => 'OpenTelemetry::Instrumentation::ConcurrentRuby', - 'dalli' => 'OpenTelemetry::Instrumentation::Dalli', - 'delayed_job' => 'OpenTelemetry::Instrumentation::DelayedJob', - 'ethon' => 'OpenTelemetry::Instrumentation::Ethon', - 'excon' => 'OpenTelemetry::Instrumentation::Excon', - 'faraday' => 'OpenTelemetry::Instrumentation::Faraday', - 'grape' => 'OpenTelemetry::Instrumentation::Grape', - 'graphql' => 'OpenTelemetry::Instrumentation::GraphQL', - 'http_client' => 'OpenTelemetry::Instrumentation::HttpClient', - 'mongo' => 'OpenTelemetry::Instrumentation::Mongo', - 'mysql2' => 'OpenTelemetry::Instrumentation::Mysql2', - 'net_http' => 'OpenTelemetry::Instrumentation::Net::HTTP', - 'pg' => 'OpenTelemetry::Instrumentation::PG', - 'que' => 'OpenTelemetry::Instrumentation::Que', - 'racecar' => 'OpenTelemetry::Instrumentation::Racecar', - 'rack' => 'OpenTelemetry::Instrumentation::Rack', - 'rails' => 'OpenTelemetry::Instrumentation::Rails', - 'rake' => 'OpenTelemetry::Instrumentation::Rake', - 'rdkafka' => 'OpenTelemetry::Instrumentation::Rdkafka', - 'redis' => 'OpenTelemetry::Instrumentation::Redis', - 'restclient' => 'OpenTelemetry::Instrumentation::RestClient', - 'resque' => 'OpenTelemetry::Instrumentation::Resque', - 'ruby_kafka' => 'OpenTelemetry::Instrumentation::RubyKafka', - 'sidekiq' => 'OpenTelemetry::Instrumentation::Sidekiq', - 'sinatra' => 'OpenTelemetry::Instrumentation::Sinatra' - }.freeze - - def detect_resource_from_env - env = ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s - additional_resource = ::OpenTelemetry::SDK::Resources::Resource.create({}) - - env.split(',').each do |detector| - case detector - when 'container' - additional_resource = additional_resource.merge(::OpenTelemetry::Resource::Detector::Container.detect) if defined? ::OpenTelemetry::Resource::Detector::Container - when 'google_cloud_platform' - additional_resource = additional_resource.merge(::OpenTelemetry::Resource::Detector::GoogleCloudPlatform.detect) if defined? ::OpenTelemetry::Resource::Detector::GoogleCloudPlatform - when 'azure' - additional_resource = additional_resource.merge(::OpenTelemetry::Resource::Detector::Azure.detect) if defined? ::OpenTelemetry::Resource::Detector::Azure + additional_resource end - end - additional_resource - end + def determine_enabled_instrumentation + env = ENV['OTEL_RUBY_ENABLED_INSTRUMENTATIONS'].to_s - def determine_enabled_instrumentation - env = ENV['OTEL_RUBY_ENABLED_INSTRUMENTATIONS'].to_s + env.split(',').map { |instrumentation| OTEL_INSTRUMENTATION_MAP[instrumentation] } + end - env.split(',').map { |instrumentation| OTEL_INSTRUMENTATION_MAP[instrumentation] } - end + def setup_load_path + lib = File.expand_path('..', __dir__) + $LOAD_PATH.reject! { |path| path.include?('opentelemetry-auto-instrumentation') } + $LOAD_PATH.unshift(lib) + end - def require_otel - lib = File.expand_path('..', __dir__) - $LOAD_PATH.reject! { |path| path.include?('opentelemetry-auto-instrumentation') } - $LOAD_PATH.unshift(lib) - - begin - required_instrumentation = determine_enabled_instrumentation - - OpenTelemetry::SDK.configure do |c| - c.resource = detect_resource_from_env - if required_instrumentation.empty? - c.use_all # enables all instrumentation! - else - required_instrumentation.each do |instrumentation| - c.use instrumentation + def initialize_opentelemetry + setup_load_path + + begin + required_instrumentation = determine_enabled_instrumentation + + OpenTelemetry::SDK.configure do |c| + c.resource = detect_resource_from_env + if required_instrumentation.empty? + c.use_all # enables all instrumentation! + else + required_instrumentation.each do |instrumentation| + c.use instrumentation + end + end end + OpenTelemetry.logger.info { 'Auto-instrumentation initialized' } + rescue StandardError => e + $stderr.puts "Auto-instrumentation failed to initialize. Error: #{e.message}" end end - OpenTelemetry.logger.info { 'Auto-instrumentation initialized' } - rescue StandardError => e - puts "Auto-instrumentation failed to initialize. Error: #{e.message}" + end + end + + # ref: https://github.com/newrelic/newrelic-ruby-agent/blob/dev/lib/boot/strap.rb + unless method_defined?(:require) + def require(...) + super + Initializer.initialize_opentelemetry end end end require 'bundler' +aws = ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s.include?('aws') container = ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s.include?('container') google_cloud_platform = ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s.include?('google_cloud_platform') azure = ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s.include?('azure') @@ -115,7 +131,7 @@ def require_otel # /otel-auto-instrumentation-ruby is set in opentelemetry-operator ruby.go operator_gem_path = ENV['OTEL_RUBY_OPERATOR'].nil? || ENV['OTEL_RUBY_OPERATOR'] == 'true' ? '/otel-auto-instrumentation-ruby' : nil additional_gem_path = operator_gem_path || ENV['OTEL_RUBY_ADDITIONAL_GEM_PATH'] || Gem.dir -puts "Loading the additional gem path from #{additional_gem_path}" if ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] == 'true' +$stdout.puts "Loading the additional gem path from #{additional_gem_path}" if ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] == 'true' # use OTEL_RUBY_UNLOAD_LIBRARY to avoid certain gem preload (esp. google protobuf) # e.g export OTEL_RUBY_UNLOAD_LIBRARY=google-protobuf;googleapis-common-protos-types @@ -126,24 +142,23 @@ def require_otel (file_path.include?('opentelemetry') || file_path.include?('google')) && !unload_libraries.include?(gem_name) end -puts "Loaded Library File Paths #{loaded_library_file_path.join(',')}" if ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] == 'true' +$stdout.puts "Loaded Library File Paths #{loaded_library_file_path.join(',')}" if ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] == 'true' loaded_library_file_path.each do |file_path| $LOAD_PATH.unshift("#{file_path}/lib") end -puts "$LOAD_PATH after unshift: #{$LOAD_PATH.join(',')}" if ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] == 'true' +$stdout.puts "$LOAD_PATH after unshift: #{$LOAD_PATH.join(',')}" if ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] == 'true' require 'opentelemetry-sdk' require 'opentelemetry-instrumentation-all' -require 'opentelemetry-helpers-mysql' -require 'opentelemetry-helpers-sql-obfuscation' require 'opentelemetry-exporter-otlp' require 'opentelemetry-resource-detector-container' if container require 'opentelemetry-resource-detector-google_cloud_platform' if google_cloud_platform require 'opentelemetry-resource-detector-azure' if azure +require 'opentelemetry-resource-detector-aws' if aws Bundler::Runtime.prepend(OTelBundlerPatch) -Bundler.require if ENV['OTEL_RUBY_REQUIRE_BUNDLER'].to_s == 'true' +Bundler.require if ENV['OTEL_RUBY_REQUIRE_BUNDLER'] == 'true' diff --git a/opentelemetry-auto-instrumentation/opentelemetry-auto-instrumentation.gemspec b/opentelemetry-auto-instrumentation/opentelemetry-auto-instrumentation.gemspec index 418eae9673..ae66211d3f 100644 --- a/opentelemetry-auto-instrumentation/opentelemetry-auto-instrumentation.gemspec +++ b/opentelemetry-auto-instrumentation/opentelemetry-auto-instrumentation.gemspec @@ -24,7 +24,7 @@ Gem::Specification.new do |spec| ['LICENSE'] spec.require_paths = ['lib'] - spec.required_ruby_version = '>= 3.1' + spec.required_ruby_version = '>= 3.2' spec.add_dependency 'opentelemetry-api', '~> 1.4.0' spec.add_dependency 'opentelemetry-exporter-otlp', '~> 0.29.1' @@ -34,22 +34,13 @@ Gem::Specification.new do |spec| spec.add_dependency 'opentelemetry-resource-detector-azure', '~> 0.2.0' spec.add_dependency 'opentelemetry-resource-detector-container', '~> 0.2.0' spec.add_dependency 'opentelemetry-resource-detector-google_cloud_platform', '~> 0.2.0' + spec.add_dependency 'opentelemetry-resource-detector-aws', '~> 0.2.0' spec.add_dependency 'opentelemetry-sdk', '~> 1.6.0' - spec.add_development_dependency 'bundler', '~> 2.4' - spec.add_development_dependency 'minitest', '~> 5.0' - spec.add_development_dependency 'rake', '~> 13.0' - spec.add_development_dependency 'rubocop', '~> 1.69.1' - spec.add_development_dependency 'rubocop-performance', '~> 1.23.0' - spec.add_development_dependency 'simplecov', '~> 0.17' - spec.add_development_dependency 'yard', '~> 0.9' - if spec.respond_to?(:metadata) spec.metadata['changelog_uri'] = "https://rubydoc.info/gems/#{spec.name}/#{spec.version}/file/CHANGELOG.md" spec.metadata['source_code_uri'] = 'https://github.com/open-telemetry/opentelemetry-ruby-contrib/opentelemetry-auto-instrumentation' spec.metadata['bug_tracker_uri'] = 'https://github.com/open-telemetry/opentelemetry-ruby-contrib/issues' spec.metadata['documentation_uri'] = "https://rubydoc.info/gems/#{spec.name}/#{spec.version}" end - - # spec.post_install_message = File.read(File.expand_path('../POST_INSTALL_MESSAGE', __dir__)) end diff --git a/opentelemetry-auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb b/opentelemetry-auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb index 06824b8381..3c866e2644 100644 --- a/opentelemetry-auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb +++ b/opentelemetry-auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb @@ -12,15 +12,32 @@ end after do - OTelBundlerPatch.send(:remove_const, :OTEL_INSTRUMENTATION_MAP) - OTelBundlerPatch.send(:undef_method, :detect_resource_from_env) - OTelBundlerPatch.send(:undef_method, :determine_enabled_instrumentation) - OTelBundlerPatch.send(:undef_method, :require_otel) - OTelBundlerPatch.send(:undef_method, :require) - OpenTelemetry::Instrumentation::Net::HTTP::Instrumentation.instance_variable_get(:@instance).instance_variable_set(:@installed, false) - OpenTelemetry::Instrumentation::Rake::Instrumentation.instance_variable_get(:@instance).instance_variable_set(:@installed, false) - + # Clean up constants and methods if they exist + if defined?(OTelBundlerPatch::Initializer::OTEL_INSTRUMENTATION_MAP) + OTelBundlerPatch::Initializer.send(:remove_const, :OTEL_INSTRUMENTATION_MAP) + end + + if defined?(OTelBundlerPatch::Initializer) + OTelBundlerPatch.send(:remove_const, :Initializer) + end + + %i[require].each do |method| + if OTelBundlerPatch.method_defined?(method) + OTelBundlerPatch.send(:undef_method, method) + end + end # Reset instrumentation installation state + [ + OpenTelemetry::Instrumentation::Net::HTTP::Instrumentation, + OpenTelemetry::Instrumentation::Rake::Instrumentation + ].each do |instrumentation| + instance = instrumentation.instance_variable_get(:@instance) + instance&.instance_variable_set(:@installed, false) + end + + # Clean up environment variables ENV['OTEL_RUBY_ENABLED_INSTRUMENTATIONS'] = nil + ENV['OTEL_RUBY_INSTRUMENTATION_NET_HTTP_ENABLED'] = nil + ENV['OTEL_RUBY_RESOURCE_DETECTORS'] = nil end it 'simple_load_test' do @@ -37,8 +54,10 @@ _(resource_attributes.key?('container.id')).must_equal false registry = OpenTelemetry.tracer_provider.instance_variable_get(:@registry) + instrumentation_names = registry.map { |entry| entry.first.name }.sort - _(registry.size).must_equal 2 + _(instrumentation_names).must_include 'OpenTelemetry::Instrumentation::Net::HTTP' + _(instrumentation_names).must_include 'OpenTelemetry::Instrumentation::Rake' end it 'simple_load_with_net_http_disabled' do @@ -48,9 +67,10 @@ Bundler.require registry = OpenTelemetry.tracer_provider.instance_variable_get(:@registry) + instrumentation_names = registry.map { |entry| entry.first.name } - _(registry.size).must_equal 1 - _(registry.first.first.name).must_equal 'OpenTelemetry::Instrumentation::Rake' + _(instrumentation_names).must_include 'OpenTelemetry::Instrumentation::Rake' + _(instrumentation_names).wont_include 'OpenTelemetry::Instrumentation::Net::HTTP' ENV['OTEL_RUBY_INSTRUMENTATION_NET_HTTP_ENABLED'] = nil end @@ -62,9 +82,10 @@ Bundler.require registry = OpenTelemetry.tracer_provider.instance_variable_get(:@registry) + instrumentation_names = registry.map { |entry| entry.first.name } - _(registry.size).must_equal 1 - _(registry.first.first.name).must_equal 'OpenTelemetry::Instrumentation::Net::HTTP' + _(instrumentation_names).must_include 'OpenTelemetry::Instrumentation::Net::HTTP' + _(instrumentation_names).wont_include 'OpenTelemetry::Instrumentation::Rake' end it 'simple_load_with_additional_resource' do diff --git a/opentelemetry-auto-instrumentation/test/test_helper.rb b/opentelemetry-auto-instrumentation/test/test_helper.rb index 9798aead57..0442e10a17 100644 --- a/opentelemetry-auto-instrumentation/test/test_helper.rb +++ b/opentelemetry-auto-instrumentation/test/test_helper.rb @@ -7,8 +7,8 @@ require 'rake' require 'minitest' require 'minitest/autorun' -require 'opentelemetry-test-helpers' -require 'opentelemetry/resource/detector' -require 'net/http' require 'opentelemetry-sdk' require 'opentelemetry-instrumentation-all' +require 'opentelemetry-test-helpers' +require 'opentelemetry/resource/detector' +require 'net/http' \ No newline at end of file From f2781dfe0db8faf19c4cc5213fdbce60a48d5079 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Tue, 29 Jul 2025 14:49:04 -0400 Subject: [PATCH 32/68] lint --- opentelemetry-auto-instrumentation/Gemfile | 4 ++-- .../lib/opentelemetry-auto-instrumentation.rb | 4 ++-- .../opentelemetry-auto-instrumentation.gemspec | 2 +- .../opentelemetry-auto-instrumentation_test.rb | 14 ++++---------- .../test/test_helper.rb | 2 +- 5 files changed, 10 insertions(+), 16 deletions(-) diff --git a/opentelemetry-auto-instrumentation/Gemfile b/opentelemetry-auto-instrumentation/Gemfile index 2e60a70be6..228d700664 100644 --- a/opentelemetry-auto-instrumentation/Gemfile +++ b/opentelemetry-auto-instrumentation/Gemfile @@ -9,7 +9,7 @@ source 'https://rubygems.org' gemspec group :test do - gem 'bundler' + gem 'bundler' gem 'minitest' gem 'rake' gem 'rubocop' @@ -22,4 +22,4 @@ group :test do gem 'bigdecimal' gem 'mutex_m' end -end \ No newline at end of file +end diff --git a/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb b/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb index 61116b7cc5..03971b93b6 100644 --- a/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb +++ b/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb @@ -68,7 +68,7 @@ def detect_resource_from_env when 'azure' additional_resource = additional_resource.merge(::OpenTelemetry::Resource::Detector::Azure.detect) if defined? ::OpenTelemetry::Resource::Detector::Azure when 'aws' - additional_resource = additional_resource.merge(::OpenTelemetry::Resource::Detector::AWS.detect([:ec2, :ecs, :eks, :lambda])) if defined? ::OpenTelemetry::Resource::Detector::AWS + additional_resource = additional_resource.merge(::OpenTelemetry::Resource::Detector::AWS.detect(%i[ec2 ecs eks lambda])) if defined? ::OpenTelemetry::Resource::Detector::AWS end end @@ -105,7 +105,7 @@ def initialize_opentelemetry end OpenTelemetry.logger.info { 'Auto-instrumentation initialized' } rescue StandardError => e - $stderr.puts "Auto-instrumentation failed to initialize. Error: #{e.message}" + warn "Auto-instrumentation failed to initialize. Error: #{e.message}" end end end diff --git a/opentelemetry-auto-instrumentation/opentelemetry-auto-instrumentation.gemspec b/opentelemetry-auto-instrumentation/opentelemetry-auto-instrumentation.gemspec index ae66211d3f..47ad6d51f6 100644 --- a/opentelemetry-auto-instrumentation/opentelemetry-auto-instrumentation.gemspec +++ b/opentelemetry-auto-instrumentation/opentelemetry-auto-instrumentation.gemspec @@ -31,10 +31,10 @@ Gem::Specification.new do |spec| spec.add_dependency 'opentelemetry-helpers-mysql', '~> 0.2.0' spec.add_dependency 'opentelemetry-helpers-sql-obfuscation', '~> 0.3.0' spec.add_dependency 'opentelemetry-instrumentation-all', '~> 0.72.0' + spec.add_dependency 'opentelemetry-resource-detector-aws', '~> 0.2.0' spec.add_dependency 'opentelemetry-resource-detector-azure', '~> 0.2.0' spec.add_dependency 'opentelemetry-resource-detector-container', '~> 0.2.0' spec.add_dependency 'opentelemetry-resource-detector-google_cloud_platform', '~> 0.2.0' - spec.add_dependency 'opentelemetry-resource-detector-aws', '~> 0.2.0' spec.add_dependency 'opentelemetry-sdk', '~> 1.6.0' if spec.respond_to?(:metadata) diff --git a/opentelemetry-auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb b/opentelemetry-auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb index 3c866e2644..d2fc57ab17 100644 --- a/opentelemetry-auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb +++ b/opentelemetry-auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb @@ -13,19 +13,13 @@ after do # Clean up constants and methods if they exist - if defined?(OTelBundlerPatch::Initializer::OTEL_INSTRUMENTATION_MAP) - OTelBundlerPatch::Initializer.send(:remove_const, :OTEL_INSTRUMENTATION_MAP) - end + OTelBundlerPatch::Initializer.send(:remove_const, :OTEL_INSTRUMENTATION_MAP) if defined?(OTelBundlerPatch::Initializer::OTEL_INSTRUMENTATION_MAP) - if defined?(OTelBundlerPatch::Initializer) - OTelBundlerPatch.send(:remove_const, :Initializer) - end + OTelBundlerPatch.send(:remove_const, :Initializer) if defined?(OTelBundlerPatch::Initializer) %i[require].each do |method| - if OTelBundlerPatch.method_defined?(method) - OTelBundlerPatch.send(:undef_method, method) - end - end # Reset instrumentation installation state + OTelBundlerPatch.send(:undef_method, method) if OTelBundlerPatch.method_defined?(method) + end [ OpenTelemetry::Instrumentation::Net::HTTP::Instrumentation, OpenTelemetry::Instrumentation::Rake::Instrumentation diff --git a/opentelemetry-auto-instrumentation/test/test_helper.rb b/opentelemetry-auto-instrumentation/test/test_helper.rb index 0442e10a17..0296024b2b 100644 --- a/opentelemetry-auto-instrumentation/test/test_helper.rb +++ b/opentelemetry-auto-instrumentation/test/test_helper.rb @@ -11,4 +11,4 @@ require 'opentelemetry-instrumentation-all' require 'opentelemetry-test-helpers' require 'opentelemetry/resource/detector' -require 'net/http' \ No newline at end of file +require 'net/http' From c478025918ec2f0d1c4e4121c4b01a04690b100f Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Tue, 29 Jul 2025 14:57:44 -0400 Subject: [PATCH 33/68] reverse ci to original --- .github/workflows/ci-contrib.yml | 83 +++++++++++++------ .../example/app.rb | 3 - 2 files changed, 56 insertions(+), 30 deletions(-) diff --git a/.github/workflows/ci-contrib.yml b/.github/workflows/ci-contrib.yml index 2755535b21..25bad16739 100644 --- a/.github/workflows/ci-contrib.yml +++ b/.github/workflows/ci-contrib.yml @@ -242,38 +242,67 @@ jobs: ruby: "jruby-9.4.12.0" auto-instrumentation: - strategy: - fail-fast: false - matrix: - gem: - - opentelemetry-auto-instrumentation - os: - - ubuntu-latest - name: "${{ matrix.gem }} / ${{ matrix.os }}" - runs-on: ${{ matrix.os }} + name: "auto-instrumentation / ubuntu-latest" + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: "Test Ruby 3.4" - uses: ./.github/actions/test_gem + uses: ruby/setup-ruby@v1.247.0 with: - gem: "${{ matrix.gem }}" - ruby: "3.4" + ruby-version: "3.4" + - name: Test Gem + shell: bash + run: | + bundle install --quiet --jobs=3 --retry=4 + bundle exec rake test + rm -f Gemfile.lock + working-directory: './opentelemetry-auto-instrumentation' - name: "Test Ruby 3.3" - uses: ./.github/actions/test_gem + uses: ruby/setup-ruby@v1.247.0 with: - gem: "${{ matrix.gem }}" - ruby: "3.3" + ruby-version: "3.3" + - name: Test Gem + shell: bash + run: | + bundle install --quiet --jobs=3 --retry=4 + bundle exec rake test + rm -f Gemfile.lock + working-directory: './opentelemetry-auto-instrumentation' - name: "Test Ruby 3.2" - uses: ./.github/actions/test_gem + uses: ruby/setup-ruby@v1.247.0 with: - gem: "${{ matrix.gem }}" - ruby: "3.2" - yard: true - rubocop: true - build: true - - name: "Test JRuby" - if: "${{ matrix.os == 'ubuntu-latest' }}" - uses: ./.github/actions/test_gem - with: - gem: "${{ matrix.gem }}" - ruby: "jruby-9.4.12.0" + ruby-version: "3.2" + - name: Test Gem + shell: bash + run: | + bundle install --quiet --jobs=3 --retry=4 + bundle exec rake test + rm -f Gemfile.lock + working-directory: './opentelemetry-auto-instrumentation' + + - name: YARD + shell: bash + run: | + # 📄 Yard Docs 📄 + bundle exec rake yard + working-directory: './opentelemetry-auto-instrumentation' + + - name: Rubocop + shell: bash + run: | + # 🤖 Rubocop 🤖 + bundle exec rake rubocop + working-directory: './opentelemetry-auto-instrumentation' + + - name: Coverage + shell: bash + run: | + bundle exec ruby -e 'require "simplecov"; SimpleCov.minimum_coverage(85)' + working-directory: './opentelemetry-auto-instrumentation' + + - name: Build Gem + shell: bash + run: | + # 📦 Build Gem 📦 + gem build opentelemetry-auto-instrumentation.gemspec + working-directory: './opentelemetry-auto-instrumentation' diff --git a/opentelemetry-auto-instrumentation/example/app.rb b/opentelemetry-auto-instrumentation/example/app.rb index f895369eda..2d1a4621f0 100644 --- a/opentelemetry-auto-instrumentation/example/app.rb +++ b/opentelemetry-auto-instrumentation/example/app.rb @@ -4,9 +4,6 @@ # # SPDX-License-Identifier: Apache-2.0 -require 'uri' -require 'net/http' - url = URI.parse("http://catfact.ninja/fact") req = Net::HTTP::Get.new(url.to_s) res = Net::HTTP.start(url.host, url.port) {|http| From a46253306cdf17b4dc1da477311b8b405985346b Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Tue, 29 Jul 2025 15:24:37 -0400 Subject: [PATCH 34/68] moved to a nested module --- .../lib/opentelemetry-auto-instrumentation.rb | 183 ++++++++---------- ...opentelemetry-auto-instrumentation_test.rb | 11 ++ 2 files changed, 97 insertions(+), 97 deletions(-) diff --git a/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb b/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb index 03971b93b6..374c38200e 100644 --- a/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb +++ b/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb @@ -6,117 +6,106 @@ # OTelBundlerPatch module OTelBundlerPatch - # Nested module to handle OpenTelemetry initialization - module Initializer - # this is used for case when user particularly want to enable single instrumentation - unless defined?(OTEL_INSTRUMENTATION_MAP) - OTEL_INSTRUMENTATION_MAP = { - 'gruf' => 'OpenTelemetry::Instrumentation::Gruf', - 'trilogy' => 'OpenTelemetry::Instrumentation::Trilogy', - 'active_support' => 'OpenTelemetry::Instrumentation::ActiveSupport', - 'action_pack' => 'OpenTelemetry::Instrumentation::ActionPack', - 'active_job' => 'OpenTelemetry::Instrumentation::ActiveJob', - 'active_record' => 'OpenTelemetry::Instrumentation::ActiveRecord', - 'action_view' => 'OpenTelemetry::Instrumentation::ActionView', - 'action_mailer' => 'OpenTelemetry::Instrumentation::ActionMailer', - 'aws_sdk' => 'OpenTelemetry::Instrumentation::AwsSdk', - 'aws_lambda' => 'OpenTelemetry::Instrumentation::AwsLambda', - 'bunny' => 'OpenTelemetry::Instrumentation::Bunny', - 'lmdb' => 'OpenTelemetry::Instrumentation::LMDB', - 'http' => 'OpenTelemetry::Instrumentation::HTTP', - 'koala' => 'OpenTelemetry::Instrumentation::Koala', - 'active_model_serializers' => 'OpenTelemetry::Instrumentation::ActiveModelSerializers', - 'concurrent_ruby' => 'OpenTelemetry::Instrumentation::ConcurrentRuby', - 'dalli' => 'OpenTelemetry::Instrumentation::Dalli', - 'delayed_job' => 'OpenTelemetry::Instrumentation::DelayedJob', - 'ethon' => 'OpenTelemetry::Instrumentation::Ethon', - 'excon' => 'OpenTelemetry::Instrumentation::Excon', - 'faraday' => 'OpenTelemetry::Instrumentation::Faraday', - 'grape' => 'OpenTelemetry::Instrumentation::Grape', - 'graphql' => 'OpenTelemetry::Instrumentation::GraphQL', - 'http_client' => 'OpenTelemetry::Instrumentation::HttpClient', - 'mongo' => 'OpenTelemetry::Instrumentation::Mongo', - 'mysql2' => 'OpenTelemetry::Instrumentation::Mysql2', - 'net_http' => 'OpenTelemetry::Instrumentation::Net::HTTP', - 'pg' => 'OpenTelemetry::Instrumentation::PG', - 'que' => 'OpenTelemetry::Instrumentation::Que', - 'racecar' => 'OpenTelemetry::Instrumentation::Racecar', - 'rack' => 'OpenTelemetry::Instrumentation::Rack', - 'rails' => 'OpenTelemetry::Instrumentation::Rails', - 'rake' => 'OpenTelemetry::Instrumentation::Rake', - 'rdkafka' => 'OpenTelemetry::Instrumentation::Rdkafka', - 'redis' => 'OpenTelemetry::Instrumentation::Redis', - 'restclient' => 'OpenTelemetry::Instrumentation::RestClient', - 'resque' => 'OpenTelemetry::Instrumentation::Resque', - 'ruby_kafka' => 'OpenTelemetry::Instrumentation::RubyKafka', - 'sidekiq' => 'OpenTelemetry::Instrumentation::Sidekiq', - 'sinatra' => 'OpenTelemetry::Instrumentation::Sinatra' - }.freeze - end - - class << self - def detect_resource_from_env - env = ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s - additional_resource = ::OpenTelemetry::SDK::Resources::Resource.create({}) - - env.split(',').each do |detector| - case detector - when 'container' - additional_resource = additional_resource.merge(::OpenTelemetry::Resource::Detector::Container.detect) if defined? ::OpenTelemetry::Resource::Detector::Container - when 'google_cloud_platform' - additional_resource = additional_resource.merge(::OpenTelemetry::Resource::Detector::GoogleCloudPlatform.detect) if defined? ::OpenTelemetry::Resource::Detector::GoogleCloudPlatform - when 'azure' - additional_resource = additional_resource.merge(::OpenTelemetry::Resource::Detector::Azure.detect) if defined? ::OpenTelemetry::Resource::Detector::Azure - when 'aws' - additional_resource = additional_resource.merge(::OpenTelemetry::Resource::Detector::AWS.detect(%i[ec2 ecs eks lambda])) if defined? ::OpenTelemetry::Resource::Detector::AWS - end + # Nested module to handle OpenTelemetry initialization logic + module OTelInitializer + OTEL_INSTRUMENTATION_MAP = { + 'gruf' => 'OpenTelemetry::Instrumentation::Gruf', + 'trilogy' => 'OpenTelemetry::Instrumentation::Trilogy', + 'active_support' => 'OpenTelemetry::Instrumentation::ActiveSupport', + 'action_pack' => 'OpenTelemetry::Instrumentation::ActionPack', + 'active_job' => 'OpenTelemetry::Instrumentation::ActiveJob', + 'active_record' => 'OpenTelemetry::Instrumentation::ActiveRecord', + 'action_view' => 'OpenTelemetry::Instrumentation::ActionView', + 'action_mailer' => 'OpenTelemetry::Instrumentation::ActionMailer', + 'aws_sdk' => 'OpenTelemetry::Instrumentation::AwsSdk', + 'aws_lambda' => 'OpenTelemetry::Instrumentation::AwsLambda', + 'bunny' => 'OpenTelemetry::Instrumentation::Bunny', + 'lmdb' => 'OpenTelemetry::Instrumentation::LMDB', + 'http' => 'OpenTelemetry::Instrumentation::HTTP', + 'koala' => 'OpenTelemetry::Instrumentation::Koala', + 'active_model_serializers' => 'OpenTelemetry::Instrumentation::ActiveModelSerializers', + 'concurrent_ruby' => 'OpenTelemetry::Instrumentation::ConcurrentRuby', + 'dalli' => 'OpenTelemetry::Instrumentation::Dalli', + 'delayed_job' => 'OpenTelemetry::Instrumentation::DelayedJob', + 'ethon' => 'OpenTelemetry::Instrumentation::Ethon', + 'excon' => 'OpenTelemetry::Instrumentation::Excon', + 'faraday' => 'OpenTelemetry::Instrumentation::Faraday', + 'grape' => 'OpenTelemetry::Instrumentation::Grape', + 'graphql' => 'OpenTelemetry::Instrumentation::GraphQL', + 'http_client' => 'OpenTelemetry::Instrumentation::HttpClient', + 'mongo' => 'OpenTelemetry::Instrumentation::Mongo', + 'mysql2' => 'OpenTelemetry::Instrumentation::Mysql2', + 'net_http' => 'OpenTelemetry::Instrumentation::Net::HTTP', + 'pg' => 'OpenTelemetry::Instrumentation::PG', + 'que' => 'OpenTelemetry::Instrumentation::Que', + 'racecar' => 'OpenTelemetry::Instrumentation::Racecar', + 'rack' => 'OpenTelemetry::Instrumentation::Rack', + 'rails' => 'OpenTelemetry::Instrumentation::Rails', + 'rake' => 'OpenTelemetry::Instrumentation::Rake', + 'rdkafka' => 'OpenTelemetry::Instrumentation::Rdkafka', + 'redis' => 'OpenTelemetry::Instrumentation::Redis', + 'restclient' => 'OpenTelemetry::Instrumentation::RestClient', + 'resque' => 'OpenTelemetry::Instrumentation::Resque', + 'ruby_kafka' => 'OpenTelemetry::Instrumentation::RubyKafka', + 'sidekiq' => 'OpenTelemetry::Instrumentation::Sidekiq', + 'sinatra' => 'OpenTelemetry::Instrumentation::Sinatra' + }.freeze + + def self.detect_resource_from_env + env = ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s + additional_resource = ::OpenTelemetry::SDK::Resources::Resource.create({}) + + env.split(',').each do |detector| + case detector + when 'container' + additional_resource = additional_resource.merge(::OpenTelemetry::Resource::Detector::Container.detect) if defined? ::OpenTelemetry::Resource::Detector::Container + when 'google_cloud_platform' + additional_resource = additional_resource.merge(::OpenTelemetry::Resource::Detector::GoogleCloudPlatform.detect) if defined? ::OpenTelemetry::Resource::Detector::GoogleCloudPlatform + when 'azure' + additional_resource = additional_resource.merge(::OpenTelemetry::Resource::Detector::Azure.detect) if defined? ::OpenTelemetry::Resource::Detector::Azure + when 'aws' + additional_resource = additional_resource.merge(::OpenTelemetry::Resource::Detector::AWS.detect(%i[ec2 ecs eks lambda])) if defined? ::OpenTelemetry::Resource::Detector::AWS end - - additional_resource end - def determine_enabled_instrumentation - env = ENV['OTEL_RUBY_ENABLED_INSTRUMENTATIONS'].to_s - - env.split(',').map { |instrumentation| OTEL_INSTRUMENTATION_MAP[instrumentation] } - end - - def setup_load_path - lib = File.expand_path('..', __dir__) - $LOAD_PATH.reject! { |path| path.include?('opentelemetry-auto-instrumentation') } - $LOAD_PATH.unshift(lib) - end + additional_resource + end - def initialize_opentelemetry - setup_load_path + def self.determine_enabled_instrumentation + env = ENV['OTEL_RUBY_ENABLED_INSTRUMENTATIONS'].to_s - begin - required_instrumentation = determine_enabled_instrumentation + env.split(',').map { |instrumentation| OTEL_INSTRUMENTATION_MAP[instrumentation] } + end - OpenTelemetry::SDK.configure do |c| - c.resource = detect_resource_from_env - if required_instrumentation.empty? - c.use_all # enables all instrumentation! - else - required_instrumentation.each do |instrumentation| - c.use instrumentation - end + def self.require_otel + lib = File.expand_path('..', __dir__) + $LOAD_PATH.reject! { |path| path.include?('opentelemetry-auto-instrumentation') } + $LOAD_PATH.unshift(lib) + + begin + required_instrumentation = determine_enabled_instrumentation + + OpenTelemetry::SDK.configure do |c| + c.resource = detect_resource_from_env + if required_instrumentation.empty? + c.use_all # enables all instrumentation! + else + required_instrumentation.each do |instrumentation| + c.use instrumentation end end - OpenTelemetry.logger.info { 'Auto-instrumentation initialized' } - rescue StandardError => e - warn "Auto-instrumentation failed to initialize. Error: #{e.message}" end + OpenTelemetry.logger.info { 'Auto-instrumentation initialized' } + rescue StandardError => e + warn "Auto-instrumentation failed to initialize. Error: #{e.message}" end end end # ref: https://github.com/newrelic/newrelic-ruby-agent/blob/dev/lib/boot/strap.rb - unless method_defined?(:require) - def require(...) - super - Initializer.initialize_opentelemetry - end + def require(...) + super + OTelInitializer.require_otel end end diff --git a/opentelemetry-auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb b/opentelemetry-auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb index d2fc57ab17..dc6b382713 100644 --- a/opentelemetry-auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb +++ b/opentelemetry-auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb @@ -15,11 +15,22 @@ # Clean up constants and methods if they exist OTelBundlerPatch::Initializer.send(:remove_const, :OTEL_INSTRUMENTATION_MAP) if defined?(OTelBundlerPatch::Initializer::OTEL_INSTRUMENTATION_MAP) + # Remove singleton methods from Initializer + if defined?(OTelBundlerPatch::Initializer) + %i[detect_resource_from_env determine_enabled_instrumentation require_otel].each do |method| + OTelBundlerPatch::Initializer.singleton_class.send(:undef_method, method) if OTelBundlerPatch::Initializer.respond_to?(method) + end + end + + # Remove the Initializer module OTelBundlerPatch.send(:remove_const, :Initializer) if defined?(OTelBundlerPatch::Initializer) + # Remove instance methods from OTelBundlerPatch %i[require].each do |method| OTelBundlerPatch.send(:undef_method, method) if OTelBundlerPatch.method_defined?(method) end + + # Reset instrumentation installation state [ OpenTelemetry::Instrumentation::Net::HTTP::Instrumentation, OpenTelemetry::Instrumentation::Rake::Instrumentation From f811cbe4d3b1cedf717e46a0b7b836d65d1d3db2 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Mon, 11 Aug 2025 17:37:01 -0400 Subject: [PATCH 35/68] doc: add more test example --- opentelemetry-auto-instrumentation/README.md | 1 + .../example/README.md | 39 +++++++++++++++ .../example/rails-example/Gemfile | 11 +++++ .../example/rails-example/app.rb | 47 +++++++++++++++++++ .../example/rails-example/config.ru | 5 ++ .../example/{ => simple-example}/Gemfile | 3 +- .../example/{ => simple-example}/app.rb | 6 +-- .../lib/opentelemetry-auto-instrumentation.rb | 2 +- 8 files changed, 109 insertions(+), 5 deletions(-) create mode 100644 opentelemetry-auto-instrumentation/example/README.md create mode 100644 opentelemetry-auto-instrumentation/example/rails-example/Gemfile create mode 100644 opentelemetry-auto-instrumentation/example/rails-example/app.rb create mode 100644 opentelemetry-auto-instrumentation/example/rails-example/config.ru rename opentelemetry-auto-instrumentation/example/{ => simple-example}/Gemfile (50%) rename opentelemetry-auto-instrumentation/example/{ => simple-example}/app.rb (62%) diff --git a/opentelemetry-auto-instrumentation/README.md b/opentelemetry-auto-instrumentation/README.md index 52cc13b6e2..9807097524 100644 --- a/opentelemetry-auto-instrumentation/README.md +++ b/opentelemetry-auto-instrumentation/README.md @@ -33,6 +33,7 @@ opentelemetry-helpers-sql-obfuscation opentelemetry-resource-detector-google_cloud_platform opentelemetry-resource-detector-azure opentelemetry-resource-detector-container +opentelemetry-resource-detector-aws ``` ### Example installation strategies diff --git a/opentelemetry-auto-instrumentation/example/README.md b/opentelemetry-auto-instrumentation/example/README.md new file mode 100644 index 0000000000..480a7c00e9 --- /dev/null +++ b/opentelemetry-auto-instrumentation/example/README.md @@ -0,0 +1,39 @@ +# Example + +## Installation + +Install opentelemetry-auto-instrumentation through `gem install` +Then `bundle install` + + +## Simple Example (simple-example) + +```bash +OTEL_RUBY_REQUIRE_BUNDLER=true OTEL_TRACES_EXPORTER=console RUBYOPT="-r opentelemetry-auto-instrumentation" OTEL_RUBY_OPERATOR=false ruby app.rb +``` + +## Rails Example (rails-example) + +Without auto-instrumentation +```bash +bundle exec rackup config.ru +``` + +In other terminal make the request call +```bash +wget http://localhost:9292 +# or curl http://localhost:9292 if you have curl on system +``` + +With auto-instrumentation +```bash +OTEL_RUBY_REQUIRE_BUNDLER=false OTEL_TRACES_EXPORTER=console RUBYOPT="-r opentelemetry-auto-instrumentation" OTEL_RUBY_OPERATOR=false bundle exec rackup config.ru +``` + +The load sequence must be opentelemetry-auto-instrumentation -> user library -> bundler.require (initialize otel sdk and instrumentation installation) + +In other terminal make the request call +```bash +wget http://localhost:9292 +# or curl http://localhost:9292 if you have curl on system +``` diff --git a/opentelemetry-auto-instrumentation/example/rails-example/Gemfile b/opentelemetry-auto-instrumentation/example/rails-example/Gemfile new file mode 100644 index 0000000000..5318a25bf7 --- /dev/null +++ b/opentelemetry-auto-instrumentation/example/rails-example/Gemfile @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +# Gemfile +source 'https://rubygems.org' + +gem 'rails' +gem 'rack' +gem 'rake', '13.0.6' +gem 'bigdecimal', '3.1.3' +gem 'logger', '1.5.3' +gem 'webrick' diff --git a/opentelemetry-auto-instrumentation/example/rails-example/app.rb b/opentelemetry-auto-instrumentation/example/rails-example/app.rb new file mode 100644 index 0000000000..9ffc35f864 --- /dev/null +++ b/opentelemetry-auto-instrumentation/example/rails-example/app.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'rails' +require 'action_controller/railtie' + +require 'bundler' +Bundler.require + +# MyApp +class MyApp < Rails::Application + config.secret_key_base = 'your_secret_key_here' + config.eager_load = false + config.logger = Logger.new($stdout) + config.api_only = true + config.active_support.to_time_preserves_timezone = :zone + + routes.draw do + get '/', to: 'application#index' + get '/hello', to: 'application#hello' + post '/data', to: 'application#create' + end +end + +# ApplicationController +class ApplicationController < ActionController::API + def index + render json: { message: 'Hello World!', time: Time.current } + end + + def hello + name = params[:name] || 'World' + render json: { greeting: "Hello #{name}!" } + end + + def create + render json: { + message: 'Data received', + data: params.except(:controller, :action) + } + end +end + +MyApp.initialize! diff --git a/opentelemetry-auto-instrumentation/example/rails-example/config.ru b/opentelemetry-auto-instrumentation/example/rails-example/config.ru new file mode 100644 index 0000000000..3ca0d86570 --- /dev/null +++ b/opentelemetry-auto-instrumentation/example/rails-example/config.ru @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +require_relative 'app' + +run MyApp diff --git a/opentelemetry-auto-instrumentation/example/Gemfile b/opentelemetry-auto-instrumentation/example/simple-example/Gemfile similarity index 50% rename from opentelemetry-auto-instrumentation/example/Gemfile rename to opentelemetry-auto-instrumentation/example/simple-example/Gemfile index c9721cb670..ebc45888f5 100644 --- a/opentelemetry-auto-instrumentation/example/Gemfile +++ b/opentelemetry-auto-instrumentation/example/simple-example/Gemfile @@ -1,2 +1,3 @@ -source 'https://rubygems.org' +# frozen_string_literal: true +source 'https://rubygems.org' diff --git a/opentelemetry-auto-instrumentation/example/app.rb b/opentelemetry-auto-instrumentation/example/simple-example/app.rb similarity index 62% rename from opentelemetry-auto-instrumentation/example/app.rb rename to opentelemetry-auto-instrumentation/example/simple-example/app.rb index 2d1a4621f0..4d6ce333ea 100644 --- a/opentelemetry-auto-instrumentation/example/app.rb +++ b/opentelemetry-auto-instrumentation/example/simple-example/app.rb @@ -4,8 +4,8 @@ # # SPDX-License-Identifier: Apache-2.0 -url = URI.parse("http://catfact.ninja/fact") +url = URI.parse('http://catfact.ninja/fact') req = Net::HTTP::Get.new(url.to_s) -res = Net::HTTP.start(url.host, url.port) {|http| +Net::HTTP.start(url.host, url.port) do |http| http.request(req) -} +end diff --git a/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb b/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb index 374c38200e..5f6594cf39 100644 --- a/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb +++ b/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb @@ -118,7 +118,7 @@ def require(...) # set OTEL_OPERATOR to false if not in autoinstrumentation-ruby image, default to /otel-auto-instrumentation-ruby # /otel-auto-instrumentation-ruby is set in opentelemetry-operator ruby.go -operator_gem_path = ENV['OTEL_RUBY_OPERATOR'].nil? || ENV['OTEL_RUBY_OPERATOR'] == 'true' ? '/otel-auto-instrumentation-ruby' : nil +operator_gem_path = ENV['OTEL_RUBY_OPERATOR'] == 'true' ? '/otel-auto-instrumentation-ruby' : nil additional_gem_path = operator_gem_path || ENV['OTEL_RUBY_ADDITIONAL_GEM_PATH'] || Gem.dir $stdout.puts "Loading the additional gem path from #{additional_gem_path}" if ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] == 'true' From 5c032ca4339935857f259d6a2c9ebcbc10691a4c Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Mon, 11 Aug 2025 17:38:20 -0400 Subject: [PATCH 36/68] typo --- .../lib/opentelemetry-auto-instrumentation.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb b/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb index 5f6594cf39..374c38200e 100644 --- a/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb +++ b/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb @@ -118,7 +118,7 @@ def require(...) # set OTEL_OPERATOR to false if not in autoinstrumentation-ruby image, default to /otel-auto-instrumentation-ruby # /otel-auto-instrumentation-ruby is set in opentelemetry-operator ruby.go -operator_gem_path = ENV['OTEL_RUBY_OPERATOR'] == 'true' ? '/otel-auto-instrumentation-ruby' : nil +operator_gem_path = ENV['OTEL_RUBY_OPERATOR'].nil? || ENV['OTEL_RUBY_OPERATOR'] == 'true' ? '/otel-auto-instrumentation-ruby' : nil additional_gem_path = operator_gem_path || ENV['OTEL_RUBY_ADDITIONAL_GEM_PATH'] || Gem.dir $stdout.puts "Loading the additional gem path from #{additional_gem_path}" if ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] == 'true' From b006c5019beb582fa835516dee933a3bb6984465 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Thu, 23 Oct 2025 12:07:08 -0400 Subject: [PATCH 37/68] update otel dependencies --- ...opentelemetry-auto-instrumentation.gemspec | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/opentelemetry-auto-instrumentation/opentelemetry-auto-instrumentation.gemspec b/opentelemetry-auto-instrumentation/opentelemetry-auto-instrumentation.gemspec index 47ad6d51f6..07958a168a 100644 --- a/opentelemetry-auto-instrumentation/opentelemetry-auto-instrumentation.gemspec +++ b/opentelemetry-auto-instrumentation/opentelemetry-auto-instrumentation.gemspec @@ -26,16 +26,16 @@ Gem::Specification.new do |spec| spec.require_paths = ['lib'] spec.required_ruby_version = '>= 3.2' - spec.add_dependency 'opentelemetry-api', '~> 1.4.0' - spec.add_dependency 'opentelemetry-exporter-otlp', '~> 0.29.1' - spec.add_dependency 'opentelemetry-helpers-mysql', '~> 0.2.0' - spec.add_dependency 'opentelemetry-helpers-sql-obfuscation', '~> 0.3.0' - spec.add_dependency 'opentelemetry-instrumentation-all', '~> 0.72.0' - spec.add_dependency 'opentelemetry-resource-detector-aws', '~> 0.2.0' - spec.add_dependency 'opentelemetry-resource-detector-azure', '~> 0.2.0' - spec.add_dependency 'opentelemetry-resource-detector-container', '~> 0.2.0' - spec.add_dependency 'opentelemetry-resource-detector-google_cloud_platform', '~> 0.2.0' - spec.add_dependency 'opentelemetry-sdk', '~> 1.6.0' + spec.add_dependency 'opentelemetry-api', '~> 1.7.0' + spec.add_dependency 'opentelemetry-exporter-otlp', '~> 0.31.1' + spec.add_dependency 'opentelemetry-helpers-mysql', '~> 0.4.0' + spec.add_dependency 'opentelemetry-helpers-sql-obfuscation', '~> 0.5.0' + spec.add_dependency 'opentelemetry-instrumentation-all', '~> 0.86.1' + spec.add_dependency 'opentelemetry-resource-detector-aws', '~> 0.5.0' + spec.add_dependency 'opentelemetry-resource-detector-azure', '~> 0.3.0' + spec.add_dependency 'opentelemetry-resource-detector-container', '~> 0.3.0' + spec.add_dependency 'opentelemetry-resource-detector-google_cloud_platform', '~> 0.3.0' + spec.add_dependency 'opentelemetry-sdk', '~> 1.10.0' if spec.respond_to?(:metadata) spec.metadata['changelog_uri'] = "https://rubydoc.info/gems/#{spec.name}/#{spec.version}/file/CHANGELOG.md" From b35cf60131f7db8e59490b39b2a57539e49b209e Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Thu, 23 Oct 2025 12:45:21 -0400 Subject: [PATCH 38/68] add updated test case --- .github/workflows/ci-contrib.yml | 30 ++++ .../lib/opentelemetry-auto-instrumentation.rb | 6 + ...opentelemetry-auto-instrumentation_test.rb | 151 +++++++++--------- 3 files changed, 113 insertions(+), 74 deletions(-) diff --git a/.github/workflows/ci-contrib.yml b/.github/workflows/ci-contrib.yml index d24873ac3d..377b4f29e3 100644 --- a/.github/workflows/ci-contrib.yml +++ b/.github/workflows/ci-contrib.yml @@ -216,3 +216,33 @@ jobs: with: gem: "opentelemetry-sampler-${{ matrix.gem }}" ruby: "jruby-10.0.2.0" + + auto-instrumentation: + strategy: + fail-fast: false + matrix: + os: + - ubuntu-latest + name: "opentelemetry-auto-instrumentation / ${{ matrix.os }}" + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v5 + - name: "Test Ruby 3.4" + uses: ./.github/actions/test_gem + with: + gem: "opentelemetry-auto-instrumentation" + ruby: "3.4" + - name: "Test Ruby 3.3" + uses: ./.github/actions/test_gem + with: + gem: "opentelemetry-auto-instrumentation" + ruby: "3.3" + - name: "Test Ruby 3.2" + uses: ./.github/actions/test_gem + with: + gem: "opentelemetry-auto-instrumentation" + ruby: "3.2" + yard: true + rubocop: true + coverage: true + build: true diff --git a/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb b/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb index 374c38200e..b34460a749 100644 --- a/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb +++ b/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb @@ -8,6 +8,8 @@ module OTelBundlerPatch # Nested module to handle OpenTelemetry initialization logic module OTelInitializer + @initialized = false + OTEL_INSTRUMENTATION_MAP = { 'gruf' => 'OpenTelemetry::Instrumentation::Gruf', 'trilogy' => 'OpenTelemetry::Instrumentation::Trilogy', @@ -78,6 +80,10 @@ def self.determine_enabled_instrumentation end def self.require_otel + return if @initialized + + @initialized = true + lib = File.expand_path('..', __dir__) $LOAD_PATH.reject! { |path| path.include?('opentelemetry-auto-instrumentation') } $LOAD_PATH.unshift(lib) diff --git a/opentelemetry-auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb b/opentelemetry-auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb index dc6b382713..f67406bd28 100644 --- a/opentelemetry-auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb +++ b/opentelemetry-auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb @@ -7,103 +7,106 @@ require 'test_helper' describe 'AutoInstrumentation' do + let(:auto_instrumentation_path) { File.expand_path('../lib/opentelemetry-auto-instrumentation.rb', __dir__) } + before do - OpenTelemetry::TestHelpers.reset_opentelemetry + ENV['OTEL_RUBY_ENABLED_INSTRUMENTATIONS'] = nil + ENV['OTEL_RUBY_INSTRUMENTATION_NET_HTTP_ENABLED'] = nil + ENV['OTEL_RUBY_RESOURCE_DETECTORS'] = nil + ENV['OTEL_RUBY_REQUIRE_BUNDLER'] = nil + ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] = nil end after do - # Clean up constants and methods if they exist - OTelBundlerPatch::Initializer.send(:remove_const, :OTEL_INSTRUMENTATION_MAP) if defined?(OTelBundlerPatch::Initializer::OTEL_INSTRUMENTATION_MAP) - - # Remove singleton methods from Initializer - if defined?(OTelBundlerPatch::Initializer) - %i[detect_resource_from_env determine_enabled_instrumentation require_otel].each do |method| - OTelBundlerPatch::Initializer.singleton_class.send(:undef_method, method) if OTelBundlerPatch::Initializer.respond_to?(method) - end - end - - # Remove the Initializer module - OTelBundlerPatch.send(:remove_const, :Initializer) if defined?(OTelBundlerPatch::Initializer) - - # Remove instance methods from OTelBundlerPatch - %i[require].each do |method| - OTelBundlerPatch.send(:undef_method, method) if OTelBundlerPatch.method_defined?(method) - end - - # Reset instrumentation installation state - [ - OpenTelemetry::Instrumentation::Net::HTTP::Instrumentation, - OpenTelemetry::Instrumentation::Rake::Instrumentation - ].each do |instrumentation| - instance = instrumentation.instance_variable_get(:@instance) - instance&.instance_variable_set(:@installed, false) - end - - # Clean up environment variables ENV['OTEL_RUBY_ENABLED_INSTRUMENTATIONS'] = nil ENV['OTEL_RUBY_INSTRUMENTATION_NET_HTTP_ENABLED'] = nil ENV['OTEL_RUBY_RESOURCE_DETECTORS'] = nil + ENV['OTEL_RUBY_REQUIRE_BUNDLER'] = nil + ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] = nil end - it 'simple_load_test' do - load './lib/opentelemetry-auto-instrumentation.rb' - Bundler.require - - _(OpenTelemetry.tracer_provider.class).must_equal OpenTelemetry::SDK::Trace::TracerProvider - - resource_attributes = OpenTelemetry.tracer_provider.instance_variable_get(:@resource).instance_variable_get(:@attributes) + def run_in_subprocess(env_vars = {}) + # Run the test in a subprocess to avoid contaminating the test environment + read_pipe, write_pipe = IO.pipe + + pid = fork do + read_pipe.close + env_vars.each { |key, value| ENV[key] = value } + + ENV['OTEL_RUBY_REQUIRE_BUNDLER'] = 'false' + begin + # Load the auto-instrumentation library + load auto_instrumentation_path + Bundler.require + + # Get tracer provider information + tracer_provider = OpenTelemetry.tracer_provider + resource = tracer_provider.instance_variable_get(:@resource) + resource_attributes = resource.instance_variable_get(:@attributes) + registry = tracer_provider.instance_variable_get(:@registry) + instrumentation_names = registry.map { |entry| entry.first.name } + + # Serialize and send results back to parent + result = Marshal.dump({ + tracer_provider_class: tracer_provider.class.name, + resource_attributes: resource_attributes, + instrumentation_names: instrumentation_names + }) + write_pipe.write(result) + rescue StandardError => e + error_result = Marshal.dump({ error: e.message, backtrace: e.backtrace }) + write_pipe.write(error_result) + ensure + write_pipe.close + exit!(0) + end + end - _(resource_attributes['service.name']).must_equal 'unknown_service' - _(resource_attributes['telemetry.sdk.name']).must_equal 'opentelemetry' - _(resource_attributes['telemetry.sdk.language']).must_equal 'ruby' - _(resource_attributes.key?('container.id')).must_equal false + write_pipe.close + result_data = read_pipe.read + read_pipe.close + Process.wait(pid) - registry = OpenTelemetry.tracer_provider.instance_variable_get(:@registry) - instrumentation_names = registry.map { |entry| entry.first.name }.sort + # rubocop:disable Security/MarshalLoad + Marshal.load(result_data) + # rubocop:enable Security/MarshalLoad + end - _(instrumentation_names).must_include 'OpenTelemetry::Instrumentation::Net::HTTP' - _(instrumentation_names).must_include 'OpenTelemetry::Instrumentation::Rake' + it 'simple_load_test' do + result = run_in_subprocess + + _(result[:error]).must_be_nil + _(result[:tracer_provider_class]).must_equal 'OpenTelemetry::SDK::Trace::TracerProvider' + _(result[:resource_attributes]['service.name']).must_equal 'unknown_service' + _(result[:resource_attributes]['telemetry.sdk.name']).must_equal 'opentelemetry' + _(result[:resource_attributes]['telemetry.sdk.language']).must_equal 'ruby' + _(result[:resource_attributes].key?('container.id')).must_equal false + _(result[:instrumentation_names]).must_include 'OpenTelemetry::Instrumentation::Net::HTTP' + _(result[:instrumentation_names]).must_include 'OpenTelemetry::Instrumentation::Rake' end it 'simple_load_with_net_http_disabled' do - ENV['OTEL_RUBY_INSTRUMENTATION_NET_HTTP_ENABLED'] = 'false' - - load './lib/opentelemetry-auto-instrumentation.rb' - Bundler.require - - registry = OpenTelemetry.tracer_provider.instance_variable_get(:@registry) - instrumentation_names = registry.map { |entry| entry.first.name } + result = run_in_subprocess('OTEL_RUBY_INSTRUMENTATION_NET_HTTP_ENABLED' => 'false') - _(instrumentation_names).must_include 'OpenTelemetry::Instrumentation::Rake' - _(instrumentation_names).wont_include 'OpenTelemetry::Instrumentation::Net::HTTP' - - ENV['OTEL_RUBY_INSTRUMENTATION_NET_HTTP_ENABLED'] = nil + _(result[:error]).must_be_nil + _(result[:instrumentation_names]).must_include 'OpenTelemetry::Instrumentation::Rake' + _(result[:instrumentation_names]).wont_include 'OpenTelemetry::Instrumentation::Net::HTTP' end it 'simple_load_with_desired_instrument_only' do - ENV['OTEL_RUBY_ENABLED_INSTRUMENTATIONS'] = 'net_http' - - load './lib/opentelemetry-auto-instrumentation.rb' - Bundler.require - - registry = OpenTelemetry.tracer_provider.instance_variable_get(:@registry) - instrumentation_names = registry.map { |entry| entry.first.name } + result = run_in_subprocess('OTEL_RUBY_ENABLED_INSTRUMENTATIONS' => 'net_http') - _(instrumentation_names).must_include 'OpenTelemetry::Instrumentation::Net::HTTP' - _(instrumentation_names).wont_include 'OpenTelemetry::Instrumentation::Rake' + _(result[:error]).must_be_nil + _(result[:instrumentation_names]).must_include 'OpenTelemetry::Instrumentation::Net::HTTP' + _(result[:instrumentation_names]).wont_include 'OpenTelemetry::Instrumentation::Rake' end it 'simple_load_with_additional_resource' do - ENV['OTEL_RUBY_RESOURCE_DETECTORS'] = 'container' + result = run_in_subprocess('OTEL_RUBY_RESOURCE_DETECTORS' => 'container') - load './lib/opentelemetry-auto-instrumentation.rb' - Bundler.require - - resource_attributes = OpenTelemetry.tracer_provider.instance_variable_get(:@resource).instance_variable_get(:@attributes) - _(resource_attributes.key?('container.id')).must_equal true - _(resource_attributes['telemetry.sdk.name']).must_equal 'opentelemetry' - _(resource_attributes['telemetry.sdk.language']).must_equal 'ruby' - - ENV['OTEL_RUBY_RESOURCE_DETECTORS'] = nil + _(result[:error]).must_be_nil + _(result[:resource_attributes].key?('container.id')).must_equal true + _(result[:resource_attributes]['telemetry.sdk.name']).must_equal 'opentelemetry' + _(result[:resource_attributes]['telemetry.sdk.language']).must_equal 'ruby' end end From 7bbd3729f190f9c6ae0b8f4f3b24e2d82d47e2ec Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Fri, 24 Oct 2025 12:44:27 -0400 Subject: [PATCH 39/68] remove the test case for container id because github workflow runs on non-docker env --- .../test/opentelemetry-auto-instrumentation_test.rb | 9 --------- 1 file changed, 9 deletions(-) diff --git a/opentelemetry-auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb b/opentelemetry-auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb index f67406bd28..6d93430da3 100644 --- a/opentelemetry-auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb +++ b/opentelemetry-auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb @@ -100,13 +100,4 @@ def run_in_subprocess(env_vars = {}) _(result[:instrumentation_names]).must_include 'OpenTelemetry::Instrumentation::Net::HTTP' _(result[:instrumentation_names]).wont_include 'OpenTelemetry::Instrumentation::Rake' end - - it 'simple_load_with_additional_resource' do - result = run_in_subprocess('OTEL_RUBY_RESOURCE_DETECTORS' => 'container') - - _(result[:error]).must_be_nil - _(result[:resource_attributes].key?('container.id')).must_equal true - _(result[:resource_attributes]['telemetry.sdk.name']).must_equal 'opentelemetry' - _(result[:resource_attributes]['telemetry.sdk.language']).must_equal 'ruby' - end end From b92cb61a444aa11b464093ba4707cf689203f56a Mon Sep 17 00:00:00 2001 From: Xuan <112967240+xuan-cao-swi@users.noreply.github.com> Date: Fri, 7 Nov 2025 14:02:39 -0500 Subject: [PATCH 40/68] Update opentelemetry-auto-instrumentation/README.md Co-authored-by: Hannah Ramadan <76922290+hannahramadan@users.noreply.github.com> --- opentelemetry-auto-instrumentation/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-auto-instrumentation/README.md b/opentelemetry-auto-instrumentation/README.md index 9807097524..3ce57a5718 100644 --- a/opentelemetry-auto-instrumentation/README.md +++ b/opentelemetry-auto-instrumentation/README.md @@ -10,7 +10,7 @@ OpenTelemetry provides a single set of APIs, libraries, agents, and collector se ## How does this gem fit in? -The `opentelemetry-auto-instrumentation` gem provides an easy way to load and initialize the OpenTelemetry Ruby SDK without changing your code initialize the SDK. This gem is particularly used with the [OpenTelemetry Operator][opentelemetry-operator]. +The `opentelemetry-auto-instrumentation` gem provides an easy way to load and initialize the OpenTelemetry Ruby SDK without changing your code. This gem is particularly used with the [OpenTelemetry Operator][opentelemetry-operator]. ## How do I get started? From 69c9fd9ffb30fd8ae046ce21e953e4069ce67024 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Mon, 17 Nov 2025 13:25:36 -0500 Subject: [PATCH 41/68] add troubleshooting in readme --- opentelemetry-auto-instrumentation/README.md | 17 +++++++++++------ .../example/README.md | 5 ++++- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/opentelemetry-auto-instrumentation/README.md b/opentelemetry-auto-instrumentation/README.md index 3ce57a5718..e29808744f 100644 --- a/opentelemetry-auto-instrumentation/README.md +++ b/opentelemetry-auto-instrumentation/README.md @@ -23,6 +23,7 @@ gem install opentelemetry-auto-instrumentation ``` Installing opentelemetry-auto-instrumentation will automatically install following gems: + ```console opentelemetry-sdk opentelemetry-api @@ -91,6 +92,14 @@ Since the `opentelemetry-auto-instrumentation` gem should be installed through ` RUBYOPT="-r {PUT YOUR GEM PATH}/gems/opentelemetry-auto-instrumentation-0.1.0/lib/opentelemetry-auto-instrumentation" bundle exec rails server ``` +If you wish to load some gems outside the Gemfile, then they need to be placed in front of opentelemetry-auto-instrumentation: + +```console +export BUNDLE_WITHOUT=development,test +gem install mysql2 +RUBYOPT="-r mysql2 -r opentelemetry-auto-instrumentation" ruby application.rb +``` + Instrument Sinatra application with rackup: If you are using a Gemfile to install the required gems but without `Bundler.require`, set `REQUIRE_BUNDLER` to true. This way, `opentelemetry-auto-instrumentation` will call `Bundler.require` to initialize the required gems prior to SDK initialization. @@ -100,13 +109,9 @@ export REQUIRE_BUNDLER=true RUBYOPT="-r opentelemetry-auto-instrumentation" rackup config.ru ``` -If you wish to load some gems outside the Gemfile, then they need to be placed in front of opentelemetry-auto-instrumentation: +### Troubleshooting -```console -export BUNDLE_WITHOUT=development,test -gem install mysql2 -RUBYOPT="-r mysql2 -r opentelemetry-auto-instrumentation" ruby application.rb -``` +The auto-instrumentation works by patching the `Bundler::Runtime#require` method to inject the `opentelemetry-auto-instrumentation` gem into your application. Rails applications automatically call `Bundler.require` during their boot process, so they work out of the box. However, many other frameworks (like Sinatra) do not call `Bundler.require` automatically, which means the OpenTelemetry SDK is never loaded. To fix this, explicitly call `Bundler.require` early in your application's startup process, or set the `OTEL_RUBY_REQUIRE_BUNDLER` environment variable to `true`. ## Example diff --git a/opentelemetry-auto-instrumentation/example/README.md b/opentelemetry-auto-instrumentation/example/README.md index 480a7c00e9..9c6718fbca 100644 --- a/opentelemetry-auto-instrumentation/example/README.md +++ b/opentelemetry-auto-instrumentation/example/README.md @@ -5,7 +5,6 @@ Install opentelemetry-auto-instrumentation through `gem install` Then `bundle install` - ## Simple Example (simple-example) ```bash @@ -15,17 +14,20 @@ OTEL_RUBY_REQUIRE_BUNDLER=true OTEL_TRACES_EXPORTER=console RUBYOPT="-r opentel ## Rails Example (rails-example) Without auto-instrumentation + ```bash bundle exec rackup config.ru ``` In other terminal make the request call + ```bash wget http://localhost:9292 # or curl http://localhost:9292 if you have curl on system ``` With auto-instrumentation + ```bash OTEL_RUBY_REQUIRE_BUNDLER=false OTEL_TRACES_EXPORTER=console RUBYOPT="-r opentelemetry-auto-instrumentation" OTEL_RUBY_OPERATOR=false bundle exec rackup config.ru ``` @@ -33,6 +35,7 @@ OTEL_RUBY_REQUIRE_BUNDLER=false OTEL_TRACES_EXPORTER=console RUBYOPT="-r opente The load sequence must be opentelemetry-auto-instrumentation -> user library -> bundler.require (initialize otel sdk and instrumentation installation) In other terminal make the request call + ```bash wget http://localhost:9292 # or curl http://localhost:9292 if you have curl on system From 1a27561a59d4850a6c9d090f40f27521354368c2 Mon Sep 17 00:00:00 2001 From: Xuan <112967240+xuan-cao-swi@users.noreply.github.com> Date: Tue, 18 Nov 2025 11:04:45 -0500 Subject: [PATCH 42/68] Update opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb Co-authored-by: Ariel Valentin --- .../lib/opentelemetry-auto-instrumentation.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb b/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb index b34460a749..f8bcdca08d 100644 --- a/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb +++ b/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb @@ -137,7 +137,7 @@ def require(...) (file_path.include?('opentelemetry') || file_path.include?('google')) && !unload_libraries.include?(gem_name) end -$stdout.puts "Loaded Library File Paths #{loaded_library_file_path.join(',')}" if ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] == 'true' +$stderr.puts "Loaded Library File Paths #{loaded_library_file_path.join(',')}" if ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] == 'true' loaded_library_file_path.each do |file_path| $LOAD_PATH.unshift("#{file_path}/lib") From 72e21610c8ae1021d1ffae77c321d8a41ddde8cf Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Tue, 18 Nov 2025 11:08:38 -0500 Subject: [PATCH 43/68] add more troubleshooting instruction --- .../example/README.md | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/opentelemetry-auto-instrumentation/example/README.md b/opentelemetry-auto-instrumentation/example/README.md index 9c6718fbca..f9571cd82a 100644 --- a/opentelemetry-auto-instrumentation/example/README.md +++ b/opentelemetry-auto-instrumentation/example/README.md @@ -40,3 +40,33 @@ In other terminal make the request call wget http://localhost:9292 # or curl http://localhost:9292 if you have curl on system ``` + +### Troubleshooting: Default Gem Version Conflicts + +If you encounter an error like "You have already activated [gem] X.X.X, but your Gemfile requires [gem] Y.Y.Y", this indicates a version conflict with a default gem (such as `json`, `bigdecimal`, or `logger`). + +**Error example:** +``` +You have already activated json 2.6.3, but your Gemfile requires json 2.16.0. +Since json is a default gem, you can either remove your dependency on it or +try updating to a newer version of bundler that supports json as a default gem. +``` + +**Solution:** + +Install the specific gem version that your Gemfile requires: + +```bash +gem install [gem-name] -v '[version]' +``` + +For example: +```bash +gem install json -v '2.16.0' +``` + +Then run your application again with auto-instrumentation. + +**Why this happens:** + +When using `RUBYOPT="-r opentelemetry-auto-instrumentation"` with `bundle exec`, the OpenTelemetry gem is loaded before `bundle exec` runs. If the OpenTelemetry dependencies activate a default gem version that differs from what your Gemfile specifies, Bundler will raise an error. Installing the required version explicitly resolves this conflict. From fc3c33a26ce66e4f1a6397de41167cd0fbc3b5eb Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Wed, 19 Nov 2025 13:14:55 -0500 Subject: [PATCH 44/68] fix ci --- opentelemetry-auto-instrumentation/example/README.md | 4 ++-- .../lib/opentelemetry-auto-instrumentation.rb | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/opentelemetry-auto-instrumentation/example/README.md b/opentelemetry-auto-instrumentation/example/README.md index f9571cd82a..ea87807ec0 100644 --- a/opentelemetry-auto-instrumentation/example/README.md +++ b/opentelemetry-auto-instrumentation/example/README.md @@ -43,7 +43,7 @@ wget http://localhost:9292 ### Troubleshooting: Default Gem Version Conflicts -If you encounter an error like "You have already activated [gem] X.X.X, but your Gemfile requires [gem] Y.Y.Y", this indicates a version conflict with a default gem (such as `json`, `bigdecimal`, or `logger`). +If you encounter an error like "You have already activated [gem] X.X.X, but your Gemfile requires [gem] Y.Y.Y", this indicates a version conflict with a default gem (such as `json` or `logger`). **Error example:** ``` @@ -69,4 +69,4 @@ Then run your application again with auto-instrumentation. **Why this happens:** -When using `RUBYOPT="-r opentelemetry-auto-instrumentation"` with `bundle exec`, the OpenTelemetry gem is loaded before `bundle exec` runs. If the OpenTelemetry dependencies activate a default gem version that differs from what your Gemfile specifies, Bundler will raise an error. Installing the required version explicitly resolves this conflict. +The OpenTelemetry gem is loaded before `bundle exec` runs. If the OpenTelemetry dependencies activate a default gem version that differs from what your Gemfile specifies, Bundler will raise an error. Installing the required version explicitly resolves this conflict. diff --git a/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb b/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb index f8bcdca08d..1263154a43 100644 --- a/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb +++ b/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb @@ -137,7 +137,9 @@ def require(...) (file_path.include?('opentelemetry') || file_path.include?('google')) && !unload_libraries.include?(gem_name) end +# rubocop:disable Style/StderrPuts $stderr.puts "Loaded Library File Paths #{loaded_library_file_path.join(',')}" if ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] == 'true' +# rubocop:enable Style/StderrPuts loaded_library_file_path.each do |file_path| $LOAD_PATH.unshift("#{file_path}/lib") From a20954280e27910e38e5f05ff449a203c2e63db5 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Tue, 13 Jan 2026 13:18:27 -0500 Subject: [PATCH 45/68] markdown lint --- opentelemetry-auto-instrumentation/README.md | 2 +- opentelemetry-auto-instrumentation/example/README.md | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/opentelemetry-auto-instrumentation/README.md b/opentelemetry-auto-instrumentation/README.md index e29808744f..03ee8b20bc 100644 --- a/opentelemetry-auto-instrumentation/README.md +++ b/opentelemetry-auto-instrumentation/README.md @@ -131,7 +131,7 @@ OTEL_RUBY_REQUIRE_BUNDLER=true OTEL_TRACES_EXPORTER=console RUBYOPT="-r opentel These environment variables are not standard OpenTelemetry environment variables; they are only feature flags for this gem. | Environment Variable | Description | Default | Example | -|----------------------|-------------|---------|---------| +| ---------------------- | ----------- | ------- | ------- | | `OTEL_RUBY_REQUIRE_BUNDLER` | Set to `true` if you are using Bundler to install gems but without `Bundler.require` in your script during initialization. | nil | N/A | | `OTEL_RUBY_ADDITIONAL_GEM_PATH` | Intended to be used for the OpenTelemetry Operator environment if you install `opentelemetry-auto-instrumentation` to a customized path. | nil | N/A | | `OTEL_RUBY_OPERATOR` | Set to `true` to set the binding path for the OpenTelemetry Operator. | `/otel-auto-instrumentation-ruby` | N/A | diff --git a/opentelemetry-auto-instrumentation/example/README.md b/opentelemetry-auto-instrumentation/example/README.md index ea87807ec0..39dbe982c7 100644 --- a/opentelemetry-auto-instrumentation/example/README.md +++ b/opentelemetry-auto-instrumentation/example/README.md @@ -46,7 +46,8 @@ wget http://localhost:9292 If you encounter an error like "You have already activated [gem] X.X.X, but your Gemfile requires [gem] Y.Y.Y", this indicates a version conflict with a default gem (such as `json` or `logger`). **Error example:** -``` + +```console You have already activated json 2.6.3, but your Gemfile requires json 2.16.0. Since json is a default gem, you can either remove your dependency on it or try updating to a newer version of bundler that supports json as a default gem. @@ -61,6 +62,7 @@ gem install [gem-name] -v '[version]' ``` For example: + ```bash gem install json -v '2.16.0' ``` From b103c85deaa03d581c46b547a758a7681b21d615 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Wed, 28 Jan 2026 15:00:18 -0500 Subject: [PATCH 46/68] refactor the script and remove support for gcp resource detection --- opentelemetry-auto-instrumentation/README.md | 161 +++++++++++------- .../example/README.md | 66 ++++--- .../lib/opentelemetry-auto-instrumentation.rb | 72 +++----- ...opentelemetry-auto-instrumentation.gemspec | 6 +- 4 files changed, 179 insertions(+), 126 deletions(-) diff --git a/opentelemetry-auto-instrumentation/README.md b/opentelemetry-auto-instrumentation/README.md index 03ee8b20bc..2694a43848 100644 --- a/opentelemetry-auto-instrumentation/README.md +++ b/opentelemetry-auto-instrumentation/README.md @@ -1,28 +1,34 @@ -# Opentelemetry Auto Instrumentation +# OpenTelemetry Auto Instrumentation -The `opentelemetry-auto-instrumentation` gem provides an easy way to load and initialize opentelemetry-ruby for auto instrumentation. +The `opentelemetry-auto-instrumentation` gem provides automatic loading and initialization of OpenTelemetry Ruby SDK for zero-code instrumentation of your applications. ## What is OpenTelemetry? -OpenTelemetry is an open source observability framework, providing a general-purpose API, SDK, and related tools required for the instrumentation of cloud-native software, frameworks, and libraries. - -OpenTelemetry provides a single set of APIs, libraries, agents, and collector services to capture distributed traces and metrics from your application. You can analyze them using Prometheus, Jaeger, and other observability tools. +OpenTelemetry is an open source observability framework that provides a unified API, SDK, and tooling for instrumenting cloud-native applications. It captures distributed traces and metrics from your application, which can be analyzed using observability backends like Prometheus, Jaeger, and others. ## How does this gem fit in? -The `opentelemetry-auto-instrumentation` gem provides an easy way to load and initialize the OpenTelemetry Ruby SDK without changing your code. This gem is particularly used with the [OpenTelemetry Operator][opentelemetry-operator]. +This gem enables OpenTelemetry instrumentation without modifying your application code. It automatically: -## How do I get started? +- Loads the OpenTelemetry SDK +- Initializes instrumentations for detected libraries +- Configures exporters and resource detectors -It's recommended to install this gem through `gem install` rather than Bundler since it doesn't require modifying your codebase (including the Gemfile). +This gem is particularly useful with the [OpenTelemetry Operator][opentelemetry-operator] for Kubernetes environments. -Install the gem using: +## How do I get started? + +Install the gem: ```console gem install opentelemetry-auto-instrumentation ``` -Installing opentelemetry-auto-instrumentation will automatically install following gems: +**Note:** Install via `gem install` rather than adding to your Gemfile, as this gem needs to load before your application starts. + +### What gets installed? + +Installing `opentelemetry-auto-instrumentation` automatically includes: ```console opentelemetry-sdk @@ -31,114 +37,155 @@ opentelemetry-instrumentation-all opentelemetry-exporter-otlp opentelemetry-helpers-mysql opentelemetry-helpers-sql-obfuscation -opentelemetry-resource-detector-google_cloud_platform opentelemetry-resource-detector-azure opentelemetry-resource-detector-container opentelemetry-resource-detector-aws ``` -### Example installation strategies +## Usage Examples + +### Basic Usage -Instrument your application: +Instrument any Ruby application: ```console RUBYOPT="-r opentelemetry-auto-instrumentation" ruby application.rb ``` -Instrument your application with additional environment variables: +### With Configuration + +Set environment variables to configure exporters, resource detectors, and service name: ```console export OTEL_TRACES_EXPORTER="otlp" export OTEL_EXPORTER_OTLP_ENDPOINT="your-endpoint" -export OTEL_RUBY_RESOURCE_DETECTORS="container" +export OTEL_RUBY_RESOURCE_DETECTORS="container,azure" export OTEL_SERVICE_NAME="your-service-name" RUBYOPT="-r opentelemetry-auto-instrumentation" ruby application.rb ``` -Instrument your application with disabling certain instrumentation (e.g. sinatra): +### Rails Applications + +Rails automatically calls `Bundler.require`, so instrumentation works out of the box: ```console -export OTEL_RUBY_INSTRUMENTATION_SINATRA_ENABLED='false' -RUBYOPT="-r opentelemetry-auto-instrumentation" ruby application.rb +RUBYOPT="-r opentelemetry-auto-instrumentation" rails server ``` -Instrument your application with only certain instrumentation installed (e.g. mysql, redis): +### Selective Instrumentation + +**Enable only specific instrumentations:** ```console -export OTEL_RUBY_ENABLED_INSTRUMENTATIONS='mysql2,redis' +export OTEL_RUBY_ENABLED_INSTRUMENTATIONS="mysql2,redis,faraday" RUBYOPT="-r opentelemetry-auto-instrumentation" ruby application.rb ``` -Instrument your application with only redis and configure its options: +**Disable specific instrumentations:** ```console -export OTEL_RUBY_ENABLED_INSTRUMENTATIONS='redis' -export OTEL_RUBY_INSTRUMENTATION_REDIS_CONFIG_OPTS='peer_service=new_service;db_statement=omit' +export OTEL_RUBY_INSTRUMENTATION_SINATRA_ENABLED="false" RUBYOPT="-r opentelemetry-auto-instrumentation" ruby application.rb ``` -Instrument Rails application: +**Configure instrumentation options:** ```console -RUBYOPT="-r opentelemetry-auto-instrumentation" rails server +export OTEL_RUBY_ENABLED_INSTRUMENTATIONS="redis" +export OTEL_RUBY_INSTRUMENTATION_REDIS_CONFIG_OPTS="peer_service=new_service;db_statement=omit" +RUBYOPT="-r opentelemetry-auto-instrumentation" ruby application.rb ``` -Instrument Rails application with `bundle exec`: +### Non-Rails Frameworks (Sinatra, Rackup, etc.) -Since the `opentelemetry-auto-instrumentation` gem should be installed through `gem install`, anything related to the OpenTelemetry gem won't be stored in Bundler's gem path. Therefore, users need to add an additional gem path that contains these gems prior to initialization. +For frameworks that don't automatically call `Bundler.require`, enable it explicitly: ```console -RUBYOPT="-r {PUT YOUR GEM PATH}/gems/opentelemetry-auto-instrumentation-0.1.0/lib/opentelemetry-auto-instrumentation" bundle exec rails server +export OTEL_RUBY_REQUIRE_BUNDLER="true" +RUBYOPT="-r opentelemetry-auto-instrumentation" rackup config.ru ``` -If you wish to load some gems outside the Gemfile, then they need to be placed in front of opentelemetry-auto-instrumentation: +### Advanced: Loading External Gems + +If you need to load gems outside your Gemfile (and have them instrumented), preload them before the auto-instrumentation: ```console -export BUNDLE_WITHOUT=development,test -gem install mysql2 -RUBYOPT="-r mysql2 -r opentelemetry-auto-instrumentation" ruby application.rb +RUBYOPT="-r mysql2 -r faraday -r opentelemetry-auto-instrumentation" ruby application.rb ``` -Instrument Sinatra application with rackup: +## Troubleshooting + +### How Auto-Instrumentation Works + +The gem works by patching `Bundler::Runtime#require` to inject OpenTelemetry initialization when gems are loaded. Rails applications call `Bundler.require` automatically during boot, so they work seamlessly. Other frameworks (like Sinatra) may require manual configuration. + +**Solution:** For non-Rails frameworks, either: + +- Call `Bundler.require` explicitly in your code, OR +- Set `OTEL_RUBY_REQUIRE_BUNDLER=true` environment variable -If you are using a Gemfile to install the required gems but without `Bundler.require`, set `REQUIRE_BUNDLER` to true. This way, `opentelemetry-auto-instrumentation` will call `Bundler.require` to initialize the required gems prior to SDK initialization. +### Instrumentation Timing Issues + +Instrumentation is only applied when gems are loaded through `Bundler.require`. If you require a library **after** `Bundler.require` has been called, it won't be instrumented. + +**Example of what doesn't work:** + +```ruby +# app.rb +Bundler.require +require 'faraday' # Loaded too late - won't be instrumented +``` + +**Solution:** Preload the gem via `RUBYOPT`: ```console -export REQUIRE_BUNDLER=true -RUBYOPT="-r opentelemetry-auto-instrumentation" rackup config.ru +RUBYOPT="-r faraday -r opentelemetry-auto-instrumentation" ruby application.rb ``` -### Troubleshooting +This ensures gems are loaded early enough for instrumentation to be applied. -The auto-instrumentation works by patching the `Bundler::Runtime#require` method to inject the `opentelemetry-auto-instrumentation` gem into your application. Rails applications automatically call `Bundler.require` during their boot process, so they work out of the box. However, many other frameworks (like Sinatra) do not call `Bundler.require` automatically, which means the OpenTelemetry SDK is never loaded. To fix this, explicitly call `Bundler.require` early in your application's startup process, or set the `OTEL_RUBY_REQUIRE_BUNDLER` environment variable to `true`. +### Dependency Version Conflicts -## Example +The auto-instrumentation gem loads OpenTelemetry components into Ruby's `$LOAD_PATH`. It also includes two non-OpenTelemetry dependencies required for OTLP exporters: -In example folder, executing the following commands should result in trace output. +- `google-protobuf` +- `googleapis-common-protos-types` -```console -# if the user doesn't want to install opentelemetry-auto-instrumentation from rubygems.org -# the user can build the gem and install it with gem install *.gem +**Problem:** If your Gemfile includes different versions of these gems, you may encounter version conflicts. -gem install opentelemetry-auto-instrumentation -bundle install -OTEL_RUBY_REQUIRE_BUNDLER=true OTEL_TRACES_EXPORTER=console RUBYOPT="-r opentelemetry-auto-instrumentation" OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG=false OTEL_RUBY_OPERATOR=false ruby app.rb +**Solution:** If you experience protobuf-related errors: + +1. Remove `google-protobuf` and `googleapis-common-protos-types` from your Gemfile +2. Let `opentelemetry-auto-instrumentation` manage these dependencies +3. In most cases, version mismatches won't cause issues, but this is the safest approach + +### Using with bundle exec + +Since the gem is installed via `gem install` (not in your Gemfile), you may need to specify the full path when using `bundle exec`: + +```console +RUBYOPT="-r /path/to/gems/opentelemetry-auto-instrumentation-X.X.X/lib/opentelemetry-auto-instrumentation" bundle exec rails server ``` +Find the path using: `gem which opentelemetry-auto-instrumentation` + +## Example + +See [example/README.md](example/README.md) + ## Configuration -These environment variables are not standard OpenTelemetry environment variables; they are only feature flags for this gem. - -| Environment Variable | Description | Default | Example | -| ---------------------- | ----------- | ------- | ------- | -| `OTEL_RUBY_REQUIRE_BUNDLER` | Set to `true` if you are using Bundler to install gems but without `Bundler.require` in your script during initialization. | nil | N/A | -| `OTEL_RUBY_ADDITIONAL_GEM_PATH` | Intended to be used for the OpenTelemetry Operator environment if you install `opentelemetry-auto-instrumentation` to a customized path. | nil | N/A | -| `OTEL_RUBY_OPERATOR` | Set to `true` to set the binding path for the OpenTelemetry Operator. | `/otel-auto-instrumentation-ruby` | N/A | -| `OTEL_RUBY_RESOURCE_DETECTORS` | Determine what kind of resource detector is needed. Currently supports `container`, `azure`, and `google_cloud_platform`. Use commas to separate multiple detectors. | nil | `container,azure` | -| `OTEL_RUBY_ENABLED_INSTRUMENTATIONS` | Configuration used when you only want to install instrumentation for specific libraries. | nil | `redis,active_record` | -| `OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG` | Set to `true` if want to see some debug information. This to avoid preloading the logger gem | nil | N/A | -| `OTEL_RUBY_UNLOAD_LIBRARY` | Use OTEL_RUBY_UNLOAD_LIBRARY to avoid certain gem preload (esp. google protobuf) | nil | N/A | +The following environment variables are specific to this gem (not standard OpenTelemetry variables): + +| Environment Variable | Description | Example | +| ---------------------- | ----------- | ------- | +| `OTEL_RUBY_REQUIRE_BUNDLER` | Set to `true` to automatically call `Bundler.require` during initialization. Required for frameworks that don't call it automatically (e.g., Sinatra). | `true` | +| `OTEL_RUBY_RESOURCE_DETECTORS` | Comma-separated list of resource detectors. Supported: `container`, `azure`, `aws`. **Note:** GCP detector not supported due to additional dependencies. | `container,azure,aws` | +| `OTEL_RUBY_ENABLED_INSTRUMENTATIONS` | Only load specific instrumentations (comma-separated). Omit to load all available. | `redis,mysql2,faraday` | +| `OTEL_RUBY_ADDITIONAL_GEM_PATH` | Custom gem installation path for OpenTelemetry Operator environments. | `/custom/gem/path` | +| `OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG` | Set to `true` for debug output during initialization. | `true` | +| `OTEL_RUBY_UNLOAD_LIBRARY` | Prevent specific gems from being preloaded (e.g., `google-protobuf`). | `google-protobuf` | ## How can I get involved? diff --git a/opentelemetry-auto-instrumentation/example/README.md b/opentelemetry-auto-instrumentation/example/README.md index 39dbe982c7..b6200294f9 100644 --- a/opentelemetry-auto-instrumentation/example/README.md +++ b/opentelemetry-auto-instrumentation/example/README.md @@ -2,50 +2,76 @@ ## Installation -Install opentelemetry-auto-instrumentation through `gem install` -Then `bundle install` +First, install the `opentelemetry-auto-instrumentation` gem using `gem install` (not through Bundler): + +```bash +gem install opentelemetry-auto-instrumentation +``` + +This gem should be installed outside your Gemfile so that it can be loaded globally through the `RUBYOPT` environment variable. ## Simple Example (simple-example) +A basic Ruby application that demonstrates auto-instrumentation. + ```bash -OTEL_RUBY_REQUIRE_BUNDLER=true OTEL_TRACES_EXPORTER=console RUBYOPT="-r opentelemetry-auto-instrumentation" OTEL_RUBY_OPERATOR=false ruby app.rb +bundle install +OTEL_RUBY_REQUIRE_BUNDLER=true OTEL_TRACES_EXPORTER=console RUBYOPT="-r opentelemetry-auto-instrumentation" ruby app.rb ``` +**What's happening:** + +- `OTEL_RUBY_REQUIRE_BUNDLER=true` tells the gem to call `Bundler.require` during initialization +- `OTEL_TRACES_EXPORTER=console` outputs trace data to the console for visibility +- `RUBYOPT` ensures `opentelemetry-auto-instrumentation` is loaded before your application code + ## Rails Example (rails-example) -Without auto-instrumentation +A Rails application demonstrating auto-instrumentation integration. + +### Without auto-instrumentation ```bash bundle exec rackup config.ru ``` -In other terminal make the request call +### With auto-instrumentation ```bash -wget http://localhost:9292 -# or curl http://localhost:9292 if you have curl on system +bundle install +OTEL_RUBY_REQUIRE_BUNDLER=false OTEL_TRACES_EXPORTER=console RUBYOPT="-r opentelemetry-auto-instrumentation" bundle exec rackup config.ru ``` -With auto-instrumentation +**What's happening:** -```bash -OTEL_RUBY_REQUIRE_BUNDLER=false OTEL_TRACES_EXPORTER=console RUBYOPT="-r opentelemetry-auto-instrumentation" OTEL_RUBY_OPERATOR=false bundle exec rackup config.ru -``` +- `OTEL_RUBY_REQUIRE_BUNDLER=false` because Rails automatically calls `Bundler.require` during boot +- The OpenTelemetry gem is loaded first via `RUBYOPT`, then Rails initializes with instrumentation automatically applied -The load sequence must be opentelemetry-auto-instrumentation -> user library -> bundler.require (initialize otel sdk and instrumentation installation) +### Test the instrumentation -In other terminal make the request call +In another terminal, make a request to generate traces: ```bash -wget http://localhost:9292 -# or curl http://localhost:9292 if you have curl on system +wget http://localhost:9292 # or use curl ``` +You should see trace output in the console where the Rails server is running. + +### Load sequence + +The correct sequence is: + +1. `opentelemetry-auto-instrumentation` is loaded (via `RUBYOPT`) +2. User libraries are required +3. `Bundler.require` is called (by Rails or manually) +4. OpenTelemetry SDK is initialized +5. Instrumentation is installed for loaded libraries + ### Troubleshooting: Default Gem Version Conflicts -If you encounter an error like "You have already activated [gem] X.X.X, but your Gemfile requires [gem] Y.Y.Y", this indicates a version conflict with a default gem (such as `json` or `logger`). +If you encounter an error like "You have already activated [gem] X.X.X, but your Gemfile requires [gem] Y.Y.Y", this indicates a version conflict with a default gem (such as `json` or `logger`). This occurs because your system's default Ruby gem versions are outdated compared to the fresh versions installed from the Gemfile in this folder. -**Error example:** +#### Error example ```console You have already activated json 2.6.3, but your Gemfile requires json 2.16.0. @@ -53,7 +79,7 @@ Since json is a default gem, you can either remove your dependency on it or try updating to a newer version of bundler that supports json as a default gem. ``` -**Solution:** +#### Solution Install the specific gem version that your Gemfile requires: @@ -69,6 +95,6 @@ gem install json -v '2.16.0' Then run your application again with auto-instrumentation. -**Why this happens:** +#### Why this happens -The OpenTelemetry gem is loaded before `bundle exec` runs. If the OpenTelemetry dependencies activate a default gem version that differs from what your Gemfile specifies, Bundler will raise an error. Installing the required version explicitly resolves this conflict. +The OpenTelemetry gem is loaded early in the Ruby startup process via `RUBYOPT`. If any of its dependencies activate a default gem version that differs from what your Gemfile specifies, Bundler will raise a conflict error. Installing the required version explicitly resolves this issue by replacing the system default with the specific version your project needs. diff --git a/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb b/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb index 1263154a43..2ccb09cf0f 100644 --- a/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb +++ b/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb @@ -57,17 +57,14 @@ def self.detect_resource_from_env env = ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s additional_resource = ::OpenTelemetry::SDK::Resources::Resource.create({}) + resource_map = { + 'container' => (defined?(::OpenTelemetry::Resource::Detector::Container) ? ::OpenTelemetry::Resource::Detector::Container : nil), + 'azure' => (defined?(::OpenTelemetry::Resource::Detector::Azure) ? ::OpenTelemetry::Resource::Detector::Azure : nil), + 'aws' => (defined?(::OpenTelemetry::Resource::Detector::AWS) ? ::OpenTelemetry::Resource::Detector::AWS : nil) + } + env.split(',').each do |detector| - case detector - when 'container' - additional_resource = additional_resource.merge(::OpenTelemetry::Resource::Detector::Container.detect) if defined? ::OpenTelemetry::Resource::Detector::Container - when 'google_cloud_platform' - additional_resource = additional_resource.merge(::OpenTelemetry::Resource::Detector::GoogleCloudPlatform.detect) if defined? ::OpenTelemetry::Resource::Detector::GoogleCloudPlatform - when 'azure' - additional_resource = additional_resource.merge(::OpenTelemetry::Resource::Detector::Azure.detect) if defined? ::OpenTelemetry::Resource::Detector::Azure - when 'aws' - additional_resource = additional_resource.merge(::OpenTelemetry::Resource::Detector::AWS.detect(%i[ec2 ecs eks lambda])) if defined? ::OpenTelemetry::Resource::Detector::AWS - end + additional_resource = additional_resource.merge(resource_map[detector].detect) if resource_map[detector] end additional_resource @@ -84,17 +81,13 @@ def self.require_otel @initialized = true - lib = File.expand_path('..', __dir__) - $LOAD_PATH.reject! { |path| path.include?('opentelemetry-auto-instrumentation') } - $LOAD_PATH.unshift(lib) - begin required_instrumentation = determine_enabled_instrumentation OpenTelemetry::SDK.configure do |c| c.resource = detect_resource_from_env if required_instrumentation.empty? - c.use_all # enables all instrumentation! + c.use_all else required_instrumentation.each do |instrumentation| c.use instrumentation @@ -108,7 +101,6 @@ def self.require_otel end end - # ref: https://github.com/newrelic/newrelic-ruby-agent/blob/dev/lib/boot/strap.rb def require(...) super OTelInitializer.require_otel @@ -117,44 +109,32 @@ def require(...) require 'bundler' -aws = ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s.include?('aws') -container = ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s.include?('container') -google_cloud_platform = ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s.include?('google_cloud_platform') -azure = ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s.include?('azure') - -# set OTEL_OPERATOR to false if not in autoinstrumentation-ruby image, default to /otel-auto-instrumentation-ruby -# /otel-auto-instrumentation-ruby is set in opentelemetry-operator ruby.go -operator_gem_path = ENV['OTEL_RUBY_OPERATOR'].nil? || ENV['OTEL_RUBY_OPERATOR'] == 'true' ? '/otel-auto-instrumentation-ruby' : nil -additional_gem_path = operator_gem_path || ENV['OTEL_RUBY_ADDITIONAL_GEM_PATH'] || Gem.dir -$stdout.puts "Loading the additional gem path from #{additional_gem_path}" if ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] == 'true' - -# use OTEL_RUBY_UNLOAD_LIBRARY to avoid certain gem preload (esp. google protobuf) -# e.g export OTEL_RUBY_UNLOAD_LIBRARY=google-protobuf;googleapis-common-protos-types -unload_libraries = ENV['OTEL_RUBY_UNLOAD_LIBRARY'].to_s.split(';') - -loaded_library_file_path = Dir.glob("#{additional_gem_path}/gems/*").select do |file_path| - gem_name = file_path.match(%r{/gems/([^/]+)-\d})[1] - (file_path.include?('opentelemetry') || file_path.include?('google')) && !unload_libraries.include?(gem_name) -end - -# rubocop:disable Style/StderrPuts -$stderr.puts "Loaded Library File Paths #{loaded_library_file_path.join(',')}" if ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] == 'true' -# rubocop:enable Style/StderrPuts - -loaded_library_file_path.each do |file_path| - $LOAD_PATH.unshift("#{file_path}/lib") +# /otel-auto-instrumentation-ruby is default path for otel operator (ruby.go) +# If requires different gem path to load gem, set env OTEL_RUBY_ADDITIONAL_GEM_PATH +gem_path = ENV['OTEL_RUBY_ADDITIONAL_GEM_PATH'] || '/otel-auto-instrumentation-ruby' || Gem.dir +$stdout.puts "Loading the gem path from #{gem_path}" if ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] == 'true' + +# Load OpenTelemetry components and their dependencies +# googleapis-common-protos-types and google-protobuf are dependencies for otlp exporters +loaded_library_file_path = Dir.glob("#{gem_path}/gems/*").select do |file_path| + file_path.include?('opentelemetry') || + file_path.include?('googleapis-common-protos-types') || + file_path.include?('google-protobuf') end +# unshift file_path add opentelemetry component at the top of $LOAD_PATH +loaded_library_file_path.each { |file_path| $LOAD_PATH.unshift("#{file_path}/lib") } $stdout.puts "$LOAD_PATH after unshift: #{$LOAD_PATH.join(',')}" if ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] == 'true' +# These are required for the prepend OTelBundlerPatch to fetch OpenTelemetry::SDK.configure require 'opentelemetry-sdk' require 'opentelemetry-instrumentation-all' require 'opentelemetry-exporter-otlp' -require 'opentelemetry-resource-detector-container' if container -require 'opentelemetry-resource-detector-google_cloud_platform' if google_cloud_platform -require 'opentelemetry-resource-detector-azure' if azure -require 'opentelemetry-resource-detector-aws' if aws +resource_detectors = ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s +require 'opentelemetry-resource-detector-container' if resource_detectors.include?('container') +require 'opentelemetry-resource-detector-azure' if resource_detectors.include?('azure') +require 'opentelemetry-resource-detector-aws' if resource_detectors.include?('aws') Bundler::Runtime.prepend(OTelBundlerPatch) diff --git a/opentelemetry-auto-instrumentation/opentelemetry-auto-instrumentation.gemspec b/opentelemetry-auto-instrumentation/opentelemetry-auto-instrumentation.gemspec index 07958a168a..c3ba688233 100644 --- a/opentelemetry-auto-instrumentation/opentelemetry-auto-instrumentation.gemspec +++ b/opentelemetry-auto-instrumentation/opentelemetry-auto-instrumentation.gemspec @@ -29,12 +29,12 @@ Gem::Specification.new do |spec| spec.add_dependency 'opentelemetry-api', '~> 1.7.0' spec.add_dependency 'opentelemetry-exporter-otlp', '~> 0.31.1' spec.add_dependency 'opentelemetry-helpers-mysql', '~> 0.4.0' - spec.add_dependency 'opentelemetry-helpers-sql-obfuscation', '~> 0.5.0' - spec.add_dependency 'opentelemetry-instrumentation-all', '~> 0.86.1' + spec.add_dependency 'opentelemetry-helpers-sql', '~> 0.3.0' + spec.add_dependency 'opentelemetry-helpers-sql-processor', '~> 0.4.0' + spec.add_dependency 'opentelemetry-instrumentation-all', '~> 0.90.1' spec.add_dependency 'opentelemetry-resource-detector-aws', '~> 0.5.0' spec.add_dependency 'opentelemetry-resource-detector-azure', '~> 0.3.0' spec.add_dependency 'opentelemetry-resource-detector-container', '~> 0.3.0' - spec.add_dependency 'opentelemetry-resource-detector-google_cloud_platform', '~> 0.3.0' spec.add_dependency 'opentelemetry-sdk', '~> 1.10.0' if spec.respond_to?(:metadata) From 10675d7bf19331f4310623ba14eebc26e4409a38 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Tue, 3 Feb 2026 14:40:09 -0500 Subject: [PATCH 47/68] rename the gem and move to packages/ folder --- .github/workflows/ci-contrib.yml | 17 ++- .../CHANGELOG.md | 1 - .../auto-instrumentation}/.rubocop.yml | 2 +- packages/auto-instrumentation/CHANGELOG.md | 1 + .../auto-instrumentation}/Gemfile | 0 .../auto-instrumentation}/LICENSE | 0 .../auto-instrumentation}/README.md | 34 +++--- .../auto-instrumentation}/Rakefile | 0 .../auto-instrumentation.gemspec | 6 +- .../auto-instrumentation}/example/README.md | 12 +- .../example/rails-example/Gemfile | 0 .../example/rails-example/app.rb | 0 .../example/rails-example/config.ru | 0 .../example/simple-example/Gemfile | 0 .../example/simple-example/app.rb | 0 .../lib/auto-instrumentation.rb | 0 .../auto-instrumentation}/lib/version.rb | 6 +- .../test/auto-instrumentation_test.rb | 103 ++++++++++++++++++ ...opentelemetry-auto-instrumentation_test.rb | 2 +- .../auto-instrumentation}/test/test_helper.rb | 0 20 files changed, 149 insertions(+), 35 deletions(-) delete mode 100644 opentelemetry-auto-instrumentation/CHANGELOG.md rename {opentelemetry-auto-instrumentation => packages/auto-instrumentation}/.rubocop.yml (71%) create mode 100644 packages/auto-instrumentation/CHANGELOG.md rename {opentelemetry-auto-instrumentation => packages/auto-instrumentation}/Gemfile (100%) rename {opentelemetry-auto-instrumentation => packages/auto-instrumentation}/LICENSE (100%) rename {opentelemetry-auto-instrumentation => packages/auto-instrumentation}/README.md (82%) rename {opentelemetry-auto-instrumentation => packages/auto-instrumentation}/Rakefile (100%) rename opentelemetry-auto-instrumentation/opentelemetry-auto-instrumentation.gemspec => packages/auto-instrumentation/auto-instrumentation.gemspec (91%) rename {opentelemetry-auto-instrumentation => packages/auto-instrumentation}/example/README.md (86%) rename {opentelemetry-auto-instrumentation => packages/auto-instrumentation}/example/rails-example/Gemfile (100%) rename {opentelemetry-auto-instrumentation => packages/auto-instrumentation}/example/rails-example/app.rb (100%) rename {opentelemetry-auto-instrumentation => packages/auto-instrumentation}/example/rails-example/config.ru (100%) rename {opentelemetry-auto-instrumentation => packages/auto-instrumentation}/example/simple-example/Gemfile (100%) rename {opentelemetry-auto-instrumentation => packages/auto-instrumentation}/example/simple-example/app.rb (100%) rename opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb => packages/auto-instrumentation/lib/auto-instrumentation.rb (100%) rename {opentelemetry-auto-instrumentation => packages/auto-instrumentation}/lib/version.rb (57%) create mode 100644 packages/auto-instrumentation/test/auto-instrumentation_test.rb rename {opentelemetry-auto-instrumentation => packages/auto-instrumentation}/test/opentelemetry-auto-instrumentation_test.rb (97%) rename {opentelemetry-auto-instrumentation => packages/auto-instrumentation}/test/test_helper.rb (100%) diff --git a/.github/workflows/ci-contrib.yml b/.github/workflows/ci-contrib.yml index c368aa2844..e34e90e480 100644 --- a/.github/workflows/ci-contrib.yml +++ b/.github/workflows/ci-contrib.yml @@ -11,6 +11,7 @@ on: - "resources/**" - "processor/**" - "sampler/**" + - "packages/**" - ".github/workflows/ci-contrib.yml" - ".github/actions/**" - "Gemfile" @@ -260,29 +261,35 @@ jobs: ruby: "jruby-10.0.2.0" auto-instrumentation: + if: ${{ github.repository == 'open-telemetry/opentelemetry-ruby-contrib' }} strategy: fail-fast: false matrix: os: - ubuntu-latest - name: "opentelemetry-auto-instrumentation / ${{ matrix.os }}" + name: "auto-instrumentation / ${{ matrix.os }}" runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: "Test Ruby 4.0" + uses: ./.github/actions/test_gem + with: + gem: "auto-instrumentation" + ruby: "4.0" - name: "Test Ruby 3.4" uses: ./.github/actions/test_gem with: - gem: "opentelemetry-auto-instrumentation" + gem: "auto-instrumentation" ruby: "3.4" - name: "Test Ruby 3.3" uses: ./.github/actions/test_gem with: - gem: "opentelemetry-auto-instrumentation" + gem: "auto-instrumentation" ruby: "3.3" - name: "Test Ruby 3.2" uses: ./.github/actions/test_gem with: - gem: "opentelemetry-auto-instrumentation" + gem: "auto-instrumentation" ruby: "3.2" yard: true rubocop: true diff --git a/opentelemetry-auto-instrumentation/CHANGELOG.md b/opentelemetry-auto-instrumentation/CHANGELOG.md deleted file mode 100644 index d068b6d9e8..0000000000 --- a/opentelemetry-auto-instrumentation/CHANGELOG.md +++ /dev/null @@ -1 +0,0 @@ -# Release History: opentelemetry-auto-instrumentation diff --git a/opentelemetry-auto-instrumentation/.rubocop.yml b/packages/auto-instrumentation/.rubocop.yml similarity index 71% rename from opentelemetry-auto-instrumentation/.rubocop.yml rename to packages/auto-instrumentation/.rubocop.yml index 154b948f5d..4c9e227275 100644 --- a/opentelemetry-auto-instrumentation/.rubocop.yml +++ b/packages/auto-instrumentation/.rubocop.yml @@ -1,4 +1,4 @@ -inherit_from: ../.rubocop.yml +inherit_from: ../../.rubocop.yml Naming/FileName: Enabled: false Gemspec/DevelopmentDependencies: diff --git a/packages/auto-instrumentation/CHANGELOG.md b/packages/auto-instrumentation/CHANGELOG.md new file mode 100644 index 0000000000..35e6f8e8b7 --- /dev/null +++ b/packages/auto-instrumentation/CHANGELOG.md @@ -0,0 +1 @@ +# Release History: auto-instrumentation diff --git a/opentelemetry-auto-instrumentation/Gemfile b/packages/auto-instrumentation/Gemfile similarity index 100% rename from opentelemetry-auto-instrumentation/Gemfile rename to packages/auto-instrumentation/Gemfile diff --git a/opentelemetry-auto-instrumentation/LICENSE b/packages/auto-instrumentation/LICENSE similarity index 100% rename from opentelemetry-auto-instrumentation/LICENSE rename to packages/auto-instrumentation/LICENSE diff --git a/opentelemetry-auto-instrumentation/README.md b/packages/auto-instrumentation/README.md similarity index 82% rename from opentelemetry-auto-instrumentation/README.md rename to packages/auto-instrumentation/README.md index 2694a43848..8431d133b8 100644 --- a/opentelemetry-auto-instrumentation/README.md +++ b/packages/auto-instrumentation/README.md @@ -1,6 +1,6 @@ # OpenTelemetry Auto Instrumentation -The `opentelemetry-auto-instrumentation` gem provides automatic loading and initialization of OpenTelemetry Ruby SDK for zero-code instrumentation of your applications. +The `auto-instrumentation` gem provides automatic loading and initialization of OpenTelemetry Ruby SDK for zero-code instrumentation of your applications. ## What is OpenTelemetry? @@ -21,14 +21,14 @@ This gem is particularly useful with the [OpenTelemetry Operator][opentelemetry- Install the gem: ```console -gem install opentelemetry-auto-instrumentation +gem install auto-instrumentation ``` **Note:** Install via `gem install` rather than adding to your Gemfile, as this gem needs to load before your application starts. ### What gets installed? -Installing `opentelemetry-auto-instrumentation` automatically includes: +Installing `auto-instrumentation` automatically includes: ```console opentelemetry-sdk @@ -49,7 +49,7 @@ opentelemetry-resource-detector-aws Instrument any Ruby application: ```console -RUBYOPT="-r opentelemetry-auto-instrumentation" ruby application.rb +RUBYOPT="-r auto-instrumentation" ruby application.rb ``` ### With Configuration @@ -62,7 +62,7 @@ export OTEL_EXPORTER_OTLP_ENDPOINT="your-endpoint" export OTEL_RUBY_RESOURCE_DETECTORS="container,azure" export OTEL_SERVICE_NAME="your-service-name" -RUBYOPT="-r opentelemetry-auto-instrumentation" ruby application.rb +RUBYOPT="-r auto-instrumentation" ruby application.rb ``` ### Rails Applications @@ -70,7 +70,7 @@ RUBYOPT="-r opentelemetry-auto-instrumentation" ruby application.rb Rails automatically calls `Bundler.require`, so instrumentation works out of the box: ```console -RUBYOPT="-r opentelemetry-auto-instrumentation" rails server +RUBYOPT="-r auto-instrumentation" rails server ``` ### Selective Instrumentation @@ -79,14 +79,14 @@ RUBYOPT="-r opentelemetry-auto-instrumentation" rails server ```console export OTEL_RUBY_ENABLED_INSTRUMENTATIONS="mysql2,redis,faraday" -RUBYOPT="-r opentelemetry-auto-instrumentation" ruby application.rb +RUBYOPT="-r auto-instrumentation" ruby application.rb ``` **Disable specific instrumentations:** ```console export OTEL_RUBY_INSTRUMENTATION_SINATRA_ENABLED="false" -RUBYOPT="-r opentelemetry-auto-instrumentation" ruby application.rb +RUBYOPT="-r auto-instrumentation" ruby application.rb ``` **Configure instrumentation options:** @@ -94,7 +94,7 @@ RUBYOPT="-r opentelemetry-auto-instrumentation" ruby application.rb ```console export OTEL_RUBY_ENABLED_INSTRUMENTATIONS="redis" export OTEL_RUBY_INSTRUMENTATION_REDIS_CONFIG_OPTS="peer_service=new_service;db_statement=omit" -RUBYOPT="-r opentelemetry-auto-instrumentation" ruby application.rb +RUBYOPT="-r auto-instrumentation" ruby application.rb ``` ### Non-Rails Frameworks (Sinatra, Rackup, etc.) @@ -103,7 +103,7 @@ For frameworks that don't automatically call `Bundler.require`, enable it explic ```console export OTEL_RUBY_REQUIRE_BUNDLER="true" -RUBYOPT="-r opentelemetry-auto-instrumentation" rackup config.ru +RUBYOPT="-r auto-instrumentation" rackup config.ru ``` ### Advanced: Loading External Gems @@ -111,7 +111,7 @@ RUBYOPT="-r opentelemetry-auto-instrumentation" rackup config.ru If you need to load gems outside your Gemfile (and have them instrumented), preload them before the auto-instrumentation: ```console -RUBYOPT="-r mysql2 -r faraday -r opentelemetry-auto-instrumentation" ruby application.rb +RUBYOPT="-r mysql2 -r faraday -r auto-instrumentation" ruby application.rb ``` ## Troubleshooting @@ -140,7 +140,7 @@ require 'faraday' # Loaded too late - won't be instrumented **Solution:** Preload the gem via `RUBYOPT`: ```console -RUBYOPT="-r faraday -r opentelemetry-auto-instrumentation" ruby application.rb +RUBYOPT="-r faraday -r auto-instrumentation" ruby application.rb ``` This ensures gems are loaded early enough for instrumentation to be applied. @@ -157,7 +157,7 @@ The auto-instrumentation gem loads OpenTelemetry components into Ruby's `$LOAD_P **Solution:** If you experience protobuf-related errors: 1. Remove `google-protobuf` and `googleapis-common-protos-types` from your Gemfile -2. Let `opentelemetry-auto-instrumentation` manage these dependencies +2. Let `auto-instrumentation` manage these dependencies 3. In most cases, version mismatches won't cause issues, but this is the safest approach ### Using with bundle exec @@ -165,10 +165,10 @@ The auto-instrumentation gem loads OpenTelemetry components into Ruby's `$LOAD_P Since the gem is installed via `gem install` (not in your Gemfile), you may need to specify the full path when using `bundle exec`: ```console -RUBYOPT="-r /path/to/gems/opentelemetry-auto-instrumentation-X.X.X/lib/opentelemetry-auto-instrumentation" bundle exec rails server +RUBYOPT="-r /path/to/gems/auto-instrumentation-X.X.X/lib/auto-instrumentation" bundle exec rails server ``` -Find the path using: `gem which opentelemetry-auto-instrumentation` +Find the path using: `gem which auto-instrumentation` ## Example @@ -189,13 +189,13 @@ The following environment variables are specific to this gem (not standard OpenT ## How can I get involved? -The `opentelemetry-auto-instrumentation` gem source is on GitHub, along with related gems. +The `auto-instrumentation` gem source is on GitHub, along with related gems. The OpenTelemetry Ruby gems are maintained by the OpenTelemetry Ruby special interest group (SIG). You can get involved by joining us on our [GitHub Discussions][discussions-url], [Slack Channel][slack-channel] or attending our weekly meeting. See the [meeting calendar][community-meetings] for dates and times. For more information on this and other language SIGs, see the OpenTelemetry [community page][ruby-sig]. ## License -The `opentelemetry-auto-instrumentation` gem is distributed under the Apache 2.0 license. See LICENSE for more information. +The `auto-instrumentation` gem is distributed under the Apache 2.0 license. See LICENSE for more information. [ruby-sig]: https://github.com/open-telemetry/community#ruby-sig [community-meetings]: https://github.com/open-telemetry/community#community-meetings diff --git a/opentelemetry-auto-instrumentation/Rakefile b/packages/auto-instrumentation/Rakefile similarity index 100% rename from opentelemetry-auto-instrumentation/Rakefile rename to packages/auto-instrumentation/Rakefile diff --git a/opentelemetry-auto-instrumentation/opentelemetry-auto-instrumentation.gemspec b/packages/auto-instrumentation/auto-instrumentation.gemspec similarity index 91% rename from opentelemetry-auto-instrumentation/opentelemetry-auto-instrumentation.gemspec rename to packages/auto-instrumentation/auto-instrumentation.gemspec index c3ba688233..2e7aaf493c 100644 --- a/opentelemetry-auto-instrumentation/opentelemetry-auto-instrumentation.gemspec +++ b/packages/auto-instrumentation/auto-instrumentation.gemspec @@ -9,8 +9,8 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'version' Gem::Specification.new do |spec| - spec.name = 'opentelemetry-auto-instrumentation' - spec.version = VERSION + spec.name = 'auto-instrumentation' + spec.version = OpenTelemetry::AutoInstrumentation::VERSION spec.authors = ['OpenTelemetry Authors'] spec.email = ['cncf-opentelemetry-contributors@lists.cncf.io'] @@ -39,7 +39,7 @@ Gem::Specification.new do |spec| if spec.respond_to?(:metadata) spec.metadata['changelog_uri'] = "https://rubydoc.info/gems/#{spec.name}/#{spec.version}/file/CHANGELOG.md" - spec.metadata['source_code_uri'] = 'https://github.com/open-telemetry/opentelemetry-ruby-contrib/opentelemetry-auto-instrumentation' + spec.metadata['source_code_uri'] = 'https://github.com/open-telemetry/opentelemetry-ruby-contrib/auto-instrumentation' spec.metadata['bug_tracker_uri'] = 'https://github.com/open-telemetry/opentelemetry-ruby-contrib/issues' spec.metadata['documentation_uri'] = "https://rubydoc.info/gems/#{spec.name}/#{spec.version}" end diff --git a/opentelemetry-auto-instrumentation/example/README.md b/packages/auto-instrumentation/example/README.md similarity index 86% rename from opentelemetry-auto-instrumentation/example/README.md rename to packages/auto-instrumentation/example/README.md index b6200294f9..b6f42bdb3a 100644 --- a/opentelemetry-auto-instrumentation/example/README.md +++ b/packages/auto-instrumentation/example/README.md @@ -2,10 +2,10 @@ ## Installation -First, install the `opentelemetry-auto-instrumentation` gem using `gem install` (not through Bundler): +First, install the `auto-instrumentation` gem using `gem install` (not through Bundler): ```bash -gem install opentelemetry-auto-instrumentation +gem install auto-instrumentation ``` This gem should be installed outside your Gemfile so that it can be loaded globally through the `RUBYOPT` environment variable. @@ -16,14 +16,14 @@ A basic Ruby application that demonstrates auto-instrumentation. ```bash bundle install -OTEL_RUBY_REQUIRE_BUNDLER=true OTEL_TRACES_EXPORTER=console RUBYOPT="-r opentelemetry-auto-instrumentation" ruby app.rb +OTEL_RUBY_REQUIRE_BUNDLER=true OTEL_TRACES_EXPORTER=console RUBYOPT="-r auto-instrumentation" ruby app.rb ``` **What's happening:** - `OTEL_RUBY_REQUIRE_BUNDLER=true` tells the gem to call `Bundler.require` during initialization - `OTEL_TRACES_EXPORTER=console` outputs trace data to the console for visibility -- `RUBYOPT` ensures `opentelemetry-auto-instrumentation` is loaded before your application code +- `RUBYOPT` ensures `auto-instrumentation` is loaded before your application code ## Rails Example (rails-example) @@ -39,7 +39,7 @@ bundle exec rackup config.ru ```bash bundle install -OTEL_RUBY_REQUIRE_BUNDLER=false OTEL_TRACES_EXPORTER=console RUBYOPT="-r opentelemetry-auto-instrumentation" bundle exec rackup config.ru +OTEL_RUBY_REQUIRE_BUNDLER=false OTEL_TRACES_EXPORTER=console RUBYOPT="-r auto-instrumentation" bundle exec rackup config.ru ``` **What's happening:** @@ -61,7 +61,7 @@ You should see trace output in the console where the Rails server is running. The correct sequence is: -1. `opentelemetry-auto-instrumentation` is loaded (via `RUBYOPT`) +1. `auto-instrumentation` is loaded (via `RUBYOPT`) 2. User libraries are required 3. `Bundler.require` is called (by Rails or manually) 4. OpenTelemetry SDK is initialized diff --git a/opentelemetry-auto-instrumentation/example/rails-example/Gemfile b/packages/auto-instrumentation/example/rails-example/Gemfile similarity index 100% rename from opentelemetry-auto-instrumentation/example/rails-example/Gemfile rename to packages/auto-instrumentation/example/rails-example/Gemfile diff --git a/opentelemetry-auto-instrumentation/example/rails-example/app.rb b/packages/auto-instrumentation/example/rails-example/app.rb similarity index 100% rename from opentelemetry-auto-instrumentation/example/rails-example/app.rb rename to packages/auto-instrumentation/example/rails-example/app.rb diff --git a/opentelemetry-auto-instrumentation/example/rails-example/config.ru b/packages/auto-instrumentation/example/rails-example/config.ru similarity index 100% rename from opentelemetry-auto-instrumentation/example/rails-example/config.ru rename to packages/auto-instrumentation/example/rails-example/config.ru diff --git a/opentelemetry-auto-instrumentation/example/simple-example/Gemfile b/packages/auto-instrumentation/example/simple-example/Gemfile similarity index 100% rename from opentelemetry-auto-instrumentation/example/simple-example/Gemfile rename to packages/auto-instrumentation/example/simple-example/Gemfile diff --git a/opentelemetry-auto-instrumentation/example/simple-example/app.rb b/packages/auto-instrumentation/example/simple-example/app.rb similarity index 100% rename from opentelemetry-auto-instrumentation/example/simple-example/app.rb rename to packages/auto-instrumentation/example/simple-example/app.rb diff --git a/opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb b/packages/auto-instrumentation/lib/auto-instrumentation.rb similarity index 100% rename from opentelemetry-auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb rename to packages/auto-instrumentation/lib/auto-instrumentation.rb diff --git a/opentelemetry-auto-instrumentation/lib/version.rb b/packages/auto-instrumentation/lib/version.rb similarity index 57% rename from opentelemetry-auto-instrumentation/lib/version.rb rename to packages/auto-instrumentation/lib/version.rb index 57bc2eb570..889a90ebd8 100644 --- a/opentelemetry-auto-instrumentation/lib/version.rb +++ b/packages/auto-instrumentation/lib/version.rb @@ -4,4 +4,8 @@ # # SPDX-License-Identifier: Apache-2.0 -VERSION = '0.0.0' +module OpenTelemetry + module AutoInstrumentation + VERSION = '0.0.0' + end +end diff --git a/packages/auto-instrumentation/test/auto-instrumentation_test.rb b/packages/auto-instrumentation/test/auto-instrumentation_test.rb new file mode 100644 index 0000000000..13eb778710 --- /dev/null +++ b/packages/auto-instrumentation/test/auto-instrumentation_test.rb @@ -0,0 +1,103 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'test_helper' + +describe 'AutoInstrumentation' do + let(:auto_instrumentation_path) { File.expand_path('../lib/auto-instrumentation.rb', __dir__) } + + before do + ENV['OTEL_RUBY_ENABLED_INSTRUMENTATIONS'] = nil + ENV['OTEL_RUBY_INSTRUMENTATION_NET_HTTP_ENABLED'] = nil + ENV['OTEL_RUBY_RESOURCE_DETECTORS'] = nil + ENV['OTEL_RUBY_REQUIRE_BUNDLER'] = nil + ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] = nil + end + + after do + ENV['OTEL_RUBY_ENABLED_INSTRUMENTATIONS'] = nil + ENV['OTEL_RUBY_INSTRUMENTATION_NET_HTTP_ENABLED'] = nil + ENV['OTEL_RUBY_RESOURCE_DETECTORS'] = nil + ENV['OTEL_RUBY_REQUIRE_BUNDLER'] = nil + ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] = nil + end + + def run_in_subprocess(env_vars = {}) + # Run the test in a subprocess to avoid contaminating the test environment + read_pipe, write_pipe = IO.pipe + + pid = fork do + read_pipe.close + env_vars.each { |key, value| ENV[key] = value } + + ENV['OTEL_RUBY_REQUIRE_BUNDLER'] = 'false' + begin + # Load the auto-instrumentation library + load auto_instrumentation_path + Bundler.require + + # Get tracer provider information + tracer_provider = OpenTelemetry.tracer_provider + resource = tracer_provider.instance_variable_get(:@resource) + resource_attributes = resource.instance_variable_get(:@attributes) + registry = tracer_provider.instance_variable_get(:@registry) + instrumentation_names = registry.map { |entry| entry.first.name } + + # Serialize and send results back to parent + result = Marshal.dump({ + tracer_provider_class: tracer_provider.class.name, + resource_attributes: resource_attributes, + instrumentation_names: instrumentation_names + }) + write_pipe.write(result) + rescue StandardError => e + error_result = Marshal.dump({ error: e.message, backtrace: e.backtrace }) + write_pipe.write(error_result) + ensure + write_pipe.close + exit!(0) + end + end + + write_pipe.close + result_data = read_pipe.read + read_pipe.close + Process.wait(pid) + + # rubocop:disable Security/MarshalLoad + Marshal.load(result_data) + # rubocop:enable Security/MarshalLoad + end + + it 'simple_load_test' do + result = run_in_subprocess + + _(result[:error]).must_be_nil + _(result[:tracer_provider_class]).must_equal 'OpenTelemetry::SDK::Trace::TracerProvider' + _(result[:resource_attributes]['service.name']).must_equal 'unknown_service' + _(result[:resource_attributes]['telemetry.sdk.name']).must_equal 'opentelemetry' + _(result[:resource_attributes]['telemetry.sdk.language']).must_equal 'ruby' + _(result[:resource_attributes].key?('container.id')).must_equal false + _(result[:instrumentation_names]).must_include 'OpenTelemetry::Instrumentation::Net::HTTP' + _(result[:instrumentation_names]).must_include 'OpenTelemetry::Instrumentation::Rake' + end + + it 'simple_load_with_net_http_disabled' do + result = run_in_subprocess('OTEL_RUBY_INSTRUMENTATION_NET_HTTP_ENABLED' => 'false') + + _(result[:error]).must_be_nil + _(result[:instrumentation_names]).must_include 'OpenTelemetry::Instrumentation::Rake' + _(result[:instrumentation_names]).wont_include 'OpenTelemetry::Instrumentation::Net::HTTP' + end + + it 'simple_load_with_desired_instrument_only' do + result = run_in_subprocess('OTEL_RUBY_ENABLED_INSTRUMENTATIONS' => 'net_http') + + _(result[:error]).must_be_nil + _(result[:instrumentation_names]).must_include 'OpenTelemetry::Instrumentation::Net::HTTP' + _(result[:instrumentation_names]).wont_include 'OpenTelemetry::Instrumentation::Rake' + end +end diff --git a/opentelemetry-auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb b/packages/auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb similarity index 97% rename from opentelemetry-auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb rename to packages/auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb index 6d93430da3..13eb778710 100644 --- a/opentelemetry-auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb +++ b/packages/auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb @@ -7,7 +7,7 @@ require 'test_helper' describe 'AutoInstrumentation' do - let(:auto_instrumentation_path) { File.expand_path('../lib/opentelemetry-auto-instrumentation.rb', __dir__) } + let(:auto_instrumentation_path) { File.expand_path('../lib/auto-instrumentation.rb', __dir__) } before do ENV['OTEL_RUBY_ENABLED_INSTRUMENTATIONS'] = nil diff --git a/opentelemetry-auto-instrumentation/test/test_helper.rb b/packages/auto-instrumentation/test/test_helper.rb similarity index 100% rename from opentelemetry-auto-instrumentation/test/test_helper.rb rename to packages/auto-instrumentation/test/test_helper.rb From a6e2c13a8caea48dd7c023d31ddb9a50ea86df25 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Tue, 3 Feb 2026 14:43:28 -0500 Subject: [PATCH 48/68] add logger for 4.0.0 --- packages/auto-instrumentation/Gemfile | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/auto-instrumentation/Gemfile b/packages/auto-instrumentation/Gemfile index 228d700664..f38b803f26 100644 --- a/packages/auto-instrumentation/Gemfile +++ b/packages/auto-instrumentation/Gemfile @@ -22,4 +22,5 @@ group :test do gem 'bigdecimal' gem 'mutex_m' end + gem 'logger' if RUBY_VERSION >= '4.0.0' end From 1c43468363cb89a7ebfbf8477986dd86503800c9 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Wed, 4 Mar 2026 10:56:52 +1100 Subject: [PATCH 49/68] emit warning if user Gemfile contain opentelemetry-related gem --- .../example/rails-example/app.rb | 2 + .../lib/auto-instrumentation.rb | 20 +++++ ...opentelemetry-auto-instrumentation_test.rb | 90 ++++++++----------- .../auto-instrumentation/test/test_helper.rb | 83 +++++++++++++++++ 4 files changed, 140 insertions(+), 55 deletions(-) diff --git a/packages/auto-instrumentation/example/rails-example/app.rb b/packages/auto-instrumentation/example/rails-example/app.rb index 9ffc35f864..1c3bf91169 100644 --- a/packages/auto-instrumentation/example/rails-example/app.rb +++ b/packages/auto-instrumentation/example/rails-example/app.rb @@ -10,6 +10,7 @@ require 'bundler' Bundler.require +# rubocop:disable Style/OneClassPerFile # MyApp class MyApp < Rails::Application config.secret_key_base = 'your_secret_key_here' @@ -43,5 +44,6 @@ def create } end end +# rubocop:enable Style/OneClassPerFile MyApp.initialize! diff --git a/packages/auto-instrumentation/lib/auto-instrumentation.rb b/packages/auto-instrumentation/lib/auto-instrumentation.rb index 2ccb09cf0f..df74d70ede 100644 --- a/packages/auto-instrumentation/lib/auto-instrumentation.rb +++ b/packages/auto-instrumentation/lib/auto-instrumentation.rb @@ -76,12 +76,32 @@ def self.determine_enabled_instrumentation env.split(',').map { |instrumentation| OTEL_INSTRUMENTATION_MAP[instrumentation] } end + def self.check_for_bundled_otel_gems + bundled_otel_gems = Bundler.definition.dependencies.select do |dep| + dep.name.start_with?('opentelemetry-') + end + + return if bundled_otel_gems.empty? + + gem_names = bundled_otel_gems.map(&:name).sort.join(', ') + warn '[OpenTelemetry] WARNING: Detected OpenTelemetry gems in your Gemfile: ' \ + "#{gem_names}. When using auto-instrumentation, OpenTelemetry gems are loaded " \ + 'from the auto-instrumentation gem path, NOT from your bundle. The gem versions ' \ + 'in your Gemfile/Gemfile.lock are not used and may cause version conflicts or ' \ + 'unexpected behavior. Please remove these gems from your Gemfile when using ' \ + 'auto-instrumentation.' + rescue StandardError => e + warn "[OpenTelemetry] WARNING: Unable to check Gemfile for OpenTelemetry gems: #{e.message}" if ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] == 'true' + end + def self.require_otel return if @initialized @initialized = true begin + check_for_bundled_otel_gems + required_instrumentation = determine_enabled_instrumentation OpenTelemetry::SDK.configure do |c| diff --git a/packages/auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb b/packages/auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb index 13eb778710..9e3313ebf9 100644 --- a/packages/auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb +++ b/packages/auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb @@ -17,61 +17,6 @@ ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] = nil end - after do - ENV['OTEL_RUBY_ENABLED_INSTRUMENTATIONS'] = nil - ENV['OTEL_RUBY_INSTRUMENTATION_NET_HTTP_ENABLED'] = nil - ENV['OTEL_RUBY_RESOURCE_DETECTORS'] = nil - ENV['OTEL_RUBY_REQUIRE_BUNDLER'] = nil - ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] = nil - end - - def run_in_subprocess(env_vars = {}) - # Run the test in a subprocess to avoid contaminating the test environment - read_pipe, write_pipe = IO.pipe - - pid = fork do - read_pipe.close - env_vars.each { |key, value| ENV[key] = value } - - ENV['OTEL_RUBY_REQUIRE_BUNDLER'] = 'false' - begin - # Load the auto-instrumentation library - load auto_instrumentation_path - Bundler.require - - # Get tracer provider information - tracer_provider = OpenTelemetry.tracer_provider - resource = tracer_provider.instance_variable_get(:@resource) - resource_attributes = resource.instance_variable_get(:@attributes) - registry = tracer_provider.instance_variable_get(:@registry) - instrumentation_names = registry.map { |entry| entry.first.name } - - # Serialize and send results back to parent - result = Marshal.dump({ - tracer_provider_class: tracer_provider.class.name, - resource_attributes: resource_attributes, - instrumentation_names: instrumentation_names - }) - write_pipe.write(result) - rescue StandardError => e - error_result = Marshal.dump({ error: e.message, backtrace: e.backtrace }) - write_pipe.write(error_result) - ensure - write_pipe.close - exit!(0) - end - end - - write_pipe.close - result_data = read_pipe.read - read_pipe.close - Process.wait(pid) - - # rubocop:disable Security/MarshalLoad - Marshal.load(result_data) - # rubocop:enable Security/MarshalLoad - end - it 'simple_load_test' do result = run_in_subprocess @@ -100,4 +45,39 @@ def run_in_subprocess(env_vars = {}) _(result[:instrumentation_names]).must_include 'OpenTelemetry::Instrumentation::Net::HTTP' _(result[:instrumentation_names]).wont_include 'OpenTelemetry::Instrumentation::Rake' end + + describe 'check_for_bundled_otel_gems' do + it 'emits no warning when there are no opentelemetry gems in the bundle' do + result = run_in_subprocess({}, dep_names: %w[rack faraday]) + + _(result[:error]).must_be_nil + _(result[:warning_output]).must_be_empty + end + + it 'emits a warning listing detected opentelemetry gems' do + otel_gems = %w[opentelemetry-sdk opentelemetry-instrumentation-net_http rack] + result = run_in_subprocess({}, dep_names: otel_gems) + + _(result[:error]).must_be_nil + _(result[:warning_output]).must_include '[OpenTelemetry] WARNING' + _(result[:warning_output]).must_include 'opentelemetry-instrumentation-net_http' + _(result[:warning_output]).must_include 'opentelemetry-sdk' + _(result[:warning_output]).wont_include 'rack' + end + + it 'emits no warning when Bundler.definition raises and debug mode is off' do + result = run_in_subprocess({}, raise_error: true) + + _(result[:error]).must_be_nil + _(result[:warning_output]).must_be_empty + end + + it 'emits a warning when Bundler.definition raises and debug mode is on' do + result = run_in_subprocess({ 'OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG' => 'true' }, raise_error: true) + + _(result[:error]).must_be_nil + _(result[:warning_output]).must_include '[OpenTelemetry] WARNING: Unable to check Gemfile' + _(result[:warning_output]).must_include 'simulated bundler error' + end + end end diff --git a/packages/auto-instrumentation/test/test_helper.rb b/packages/auto-instrumentation/test/test_helper.rb index 0296024b2b..a2f0da7bed 100644 --- a/packages/auto-instrumentation/test/test_helper.rb +++ b/packages/auto-instrumentation/test/test_helper.rb @@ -12,3 +12,86 @@ require 'opentelemetry-test-helpers' require 'opentelemetry/resource/detector' require 'net/http' + +# Helper function that execute the auto-instrumentation in isolated env +def run_in_subprocess(env_vars = {}, opts = {}) + dep_names = opts[:dep_names] + raise_error = opts.fetch(:raise_error, false) + + read_pipe, write_pipe = IO.pipe + + pid = fork do + read_pipe.close + env_vars.each { |key, value| ENV[key] = value } + ENV['OTEL_RUBY_REQUIRE_BUNDLER'] = 'false' + + begin + load auto_instrumentation_path + + require 'stringio' + stderr_capture = StringIO.new + old_stderr = $stderr + $stderr = stderr_capture + + result = {} + + # check the log msg based on different condition + if !dep_names.nil? || raise_error + fake_dep = Struct.new(:name) + fake_deps = (dep_names || []).map { |n| fake_dep.new(n) } + + if raise_error + # Suppress the Ruby "method redefined" warning that define_singleton_method + # produces when overwriting Bundler's existing :definition method. + old_verbose = $VERBOSE + $VERBOSE = nil + Bundler.define_singleton_method(:definition) { raise StandardError, 'simulated bundler error' } + else + fake_definition = Object.new + fake_definition.define_singleton_method(:dependencies) { fake_deps } + old_verbose = $VERBOSE + $VERBOSE = nil + Bundler.define_singleton_method(:definition) { fake_definition } + end + $VERBOSE = old_verbose + + OTelBundlerPatch::OTelInitializer.check_for_bundled_otel_gems + else + Bundler.require + + tracer_provider = OpenTelemetry.tracer_provider + resource = tracer_provider.instance_variable_get(:@resource) + resource_attributes = resource.instance_variable_get(:@attributes) + registry = tracer_provider.instance_variable_get(:@registry) + instrumentation_names = registry.map { |entry| entry.first.name } + + result.merge!( + tracer_provider_class: tracer_provider.class.name, + resource_attributes: resource_attributes, + instrumentation_names: instrumentation_names + ) + end + + $stderr = old_stderr + result[:warning_output] = stderr_capture.string + write_pipe.write(Marshal.dump(result)) + rescue StandardError => e + $stderr = old_stderr if defined?(old_stderr) + error_result = Marshal.dump({ error: e.message, backtrace: e.backtrace, warning_output: defined?(stderr_capture) ? stderr_capture.string : '' }) + write_pipe.write(error_result) + ensure + $stderr = old_stderr if defined?(old_stderr) + write_pipe.close + exit!(0) + end + end + + write_pipe.close + result_data = read_pipe.read + read_pipe.close + Process.wait(pid) + + # rubocop:disable Security/MarshalLoad + Marshal.load(result_data) + # rubocop:enable Security/MarshalLoad +end From 608417a1bfab37e02cdb19357f8321becdb42c8f Mon Sep 17 00:00:00 2001 From: Xuan <112967240+xuan-cao-swi@users.noreply.github.com> Date: Mon, 13 Apr 2026 11:23:15 -0400 Subject: [PATCH 50/68] Apply suggestions from code review Co-authored-by: Ariel Valentin Co-authored-by: James Thompson --- .github/workflows/ci-contrib.yml | 6 ------ packages/auto-instrumentation/CHANGELOG.md | 2 +- packages/auto-instrumentation/auto-instrumentation.gemspec | 2 +- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci-contrib.yml b/.github/workflows/ci-contrib.yml index de6e63733c..70be6257cc 100644 --- a/.github/workflows/ci-contrib.yml +++ b/.github/workflows/ci-contrib.yml @@ -255,12 +255,6 @@ jobs: with: gem: "auto-instrumentation" ruby: "3.3" - - name: "Test Ruby 3.2" - uses: ./.github/actions/test_gem with: - gem: "auto-instrumentation" - ruby: "3.2" yard: true - rubocop: true - coverage: true build: true diff --git a/packages/auto-instrumentation/CHANGELOG.md b/packages/auto-instrumentation/CHANGELOG.md index 35e6f8e8b7..d068b6d9e8 100644 --- a/packages/auto-instrumentation/CHANGELOG.md +++ b/packages/auto-instrumentation/CHANGELOG.md @@ -1 +1 @@ -# Release History: auto-instrumentation +# Release History: opentelemetry-auto-instrumentation diff --git a/packages/auto-instrumentation/auto-instrumentation.gemspec b/packages/auto-instrumentation/auto-instrumentation.gemspec index 2e7aaf493c..8ab1a131a7 100644 --- a/packages/auto-instrumentation/auto-instrumentation.gemspec +++ b/packages/auto-instrumentation/auto-instrumentation.gemspec @@ -24,7 +24,7 @@ Gem::Specification.new do |spec| ['LICENSE'] spec.require_paths = ['lib'] - spec.required_ruby_version = '>= 3.2' + spec.required_ruby_version = '>= 3.3' spec.add_dependency 'opentelemetry-api', '~> 1.7.0' spec.add_dependency 'opentelemetry-exporter-otlp', '~> 0.31.1' From 95958ec274b0d39e9c25ff5812494e8d568b8d66 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Mon, 13 Apr 2026 11:35:09 -0400 Subject: [PATCH 51/68] revision --- .cspell.yml | 1 - .github/workflows/ci-contrib.yml | 1 - packages/auto-instrumentation/Gemfile | 16 ++++++------- .../example/rails-example/app.rb | 1 - .../lib/auto-instrumentation.rb | 23 ++++++++++--------- .../auto-instrumentation/test/test_helper.rb | 2 +- 6 files changed, 21 insertions(+), 23 deletions(-) diff --git a/.cspell.yml b/.cspell.yml index d465ea51dd..2d92b1789a 100644 --- a/.cspell.yml +++ b/.cspell.yml @@ -88,7 +88,6 @@ words: - webmocks - yardoc - rackup - - traceparent - tracestate - linkspector - sarif diff --git a/.github/workflows/ci-contrib.yml b/.github/workflows/ci-contrib.yml index 70be6257cc..31a3729a94 100644 --- a/.github/workflows/ci-contrib.yml +++ b/.github/workflows/ci-contrib.yml @@ -255,6 +255,5 @@ jobs: with: gem: "auto-instrumentation" ruby: "3.3" - with: yard: true build: true diff --git a/packages/auto-instrumentation/Gemfile b/packages/auto-instrumentation/Gemfile index f38b803f26..260ba92655 100644 --- a/packages/auto-instrumentation/Gemfile +++ b/packages/auto-instrumentation/Gemfile @@ -9,14 +9,14 @@ source 'https://rubygems.org' gemspec group :test do - gem 'bundler' - gem 'minitest' - gem 'rake' - gem 'rubocop' - gem 'rubocop-performance' - gem 'simplecov' - gem 'yard' - gem 'opentelemetry-test-helpers' + gem 'bundler', '~> 2.4' + gem 'minitest', '~> 6.0' + gem 'rake', '~> 13.0' + gem 'rubocop', '~> 1.86.0' + gem 'rubocop-performance', '~> 1.26.0' + gem 'simplecov', '~> 0.22.0' + gem 'yard', '~> 0.9' + gem 'opentelemetry-test-helpers', '~> 0.8.0' if RUBY_VERSION >= '3.4' gem 'base64' gem 'bigdecimal' diff --git a/packages/auto-instrumentation/example/rails-example/app.rb b/packages/auto-instrumentation/example/rails-example/app.rb index 1c3bf91169..e19d0ab287 100644 --- a/packages/auto-instrumentation/example/rails-example/app.rb +++ b/packages/auto-instrumentation/example/rails-example/app.rb @@ -10,7 +10,6 @@ require 'bundler' Bundler.require -# rubocop:disable Style/OneClassPerFile # MyApp class MyApp < Rails::Application config.secret_key_base = 'your_secret_key_here' diff --git a/packages/auto-instrumentation/lib/auto-instrumentation.rb b/packages/auto-instrumentation/lib/auto-instrumentation.rb index df74d70ede..957eacabf0 100644 --- a/packages/auto-instrumentation/lib/auto-instrumentation.rb +++ b/packages/auto-instrumentation/lib/auto-instrumentation.rb @@ -8,7 +8,7 @@ module OTelBundlerPatch # Nested module to handle OpenTelemetry initialization logic module OTelInitializer - @initialized = false + @_otel_initialized = false OTEL_INSTRUMENTATION_MAP = { 'gruf' => 'OpenTelemetry::Instrumentation::Gruf', @@ -52,8 +52,9 @@ module OTelInitializer 'sidekiq' => 'OpenTelemetry::Instrumentation::Sidekiq', 'sinatra' => 'OpenTelemetry::Instrumentation::Sinatra' }.freeze + private_constant :OTEL_INSTRUMENTATION_MAP - def self.detect_resource_from_env + def self._otel_detect_resource_from_env env = ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s additional_resource = ::OpenTelemetry::SDK::Resources::Resource.create({}) @@ -70,13 +71,13 @@ def self.detect_resource_from_env additional_resource end - def self.determine_enabled_instrumentation + def self._otel_determine_enabled_instrumentation env = ENV['OTEL_RUBY_ENABLED_INSTRUMENTATIONS'].to_s env.split(',').map { |instrumentation| OTEL_INSTRUMENTATION_MAP[instrumentation] } end - def self.check_for_bundled_otel_gems + def self._otel_check_for_bundled_otel_gems bundled_otel_gems = Bundler.definition.dependencies.select do |dep| dep.name.start_with?('opentelemetry-') end @@ -94,18 +95,18 @@ def self.check_for_bundled_otel_gems warn "[OpenTelemetry] WARNING: Unable to check Gemfile for OpenTelemetry gems: #{e.message}" if ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] == 'true' end - def self.require_otel - return if @initialized + def self._otel_require_otel + return if @_otel_initialized - @initialized = true + @_otel_initialized = true begin - check_for_bundled_otel_gems + _otel_check_for_bundled_otel_gems - required_instrumentation = determine_enabled_instrumentation + required_instrumentation = _otel_determine_enabled_instrumentation OpenTelemetry::SDK.configure do |c| - c.resource = detect_resource_from_env + c.resource = _otel_detect_resource_from_env if required_instrumentation.empty? c.use_all else @@ -123,7 +124,7 @@ def self.require_otel def require(...) super - OTelInitializer.require_otel + OTelInitializer._otel_require_otel end end diff --git a/packages/auto-instrumentation/test/test_helper.rb b/packages/auto-instrumentation/test/test_helper.rb index a2f0da7bed..016bdd6202 100644 --- a/packages/auto-instrumentation/test/test_helper.rb +++ b/packages/auto-instrumentation/test/test_helper.rb @@ -55,7 +55,7 @@ def run_in_subprocess(env_vars = {}, opts = {}) end $VERBOSE = old_verbose - OTelBundlerPatch::OTelInitializer.check_for_bundled_otel_gems + OTelBundlerPatch::OTelInitializer._otel_check_for_bundled_otel_gems else Bundler.require From c9b79f3d4aad0d8d091cea7bda0b5b6a83aa273a Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Mon, 13 Apr 2026 11:39:02 -0400 Subject: [PATCH 52/68] update bundler version --- packages/auto-instrumentation/Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/auto-instrumentation/Gemfile b/packages/auto-instrumentation/Gemfile index 260ba92655..1ee9277da8 100644 --- a/packages/auto-instrumentation/Gemfile +++ b/packages/auto-instrumentation/Gemfile @@ -9,7 +9,7 @@ source 'https://rubygems.org' gemspec group :test do - gem 'bundler', '~> 2.4' + gem 'bundler', '~> 4.0.0' gem 'minitest', '~> 6.0' gem 'rake', '~> 13.0' gem 'rubocop', '~> 1.86.0' From 31ea3529e8536fdffb40b0376c310f658d96cfc5 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Mon, 13 Apr 2026 11:40:50 -0400 Subject: [PATCH 53/68] update bundler version --- packages/auto-instrumentation/Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/auto-instrumentation/Gemfile b/packages/auto-instrumentation/Gemfile index 1ee9277da8..ae16ca97cc 100644 --- a/packages/auto-instrumentation/Gemfile +++ b/packages/auto-instrumentation/Gemfile @@ -9,7 +9,7 @@ source 'https://rubygems.org' gemspec group :test do - gem 'bundler', '~> 4.0.0' + gem 'bundler' gem 'minitest', '~> 6.0' gem 'rake', '~> 13.0' gem 'rubocop', '~> 1.86.0' From 4a15e56d45dcc46011fc86f2a058747ba74dd9cb Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Mon, 13 Apr 2026 12:44:28 -0400 Subject: [PATCH 54/68] update --- packages/auto-instrumentation/.rubocop.yml | 6 +- packages/auto-instrumentation/CHANGELOG.md | 2 +- packages/auto-instrumentation/Gemfile | 1 - .../auto-instrumentation.gemspec | 12 +-- .../test/auto-instrumentation_test.rb | 90 ++++++++----------- ...opentelemetry-auto-instrumentation_test.rb | 83 ----------------- 6 files changed, 45 insertions(+), 149 deletions(-) delete mode 100644 packages/auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb diff --git a/packages/auto-instrumentation/.rubocop.yml b/packages/auto-instrumentation/.rubocop.yml index 4c9e227275..a5a5dd5f94 100644 --- a/packages/auto-instrumentation/.rubocop.yml +++ b/packages/auto-instrumentation/.rubocop.yml @@ -1,5 +1,5 @@ inherit_from: ../../.rubocop.yml Naming/FileName: - Enabled: false -Gemspec/DevelopmentDependencies: - Enabled: false + Exclude: + - 'lib/auto-instrumentation.rb' + - 'test/auto-instrumentation_test.rb' diff --git a/packages/auto-instrumentation/CHANGELOG.md b/packages/auto-instrumentation/CHANGELOG.md index d068b6d9e8..35e6f8e8b7 100644 --- a/packages/auto-instrumentation/CHANGELOG.md +++ b/packages/auto-instrumentation/CHANGELOG.md @@ -1 +1 @@ -# Release History: opentelemetry-auto-instrumentation +# Release History: auto-instrumentation diff --git a/packages/auto-instrumentation/Gemfile b/packages/auto-instrumentation/Gemfile index ae16ca97cc..52d2b2250c 100644 --- a/packages/auto-instrumentation/Gemfile +++ b/packages/auto-instrumentation/Gemfile @@ -9,7 +9,6 @@ source 'https://rubygems.org' gemspec group :test do - gem 'bundler' gem 'minitest', '~> 6.0' gem 'rake', '~> 13.0' gem 'rubocop', '~> 1.86.0' diff --git a/packages/auto-instrumentation/auto-instrumentation.gemspec b/packages/auto-instrumentation/auto-instrumentation.gemspec index 8ab1a131a7..85c2ca1662 100644 --- a/packages/auto-instrumentation/auto-instrumentation.gemspec +++ b/packages/auto-instrumentation/auto-instrumentation.gemspec @@ -26,20 +26,20 @@ Gem::Specification.new do |spec| spec.require_paths = ['lib'] spec.required_ruby_version = '>= 3.3' - spec.add_dependency 'opentelemetry-api', '~> 1.7.0' - spec.add_dependency 'opentelemetry-exporter-otlp', '~> 0.31.1' - spec.add_dependency 'opentelemetry-helpers-mysql', '~> 0.4.0' + spec.add_dependency 'opentelemetry-api', '~> 1.9.0' + spec.add_dependency 'opentelemetry-exporter-otlp', '~> 0.33.0' + spec.add_dependency 'opentelemetry-helpers-mysql', '~> 0.5.0' spec.add_dependency 'opentelemetry-helpers-sql', '~> 0.3.0' spec.add_dependency 'opentelemetry-helpers-sql-processor', '~> 0.4.0' - spec.add_dependency 'opentelemetry-instrumentation-all', '~> 0.90.1' + spec.add_dependency 'opentelemetry-instrumentation-all', '~> 0.91.0' spec.add_dependency 'opentelemetry-resource-detector-aws', '~> 0.5.0' spec.add_dependency 'opentelemetry-resource-detector-azure', '~> 0.3.0' spec.add_dependency 'opentelemetry-resource-detector-container', '~> 0.3.0' - spec.add_dependency 'opentelemetry-sdk', '~> 1.10.0' + spec.add_dependency 'opentelemetry-sdk', '~> 1.11.0' if spec.respond_to?(:metadata) spec.metadata['changelog_uri'] = "https://rubydoc.info/gems/#{spec.name}/#{spec.version}/file/CHANGELOG.md" - spec.metadata['source_code_uri'] = 'https://github.com/open-telemetry/opentelemetry-ruby-contrib/auto-instrumentation' + spec.metadata['source_code_uri'] = "https://github.com/open-telemetry/opentelemetry-ruby-contrib/tree/#{spec.name}/v#{spec.version}/packages/auto-instrumentation" spec.metadata['bug_tracker_uri'] = 'https://github.com/open-telemetry/opentelemetry-ruby-contrib/issues' spec.metadata['documentation_uri'] = "https://rubydoc.info/gems/#{spec.name}/#{spec.version}" end diff --git a/packages/auto-instrumentation/test/auto-instrumentation_test.rb b/packages/auto-instrumentation/test/auto-instrumentation_test.rb index 13eb778710..9e3313ebf9 100644 --- a/packages/auto-instrumentation/test/auto-instrumentation_test.rb +++ b/packages/auto-instrumentation/test/auto-instrumentation_test.rb @@ -17,61 +17,6 @@ ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] = nil end - after do - ENV['OTEL_RUBY_ENABLED_INSTRUMENTATIONS'] = nil - ENV['OTEL_RUBY_INSTRUMENTATION_NET_HTTP_ENABLED'] = nil - ENV['OTEL_RUBY_RESOURCE_DETECTORS'] = nil - ENV['OTEL_RUBY_REQUIRE_BUNDLER'] = nil - ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] = nil - end - - def run_in_subprocess(env_vars = {}) - # Run the test in a subprocess to avoid contaminating the test environment - read_pipe, write_pipe = IO.pipe - - pid = fork do - read_pipe.close - env_vars.each { |key, value| ENV[key] = value } - - ENV['OTEL_RUBY_REQUIRE_BUNDLER'] = 'false' - begin - # Load the auto-instrumentation library - load auto_instrumentation_path - Bundler.require - - # Get tracer provider information - tracer_provider = OpenTelemetry.tracer_provider - resource = tracer_provider.instance_variable_get(:@resource) - resource_attributes = resource.instance_variable_get(:@attributes) - registry = tracer_provider.instance_variable_get(:@registry) - instrumentation_names = registry.map { |entry| entry.first.name } - - # Serialize and send results back to parent - result = Marshal.dump({ - tracer_provider_class: tracer_provider.class.name, - resource_attributes: resource_attributes, - instrumentation_names: instrumentation_names - }) - write_pipe.write(result) - rescue StandardError => e - error_result = Marshal.dump({ error: e.message, backtrace: e.backtrace }) - write_pipe.write(error_result) - ensure - write_pipe.close - exit!(0) - end - end - - write_pipe.close - result_data = read_pipe.read - read_pipe.close - Process.wait(pid) - - # rubocop:disable Security/MarshalLoad - Marshal.load(result_data) - # rubocop:enable Security/MarshalLoad - end - it 'simple_load_test' do result = run_in_subprocess @@ -100,4 +45,39 @@ def run_in_subprocess(env_vars = {}) _(result[:instrumentation_names]).must_include 'OpenTelemetry::Instrumentation::Net::HTTP' _(result[:instrumentation_names]).wont_include 'OpenTelemetry::Instrumentation::Rake' end + + describe 'check_for_bundled_otel_gems' do + it 'emits no warning when there are no opentelemetry gems in the bundle' do + result = run_in_subprocess({}, dep_names: %w[rack faraday]) + + _(result[:error]).must_be_nil + _(result[:warning_output]).must_be_empty + end + + it 'emits a warning listing detected opentelemetry gems' do + otel_gems = %w[opentelemetry-sdk opentelemetry-instrumentation-net_http rack] + result = run_in_subprocess({}, dep_names: otel_gems) + + _(result[:error]).must_be_nil + _(result[:warning_output]).must_include '[OpenTelemetry] WARNING' + _(result[:warning_output]).must_include 'opentelemetry-instrumentation-net_http' + _(result[:warning_output]).must_include 'opentelemetry-sdk' + _(result[:warning_output]).wont_include 'rack' + end + + it 'emits no warning when Bundler.definition raises and debug mode is off' do + result = run_in_subprocess({}, raise_error: true) + + _(result[:error]).must_be_nil + _(result[:warning_output]).must_be_empty + end + + it 'emits a warning when Bundler.definition raises and debug mode is on' do + result = run_in_subprocess({ 'OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG' => 'true' }, raise_error: true) + + _(result[:error]).must_be_nil + _(result[:warning_output]).must_include '[OpenTelemetry] WARNING: Unable to check Gemfile' + _(result[:warning_output]).must_include 'simulated bundler error' + end + end end diff --git a/packages/auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb b/packages/auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb deleted file mode 100644 index 9e3313ebf9..0000000000 --- a/packages/auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb +++ /dev/null @@ -1,83 +0,0 @@ -# frozen_string_literal: true - -# Copyright The OpenTelemetry Authors -# -# SPDX-License-Identifier: Apache-2.0 - -require 'test_helper' - -describe 'AutoInstrumentation' do - let(:auto_instrumentation_path) { File.expand_path('../lib/auto-instrumentation.rb', __dir__) } - - before do - ENV['OTEL_RUBY_ENABLED_INSTRUMENTATIONS'] = nil - ENV['OTEL_RUBY_INSTRUMENTATION_NET_HTTP_ENABLED'] = nil - ENV['OTEL_RUBY_RESOURCE_DETECTORS'] = nil - ENV['OTEL_RUBY_REQUIRE_BUNDLER'] = nil - ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] = nil - end - - it 'simple_load_test' do - result = run_in_subprocess - - _(result[:error]).must_be_nil - _(result[:tracer_provider_class]).must_equal 'OpenTelemetry::SDK::Trace::TracerProvider' - _(result[:resource_attributes]['service.name']).must_equal 'unknown_service' - _(result[:resource_attributes]['telemetry.sdk.name']).must_equal 'opentelemetry' - _(result[:resource_attributes]['telemetry.sdk.language']).must_equal 'ruby' - _(result[:resource_attributes].key?('container.id')).must_equal false - _(result[:instrumentation_names]).must_include 'OpenTelemetry::Instrumentation::Net::HTTP' - _(result[:instrumentation_names]).must_include 'OpenTelemetry::Instrumentation::Rake' - end - - it 'simple_load_with_net_http_disabled' do - result = run_in_subprocess('OTEL_RUBY_INSTRUMENTATION_NET_HTTP_ENABLED' => 'false') - - _(result[:error]).must_be_nil - _(result[:instrumentation_names]).must_include 'OpenTelemetry::Instrumentation::Rake' - _(result[:instrumentation_names]).wont_include 'OpenTelemetry::Instrumentation::Net::HTTP' - end - - it 'simple_load_with_desired_instrument_only' do - result = run_in_subprocess('OTEL_RUBY_ENABLED_INSTRUMENTATIONS' => 'net_http') - - _(result[:error]).must_be_nil - _(result[:instrumentation_names]).must_include 'OpenTelemetry::Instrumentation::Net::HTTP' - _(result[:instrumentation_names]).wont_include 'OpenTelemetry::Instrumentation::Rake' - end - - describe 'check_for_bundled_otel_gems' do - it 'emits no warning when there are no opentelemetry gems in the bundle' do - result = run_in_subprocess({}, dep_names: %w[rack faraday]) - - _(result[:error]).must_be_nil - _(result[:warning_output]).must_be_empty - end - - it 'emits a warning listing detected opentelemetry gems' do - otel_gems = %w[opentelemetry-sdk opentelemetry-instrumentation-net_http rack] - result = run_in_subprocess({}, dep_names: otel_gems) - - _(result[:error]).must_be_nil - _(result[:warning_output]).must_include '[OpenTelemetry] WARNING' - _(result[:warning_output]).must_include 'opentelemetry-instrumentation-net_http' - _(result[:warning_output]).must_include 'opentelemetry-sdk' - _(result[:warning_output]).wont_include 'rack' - end - - it 'emits no warning when Bundler.definition raises and debug mode is off' do - result = run_in_subprocess({}, raise_error: true) - - _(result[:error]).must_be_nil - _(result[:warning_output]).must_be_empty - end - - it 'emits a warning when Bundler.definition raises and debug mode is on' do - result = run_in_subprocess({ 'OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG' => 'true' }, raise_error: true) - - _(result[:error]).must_be_nil - _(result[:warning_output]).must_include '[OpenTelemetry] WARNING: Unable to check Gemfile' - _(result[:warning_output]).must_include 'simulated bundler error' - end - end -end From ef6d1d3a217ca63896e6546968b458263cbddfed Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Mon, 13 Apr 2026 14:01:54 -0400 Subject: [PATCH 55/68] rubocop format --- packages/auto-instrumentation/.rubocop.yml | 4 ++-- packages/auto-instrumentation/example/rails-example/app.rb | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/auto-instrumentation/.rubocop.yml b/packages/auto-instrumentation/.rubocop.yml index a5a5dd5f94..9248ef824e 100644 --- a/packages/auto-instrumentation/.rubocop.yml +++ b/packages/auto-instrumentation/.rubocop.yml @@ -1,5 +1,5 @@ inherit_from: ../../.rubocop.yml Naming/FileName: Exclude: - - 'lib/auto-instrumentation.rb' - - 'test/auto-instrumentation_test.rb' + - "lib/auto-instrumentation.rb" + - "test/auto-instrumentation_test.rb" diff --git a/packages/auto-instrumentation/example/rails-example/app.rb b/packages/auto-instrumentation/example/rails-example/app.rb index e19d0ab287..59fc4c8ceb 100644 --- a/packages/auto-instrumentation/example/rails-example/app.rb +++ b/packages/auto-instrumentation/example/rails-example/app.rb @@ -26,6 +26,7 @@ class MyApp < Rails::Application end # ApplicationController +# rubocop disable:Style/OneClassPerFile class ApplicationController < ActionController::API def index render json: { message: 'Hello World!', time: Time.current } @@ -43,6 +44,6 @@ def create } end end -# rubocop:enable Style/OneClassPerFile +# rubocop enable:Style/OneClassPerFile MyApp.initialize! From 74a9bc1ae03b55bf0ee442985213bfc9a8a499fe Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Mon, 13 Apr 2026 23:06:53 -0400 Subject: [PATCH 56/68] remove cspell words --- .cspell.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.cspell.yml b/.cspell.yml index 2d92b1789a..2bb01ca500 100644 --- a/.cspell.yml +++ b/.cspell.yml @@ -81,13 +81,6 @@ words: - traceid - traceparent - traceresponse - - Triager - - triagers - - Untrace - - vitess - - webmocks - - yardoc - - rackup - tracestate - linkspector - sarif From 362d6e183a6d6493ebb56c5a4ad4030c8974f355 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Thu, 16 Apr 2026 12:48:03 -0400 Subject: [PATCH 57/68] revision --- Dockerfile | 3 +- packages/auto-instrumentation/.rubocop.yml | 7 +- packages/auto-instrumentation/CHANGELOG.md | 2 +- packages/auto-instrumentation/README.md | 216 ++++++++++-------- .../auto-instrumentation/example/README.md | 83 ++++--- .../example/rails-example/Gemfile | 4 +- .../example/rails-example/app.rb | 24 +- ... => opentelemetry-auto-instrumentation.rb} | 45 ++-- ...pentelemetry-auto-instrumentation.gemspec} | 14 +- .../test/auto-instrumentation_test.rb | 83 ------- ...opentelemetry-auto-instrumentation_test.rb | 170 ++++++++++++++ .../auto-instrumentation/test/test_helper.rb | 68 +++++- 12 files changed, 476 insertions(+), 243 deletions(-) rename packages/auto-instrumentation/lib/{auto-instrumentation.rb => opentelemetry-auto-instrumentation.rb} (88%) rename packages/auto-instrumentation/{auto-instrumentation.gemspec => opentelemetry-auto-instrumentation.gemspec} (75%) delete mode 100644 packages/auto-instrumentation/test/auto-instrumentation_test.rb create mode 100644 packages/auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb diff --git a/Dockerfile b/Dockerfile index 65aab2c36d..267df83b5a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM ruby:3.2.2-alpine3.17@sha256:ab02a738d52d0077fe0d00fc7e9e7c014616bd175f163e7b15c4451b904ee04b as ruby +FROM ruby:3.3-alpine3.22 as ruby # Metadata LABEL maintainer="open-telemetry/opentelemetry-ruby-contrib" @@ -35,6 +35,7 @@ ARG PACKAGES="\ tzdata \ util-linux \ imagemagick \ + yaml-dev \ " # Install packages RUN apk update && \ diff --git a/packages/auto-instrumentation/.rubocop.yml b/packages/auto-instrumentation/.rubocop.yml index 9248ef824e..376945e736 100644 --- a/packages/auto-instrumentation/.rubocop.yml +++ b/packages/auto-instrumentation/.rubocop.yml @@ -1,5 +1,8 @@ inherit_from: ../../.rubocop.yml Naming/FileName: Exclude: - - "lib/auto-instrumentation.rb" - - "test/auto-instrumentation_test.rb" + - "lib/opentelemetry-auto-instrumentation.rb" + - "test/opentelemetry-auto-instrumentation_test.rb" +Metrics/ModuleLength: + Exclude: + - "lib/opentelemetry-auto-instrumentation.rb" \ No newline at end of file diff --git a/packages/auto-instrumentation/CHANGELOG.md b/packages/auto-instrumentation/CHANGELOG.md index 35e6f8e8b7..d068b6d9e8 100644 --- a/packages/auto-instrumentation/CHANGELOG.md +++ b/packages/auto-instrumentation/CHANGELOG.md @@ -1 +1 @@ -# Release History: auto-instrumentation +# Release History: opentelemetry-auto-instrumentation diff --git a/packages/auto-instrumentation/README.md b/packages/auto-instrumentation/README.md index 8431d133b8..1e044f2971 100644 --- a/packages/auto-instrumentation/README.md +++ b/packages/auto-instrumentation/README.md @@ -1,204 +1,238 @@ # OpenTelemetry Auto Instrumentation -The `auto-instrumentation` gem provides automatic loading and initialization of OpenTelemetry Ruby SDK for zero-code instrumentation of your applications. +The `opentelemetry-auto-instrumentation` gem provides automatic loading and initialization of the OpenTelemetry Ruby SDK for zero-code instrumentation of your applications. + +## Table of Contents + +- [What is OpenTelemetry?](#what-is-opentelemetry) +- [How does this gem fit in?](#how-does-this-gem-fit-in) +- [Getting Started](#getting-started) +- [Telemetry Signals](#telemetry-signals) + - [Traces](#traces) + - [Metrics](#metrics) + - [Logs](#logs) +- [Usage](#usage) +- [Configuration](#configuration) +- [Troubleshooting](#troubleshooting) +- [Example](#example) +- [How can I get involved?](#how-can-i-get-involved) +- [License](#license) ## What is OpenTelemetry? -OpenTelemetry is an open source observability framework that provides a unified API, SDK, and tooling for instrumenting cloud-native applications. It captures distributed traces and metrics from your application, which can be analyzed using observability backends like Prometheus, Jaeger, and others. +OpenTelemetry is an open source observability framework that provides a unified API, SDK, and tooling for instrumenting cloud-native applications. It captures distributed traces, metrics, and logs from your application, which can be analyzed using observability backends like Prometheus, Jaeger, and others. ## How does this gem fit in? This gem enables OpenTelemetry instrumentation without modifying your application code. It automatically: -- Loads the OpenTelemetry SDK +- Loads the OpenTelemetry SDK (traces, metrics, and logs) - Initializes instrumentations for detected libraries -- Configures exporters and resource detectors +- Configures OTLP exporters for all three signals +- Optionally configures resource detectors This gem is particularly useful with the [OpenTelemetry Operator][opentelemetry-operator] for Kubernetes environments. -## How do I get started? +## Getting Started Install the gem: ```console -gem install auto-instrumentation +gem install opentelemetry-auto-instrumentation ``` **Note:** Install via `gem install` rather than adding to your Gemfile, as this gem needs to load before your application starts. +Then instrument any Ruby application: + +```console +RUBYOPT="-r opentelemetry-auto-instrumentation" ruby application.rb +``` + +For Rails (which calls `Bundler.require` automatically): + +```console +RUBYOPT="-r opentelemetry-auto-instrumentation" rails server +``` + +For other frameworks (Sinatra, Rackup, etc.) that don't call `Bundler.require` automatically: + +```console +OTEL_RUBY_REQUIRE_BUNDLER=true RUBYOPT="-r opentelemetry-auto-instrumentation" rackup config.ru +``` + ### What gets installed? -Installing `auto-instrumentation` automatically includes: +Installing `opentelemetry-auto-instrumentation` automatically includes: ```console opentelemetry-sdk opentelemetry-api opentelemetry-instrumentation-all opentelemetry-exporter-otlp +opentelemetry-exporter-otlp-metrics +opentelemetry-exporter-otlp-logs opentelemetry-helpers-mysql -opentelemetry-helpers-sql-obfuscation +opentelemetry-helpers-sql-processor opentelemetry-resource-detector-azure opentelemetry-resource-detector-container opentelemetry-resource-detector-aws ``` -## Usage Examples +## Telemetry Signals -### Basic Usage +By default, this gem sets up **traces, metrics, and logs** and exports all three to an OTLP endpoint (`http://localhost:4318`). Each signal can be configured or disabled independently via standard OpenTelemetry environment variables. -Instrument any Ruby application: - -```console -RUBYOPT="-r auto-instrumentation" ruby application.rb -``` +### Traces -### With Configuration +Traces are enabled by default using the OTLP exporter. See the [opentelemetry-sdk README][otel-sdk-readme] for full configuration options. -Set environment variables to configure exporters, resource detectors, and service name: +**Disable traces:** ```console -export OTEL_TRACES_EXPORTER="otlp" -export OTEL_EXPORTER_OTLP_ENDPOINT="your-endpoint" -export OTEL_RUBY_RESOURCE_DETECTORS="container,azure" -export OTEL_SERVICE_NAME="your-service-name" - -RUBYOPT="-r auto-instrumentation" ruby application.rb +OTEL_TRACES_EXPORTER=none RUBYOPT="-r opentelemetry-auto-instrumentation" ruby application.rb ``` -### Rails Applications - -Rails automatically calls `Bundler.require`, so instrumentation works out of the box: +**Custom endpoint:** ```console -RUBYOPT="-r auto-instrumentation" rails server +OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://my-collector:4318/v1/traces \ + RUBYOPT="-r opentelemetry-auto-instrumentation" ruby application.rb ``` -### Selective Instrumentation +### Metrics -**Enable only specific instrumentations:** +Metrics are enabled by default using the OTLP metrics exporter. See the [opentelemetry-metrics-sdk README][otel-metrics-sdk-readme] for full configuration options. + +**Disable metrics:** ```console -export OTEL_RUBY_ENABLED_INSTRUMENTATIONS="mysql2,redis,faraday" -RUBYOPT="-r auto-instrumentation" ruby application.rb +OTEL_METRICS_EXPORTER=none RUBYOPT="-r opentelemetry-auto-instrumentation" ruby application.rb ``` -**Disable specific instrumentations:** +**Custom endpoint:** ```console -export OTEL_RUBY_INSTRUMENTATION_SINATRA_ENABLED="false" -RUBYOPT="-r auto-instrumentation" ruby application.rb +OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://my-collector:4318/v1/metrics \ + RUBYOPT="-r opentelemetry-auto-instrumentation" ruby application.rb ``` -**Configure instrumentation options:** +### Logs + +Logs are enabled by default using the OTLP logs exporter. See the [opentelemetry-logs-sdk README][otel-logs-sdk-readme] for full configuration options. + +**Disable logs:** ```console -export OTEL_RUBY_ENABLED_INSTRUMENTATIONS="redis" -export OTEL_RUBY_INSTRUMENTATION_REDIS_CONFIG_OPTS="peer_service=new_service;db_statement=omit" -RUBYOPT="-r auto-instrumentation" ruby application.rb +OTEL_LOGS_EXPORTER=none RUBYOPT="-r opentelemetry-auto-instrumentation" ruby application.rb ``` -### Non-Rails Frameworks (Sinatra, Rackup, etc.) - -For frameworks that don't automatically call `Bundler.require`, enable it explicitly: +**Custom endpoint:** ```console -export OTEL_RUBY_REQUIRE_BUNDLER="true" -RUBYOPT="-r auto-instrumentation" rackup config.ru +OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=http://my-collector:4318/v1/logs \ + RUBYOPT="-r opentelemetry-auto-instrumentation" ruby application.rb ``` -### Advanced: Loading External Gems - -If you need to load gems outside your Gemfile (and have them instrumented), preload them before the auto-instrumentation: +### Disable all signals except traces ```console -RUBYOPT="-r mysql2 -r faraday -r auto-instrumentation" ruby application.rb +OTEL_METRICS_EXPORTER=none OTEL_LOGS_EXPORTER=none \ + RUBYOPT="-r opentelemetry-auto-instrumentation" ruby application.rb ``` -## Troubleshooting +## Usage -### How Auto-Instrumentation Works +**Send all signals to a collector with a service name:** -The gem works by patching `Bundler::Runtime#require` to inject OpenTelemetry initialization when gems are loaded. Rails applications call `Bundler.require` automatically during boot, so they work seamlessly. Other frameworks (like Sinatra) may require manual configuration. +```console +export OTEL_EXPORTER_OTLP_ENDPOINT="http://my-collector:4318" +export OTEL_SERVICE_NAME="my-service" +RUBYOPT="-r opentelemetry-auto-instrumentation" ruby application.rb +``` -**Solution:** For non-Rails frameworks, either: +**Enable only specific instrumentations:** -- Call `Bundler.require` explicitly in your code, OR -- Set `OTEL_RUBY_REQUIRE_BUNDLER=true` environment variable +```console +OTEL_RUBY_ENABLED_INSTRUMENTATIONS="mysql2,redis" \ + RUBYOPT="-r opentelemetry-auto-instrumentation" ruby application.rb +``` -### Instrumentation Timing Issues +**Disable a specific instrumentation:** -Instrumentation is only applied when gems are loaded through `Bundler.require`. If you require a library **after** `Bundler.require` has been called, it won't be instrumented. +```console +OTEL_RUBY_INSTRUMENTATION_SINATRA_ENABLED=false \ + RUBYOPT="-r opentelemetry-auto-instrumentation" ruby application.rb +``` -**Example of what doesn't work:** +**Preload gems that need to be instrumented but aren't in your Gemfile:** -```ruby -# app.rb -Bundler.require -require 'faraday' # Loaded too late - won't be instrumented +```console +RUBYOPT="-r faraday -r opentelemetry-auto-instrumentation" ruby application.rb ``` -**Solution:** Preload the gem via `RUBYOPT`: +**Using with `bundle exec`** (when the gem is installed outside the bundle): ```console -RUBYOPT="-r faraday -r auto-instrumentation" ruby application.rb +RUBYOPT="-r $(gem which opentelemetry-auto-instrumentation)" bundle exec rails server ``` -This ensures gems are loaded early enough for instrumentation to be applied. +## Configuration -### Dependency Version Conflicts +The following environment variables are specific to this gem (not standard OpenTelemetry variables): -The auto-instrumentation gem loads OpenTelemetry components into Ruby's `$LOAD_PATH`. It also includes two non-OpenTelemetry dependencies required for OTLP exporters: +| Environment Variable | Description | Example | +| -------------------- | ----------- | ------- | +| `OTEL_RUBY_REQUIRE_BUNDLER` | Set to `true` to automatically call `Bundler.require` during initialization. Required for frameworks that don't call it automatically (e.g., Sinatra). | `true` | +| `OTEL_RUBY_RESOURCE_DETECTORS` | Comma-separated list of resource detectors. Supported: `container`, `azure`, `aws`. **Note:** The GCP detector is not included — its `google-cloud-env` dependency makes blocking HTTP requests to the GCP metadata server, causing timeouts in non-GCP environments. | `container,azure,aws` | +| `OTEL_RUBY_ENABLED_INSTRUMENTATIONS` | Only load specific instrumentations (comma-separated). Omit to load all available. | `redis,mysql2,faraday` | +| `OTEL_RUBY_ADDITIONAL_GEM_PATH` | Custom gem installation path for OpenTelemetry Operator environments. | `/custom/gem/path` | +| `OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG` | Set to `true` for debug output during initialization. | `true` | +| `OTEL_RUBY_UNLOAD_LIBRARY` | Prevent specific gems from being preloaded (e.g., `google-protobuf`). | `google-protobuf` | + +For standard OpenTelemetry environment variables (exporters, endpoints, resource attributes, etc.), refer to the SDK READMEs: -- `google-protobuf` -- `googleapis-common-protos-types` +- [opentelemetry-sdk (traces)][otel-sdk-readme] +- [opentelemetry-metrics-sdk (metrics)][otel-metrics-sdk-readme] +- [opentelemetry-logs-sdk (logs)][otel-logs-sdk-readme] -**Problem:** If your Gemfile includes different versions of these gems, you may encounter version conflicts. +## Troubleshooting -**Solution:** If you experience protobuf-related errors: +### How Auto-Instrumentation Works -1. Remove `google-protobuf` and `googleapis-common-protos-types` from your Gemfile -2. Let `auto-instrumentation` manage these dependencies -3. In most cases, version mismatches won't cause issues, but this is the safest approach +The gem patches `Bundler::Runtime#require` to inject OpenTelemetry initialization when gems are loaded. Rails calls `Bundler.require` automatically during boot; other frameworks need `OTEL_RUBY_REQUIRE_BUNDLER=true`. -### Using with bundle exec +### Instrumentation Timing Issues -Since the gem is installed via `gem install` (not in your Gemfile), you may need to specify the full path when using `bundle exec`: +Instrumentation is only applied to libraries loaded through `Bundler.require`. If you require a library after `Bundler.require` has already been called, it won't be instrumented. Preload it via `RUBYOPT` instead: ```console -RUBYOPT="-r /path/to/gems/auto-instrumentation-X.X.X/lib/auto-instrumentation" bundle exec rails server +RUBYOPT="-r faraday -r opentelemetry-auto-instrumentation" ruby application.rb ``` -Find the path using: `gem which auto-instrumentation` +### Dependency Version Conflicts + +This gem loads OpenTelemetry components (including `google-protobuf` and `googleapis-common-protos-types`) directly into `$LOAD_PATH`. If your Gemfile pins different versions of these gems, you may encounter conflicts. Remove them from your Gemfile and let this gem manage them. ## Example See [example/README.md](example/README.md) -## Configuration - -The following environment variables are specific to this gem (not standard OpenTelemetry variables): - -| Environment Variable | Description | Example | -| ---------------------- | ----------- | ------- | -| `OTEL_RUBY_REQUIRE_BUNDLER` | Set to `true` to automatically call `Bundler.require` during initialization. Required for frameworks that don't call it automatically (e.g., Sinatra). | `true` | -| `OTEL_RUBY_RESOURCE_DETECTORS` | Comma-separated list of resource detectors. Supported: `container`, `azure`, `aws`. **Note:** GCP detector not supported due to additional dependencies. | `container,azure,aws` | -| `OTEL_RUBY_ENABLED_INSTRUMENTATIONS` | Only load specific instrumentations (comma-separated). Omit to load all available. | `redis,mysql2,faraday` | -| `OTEL_RUBY_ADDITIONAL_GEM_PATH` | Custom gem installation path for OpenTelemetry Operator environments. | `/custom/gem/path` | -| `OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG` | Set to `true` for debug output during initialization. | `true` | -| `OTEL_RUBY_UNLOAD_LIBRARY` | Prevent specific gems from being preloaded (e.g., `google-protobuf`). | `google-protobuf` | - ## How can I get involved? -The `auto-instrumentation` gem source is on GitHub, along with related gems. +The `opentelemetry-auto-instrumentation` gem source is on GitHub, along with related gems. The OpenTelemetry Ruby gems are maintained by the OpenTelemetry Ruby special interest group (SIG). You can get involved by joining us on our [GitHub Discussions][discussions-url], [Slack Channel][slack-channel] or attending our weekly meeting. See the [meeting calendar][community-meetings] for dates and times. For more information on this and other language SIGs, see the OpenTelemetry [community page][ruby-sig]. ## License -The `auto-instrumentation` gem is distributed under the Apache 2.0 license. See LICENSE for more information. +The `opentelemetry-auto-instrumentation` gem is distributed under the Apache 2.0 license. See LICENSE for more information. [ruby-sig]: https://github.com/open-telemetry/community#ruby-sig [community-meetings]: https://github.com/open-telemetry/community#community-meetings [slack-channel]: https://cloud-native.slack.com/archives/C01NWKKMKMY [discussions-url]: https://github.com/open-telemetry/opentelemetry-ruby/discussions [opentelemetry-operator]: https://github.com/open-telemetry/opentelemetry-operator +[otel-sdk-readme]: https://github.com/open-telemetry/opentelemetry-ruby/tree/main/sdk +[otel-metrics-sdk-readme]: https://github.com/open-telemetry/opentelemetry-ruby/tree/main/metrics_sdk +[otel-logs-sdk-readme]: https://github.com/open-telemetry/opentelemetry-ruby/tree/main/logs_sdk diff --git a/packages/auto-instrumentation/example/README.md b/packages/auto-instrumentation/example/README.md index b6f42bdb3a..66d2e4753c 100644 --- a/packages/auto-instrumentation/example/README.md +++ b/packages/auto-instrumentation/example/README.md @@ -2,57 +2,92 @@ ## Installation -First, install the `auto-instrumentation` gem using `gem install` (not through Bundler): +First, install the `opentelemetry-auto-instrumentation` gem using `gem install` (not through Bundler): ```bash -gem install auto-instrumentation +gem install opentelemetry-auto-instrumentation ``` This gem should be installed outside your Gemfile so that it can be loaded globally through the `RUBYOPT` environment variable. ## Simple Example (simple-example) -A basic Ruby application that demonstrates auto-instrumentation. +A basic Ruby application that demonstrates opentelemetry-auto-instrumentation. ```bash bundle install -OTEL_RUBY_REQUIRE_BUNDLER=true OTEL_TRACES_EXPORTER=console RUBYOPT="-r auto-instrumentation" ruby app.rb +OTEL_RUBY_REQUIRE_BUNDLER=true OTEL_TRACES_EXPORTER=console RUBYOPT="-r opentelemetry-auto-instrumentation" ruby app.rb +``` + +To also export metrics and logs to the console: + +```bash +bundle install +OTEL_RUBY_REQUIRE_BUNDLER=true \ + OTEL_TRACES_EXPORTER=console \ + OTEL_METRICS_EXPORTER=console \ + OTEL_LOGS_EXPORTER=console \ + RUBYOPT="-r opentelemetry-auto-instrumentation" ruby app.rb ``` **What's happening:** - `OTEL_RUBY_REQUIRE_BUNDLER=true` tells the gem to call `Bundler.require` during initialization - `OTEL_TRACES_EXPORTER=console` outputs trace data to the console for visibility -- `RUBYOPT` ensures `auto-instrumentation` is loaded before your application code +- `OTEL_METRICS_EXPORTER=console` outputs metrics data to the console +- `OTEL_LOGS_EXPORTER=console` outputs log records to the console +- `RUBYOPT` ensures `opentelemetry-auto-instrumentation` is loaded before your application code ## Rails Example (rails-example) -A Rails application demonstrating auto-instrumentation integration. +A Rails application demonstrating opentelemetry-auto-instrumentation integration. -### Without auto-instrumentation +### Without opentelemetry-auto-instrumentation ```bash bundle exec rackup config.ru ``` -### With auto-instrumentation +### With opentelemetry-auto-instrumentation ```bash bundle install -OTEL_RUBY_REQUIRE_BUNDLER=false OTEL_TRACES_EXPORTER=console RUBYOPT="-r auto-instrumentation" bundle exec rackup config.ru +OTEL_RUBY_REQUIRE_BUNDLER=false OTEL_TRACES_EXPORTER=console RUBYOPT="-r opentelemetry-auto-instrumentation" bundle exec rackup config.ru +``` + +To also export metrics and logs: + +```bash +bundle install +OTEL_RUBY_REQUIRE_BUNDLER=false \ + OTEL_TRACES_EXPORTER=console \ + OTEL_METRICS_EXPORTER=console \ + OTEL_LOGS_EXPORTER=console \ + RUBYOPT="-r opentelemetry-auto-instrumentation" bundle exec rackup config.ru +``` + +To send all signals to an OTLP collector: + +```bash +bundle install +OTEL_RUBY_REQUIRE_BUNDLER=false \ + OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318" \ + OTEL_SERVICE_NAME="my-rails-app" \ + RUBYOPT="-r opentelemetry-auto-instrumentation" bundle exec rackup config.ru ``` **What's happening:** - `OTEL_RUBY_REQUIRE_BUNDLER=false` because Rails automatically calls `Bundler.require` during boot - The OpenTelemetry gem is loaded first via `RUBYOPT`, then Rails initializes with instrumentation automatically applied +- When no exporter env vars are set, traces, metrics, and logs default to the OTLP exporter sending to `http://localhost:4318` ### Test the instrumentation In another terminal, make a request to generate traces: ```bash -wget http://localhost:9292 # or use curl +curl http://localhost:9292 ``` You should see trace output in the console where the Rails server is running. @@ -61,7 +96,7 @@ You should see trace output in the console where the Rails server is running. The correct sequence is: -1. `auto-instrumentation` is loaded (via `RUBYOPT`) +1. `opentelemetry-auto-instrumentation` is loaded (via `RUBYOPT`) 2. User libraries are required 3. `Bundler.require` is called (by Rails or manually) 4. OpenTelemetry SDK is initialized @@ -69,32 +104,12 @@ The correct sequence is: ### Troubleshooting: Default Gem Version Conflicts -If you encounter an error like "You have already activated [gem] X.X.X, but your Gemfile requires [gem] Y.Y.Y", this indicates a version conflict with a default gem (such as `json` or `logger`). This occurs because your system's default Ruby gem versions are outdated compared to the fresh versions installed from the Gemfile in this folder. - -#### Error example - -```console -You have already activated json 2.6.3, but your Gemfile requires json 2.16.0. -Since json is a default gem, you can either remove your dependency on it or -try updating to a newer version of bundler that supports json as a default gem. -``` - -#### Solution - -Install the specific gem version that your Gemfile requires: +If you encounter an error like "You have already activated [gem] X.X.X, but your Gemfile requires [gem] Y.Y.Y", install the required version explicitly: ```bash gem install [gem-name] -v '[version]' ``` -For example: - -```bash -gem install json -v '2.16.0' -``` - -Then run your application again with auto-instrumentation. - -#### Why this happens +This occurs because OpenTelemetry is loaded early via `RUBYOPT`, and if any of its dependencies activate a default gem version that differs from your Gemfile, Bundler raises a conflict error. -The OpenTelemetry gem is loaded early in the Ruby startup process via `RUBYOPT`. If any of its dependencies activate a default gem version that differs from what your Gemfile specifies, Bundler will raise a conflict error. Installing the required version explicitly resolves this issue by replacing the system default with the specific version your project needs. +This won't cause issues in the operator because only OpenTelemetry-related gems will be included in your Ruby environment. diff --git a/packages/auto-instrumentation/example/rails-example/Gemfile b/packages/auto-instrumentation/example/rails-example/Gemfile index 5318a25bf7..e3933128b0 100644 --- a/packages/auto-instrumentation/example/rails-example/Gemfile +++ b/packages/auto-instrumentation/example/rails-example/Gemfile @@ -5,7 +5,5 @@ source 'https://rubygems.org' gem 'rails' gem 'rack' -gem 'rake', '13.0.6' -gem 'bigdecimal', '3.1.3' -gem 'logger', '1.5.3' +gem 'rake' gem 'webrick' diff --git a/packages/auto-instrumentation/example/rails-example/app.rb b/packages/auto-instrumentation/example/rails-example/app.rb index 59fc4c8ceb..a29631f227 100644 --- a/packages/auto-instrumentation/example/rails-example/app.rb +++ b/packages/auto-instrumentation/example/rails-example/app.rb @@ -18,26 +18,34 @@ class MyApp < Rails::Application config.api_only = true config.active_support.to_time_preserves_timezone = :zone - routes.draw do - get '/', to: 'application#index' - get '/hello', to: 'application#hello' - post '/data', to: 'application#create' - end + # Share OpenTelemetry objects across the app through Rails config. + config.x.otel_meter = OpenTelemetry.meter_provider.meter('rails-example') + config.x.otel_request_counter = config.x.otel_meter.create_counter( + 'http.request.count', + description: 'Counts the number of HTTP requests' + ) + config.x.otel_logger = OpenTelemetry.logger_provider.logger(name: 'rails-example') end # ApplicationController # rubocop disable:Style/OneClassPerFile class ApplicationController < ActionController::API def index + MyApp.config.x.otel_request_counter.add(1, attributes: { 'http.route' => '/' }) + MyApp.config.x.otel_logger.on_emit(severity_text: 'INFO', body: 'Handling request: GET /') render json: { message: 'Hello World!', time: Time.current } end def hello + MyApp.config.x.otel_request_counter.add(1, attributes: { 'http.route' => '/hello' }) name = params[:name] || 'World' + MyApp.config.x.otel_logger.on_emit(severity_text: 'INFO', body: "Handling request: GET /hello, name=#{name}") render json: { greeting: "Hello #{name}!" } end def create + MyApp.config.x.otel_request_counter.add(1, attributes: { 'http.route' => '/data' }) + MyApp.config.x.otel_logger.on_emit(severity_text: 'INFO', body: 'Handling request: POST /data') render json: { message: 'Data received', data: params.except(:controller, :action) @@ -47,3 +55,9 @@ def create # rubocop enable:Style/OneClassPerFile MyApp.initialize! + +MyApp.routes.draw do + get '/', to: 'application#index' + get '/hello', to: 'application#hello' + post '/data', to: 'application#create' +end diff --git a/packages/auto-instrumentation/lib/auto-instrumentation.rb b/packages/auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb similarity index 88% rename from packages/auto-instrumentation/lib/auto-instrumentation.rb rename to packages/auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb index 957eacabf0..2cc5d4f2d4 100644 --- a/packages/auto-instrumentation/lib/auto-instrumentation.rb +++ b/packages/auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb @@ -11,21 +11,18 @@ module OTelInitializer @_otel_initialized = false OTEL_INSTRUMENTATION_MAP = { - 'gruf' => 'OpenTelemetry::Instrumentation::Gruf', - 'trilogy' => 'OpenTelemetry::Instrumentation::Trilogy', - 'active_support' => 'OpenTelemetry::Instrumentation::ActiveSupport', + 'action_mailer' => 'OpenTelemetry::Instrumentation::ActionMailer', 'action_pack' => 'OpenTelemetry::Instrumentation::ActionPack', + 'action_view' => 'OpenTelemetry::Instrumentation::ActionView', 'active_job' => 'OpenTelemetry::Instrumentation::ActiveJob', + 'active_model_serializers' => 'OpenTelemetry::Instrumentation::ActiveModelSerializers', 'active_record' => 'OpenTelemetry::Instrumentation::ActiveRecord', - 'action_view' => 'OpenTelemetry::Instrumentation::ActionView', - 'action_mailer' => 'OpenTelemetry::Instrumentation::ActionMailer', - 'aws_sdk' => 'OpenTelemetry::Instrumentation::AwsSdk', + 'active_storage' => 'OpenTelemetry::Instrumentation::ActiveStorage', + 'active_support' => 'OpenTelemetry::Instrumentation::ActiveSupport', + 'anthropic' => 'OpenTelemetry::Instrumentation::Anthropic', 'aws_lambda' => 'OpenTelemetry::Instrumentation::AwsLambda', + 'aws_sdk' => 'OpenTelemetry::Instrumentation::AwsSdk', 'bunny' => 'OpenTelemetry::Instrumentation::Bunny', - 'lmdb' => 'OpenTelemetry::Instrumentation::LMDB', - 'http' => 'OpenTelemetry::Instrumentation::HTTP', - 'koala' => 'OpenTelemetry::Instrumentation::Koala', - 'active_model_serializers' => 'OpenTelemetry::Instrumentation::ActiveModelSerializers', 'concurrent_ruby' => 'OpenTelemetry::Instrumentation::ConcurrentRuby', 'dalli' => 'OpenTelemetry::Instrumentation::Dalli', 'delayed_job' => 'OpenTelemetry::Instrumentation::DelayedJob', @@ -34,7 +31,13 @@ module OTelInitializer 'faraday' => 'OpenTelemetry::Instrumentation::Faraday', 'grape' => 'OpenTelemetry::Instrumentation::Grape', 'graphql' => 'OpenTelemetry::Instrumentation::GraphQL', + 'grpc' => 'OpenTelemetry::Instrumentation::Grpc', + 'gruf' => 'OpenTelemetry::Instrumentation::Gruf', + 'http' => 'OpenTelemetry::Instrumentation::HTTP', 'http_client' => 'OpenTelemetry::Instrumentation::HttpClient', + 'httpx' => 'OpenTelemetry::Instrumentation::HTTPX', + 'koala' => 'OpenTelemetry::Instrumentation::Koala', + 'lmdb' => 'OpenTelemetry::Instrumentation::LMDB', 'mongo' => 'OpenTelemetry::Instrumentation::Mongo', 'mysql2' => 'OpenTelemetry::Instrumentation::Mysql2', 'net_http' => 'OpenTelemetry::Instrumentation::Net::HTTP', @@ -46,11 +49,12 @@ module OTelInitializer 'rake' => 'OpenTelemetry::Instrumentation::Rake', 'rdkafka' => 'OpenTelemetry::Instrumentation::Rdkafka', 'redis' => 'OpenTelemetry::Instrumentation::Redis', - 'restclient' => 'OpenTelemetry::Instrumentation::RestClient', 'resque' => 'OpenTelemetry::Instrumentation::Resque', + 'restclient' => 'OpenTelemetry::Instrumentation::RestClient', 'ruby_kafka' => 'OpenTelemetry::Instrumentation::RubyKafka', 'sidekiq' => 'OpenTelemetry::Instrumentation::Sidekiq', - 'sinatra' => 'OpenTelemetry::Instrumentation::Sinatra' + 'sinatra' => 'OpenTelemetry::Instrumentation::Sinatra', + 'trilogy' => 'OpenTelemetry::Instrumentation::Trilogy' }.freeze private_constant :OTEL_INSTRUMENTATION_MAP @@ -86,11 +90,11 @@ def self._otel_check_for_bundled_otel_gems gem_names = bundled_otel_gems.map(&:name).sort.join(', ') warn '[OpenTelemetry] WARNING: Detected OpenTelemetry gems in your Gemfile: ' \ - "#{gem_names}. When using auto-instrumentation, OpenTelemetry gems are loaded " \ - 'from the auto-instrumentation gem path, NOT from your bundle. The gem versions ' \ + "#{gem_names}. When using opentelemetry-auto-instrumentation, OpenTelemetry gems are loaded " \ + 'from the opentelemetry-auto-instrumentation gem path, NOT from your bundle. The gem versions ' \ 'in your Gemfile/Gemfile.lock are not used and may cause version conflicts or ' \ 'unexpected behavior. Please remove these gems from your Gemfile when using ' \ - 'auto-instrumentation.' + 'opentelemetry-auto-instrumentation.' rescue StandardError => e warn "[OpenTelemetry] WARNING: Unable to check Gemfile for OpenTelemetry gems: #{e.message}" if ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] == 'true' end @@ -105,8 +109,10 @@ def self._otel_require_otel required_instrumentation = _otel_determine_enabled_instrumentation + resource = _otel_detect_resource_from_env + OpenTelemetry::SDK.configure do |c| - c.resource = _otel_detect_resource_from_env + c.resource = resource if required_instrumentation.empty? c.use_all else @@ -115,6 +121,7 @@ def self._otel_require_otel end end end + OpenTelemetry.logger.info { 'Auto-instrumentation initialized' } rescue StandardError => e warn "Auto-instrumentation failed to initialize. Error: #{e.message}" @@ -149,8 +156,12 @@ def require(...) # These are required for the prepend OTelBundlerPatch to fetch OpenTelemetry::SDK.configure require 'opentelemetry-sdk' -require 'opentelemetry-instrumentation-all' +require 'opentelemetry-metrics-sdk' +require 'opentelemetry-logs-sdk' require 'opentelemetry-exporter-otlp' +require 'opentelemetry-exporter-otlp-metrics' +require 'opentelemetry-exporter-otlp-logs' +require 'opentelemetry-instrumentation-all' resource_detectors = ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s require 'opentelemetry-resource-detector-container' if resource_detectors.include?('container') diff --git a/packages/auto-instrumentation/auto-instrumentation.gemspec b/packages/auto-instrumentation/opentelemetry-auto-instrumentation.gemspec similarity index 75% rename from packages/auto-instrumentation/auto-instrumentation.gemspec rename to packages/auto-instrumentation/opentelemetry-auto-instrumentation.gemspec index 85c2ca1662..a747394179 100644 --- a/packages/auto-instrumentation/auto-instrumentation.gemspec +++ b/packages/auto-instrumentation/opentelemetry-auto-instrumentation.gemspec @@ -9,13 +9,13 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'version' Gem::Specification.new do |spec| - spec.name = 'auto-instrumentation' + spec.name = 'opentelemetry-auto-instrumentation' spec.version = OpenTelemetry::AutoInstrumentation::VERSION spec.authors = ['OpenTelemetry Authors'] spec.email = ['cncf-opentelemetry-contributors@lists.cncf.io'] - spec.summary = 'auto-instrumentation for opentelemetry ruby' - spec.description = 'auto-instrumentation for opentelemetry ruby' + spec.summary = 'Auto-instrumentation for OpenTelemetry Ruby' + spec.description = 'Auto-instrumentation for OpenTelemetry Ruby' spec.homepage = 'https://github.com/open-telemetry/opentelemetry-ruby-contrib' spec.license = 'Apache-2.0' @@ -28,10 +28,16 @@ Gem::Specification.new do |spec| spec.add_dependency 'opentelemetry-api', '~> 1.9.0' spec.add_dependency 'opentelemetry-exporter-otlp', '~> 0.33.0' + spec.add_dependency 'opentelemetry-exporter-otlp-logs', '~> 0.4.0' + spec.add_dependency 'opentelemetry-exporter-otlp-metrics', '~> 0.8.0' spec.add_dependency 'opentelemetry-helpers-mysql', '~> 0.5.0' spec.add_dependency 'opentelemetry-helpers-sql', '~> 0.3.0' spec.add_dependency 'opentelemetry-helpers-sql-processor', '~> 0.4.0' spec.add_dependency 'opentelemetry-instrumentation-all', '~> 0.91.0' + spec.add_dependency 'opentelemetry-logs-api', '~> 0.3.0' + spec.add_dependency 'opentelemetry-logs-sdk', '~> 0.5.1' + spec.add_dependency 'opentelemetry-metrics-api', '~> 0.5.0' + spec.add_dependency 'opentelemetry-metrics-sdk', '~> 0.13.1' spec.add_dependency 'opentelemetry-resource-detector-aws', '~> 0.5.0' spec.add_dependency 'opentelemetry-resource-detector-azure', '~> 0.3.0' spec.add_dependency 'opentelemetry-resource-detector-container', '~> 0.3.0' @@ -39,7 +45,7 @@ Gem::Specification.new do |spec| if spec.respond_to?(:metadata) spec.metadata['changelog_uri'] = "https://rubydoc.info/gems/#{spec.name}/#{spec.version}/file/CHANGELOG.md" - spec.metadata['source_code_uri'] = "https://github.com/open-telemetry/opentelemetry-ruby-contrib/tree/#{spec.name}/v#{spec.version}/packages/auto-instrumentation" + spec.metadata['source_code_uri'] = "https://github.com/open-telemetry/opentelemetry-ruby-contrib/tree/#{spec.name}/v#{spec.version}/packages/opentelemetry-auto-instrumentation" spec.metadata['bug_tracker_uri'] = 'https://github.com/open-telemetry/opentelemetry-ruby-contrib/issues' spec.metadata['documentation_uri'] = "https://rubydoc.info/gems/#{spec.name}/#{spec.version}" end diff --git a/packages/auto-instrumentation/test/auto-instrumentation_test.rb b/packages/auto-instrumentation/test/auto-instrumentation_test.rb deleted file mode 100644 index 9e3313ebf9..0000000000 --- a/packages/auto-instrumentation/test/auto-instrumentation_test.rb +++ /dev/null @@ -1,83 +0,0 @@ -# frozen_string_literal: true - -# Copyright The OpenTelemetry Authors -# -# SPDX-License-Identifier: Apache-2.0 - -require 'test_helper' - -describe 'AutoInstrumentation' do - let(:auto_instrumentation_path) { File.expand_path('../lib/auto-instrumentation.rb', __dir__) } - - before do - ENV['OTEL_RUBY_ENABLED_INSTRUMENTATIONS'] = nil - ENV['OTEL_RUBY_INSTRUMENTATION_NET_HTTP_ENABLED'] = nil - ENV['OTEL_RUBY_RESOURCE_DETECTORS'] = nil - ENV['OTEL_RUBY_REQUIRE_BUNDLER'] = nil - ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] = nil - end - - it 'simple_load_test' do - result = run_in_subprocess - - _(result[:error]).must_be_nil - _(result[:tracer_provider_class]).must_equal 'OpenTelemetry::SDK::Trace::TracerProvider' - _(result[:resource_attributes]['service.name']).must_equal 'unknown_service' - _(result[:resource_attributes]['telemetry.sdk.name']).must_equal 'opentelemetry' - _(result[:resource_attributes]['telemetry.sdk.language']).must_equal 'ruby' - _(result[:resource_attributes].key?('container.id')).must_equal false - _(result[:instrumentation_names]).must_include 'OpenTelemetry::Instrumentation::Net::HTTP' - _(result[:instrumentation_names]).must_include 'OpenTelemetry::Instrumentation::Rake' - end - - it 'simple_load_with_net_http_disabled' do - result = run_in_subprocess('OTEL_RUBY_INSTRUMENTATION_NET_HTTP_ENABLED' => 'false') - - _(result[:error]).must_be_nil - _(result[:instrumentation_names]).must_include 'OpenTelemetry::Instrumentation::Rake' - _(result[:instrumentation_names]).wont_include 'OpenTelemetry::Instrumentation::Net::HTTP' - end - - it 'simple_load_with_desired_instrument_only' do - result = run_in_subprocess('OTEL_RUBY_ENABLED_INSTRUMENTATIONS' => 'net_http') - - _(result[:error]).must_be_nil - _(result[:instrumentation_names]).must_include 'OpenTelemetry::Instrumentation::Net::HTTP' - _(result[:instrumentation_names]).wont_include 'OpenTelemetry::Instrumentation::Rake' - end - - describe 'check_for_bundled_otel_gems' do - it 'emits no warning when there are no opentelemetry gems in the bundle' do - result = run_in_subprocess({}, dep_names: %w[rack faraday]) - - _(result[:error]).must_be_nil - _(result[:warning_output]).must_be_empty - end - - it 'emits a warning listing detected opentelemetry gems' do - otel_gems = %w[opentelemetry-sdk opentelemetry-instrumentation-net_http rack] - result = run_in_subprocess({}, dep_names: otel_gems) - - _(result[:error]).must_be_nil - _(result[:warning_output]).must_include '[OpenTelemetry] WARNING' - _(result[:warning_output]).must_include 'opentelemetry-instrumentation-net_http' - _(result[:warning_output]).must_include 'opentelemetry-sdk' - _(result[:warning_output]).wont_include 'rack' - end - - it 'emits no warning when Bundler.definition raises and debug mode is off' do - result = run_in_subprocess({}, raise_error: true) - - _(result[:error]).must_be_nil - _(result[:warning_output]).must_be_empty - end - - it 'emits a warning when Bundler.definition raises and debug mode is on' do - result = run_in_subprocess({ 'OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG' => 'true' }, raise_error: true) - - _(result[:error]).must_be_nil - _(result[:warning_output]).must_include '[OpenTelemetry] WARNING: Unable to check Gemfile' - _(result[:warning_output]).must_include 'simulated bundler error' - end - end -end diff --git a/packages/auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb b/packages/auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb new file mode 100644 index 0000000000..a5976df4a5 --- /dev/null +++ b/packages/auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb @@ -0,0 +1,170 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'test_helper' + +describe 'OpenTelemetry::AutoInstrumentation' do + let(:auto_instrumentation_path) { File.expand_path('../lib/opentelemetry-auto-instrumentation.rb', __dir__) } + + before do + ENV['OTEL_RUBY_ENABLED_INSTRUMENTATIONS'] = nil + ENV['OTEL_RUBY_INSTRUMENTATION_NET_HTTP_ENABLED'] = nil + ENV['OTEL_RUBY_RESOURCE_DETECTORS'] = nil + ENV['OTEL_RUBY_REQUIRE_BUNDLER'] = nil + ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] = nil + end + + # Verifies that loading the auto-instrumentation gem initialises the TracerProvider + # with the SDK implementation and attaches the expected resource attributes and + # default instrumentation libraries. + it 'simple_load_test' do + result = run_in_subprocess + + _(result[:error]).must_be_nil + _(result[:tracer_provider_class]).must_equal 'OpenTelemetry::SDK::Trace::TracerProvider' + _(result[:resource_attributes]['service.name']).must_equal 'unknown_service' + _(result[:resource_attributes]['telemetry.sdk.name']).must_equal 'opentelemetry' + _(result[:resource_attributes]['telemetry.sdk.language']).must_equal 'ruby' + _(result[:resource_attributes].key?('container.id')).must_equal false + _(result[:instrumentation_names]).must_include 'OpenTelemetry::Instrumentation::Net::HTTP' + _(result[:instrumentation_names]).must_include 'OpenTelemetry::Instrumentation::Rake' + end + + # Verifies that setting OTEL_RUBY_INSTRUMENTATION__ENABLED=false suppresses + # a specific instrumentation while leaving others active. + it 'simple_load_with_net_http_disabled' do + result = run_in_subprocess('OTEL_RUBY_INSTRUMENTATION_NET_HTTP_ENABLED' => 'false') + + _(result[:error]).must_be_nil + _(result[:instrumentation_names]).must_include 'OpenTelemetry::Instrumentation::Rake' + _(result[:instrumentation_names]).wont_include 'OpenTelemetry::Instrumentation::Net::HTTP' + end + + # Verifies that OTEL_RUBY_ENABLED_INSTRUMENTATIONS restricts initialisation to + # only the listed instrumentation libraries, ignoring all others. + it 'simple_load_with_desired_instrument_only' do + result = run_in_subprocess('OTEL_RUBY_ENABLED_INSTRUMENTATIONS' => 'net_http') + + _(result[:error]).must_be_nil + _(result[:instrumentation_names]).must_include 'OpenTelemetry::Instrumentation::Net::HTTP' + _(result[:instrumentation_names]).wont_include 'OpenTelemetry::Instrumentation::Rake' + end + + describe 'metrics and logs sdk' do + # Verifies that opentelemetry-metrics-sdk is loaded and the global meter_provider + # is replaced with the full SDK implementation rather than the no-op default. + it 'loads_metrics_sdk' do + result = run_in_subprocess + + _(result[:error]).must_be_nil + _(result[:meter_provider_class]).must_equal 'OpenTelemetry::SDK::Metrics::MeterProvider' + end + + # Verifies that opentelemetry-logs-sdk is loaded and the global logger_provider + # is replaced with the full SDK implementation rather than the no-op default. + it 'loads_logs_sdk' do + result = run_in_subprocess + + _(result[:error]).must_be_nil + _(result[:logger_provider_class]).must_equal 'OpenTelemetry::SDK::Logs::LoggerProvider' + end + end + + describe 'signal data' do + # Verifies that the TracerProvider can record a span end-to-end: creates a + # tracer, opens a span with a custom attribute, and confirms the finished span + # is captured by an in-memory exporter with the correct name and attribute. + it 'captures_trace_span' do + result = run_in_subprocess( + { 'OTEL_TRACES_EXPORTER' => 'none', 'OTEL_METRICS_EXPORTER' => 'none', 'OTEL_LOGS_EXPORTER' => 'none' }, + { inspect_signals: true } + ) + + _(result[:error]).must_be_nil + _(result[:spans]).wont_be_empty + span = result[:spans].first + _(span[:name]).must_equal 'test-span' + _(span[:attributes]['test.key']).must_equal 'test.value' + end + + # Verifies that the MeterProvider can record a counter measurement end-to-end: + # creates a counter, adds a value with attributes, pulls the metric reader, and + # confirms the data point is captured with the correct value and attributes. + it 'captures_metric_counter' do + result = run_in_subprocess( + { 'OTEL_TRACES_EXPORTER' => 'none', 'OTEL_METRICS_EXPORTER' => 'none', 'OTEL_LOGS_EXPORTER' => 'none' }, + { inspect_signals: true } + ) + + _(result[:error]).must_be_nil + _(result[:metrics]).wont_be_empty + metric = result[:metrics].find { |m| m[:name] == 'test.counter' } + _(metric).wont_be_nil + data_point = metric[:data_points].first + _(data_point[:value]).must_equal 3 + _(data_point[:attributes]['env']).must_equal 'test' + end + + # Verifies that the LoggerProvider can emit a log record end-to-end: obtains a + # logger, emits a record with a severity and body, and confirms the record is + # captured by an in-memory exporter with the correct body and severity text. + it 'captures_log_record' do + result = run_in_subprocess( + { 'OTEL_TRACES_EXPORTER' => 'none', 'OTEL_METRICS_EXPORTER' => 'none', 'OTEL_LOGS_EXPORTER' => 'none' }, + { inspect_signals: true } + ) + + _(result[:error]).must_be_nil + _(result[:logs]).wont_be_empty + log = result[:logs].first + _(log[:body]).must_equal 'test log message' + _(log[:severity_text]).must_equal 'INFO' + end + end + + describe 'check_for_bundled_otel_gems' do + # Verifies that no warning is emitted when the user's Gemfile contains only + # non-OpenTelemetry gems, since there is no conflict to report. + it 'emits no warning when there are no opentelemetry gems in the bundle' do + result = run_in_subprocess({}, dep_names: %w[rack faraday]) + + _(result[:error]).must_be_nil + _(result[:warning_output]).must_be_empty + end + + # Verifies that a warning naming the conflicting gems is emitted when the + # Gemfile contains OpenTelemetry gems, and that non-OTel gems are not mentioned. + it 'emits a warning listing detected opentelemetry gems' do + otel_gems = %w[opentelemetry-sdk opentelemetry-instrumentation-net_http rack] + result = run_in_subprocess({}, dep_names: otel_gems) + + _(result[:error]).must_be_nil + _(result[:warning_output]).must_include '[OpenTelemetry] WARNING' + _(result[:warning_output]).must_include 'opentelemetry-instrumentation-net_http' + _(result[:warning_output]).must_include 'opentelemetry-sdk' + _(result[:warning_output]).wont_include 'rack' + end + + # Verifies that a Bundler error during the gem-list check is silently swallowed + # when debug mode is off, so the application still starts cleanly. + it 'emits no warning when Bundler.definition raises and debug mode is off' do + result = run_in_subprocess({}, raise_error: true) + + _(result[:error]).must_be_nil + _(result[:warning_output]).must_be_empty + end + + # Verifies that the same Bundler error is surfaced as a warning when debug mode + # is enabled, including the original error message for easier diagnosis. + it 'emits a warning when Bundler.definition raises and debug mode is on' do + result = run_in_subprocess({ 'OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG' => 'true' }, raise_error: true) + + _(result[:error]).must_be_nil + _(result[:warning_output]).must_include '[OpenTelemetry] WARNING: Unable to check Gemfile' + _(result[:warning_output]).must_include 'simulated bundler error' + end + end +end diff --git a/packages/auto-instrumentation/test/test_helper.rb b/packages/auto-instrumentation/test/test_helper.rb index 016bdd6202..e4ef90a5da 100644 --- a/packages/auto-instrumentation/test/test_helper.rb +++ b/packages/auto-instrumentation/test/test_helper.rb @@ -13,7 +13,29 @@ require 'opentelemetry/resource/detector' require 'net/http' -# Helper function that execute the auto-instrumentation in isolated env +# Runs the auto-instrumentation loading logic inside a forked subprocess so that +# each test starts from a clean Ruby process with no previously loaded constants, +# initialized providers, or mutated global state. +# +# Three mutually-exclusive execution branches exist to cover distinct scenarios: +# +# Branch 1 – Bundler warning simulation (dep_names: or raise_error: opts) +# Simulates what happens when the user's Gemfile contains OpenTelemetry gems, or +# when Bundler itself raises during the gem-list check. The subprocess stubs +# Bundler.definition so no real bundle resolution occurs, then calls +# _otel_check_for_bundled_otel_gems directly and captures any stderr warnings. +# +# Branch 2 – Signal data inspection (inspect_signals: true opt) +# Simulates a fully initialised SDK where real telemetry data is produced and +# collected in-memory. Registers in-memory exporters for all three signals +# (traces, metrics, logs), exercises each signal, and returns the captured data +# so tests can assert on specific span names, counter values, and log bodies. +# +# Branch 3 – Default provider class verification (no special opts) +# Simulates normal application startup. Loads the auto-instrumentation and +# calls Bundler.require, then returns provider class names, resource attributes, +# and the list of installed instrumentation so tests can assert the SDK wired +# up the correct implementation classes. def run_in_subprocess(env_vars = {}, opts = {}) dep_names = opts[:dep_names] raise_error = opts.fetch(:raise_error, false) @@ -35,7 +57,7 @@ def run_in_subprocess(env_vars = {}, opts = {}) result = {} - # check the log msg based on different condition + # Branch 1: Bundler warning simulation if !dep_names.nil? || raise_error fake_dep = Struct.new(:name) fake_deps = (dep_names || []).map { |n| fake_dep.new(n) } @@ -56,6 +78,46 @@ def run_in_subprocess(env_vars = {}, opts = {}) $VERBOSE = old_verbose OTelBundlerPatch::OTelInitializer._otel_check_for_bundled_otel_gems + # Branch 2: Signal data inspection + # it attaches in-memory exporters to the already-configured providers + # and exercises them with real data. + elsif opts[:inspect_signals] + Bundler.require + + # --- Traces --- + span_exporter = OpenTelemetry::SDK::Trace::Export::InMemorySpanExporter.new + span_processor = OpenTelemetry::SDK::Trace::Export::SimpleSpanProcessor.new(span_exporter) + OpenTelemetry.tracer_provider.add_span_processor(span_processor) + tracer = OpenTelemetry.tracer_provider.tracer('test-tracer') + tracer.in_span('test-span') { |span| span.set_attribute('test.key', 'test.value') } + result[:spans] = span_exporter.finished_spans.map do |s| + { name: s.name, attributes: s.attributes.to_h } + end + + # --- Metrics --- + metric_exporter = OpenTelemetry::SDK::Metrics::Export::InMemoryMetricPullExporter.new + OpenTelemetry.meter_provider.add_metric_reader(metric_exporter) + meter = OpenTelemetry.meter_provider.meter('test-meter') + counter = meter.create_counter('test.counter', unit: '1', description: 'Test counter') + counter.add(3, attributes: { 'env' => 'test' }) + metric_exporter.pull + result[:metrics] = metric_exporter.metric_snapshots.map do |m| + { + name: m.name, + data_points: m.data_points.map { |dp| { value: dp.value, attributes: dp.attributes.to_h } } + } + end + + # --- Logs --- + log_exporter = OpenTelemetry::SDK::Logs::Export::InMemoryLogRecordExporter.new + log_processor = OpenTelemetry::SDK::Logs::Export::SimpleLogRecordProcessor.new(log_exporter) + OpenTelemetry.logger_provider.add_log_record_processor(log_processor) + otel_logger = OpenTelemetry.logger_provider.logger(name: 'test-logger') + otel_logger.on_emit(severity_text: 'INFO', body: 'test log message') + result[:logs] = log_exporter.emitted_log_records.map do |lr| + { body: lr.body, severity_text: lr.severity_text } + end + # Branch 3: Default provider class verification else Bundler.require @@ -67,6 +129,8 @@ def run_in_subprocess(env_vars = {}, opts = {}) result.merge!( tracer_provider_class: tracer_provider.class.name, + meter_provider_class: OpenTelemetry.meter_provider.class.name, + logger_provider_class: OpenTelemetry.logger_provider.class.name, resource_attributes: resource_attributes, instrumentation_names: instrumentation_names ) From 387f1a79caa0665d03a92c89ab54c8b9a6994dd2 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Thu, 16 Apr 2026 13:10:27 -0400 Subject: [PATCH 58/68] lint --- packages/auto-instrumentation/.rubocop.yml | 2 +- packages/auto-instrumentation/README.md | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/auto-instrumentation/.rubocop.yml b/packages/auto-instrumentation/.rubocop.yml index 376945e736..cb254a9080 100644 --- a/packages/auto-instrumentation/.rubocop.yml +++ b/packages/auto-instrumentation/.rubocop.yml @@ -5,4 +5,4 @@ Naming/FileName: - "test/opentelemetry-auto-instrumentation_test.rb" Metrics/ModuleLength: Exclude: - - "lib/opentelemetry-auto-instrumentation.rb" \ No newline at end of file + - "lib/opentelemetry-auto-instrumentation.rb" diff --git a/packages/auto-instrumentation/README.md b/packages/auto-instrumentation/README.md index 1e044f2971..1bf03ddaee 100644 --- a/packages/auto-instrumentation/README.md +++ b/packages/auto-instrumentation/README.md @@ -190,7 +190,24 @@ The following environment variables are specific to this gem (not standard OpenT | `OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG` | Set to `true` for debug output during initialization. | `true` | | `OTEL_RUBY_UNLOAD_LIBRARY` | Prevent specific gems from being preloaded (e.g., `google-protobuf`). | `google-protobuf` | -For standard OpenTelemetry environment variables (exporters, endpoints, resource attributes, etc.), refer to the SDK READMEs: +### Standard OpenTelemetry Environment Variables + +This gem fully supports all standard OpenTelemetry environment variables for configuring exporters, endpoints, resource attributes, sampling, and other SDK behavior. These variables work alongside the gem-specific variables listed above. + +Examples of standard variables you can use: + +- `OTEL_EXPORTER_OTLP_ENDPOINT` — OTLP receiver endpoint (default: `http://localhost:4318`) +- `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` — traces-specific endpoint +- `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` — metrics-specific endpoint +- `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` — logs-specific endpoint +- `OTEL_SERVICE_NAME` — service name resource attribute +- `OTEL_RESOURCE_ATTRIBUTES` — comma-separated key=value pairs for resource attributes +- `OTEL_TRACES_EXPORTER` — trace exporter (default: `otlp`; use `console` or `none`) +- `OTEL_METRICS_EXPORTER` — metrics exporter (default: `otlp`; use `console` or `none`) +- `OTEL_LOGS_EXPORTER` — logs exporter (default: `otlp`; use `console` or `none`) +- `OTEL_TRACES_SAMPLER` — sampling strategy (e.g., `always_on`, `always_off`, `traceidratio`) + +For a complete list of standard variables, refer to the SDK READMEs: - [opentelemetry-sdk (traces)][otel-sdk-readme] - [opentelemetry-metrics-sdk (metrics)][otel-metrics-sdk-readme] From d62d69c49adcc4a052d858485e504f3ad01958ac Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Thu, 16 Apr 2026 13:13:24 -0400 Subject: [PATCH 59/68] remove unnecessary require in test --- packages/auto-instrumentation/test/test_helper.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/auto-instrumentation/test/test_helper.rb b/packages/auto-instrumentation/test/test_helper.rb index e4ef90a5da..f8fe20e618 100644 --- a/packages/auto-instrumentation/test/test_helper.rb +++ b/packages/auto-instrumentation/test/test_helper.rb @@ -7,10 +7,6 @@ require 'rake' require 'minitest' require 'minitest/autorun' -require 'opentelemetry-sdk' -require 'opentelemetry-instrumentation-all' -require 'opentelemetry-test-helpers' -require 'opentelemetry/resource/detector' require 'net/http' # Runs the auto-instrumentation loading logic inside a forked subprocess so that From 82ed423cab659f6bb30ce92f7f5a0c83dbf27ac1 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Thu, 16 Apr 2026 14:50:03 -0400 Subject: [PATCH 60/68] ci fix --- .github/workflows/ci-contrib.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-contrib.yml b/.github/workflows/ci-contrib.yml index 31a3729a94..cdc667c12a 100644 --- a/.github/workflows/ci-contrib.yml +++ b/.github/workflows/ci-contrib.yml @@ -243,17 +243,17 @@ jobs: - name: "Test Ruby 4.0" uses: ./.github/actions/test_gem with: - gem: "auto-instrumentation" + gem: "opentelemetry-auto-instrumentation" ruby: "4.0" - name: "Test Ruby 3.4" uses: ./.github/actions/test_gem with: - gem: "auto-instrumentation" + gem: "opentelemetry-auto-instrumentation" ruby: "3.4" - name: "Test Ruby 3.3" uses: ./.github/actions/test_gem with: - gem: "auto-instrumentation" + gem: "opentelemetry-auto-instrumentation" ruby: "3.3" yard: true build: true From d99f4067b048d70aed5386a42fe1d5c3c6b19355 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Wed, 22 Apr 2026 12:17:45 -0400 Subject: [PATCH 61/68] revision based on copilot --- .github/workflows/ci-contrib.yml | 2 +- packages/auto-instrumentation/README.md | 1 - .../example/simple-example/app.rb | 2 ++ .../lib/opentelemetry-auto-instrumentation.rb | 12 ++++++++++-- .../auto_instrumentation}/version.rb | 0 .../opentelemetry-auto-instrumentation.gemspec | 4 ++-- 6 files changed, 15 insertions(+), 6 deletions(-) rename packages/auto-instrumentation/lib/{ => opentelemetry/auto_instrumentation}/version.rb (100%) diff --git a/.github/workflows/ci-contrib.yml b/.github/workflows/ci-contrib.yml index cdc667c12a..fa88397144 100644 --- a/.github/workflows/ci-contrib.yml +++ b/.github/workflows/ci-contrib.yml @@ -239,7 +239,7 @@ jobs: name: "auto-instrumentation / ${{ matrix.os }}" runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.1 - name: "Test Ruby 4.0" uses: ./.github/actions/test_gem with: diff --git a/packages/auto-instrumentation/README.md b/packages/auto-instrumentation/README.md index 1bf03ddaee..08fc81bbdf 100644 --- a/packages/auto-instrumentation/README.md +++ b/packages/auto-instrumentation/README.md @@ -188,7 +188,6 @@ The following environment variables are specific to this gem (not standard OpenT | `OTEL_RUBY_ENABLED_INSTRUMENTATIONS` | Only load specific instrumentations (comma-separated). Omit to load all available. | `redis,mysql2,faraday` | | `OTEL_RUBY_ADDITIONAL_GEM_PATH` | Custom gem installation path for OpenTelemetry Operator environments. | `/custom/gem/path` | | `OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG` | Set to `true` for debug output during initialization. | `true` | -| `OTEL_RUBY_UNLOAD_LIBRARY` | Prevent specific gems from being preloaded (e.g., `google-protobuf`). | `google-protobuf` | ### Standard OpenTelemetry Environment Variables diff --git a/packages/auto-instrumentation/example/simple-example/app.rb b/packages/auto-instrumentation/example/simple-example/app.rb index 4d6ce333ea..b9dd7bab10 100644 --- a/packages/auto-instrumentation/example/simple-example/app.rb +++ b/packages/auto-instrumentation/example/simple-example/app.rb @@ -3,6 +3,8 @@ # Copyright The OpenTelemetry Authors # # SPDX-License-Identifier: Apache-2.0 +require 'uri' +require 'net/http' url = URI.parse('http://catfact.ninja/fact') req = Net::HTTP::Get.new(url.to_s) diff --git a/packages/auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb b/packages/auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb index 2cc5d4f2d4..e1ef218d2b 100644 --- a/packages/auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb +++ b/packages/auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb @@ -78,7 +78,14 @@ def self._otel_detect_resource_from_env def self._otel_determine_enabled_instrumentation env = ENV['OTEL_RUBY_ENABLED_INSTRUMENTATIONS'].to_s - env.split(',').map { |instrumentation| OTEL_INSTRUMENTATION_MAP[instrumentation] } + return [] if env.strip.empty? + + env.split(',').filter_map do |instrumentation| + normalized = instrumentation.strip.downcase + value = OTEL_INSTRUMENTATION_MAP[normalized] + warn "[OpenTelemetry] WARNING: Unknown instrumentation '#{instrumentation.strip}'" if value.nil? && ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] == 'true' + value + end end def self._otel_check_for_bundled_otel_gems @@ -139,7 +146,8 @@ def require(...) # /otel-auto-instrumentation-ruby is default path for otel operator (ruby.go) # If requires different gem path to load gem, set env OTEL_RUBY_ADDITIONAL_GEM_PATH -gem_path = ENV['OTEL_RUBY_ADDITIONAL_GEM_PATH'] || '/otel-auto-instrumentation-ruby' || Gem.dir +gem_path = ENV['OTEL_RUBY_ADDITIONAL_GEM_PATH'] || '/otel-auto-instrumentation-ruby' +gem_path = Gem.dir unless Dir.exist?(gem_path) $stdout.puts "Loading the gem path from #{gem_path}" if ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] == 'true' # Load OpenTelemetry components and their dependencies diff --git a/packages/auto-instrumentation/lib/version.rb b/packages/auto-instrumentation/lib/opentelemetry/auto_instrumentation/version.rb similarity index 100% rename from packages/auto-instrumentation/lib/version.rb rename to packages/auto-instrumentation/lib/opentelemetry/auto_instrumentation/version.rb diff --git a/packages/auto-instrumentation/opentelemetry-auto-instrumentation.gemspec b/packages/auto-instrumentation/opentelemetry-auto-instrumentation.gemspec index a747394179..3f2051d838 100644 --- a/packages/auto-instrumentation/opentelemetry-auto-instrumentation.gemspec +++ b/packages/auto-instrumentation/opentelemetry-auto-instrumentation.gemspec @@ -6,7 +6,7 @@ lib = File.expand_path('lib', __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) -require 'version' +require 'opentelemetry/auto_instrumentation/version' Gem::Specification.new do |spec| spec.name = 'opentelemetry-auto-instrumentation' @@ -45,7 +45,7 @@ Gem::Specification.new do |spec| if spec.respond_to?(:metadata) spec.metadata['changelog_uri'] = "https://rubydoc.info/gems/#{spec.name}/#{spec.version}/file/CHANGELOG.md" - spec.metadata['source_code_uri'] = "https://github.com/open-telemetry/opentelemetry-ruby-contrib/tree/#{spec.name}/v#{spec.version}/packages/opentelemetry-auto-instrumentation" + spec.metadata['source_code_uri'] = "https://github.com/open-telemetry/opentelemetry-ruby-contrib/tree/#{spec.name}/v#{spec.version}/packages/auto-instrumentation" spec.metadata['bug_tracker_uri'] = 'https://github.com/open-telemetry/opentelemetry-ruby-contrib/issues' spec.metadata['documentation_uri'] = "https://rubydoc.info/gems/#{spec.name}/#{spec.version}" end From d2828443ad5fc3eeb60defefe33000b8532d39db Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Wed, 22 Apr 2026 12:20:11 -0400 Subject: [PATCH 62/68] merge --- packages/auto-instrumentation/Gemfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/auto-instrumentation/Gemfile b/packages/auto-instrumentation/Gemfile index 52d2b2250c..4be4b3d4d3 100644 --- a/packages/auto-instrumentation/Gemfile +++ b/packages/auto-instrumentation/Gemfile @@ -14,6 +14,9 @@ group :test do gem 'rubocop', '~> 1.86.0' gem 'rubocop-performance', '~> 1.26.0' gem 'simplecov', '~> 0.22.0' + gem 'rubocop-minitest', '~> 0.39.0' + gem 'rubocop-rspec', '~> 3.9.0' + gem 'rubocop-rake', '~> 0.7.1' gem 'yard', '~> 0.9' gem 'opentelemetry-test-helpers', '~> 0.8.0' if RUBY_VERSION >= '3.4' From 14727c2ded1548990d48098fe3dc5d00b4e56226 Mon Sep 17 00:00:00 2001 From: James Thompson Date: Thu, 23 Apr 2026 02:31:17 +1000 Subject: [PATCH 63/68] Add auto-instrumentation configuration to labeler --- .github/labeler.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/labeler.yml b/.github/labeler.yml index cd8c7f7d1e..3d1f60e7f6 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -12,6 +12,11 @@ deprecated: - "instrumentation/restclient/**" - "instrumentation/ruby_kafka/**" +auto-instrumentation: + - changed-files: + - any-glob-to-any-file: + - "packages/auto-instrumentation/**" + helpers-mysql: - changed-files: - any-glob-to-any-file: From 91dde11617e9f32823ba2acd4e376ef566fd94ff Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Wed, 22 Apr 2026 12:36:54 -0400 Subject: [PATCH 64/68] simplecov --- packages/auto-instrumentation/test/test_helper.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/auto-instrumentation/test/test_helper.rb b/packages/auto-instrumentation/test/test_helper.rb index f8fe20e618..b9e5d58fd0 100644 --- a/packages/auto-instrumentation/test/test_helper.rb +++ b/packages/auto-instrumentation/test/test_helper.rb @@ -4,6 +4,7 @@ # # SPDX-License-Identifier: Apache-2.0 +require 'simplecov' require 'rake' require 'minitest' require 'minitest/autorun' @@ -142,7 +143,7 @@ def run_in_subprocess(env_vars = {}, opts = {}) ensure $stderr = old_stderr if defined?(old_stderr) write_pipe.close - exit!(0) + exit(0) end end From 4c00f6457d6f667390bee6cd499ed98c87114eba Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Thu, 23 Apr 2026 10:37:16 -0400 Subject: [PATCH 65/68] coverage --- .github/workflows/ci-contrib.yml | 2 +- packages/auto-instrumentation/test/test_helper.rb | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-contrib.yml b/.github/workflows/ci-contrib.yml index f46a68f13f..4cf1ddcafc 100644 --- a/.github/workflows/ci-contrib.yml +++ b/.github/workflows/ci-contrib.yml @@ -239,7 +239,7 @@ jobs: name: "auto-instrumentation / ${{ matrix.os }}" runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: "Test Ruby 4.0" uses: ./.github/actions/test_gem with: diff --git a/packages/auto-instrumentation/test/test_helper.rb b/packages/auto-instrumentation/test/test_helper.rb index b9e5d58fd0..7006ba54a8 100644 --- a/packages/auto-instrumentation/test/test_helper.rb +++ b/packages/auto-instrumentation/test/test_helper.rb @@ -40,6 +40,7 @@ def run_in_subprocess(env_vars = {}, opts = {}) read_pipe, write_pipe = IO.pipe pid = fork do + SimpleCov.command_name "subprocess-#{Process.pid}" read_pipe.close env_vars.each { |key, value| ENV[key] = value } ENV['OTEL_RUBY_REQUIRE_BUNDLER'] = 'false' @@ -143,7 +144,10 @@ def run_in_subprocess(env_vars = {}, opts = {}) ensure $stderr = old_stderr if defined?(old_stderr) write_pipe.close - exit(0) + + # Store the simplecov result for this process + SimpleCov::ResultMerger.store_result(SimpleCov::Result.new(Coverage.result)) if SimpleCov.running + exit!(0) end end From b931e47549c817785cf20f3e8daa4690bc316268 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Tue, 28 Apr 2026 10:05:58 -0400 Subject: [PATCH 66/68] additional resource attr --- .../lib/opentelemetry-auto-instrumentation.rb | 10 ++++++++++ .../test/opentelemetry-auto-instrumentation_test.rb | 2 ++ 2 files changed, 12 insertions(+) diff --git a/packages/auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb b/packages/auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb index e1ef218d2b..c0b0a74842 100644 --- a/packages/auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb +++ b/packages/auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb @@ -75,6 +75,15 @@ def self._otel_detect_resource_from_env additional_resource end + def self._otel_distro_resource + ::OpenTelemetry::SDK::Resources::Resource.create( + { + 'telemetry.distro.name' => 'opentelemetry-ruby-instrumentation', + 'telemetry.distro.version' => '0.0.0' + } + ) + end + def self._otel_determine_enabled_instrumentation env = ENV['OTEL_RUBY_ENABLED_INSTRUMENTATIONS'].to_s @@ -117,6 +126,7 @@ def self._otel_require_otel required_instrumentation = _otel_determine_enabled_instrumentation resource = _otel_detect_resource_from_env + resource = resource.merge(_otel_distro_resource) OpenTelemetry::SDK.configure do |c| c.resource = resource diff --git a/packages/auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb b/packages/auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb index a5976df4a5..5b292298ea 100644 --- a/packages/auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb +++ b/packages/auto-instrumentation/test/opentelemetry-auto-instrumentation_test.rb @@ -28,6 +28,8 @@ _(result[:resource_attributes]['service.name']).must_equal 'unknown_service' _(result[:resource_attributes]['telemetry.sdk.name']).must_equal 'opentelemetry' _(result[:resource_attributes]['telemetry.sdk.language']).must_equal 'ruby' + _(result[:resource_attributes]['telemetry.distro.name']).must_equal 'opentelemetry-ruby-instrumentation' + _(result[:resource_attributes]['telemetry.distro.version']).must_match(/^\d+\.\d+\.\d+/) _(result[:resource_attributes].key?('container.id')).must_equal false _(result[:instrumentation_names]).must_include 'OpenTelemetry::Instrumentation::Net::HTTP' _(result[:instrumentation_names]).must_include 'OpenTelemetry::Instrumentation::Rake' From 963e81f57a0b897d54a13f0d17d8f1694f36f34c Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Tue, 28 Apr 2026 11:06:18 -0400 Subject: [PATCH 67/68] use warn and registry --- .../lib/opentelemetry-auto-instrumentation.rb | 150 +++++++++--------- 1 file changed, 74 insertions(+), 76 deletions(-) diff --git a/packages/auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb b/packages/auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb index c0b0a74842..f455d5bf18 100644 --- a/packages/auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb +++ b/packages/auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb @@ -8,71 +8,64 @@ module OTelBundlerPatch # Nested module to handle OpenTelemetry initialization logic module OTelInitializer + @_otel_mutex = Mutex.new @_otel_initialized = false - OTEL_INSTRUMENTATION_MAP = { - 'action_mailer' => 'OpenTelemetry::Instrumentation::ActionMailer', - 'action_pack' => 'OpenTelemetry::Instrumentation::ActionPack', - 'action_view' => 'OpenTelemetry::Instrumentation::ActionView', - 'active_job' => 'OpenTelemetry::Instrumentation::ActiveJob', - 'active_model_serializers' => 'OpenTelemetry::Instrumentation::ActiveModelSerializers', - 'active_record' => 'OpenTelemetry::Instrumentation::ActiveRecord', - 'active_storage' => 'OpenTelemetry::Instrumentation::ActiveStorage', - 'active_support' => 'OpenTelemetry::Instrumentation::ActiveSupport', - 'anthropic' => 'OpenTelemetry::Instrumentation::Anthropic', - 'aws_lambda' => 'OpenTelemetry::Instrumentation::AwsLambda', - 'aws_sdk' => 'OpenTelemetry::Instrumentation::AwsSdk', - 'bunny' => 'OpenTelemetry::Instrumentation::Bunny', - 'concurrent_ruby' => 'OpenTelemetry::Instrumentation::ConcurrentRuby', - 'dalli' => 'OpenTelemetry::Instrumentation::Dalli', - 'delayed_job' => 'OpenTelemetry::Instrumentation::DelayedJob', - 'ethon' => 'OpenTelemetry::Instrumentation::Ethon', - 'excon' => 'OpenTelemetry::Instrumentation::Excon', - 'faraday' => 'OpenTelemetry::Instrumentation::Faraday', - 'grape' => 'OpenTelemetry::Instrumentation::Grape', - 'graphql' => 'OpenTelemetry::Instrumentation::GraphQL', - 'grpc' => 'OpenTelemetry::Instrumentation::Grpc', - 'gruf' => 'OpenTelemetry::Instrumentation::Gruf', - 'http' => 'OpenTelemetry::Instrumentation::HTTP', - 'http_client' => 'OpenTelemetry::Instrumentation::HttpClient', - 'httpx' => 'OpenTelemetry::Instrumentation::HTTPX', - 'koala' => 'OpenTelemetry::Instrumentation::Koala', - 'lmdb' => 'OpenTelemetry::Instrumentation::LMDB', - 'mongo' => 'OpenTelemetry::Instrumentation::Mongo', - 'mysql2' => 'OpenTelemetry::Instrumentation::Mysql2', - 'net_http' => 'OpenTelemetry::Instrumentation::Net::HTTP', - 'pg' => 'OpenTelemetry::Instrumentation::PG', - 'que' => 'OpenTelemetry::Instrumentation::Que', - 'racecar' => 'OpenTelemetry::Instrumentation::Racecar', - 'rack' => 'OpenTelemetry::Instrumentation::Rack', - 'rails' => 'OpenTelemetry::Instrumentation::Rails', - 'rake' => 'OpenTelemetry::Instrumentation::Rake', - 'rdkafka' => 'OpenTelemetry::Instrumentation::Rdkafka', - 'redis' => 'OpenTelemetry::Instrumentation::Redis', - 'resque' => 'OpenTelemetry::Instrumentation::Resque', - 'restclient' => 'OpenTelemetry::Instrumentation::RestClient', - 'ruby_kafka' => 'OpenTelemetry::Instrumentation::RubyKafka', - 'sidekiq' => 'OpenTelemetry::Instrumentation::Sidekiq', - 'sinatra' => 'OpenTelemetry::Instrumentation::Sinatra', - 'trilogy' => 'OpenTelemetry::Instrumentation::Trilogy' - }.freeze - private_constant :OTEL_INSTRUMENTATION_MAP + def self._otel_registry_instrumentation_classes + registry = ::OpenTelemetry::Instrumentation.registry - def self._otel_detect_resource_from_env - env = ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s - additional_resource = ::OpenTelemetry::SDK::Resources::Resource.create({}) + # The registry only exposes lookup/install methods publicly, so enumerate + # the internal collection to derive supported instrumentation names. + registry.instance_variable_get(:@instrumentation) || [] + rescue StandardError + [] + end + + def self._otel_snake_case(value) + value + .gsub(/([A-Z]+)([A-Z][a-z])/, '\\1_\\2') + .gsub(/([a-z\\d])([A-Z])/, '\\1_\\2') + .tr('-', '_') + .downcase + end + + def self._otel_registry_aliases_for(instrumentation_name) + suffix = instrumentation_name.delete_prefix('OpenTelemetry::Instrumentation::') + segment_variants = suffix.split('::').map do |segment| + snake = _otel_snake_case(segment) + compact = segment.downcase + [snake, compact].uniq + end + + segment_variants.reduce(['']) do |aliases, variants| + aliases.flat_map do |alias_prefix| + variants.map { |variant| alias_prefix.empty? ? variant : "#{alias_prefix}_#{variant}" } + end + end + end + def self._otel_registry_lookup + @_otel_registry_lookup ||= _otel_registry_instrumentation_classes.each_with_object({}) do |instrumentation_class, lookup| + instrumentation_name = instrumentation_class.instance.name + _otel_registry_aliases_for(instrumentation_name).each do |alias_name| + lookup[alias_name] ||= instrumentation_name + end + rescue StandardError + next + end + end + + def self._otel_detect_resource_from_env resource_map = { 'container' => (defined?(::OpenTelemetry::Resource::Detector::Container) ? ::OpenTelemetry::Resource::Detector::Container : nil), 'azure' => (defined?(::OpenTelemetry::Resource::Detector::Azure) ? ::OpenTelemetry::Resource::Detector::Azure : nil), 'aws' => (defined?(::OpenTelemetry::Resource::Detector::AWS) ? ::OpenTelemetry::Resource::Detector::AWS : nil) } - env.split(',').each do |detector| - additional_resource = additional_resource.merge(resource_map[detector].detect) if resource_map[detector] + ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s.split(',').reduce(::OpenTelemetry::SDK::Resources::Resource.create({})) do |resource, detector| + detector_class = resource_map[detector] + detector_class ? resource.merge(detector_class.detect) : resource end - - additional_resource end def self._otel_distro_resource @@ -89,9 +82,11 @@ def self._otel_determine_enabled_instrumentation return [] if env.strip.empty? + instrumentation_lookup = _otel_registry_lookup + env.split(',').filter_map do |instrumentation| normalized = instrumentation.strip.downcase - value = OTEL_INSTRUMENTATION_MAP[normalized] + value = instrumentation_lookup[normalized] warn "[OpenTelemetry] WARNING: Unknown instrumentation '#{instrumentation.strip}'" if value.nil? && ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] == 'true' value end @@ -116,32 +111,35 @@ def self._otel_check_for_bundled_otel_gems end def self._otel_require_otel - return if @_otel_initialized + @_otel_mutex.synchronize do + return if @_otel_initialized - @_otel_initialized = true + @_otel_initialized = true - begin - _otel_check_for_bundled_otel_gems + begin + _otel_check_for_bundled_otel_gems - required_instrumentation = _otel_determine_enabled_instrumentation + required_instrumentation = _otel_determine_enabled_instrumentation - resource = _otel_detect_resource_from_env - resource = resource.merge(_otel_distro_resource) + resource = _otel_detect_resource_from_env + resource = resource.merge(_otel_distro_resource) - OpenTelemetry::SDK.configure do |c| - c.resource = resource - if required_instrumentation.empty? - c.use_all - else - required_instrumentation.each do |instrumentation| - c.use instrumentation + OpenTelemetry::SDK.configure do |c| + c.resource = resource + if required_instrumentation.empty? + c.use_all + else + required_instrumentation.each do |instrumentation| + c.use instrumentation + end end end - end - OpenTelemetry.logger.info { 'Auto-instrumentation initialized' } - rescue StandardError => e - warn "Auto-instrumentation failed to initialize. Error: #{e.message}" + OpenTelemetry.logger.info { 'Auto-instrumentation initialized' } + rescue StandardError => e + @_otel_initialized = false + warn "Auto-instrumentation failed to initialize. Error: #{e.message}" + end end end end @@ -158,7 +156,7 @@ def require(...) # If requires different gem path to load gem, set env OTEL_RUBY_ADDITIONAL_GEM_PATH gem_path = ENV['OTEL_RUBY_ADDITIONAL_GEM_PATH'] || '/otel-auto-instrumentation-ruby' gem_path = Gem.dir unless Dir.exist?(gem_path) -$stdout.puts "Loading the gem path from #{gem_path}" if ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] == 'true' +warn "Loading the gem path from #{gem_path}" if ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] == 'true' # Load OpenTelemetry components and their dependencies # googleapis-common-protos-types and google-protobuf are dependencies for otlp exporters @@ -170,7 +168,7 @@ def require(...) # unshift file_path add opentelemetry component at the top of $LOAD_PATH loaded_library_file_path.each { |file_path| $LOAD_PATH.unshift("#{file_path}/lib") } -$stdout.puts "$LOAD_PATH after unshift: #{$LOAD_PATH.join(',')}" if ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] == 'true' +warn "$LOAD_PATH after unshift: #{$LOAD_PATH.join(',')}" if ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] == 'true' # These are required for the prepend OTelBundlerPatch to fetch OpenTelemetry::SDK.configure require 'opentelemetry-sdk' @@ -181,7 +179,7 @@ def require(...) require 'opentelemetry-exporter-otlp-logs' require 'opentelemetry-instrumentation-all' -resource_detectors = ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s +resource_detectors = ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s.split(',').map(&:strip) require 'opentelemetry-resource-detector-container' if resource_detectors.include?('container') require 'opentelemetry-resource-detector-azure' if resource_detectors.include?('azure') require 'opentelemetry-resource-detector-aws' if resource_detectors.include?('aws') From 26b53d0e951854957a094a22195d4015997d7501 Mon Sep 17 00:00:00 2001 From: xuan-cao-swi Date: Tue, 28 Apr 2026 12:26:45 -0400 Subject: [PATCH 68/68] revision --- packages/auto-instrumentation/README.md | 14 ++++++- .../auto-instrumentation/example/README.md | 1 + .../example/rails-example/app.rb | 31 ++++++++++++--- .../lib/opentelemetry-auto-instrumentation.rb | 38 ++++++++++++++----- 4 files changed, 66 insertions(+), 18 deletions(-) diff --git a/packages/auto-instrumentation/README.md b/packages/auto-instrumentation/README.md index 08fc81bbdf..e70ab7986d 100644 --- a/packages/auto-instrumentation/README.md +++ b/packages/auto-instrumentation/README.md @@ -184,11 +184,21 @@ The following environment variables are specific to this gem (not standard OpenT | Environment Variable | Description | Example | | -------------------- | ----------- | ------- | | `OTEL_RUBY_REQUIRE_BUNDLER` | Set to `true` to automatically call `Bundler.require` during initialization. Required for frameworks that don't call it automatically (e.g., Sinatra). | `true` | -| `OTEL_RUBY_RESOURCE_DETECTORS` | Comma-separated list of resource detectors. Supported: `container`, `azure`, `aws`. **Note:** The GCP detector is not included — its `google-cloud-env` dependency makes blocking HTTP requests to the GCP metadata server, causing timeouts in non-GCP environments. | `container,azure,aws` | +| `OTEL_RUBY_RESOURCE_DETECTORS` | Comma-separated list of resource detectors. Supported: `container`, `azure`, `aws`. | `container,azure,aws` | | `OTEL_RUBY_ENABLED_INSTRUMENTATIONS` | Only load specific instrumentations (comma-separated). Omit to load all available. | `redis,mysql2,faraday` | | `OTEL_RUBY_ADDITIONAL_GEM_PATH` | Custom gem installation path for OpenTelemetry Operator environments. | `/custom/gem/path` | +| `DISALLOWED_LIB_PATH` | Comma-separated list of non-OpenTelemetry helper gems to exclude from additional load-path injection. This filters the internal `ADDITIONAL_LIB_GEM_ALLOWLIST`. Supported values: `googleapis-common-protos-types`, `google-protobuf`. | `google-protobuf` | | `OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG` | Set to `true` for debug output during initialization. | `true` | +### Additional Gem Allowlist + +The loader always injects OpenTelemetry gems from `OTEL_RUBY_ADDITIONAL_GEM_PATH` into `$LOAD_PATH`. For non-OpenTelemetry helper dependencies, it uses an internal allowlist: + +- `googleapis-common-protos-types` +- `google-protobuf` + +This internal list is implemented as `ADDITIONAL_LIB_GEM_ALLOWLIST` in [lib/opentelemetry-auto-instrumentation.rb](lib/opentelemetry-auto-instrumentation.rb). Use `DISALLOWED_LIB_PATH` if you need to exclude one or more entries for compatibility reasons. + ### Standard OpenTelemetry Environment Variables This gem fully supports all standard OpenTelemetry environment variables for configuring exporters, endpoints, resource attributes, sampling, and other SDK behavior. These variables work alongside the gem-specific variables listed above. @@ -228,7 +238,7 @@ RUBYOPT="-r faraday -r opentelemetry-auto-instrumentation" ruby application.rb ### Dependency Version Conflicts -This gem loads OpenTelemetry components (including `google-protobuf` and `googleapis-common-protos-types`) directly into `$LOAD_PATH`. If your Gemfile pins different versions of these gems, you may encounter conflicts. Remove them from your Gemfile and let this gem manage them. +This gem loads OpenTelemetry components and allowlisted helper dependencies (for example `google-protobuf` and `googleapis-common-protos-types`) directly into `$LOAD_PATH`. If your Gemfile pins different versions of these gems, you may encounter conflicts. Remove them from your Gemfile and let this gem manage them, or use `DISALLOWED_LIB_PATH` to exclude specific helper dependencies. ## Example diff --git a/packages/auto-instrumentation/example/README.md b/packages/auto-instrumentation/example/README.md index 66d2e4753c..d3bc0f09a3 100644 --- a/packages/auto-instrumentation/example/README.md +++ b/packages/auto-instrumentation/example/README.md @@ -62,6 +62,7 @@ bundle install OTEL_RUBY_REQUIRE_BUNDLER=false \ OTEL_TRACES_EXPORTER=console \ OTEL_METRICS_EXPORTER=console \ + OTEL_METRIC_EXPORT_INTERVAL=5000 \ OTEL_LOGS_EXPORTER=console \ RUBYOPT="-r opentelemetry-auto-instrumentation" bundle exec rackup config.ru ``` diff --git a/packages/auto-instrumentation/example/rails-example/app.rb b/packages/auto-instrumentation/example/rails-example/app.rb index a29631f227..3264032379 100644 --- a/packages/auto-instrumentation/example/rails-example/app.rb +++ b/packages/auto-instrumentation/example/rails-example/app.rb @@ -10,6 +10,20 @@ require 'bundler' Bundler.require +# NoOp meter for when OpenTelemetry is not available +class NoOpCounter + def add(value, attributes: {}) + # No-op implementation + end +end + +# NoOp logger for when OpenTelemetry is not available +class NoOpLogger + def on_emit(severity_text: nil, body: nil) + # No-op implementation + end +end + # MyApp class MyApp < Rails::Application config.secret_key_base = 'your_secret_key_here' @@ -19,12 +33,17 @@ class MyApp < Rails::Application config.active_support.to_time_preserves_timezone = :zone # Share OpenTelemetry objects across the app through Rails config. - config.x.otel_meter = OpenTelemetry.meter_provider.meter('rails-example') - config.x.otel_request_counter = config.x.otel_meter.create_counter( - 'http.request.count', - description: 'Counts the number of HTTP requests' - ) - config.x.otel_logger = OpenTelemetry.logger_provider.logger(name: 'rails-example') + if defined?(OpenTelemetry) + config.x.otel_meter = OpenTelemetry.meter_provider.meter('rails-example') + config.x.otel_request_counter = config.x.otel_meter.create_counter( + 'http.request.count', + description: 'Counts the number of HTTP requests' + ) + config.x.otel_logger = OpenTelemetry.logger_provider.logger(name: 'rails-example') + else + config.x.otel_request_counter = NoOpCounter.new + config.x.otel_logger = NoOpLogger.new + end end # ApplicationController diff --git a/packages/auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb b/packages/auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb index f455d5bf18..ccd0db005e 100644 --- a/packages/auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb +++ b/packages/auto-instrumentation/lib/opentelemetry-auto-instrumentation.rb @@ -62,7 +62,7 @@ def self._otel_detect_resource_from_env 'aws' => (defined?(::OpenTelemetry::Resource::Detector::AWS) ? ::OpenTelemetry::Resource::Detector::AWS : nil) } - ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s.split(',').reduce(::OpenTelemetry::SDK::Resources::Resource.create({})) do |resource, detector| + ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s.split(',').map(&:strip).reject(&:empty?).reduce(::OpenTelemetry::SDK::Resources::Resource.create({})) do |resource, detector| detector_class = resource_map[detector] detector_class ? resource.merge(detector_class.detect) : resource end @@ -152,23 +152,41 @@ def require(...) require 'bundler' +ADDITIONAL_LIB_GEM_ALLOWLIST = %w[ + googleapis-common-protos-types + google-protobuf +].freeze + +parse_env_list = lambda do |key| + ENV[key].to_s.split(',').map(&:strip).reject(&:empty?) +end + # /otel-auto-instrumentation-ruby is default path for otel operator (ruby.go) # If requires different gem path to load gem, set env OTEL_RUBY_ADDITIONAL_GEM_PATH gem_path = ENV['OTEL_RUBY_ADDITIONAL_GEM_PATH'] || '/otel-auto-instrumentation-ruby' gem_path = Gem.dir unless Dir.exist?(gem_path) -warn "Loading the gem path from #{gem_path}" if ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] == 'true' -# Load OpenTelemetry components and their dependencies +gem_entries = Dir.glob("#{gem_path}/gems/*") + # googleapis-common-protos-types and google-protobuf are dependencies for otlp exporters -loaded_library_file_path = Dir.glob("#{gem_path}/gems/*").select do |file_path| - file_path.include?('opentelemetry') || - file_path.include?('googleapis-common-protos-types') || - file_path.include?('google-protobuf') +# google-cloud-env are dependencies for gcp resource detectors +otel_lib_path = gem_entries.select do |file_path| + File.basename(file_path).start_with?('opentelemetry-') +end + +disallowed_lib_paths = parse_env_list.call('DISALLOWED_LIB_PATH') +allowed_additional_lib_gems = ADDITIONAL_LIB_GEM_ALLOWLIST - disallowed_lib_paths + +additional_lib_path = gem_entries.select do |file_path| + gem_dir_name = File.basename(file_path) + allowed_additional_lib_gems.any? { |gem_name| gem_dir_name.start_with?("#{gem_name}-") } end # unshift file_path add opentelemetry component at the top of $LOAD_PATH -loaded_library_file_path.each { |file_path| $LOAD_PATH.unshift("#{file_path}/lib") } -warn "$LOAD_PATH after unshift: #{$LOAD_PATH.join(',')}" if ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] == 'true' +otel_lib_path.each { |file_path| $LOAD_PATH.unshift("#{file_path}/lib") } +additional_lib_path.each { |file_path| $LOAD_PATH.unshift("#{file_path}/lib") } + +warn "Loading the gem path from #{gem_path}\n$LOAD_PATH after unshift: #{$LOAD_PATH.join(',')}" if ENV['OTEL_RUBY_AUTO_INSTRUMENTATION_DEBUG'] == 'true' # These are required for the prepend OTelBundlerPatch to fetch OpenTelemetry::SDK.configure require 'opentelemetry-sdk' @@ -179,7 +197,7 @@ def require(...) require 'opentelemetry-exporter-otlp-logs' require 'opentelemetry-instrumentation-all' -resource_detectors = ENV['OTEL_RUBY_RESOURCE_DETECTORS'].to_s.split(',').map(&:strip) +resource_detectors = parse_env_list.call('OTEL_RUBY_RESOURCE_DETECTORS') require 'opentelemetry-resource-detector-container' if resource_detectors.include?('container') require 'opentelemetry-resource-detector-azure' if resource_detectors.include?('azure') require 'opentelemetry-resource-detector-aws' if resource_detectors.include?('aws')