From ec2c1924617aba1fe84e2b165e3f197207031e8e Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Tue, 19 Sep 2023 08:37:52 -0700 Subject: [PATCH 01/67] WIP initial commit logger instrumentation --- .../workflows/ci-instrumentation-canary.yml | 1 + .github/workflows/ci-instrumentation.yml | 1 + .toys/.data/releases.yml | 4 + .../lib/opentelemetry/instrumentation/all.rb | 1 + .../opentelemetry-instrumentation-all.gemspec | 1 + instrumentation/logger/.rubocop.yml | 1 + instrumentation/logger/.yardopts | 9 + instrumentation/logger/Appraisals | 16 ++ instrumentation/logger/CHANGELOG.md | 1 + instrumentation/logger/Gemfile | 17 ++ instrumentation/logger/LICENSE | 201 ++++++++++++++++++ instrumentation/logger/README.md | 52 +++++ instrumentation/logger/Rakefile | 28 +++ .../opentelemetry-instrumentation-logger.rb | 7 + .../lib/opentelemetry/instrumentation.rb | 19 ++ .../opentelemetry/instrumentation/logger.rb | 19 ++ .../instrumentation/logger/instrumentation.rb | 35 +++ .../instrumentation/logger/patches/logger.rb | 42 ++++ .../instrumentation/logger/version.rb | 13 ++ ...entelemetry-instrumentation-logger.gemspec | 49 +++++ .../logger/instrumentation_test.rb | 29 +++ instrumentation/logger/test/test_helper.rb | 21 ++ 22 files changed, 567 insertions(+) create mode 100644 instrumentation/logger/.rubocop.yml create mode 100644 instrumentation/logger/.yardopts create mode 100644 instrumentation/logger/Appraisals create mode 100644 instrumentation/logger/CHANGELOG.md create mode 100644 instrumentation/logger/Gemfile create mode 100644 instrumentation/logger/LICENSE create mode 100644 instrumentation/logger/README.md create mode 100644 instrumentation/logger/Rakefile create mode 100644 instrumentation/logger/lib/opentelemetry-instrumentation-logger.rb create mode 100644 instrumentation/logger/lib/opentelemetry/instrumentation.rb create mode 100644 instrumentation/logger/lib/opentelemetry/instrumentation/logger.rb create mode 100644 instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb create mode 100644 instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb create mode 100644 instrumentation/logger/lib/opentelemetry/instrumentation/logger/version.rb create mode 100644 instrumentation/logger/opentelemetry-instrumentation-logger.gemspec create mode 100644 instrumentation/logger/test/opentelemetry/instrumentation/logger/instrumentation_test.rb create mode 100644 instrumentation/logger/test/test_helper.rb diff --git a/.github/workflows/ci-instrumentation-canary.yml b/.github/workflows/ci-instrumentation-canary.yml index e257bb2557..66398b4642 100644 --- a/.github/workflows/ci-instrumentation-canary.yml +++ b/.github/workflows/ci-instrumentation-canary.yml @@ -31,6 +31,7 @@ jobs: - http_client - koala - lmdb + - logger - net_http - rack - rails diff --git a/.github/workflows/ci-instrumentation.yml b/.github/workflows/ci-instrumentation.yml index 0a37533acc..3505b58141 100644 --- a/.github/workflows/ci-instrumentation.yml +++ b/.github/workflows/ci-instrumentation.yml @@ -35,6 +35,7 @@ jobs: - http_client - koala - lmdb + - logger - net_http - rack - rails diff --git a/.toys/.data/releases.yml b/.toys/.data/releases.yml index bf9864f635..4ff0f87c0b 100644 --- a/.toys/.data/releases.yml +++ b/.toys/.data/releases.yml @@ -29,6 +29,10 @@ commit_lint: # * changelog_path: Path to CHANGLEOG.md relative to the gem directory. # (Required only if it is not in the expected location.) gems: + - name: opentelemetry-instrumentation-logger + directory: instrumentation/logger + version_constant: [OpenTelemetry, Instrumentation, Logger, VERSION] + - name: opentelemetry-instrumentation-grape directory: instrumentation/grape version_constant: [OpenTelemetry, Instrumentation, Grape, VERSION] diff --git a/instrumentation/all/lib/opentelemetry/instrumentation/all.rb b/instrumentation/all/lib/opentelemetry/instrumentation/all.rb index 54209ec31a..bcff976ae9 100644 --- a/instrumentation/all/lib/opentelemetry/instrumentation/all.rb +++ b/instrumentation/all/lib/opentelemetry/instrumentation/all.rb @@ -4,6 +4,7 @@ # # SPDX-License-Identifier: Apache-2.0 +require 'opentelemetry-instrumentation-logger' require 'opentelemetry-instrumentation-trilogy' require 'opentelemetry-instrumentation-active_support' require 'opentelemetry-instrumentation-action_pack' diff --git a/instrumentation/all/opentelemetry-instrumentation-all.gemspec b/instrumentation/all/opentelemetry-instrumentation-all.gemspec index 0376672a6e..e3b9cd955b 100644 --- a/instrumentation/all/opentelemetry-instrumentation-all.gemspec +++ b/instrumentation/all/opentelemetry-instrumentation-all.gemspec @@ -26,6 +26,7 @@ Gem::Specification.new do |spec| spec.require_paths = ['lib'] spec.required_ruby_version = '>= 3.0' + spec.add_dependency 'opentelemetry-instrumentation-logger', '~> 0.0.0' spec.add_dependency 'opentelemetry-instrumentation-active_model_serializers', '~> 0.20.1' spec.add_dependency 'opentelemetry-instrumentation-aws_sdk', '~> 0.4.1' spec.add_dependency 'opentelemetry-instrumentation-bunny', '~> 0.20.1' diff --git a/instrumentation/logger/.rubocop.yml b/instrumentation/logger/.rubocop.yml new file mode 100644 index 0000000000..1248a2f825 --- /dev/null +++ b/instrumentation/logger/.rubocop.yml @@ -0,0 +1 @@ +inherit_from: ../../.rubocop.yml diff --git a/instrumentation/logger/.yardopts b/instrumentation/logger/.yardopts new file mode 100644 index 0000000000..0d5d214884 --- /dev/null +++ b/instrumentation/logger/.yardopts @@ -0,0 +1,9 @@ +--no-private +--title=OpenTelemetry Logger Instrumentation +--markup=markdown +--main=README.md +./lib/opentelemetry/instrumentation/**/*.rb +./lib/opentelemetry/instrumentation.rb +- +README.md +CHANGELOG.md diff --git a/instrumentation/logger/Appraisals b/instrumentation/logger/Appraisals new file mode 100644 index 0000000000..0ee9909623 --- /dev/null +++ b/instrumentation/logger/Appraisals @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +## TODO: Include the supported version to be tested here. +## Example: +# appraise 'rack-2.1' do +# gem 'rack', '~> 2.1.2' +# end + +# appraise 'rack-2.0' do +# gem 'rack', '2.0.8' +# end + diff --git a/instrumentation/logger/CHANGELOG.md b/instrumentation/logger/CHANGELOG.md new file mode 100644 index 0000000000..c62749f184 --- /dev/null +++ b/instrumentation/logger/CHANGELOG.md @@ -0,0 +1 @@ +# Release History: opentelemetry-instrumentation-logger diff --git a/instrumentation/logger/Gemfile b/instrumentation/logger/Gemfile new file mode 100644 index 0000000000..a952c5002d --- /dev/null +++ b/instrumentation/logger/Gemfile @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +source 'https://rubygems.org' + +# DO NOT ADD DEPENDENCIES HERE! +# Please declare a minimum development dependency in the gemspec, +# then target specific versions in the Appraisals file. + +gemspec + +group :test do + gem 'opentelemetry-instrumentation-base', path: '../base' +end diff --git a/instrumentation/logger/LICENSE b/instrumentation/logger/LICENSE new file mode 100644 index 0000000000..1ef7dad2c5 --- /dev/null +++ b/instrumentation/logger/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/instrumentation/logger/README.md b/instrumentation/logger/README.md new file mode 100644 index 0000000000..ef15b2cfdb --- /dev/null +++ b/instrumentation/logger/README.md @@ -0,0 +1,52 @@ +# OpenTelemetry Logger Instrumentation + +Todo: Add a description. + +## How do I get started? + +Install the gem using: + +``` +gem install opentelemetry-instrumentation-logger +``` + +Or, if you use [bundler][bundler-home], include `opentelemetry-instrumentation-logger` in your `Gemfile`. + +## Usage + +To use the instrumentation, call `use` with the name of the instrumentation: + +```ruby +OpenTelemetry::SDK.configure do |c| + c.use 'OpenTelemetry::Instrumentation::Logger' +end +``` + +Alternatively, you can also call `use_all` to install all the available instrumentation. + +```ruby +OpenTelemetry::SDK.configure do |c| + c.use_all +end +``` + +## Examples + +Example usage can be seen in the `./example/trace_demonstration.rb` file [here](https://github.com/open-telemetry/opentelemetry-ruby-contrib/blob/main/instrumentation/logger/example/trace_demonstration.rb) + +## How can I get involved? + +The `opentelemetry-instrumentation-logger` gem source is [on github][repo-github], along with related gems including `opentelemetry-api` and `opentelemetry-sdk`. + +The OpenTelemetry Ruby gems are maintained by the OpenTelemetry-Ruby special interest group (SIG). You can get involved by joining us in [GitHub Discussions][discussions-url] 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-instrumentation-logger` gem is distributed under the Apache 2.0 license. See [LICENSE][license-github] for more information. + +[bundler-home]: https://bundler.io +[repo-github]: https://github.com/open-telemetry/opentelemetry-ruby +[license-github]: https://github.com/open-telemetry/opentelemetry-ruby-contrib/blob/main/LICENSE +[ruby-sig]: https://github.com/open-telemetry/community#ruby-sig +[community-meetings]: https://github.com/open-telemetry/community#community-meetings +[discussions-url]: https://github.com/open-telemetry/opentelemetry-ruby/discussions diff --git a/instrumentation/logger/Rakefile b/instrumentation/logger/Rakefile new file mode 100644 index 0000000000..1a64ba842e --- /dev/null +++ b/instrumentation/logger/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/instrumentation/logger/lib/opentelemetry-instrumentation-logger.rb b/instrumentation/logger/lib/opentelemetry-instrumentation-logger.rb new file mode 100644 index 0000000000..baceb3cb76 --- /dev/null +++ b/instrumentation/logger/lib/opentelemetry-instrumentation-logger.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require_relative './opentelemetry/instrumentation' diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation.rb b/instrumentation/logger/lib/opentelemetry/instrumentation.rb new file mode 100644 index 0000000000..56bc743f74 --- /dev/null +++ b/instrumentation/logger/lib/opentelemetry/instrumentation.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +# 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. +# +# The OpenTelemetry module provides global accessors for telemetry objects. +# See the documentation for the `opentelemetry-api` gem for details. +module OpenTelemetry + # Instrumentation should be able to handle the case when the library is not installed on a user's system. + module Instrumentation + end +end + +require_relative './instrumentation/logger' diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger.rb new file mode 100644 index 0000000000..9ef4a5edbc --- /dev/null +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'opentelemetry' +require 'opentelemetry-instrumentation-base' + +module OpenTelemetry + module Instrumentation + # Contains the OpenTelemetry instrumentation for the Logger gem + module Logger + end + end +end + +require_relative './logger/instrumentation' +require_relative './logger/version' diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb new file mode 100644 index 0000000000..6e108fc047 --- /dev/null +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module Instrumentation + module Logger + # The Instrumentation class contains logic to detect and install the Logger instrumentation + class Instrumentation < OpenTelemetry::Instrumentation::Base + install do |_config| + require_dependencies + patch + end + + present do + defined?(Logger) + end + + # option :name, default: 'default', validate: :validation + + private + + def patch + ::Logger.prepend(Patches::Logger) + end + + def require_dependencies + require_relative 'patches/logger' + end + end + end + end +end diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb new file mode 100644 index 0000000000..09c8df0d33 --- /dev/null +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module Instrumentation + module Logger + module Patches + # Instrumention for methods from Ruby's Logger class + module Logger + # TODO: Make sure OTel logs aren't instrumented + # TODO: How to pass attributes? + + def format_message(severity, datetime, progname, msg) + formatted_message = super(severity, datetime, progname, msg) + + return formatted_message if instance_variable_get(:@skip_instrumenting) == true + + # TODO: Is there another way I can find the logger that's more + # similar to how the tracers are found/set? + OpenTelemetry.logger_provider.logger( + severity_text: severity, + severity_number: ::Logger::Severity.const_get(severity), + timestamp: datetime, + body: formatted_message + ) + end + + private + + def instrumentation_config; end + + def logger + Logger::Instrumentation.instance.logger + end + end + end + end + end +end diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/version.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/version.rb new file mode 100644 index 0000000000..8e0b8e97b3 --- /dev/null +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/version.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module Instrumentation + module Logger + VERSION = '0.0.0' + end + end +end diff --git a/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec b/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec new file mode 100644 index 0000000000..12d352d924 --- /dev/null +++ b/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec @@ -0,0 +1,49 @@ +# 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 'opentelemetry/instrumentation/logger/version' + +Gem::Specification.new do |spec| + spec.name = 'opentelemetry-instrumentation-logger' + spec.version = OpenTelemetry::Instrumentation::Logger::VERSION + spec.authors = ['OpenTelemetry Authors'] + spec.email = ['cncf-opentelemetry-contributors@lists.cncf.io'] + + spec.summary = 'Logger instrumentation for the OpenTelemetry framework' + spec.description = 'Logger instrumentation for the OpenTelemetry framework' + 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', '.yardopts'] + spec.require_paths = ['lib'] + spec.required_ruby_version = '>= 3.0' + + spec.add_dependency 'opentelemetry-api', '~> 1.2.2' + spec.add_dependency 'opentelemetry-instrumentation-base', '~> 0.22.1' + + spec.add_development_dependency 'appraisal', '~> 2.2.0' + spec.add_development_dependency 'bundler', '~> 2.4' + spec.add_development_dependency 'minitest', '~> 5.0' + spec.add_development_dependency 'opentelemetry-sdk', '~> 1.0' + spec.add_development_dependency 'opentelemetry-test-helpers', '~> 0.3' + spec.add_development_dependency 'rake', '~> 13.0' + spec.add_development_dependency 'rubocop', '~> 1.48.1' + spec.add_development_dependency 'simplecov', '~> 0.17.1' + spec.add_development_dependency 'webmock', '~> 3.7.6' + spec.add_development_dependency 'yard', '~> 0.9' + spec.add_development_dependency 'yard-doctest', '~> 0.1.6' + + 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/main/instrumentation/logger' + 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 +end diff --git a/instrumentation/logger/test/opentelemetry/instrumentation/logger/instrumentation_test.rb b/instrumentation/logger/test/opentelemetry/instrumentation/logger/instrumentation_test.rb new file mode 100644 index 0000000000..36da47604a --- /dev/null +++ b/instrumentation/logger/test/opentelemetry/instrumentation/logger/instrumentation_test.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'test_helper' + +require_relative '../../../../lib/opentelemetry/instrumentation/logger' + +describe OpenTelemetry::Instrumentation::Logger do + let(:instrumentation) { OpenTelemetry::Instrumentation::Logger::Instrumentation.instance } + + it 'has #name' do + _(instrumentation.name).must_equal 'OpenTelemetry::Instrumentation::Logger' + end + + it 'has #version' do + _(instrumentation.version).wont_be_nil + _(instrumentation.version).wont_be_empty + end + + describe '#install' do + it 'accepts argument' do + _(instrumentation.install({})).must_equal(true) + instrumentation.instance_variable_set(:@installed, false) + end + end +end diff --git a/instrumentation/logger/test/test_helper.rb b/instrumentation/logger/test/test_helper.rb new file mode 100644 index 0000000000..aa05118d16 --- /dev/null +++ b/instrumentation/logger/test/test_helper.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'bundler/setup' +Bundler.require(:default, :development, :test) + +require 'minitest/autorun' +require 'webmock/minitest' + +# global opentelemetry-sdk setup: +EXPORTER = OpenTelemetry::SDK::Trace::Export::InMemorySpanExporter.new +span_processor = OpenTelemetry::SDK::Trace::Export::SimpleSpanProcessor.new(EXPORTER) + +OpenTelemetry::SDK.configure do |c| + c.error_handler = ->(exception:, message:) { raise(exception || message) } + c.logger = Logger.new($stderr, level: ENV.fetch('OTEL_LOG_LEVEL', 'fatal').to_sym) + c.add_span_processor span_processor +end From f4add6249fc7eacbc2b792ee4a734b5a20aafd4c Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Mon, 25 Sep 2023 09:58:19 -0700 Subject: [PATCH 02/67] Rubocop require_relative --- .../logger/lib/opentelemetry/instrumentation/logger.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger.rb index 9ef4a5edbc..b23717e344 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger.rb @@ -15,5 +15,5 @@ module Logger end end -require_relative './logger/instrumentation' -require_relative './logger/version' +require_relative 'logger/instrumentation' +require_relative 'logger/version' From 456d25bcc855d1bc731879f6df809e9b8d8752cb Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Mon, 25 Sep 2023 09:58:45 -0700 Subject: [PATCH 03/67] Add :: to find Ruby logger instead of OTel logger --- .../lib/opentelemetry/instrumentation/logger/instrumentation.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb index 6e108fc047..ac23c3e9c2 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb @@ -15,7 +15,7 @@ class Instrumentation < OpenTelemetry::Instrumentation::Base end present do - defined?(Logger) + defined?(::Logger) end # option :name, default: 'default', validate: :validation From e39763a91ee2dd68b2a637156098b313a652637d Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Mon, 25 Sep 2023 09:59:08 -0700 Subject: [PATCH 04/67] Remove commented out option LOC --- .../lib/opentelemetry/instrumentation/logger/instrumentation.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb index ac23c3e9c2..984c810ab9 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb @@ -18,8 +18,6 @@ class Instrumentation < OpenTelemetry::Instrumentation::Base defined?(::Logger) end - # option :name, default: 'default', validate: :validation - private def patch From 976f32945cd7d4f79b6461cfaac20c055c75b30f Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Mon, 25 Sep 2023 10:03:08 -0700 Subject: [PATCH 05/67] Add skip_instrumenting? method --- .../opentelemetry/instrumentation/logger/patches/logger.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb index 09c8df0d33..2c0f221422 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb @@ -15,6 +15,7 @@ module Logger def format_message(severity, datetime, progname, msg) formatted_message = super(severity, datetime, progname, msg) + return formatted_message if skip_instrumenting? return formatted_message if instance_variable_get(:@skip_instrumenting) == true @@ -32,8 +33,8 @@ def format_message(severity, datetime, progname, msg) def instrumentation_config; end - def logger - Logger::Instrumentation.instance.logger + def skip_instrumenting? + instance_variable_get(:@skip_instrumenting) end end end From ffd3fc54e3609ecb5afa449be42e93c466c973e3 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Mon, 25 Sep 2023 10:03:30 -0700 Subject: [PATCH 06/67] Skip ActiveSupport::Logger#broadcast loggers --- .../instrumentation/logger/instrumentation.rb | 8 +++++++ .../logger/patches/active_support_logger.rb | 24 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/active_support_logger.rb diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb index 984c810ab9..f87eef44aa 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb @@ -22,11 +22,19 @@ class Instrumentation < OpenTelemetry::Instrumentation::Base def patch ::Logger.prepend(Patches::Logger) + active_support_patch end def require_dependencies require_relative 'patches/logger' end + + def active_support_patch + return unless defined?(::ActiveSupport::Logger) + + require_relative 'patches/active_support_logger' + ::ActiveSupport::Logger.singleton_class.prepend(Patches::ActiveSupportLogger) + end end end end diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/active_support_logger.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/active_support_logger.rb new file mode 100644 index 0000000000..5efcdc866d --- /dev/null +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/active_support_logger.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module Instrumentation + module Logger + module Patches + # Patches for the ActiveSupport::Logger class included in Rails + module ActiveSupportLogger + # The ActiveSupport::Logger.broadcast method emits identical logs to + # multiple destinations. This instance variable will prevent the broadcasted + # destinations from generating OpenTelemetry log record objects. + def broadcast(logger) + logger.instance_variable_set(:@skip_instrumenting, true) + super + end + end + end + end + end +end From bf2f99858b083ad9e9679d7120ea95610a825d82 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Mon, 25 Sep 2023 10:04:48 -0700 Subject: [PATCH 07/67] Add logger instrumentation library and version --- .../lib/opentelemetry/instrumentation/logger/patches/logger.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb index 2c0f221422..48ca7e833e 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb @@ -22,6 +22,9 @@ def format_message(severity, datetime, progname, msg) # TODO: Is there another way I can find the logger that's more # similar to how the tracers are found/set? OpenTelemetry.logger_provider.logger( + 'opentelemetry-instrumentation-logger', + OpenTelemetry::Instrumentation::Logger::VERSION + ).emit( severity_text: severity, severity_number: ::Logger::Severity.const_get(severity), timestamp: datetime, From 2df26a62953c4e0ff583aa7ddb25173f5917f0dc Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Mon, 25 Sep 2023 10:05:40 -0700 Subject: [PATCH 08/67] Update severity_number logic, return orig value --- .../instrumentation/logger/patches/logger.rb | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb index 48ca7e833e..78dd7c78da 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb @@ -12,24 +12,21 @@ module Patches module Logger # TODO: Make sure OTel logs aren't instrumented # TODO: How to pass attributes? - def format_message(severity, datetime, progname, msg) formatted_message = super(severity, datetime, progname, msg) return formatted_message if skip_instrumenting? - return formatted_message if instance_variable_get(:@skip_instrumenting) == true - - # TODO: Is there another way I can find the logger that's more - # similar to how the tracers are found/set? OpenTelemetry.logger_provider.logger( 'opentelemetry-instrumentation-logger', OpenTelemetry::Instrumentation::Logger::VERSION ).emit( severity_text: severity, - severity_number: ::Logger::Severity.const_get(severity), + severity_number: severity_number(severity), timestamp: datetime, body: formatted_message ) + + formatted_message end private @@ -39,6 +36,13 @@ def instrumentation_config; end def skip_instrumenting? instance_variable_get(:@skip_instrumenting) end + + def severity_number(severity) + ::Logger::Severity.const_get(severity) + rescue NameError => e + OpenTelemetry.handle_error(message: "Unable to coerce severity text #{severity} into severity_number. Setting severity_number to nil.", exception: e) + nil + end end end end From b0ab18f51c2b057c04637082f779b81ff4d411c6 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Tue, 3 Oct 2023 14:27:59 -0700 Subject: [PATCH 09/67] Make instrumentation name a constant --- .../logger/lib/opentelemetry/instrumentation/logger.rb | 1 + .../lib/opentelemetry/instrumentation/logger/patches/logger.rb | 2 +- .../logger/opentelemetry-instrumentation-logger.gemspec | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger.rb index b23717e344..cdfcfb47cd 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger.rb @@ -11,6 +11,7 @@ module OpenTelemetry module Instrumentation # Contains the OpenTelemetry instrumentation for the Logger gem module Logger + NAME = 'opentelemetry-instrumentation-logger' end end end diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb index 78dd7c78da..9e6a82229a 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb @@ -17,7 +17,7 @@ def format_message(severity, datetime, progname, msg) return formatted_message if skip_instrumenting? OpenTelemetry.logger_provider.logger( - 'opentelemetry-instrumentation-logger', + OpenTelemetry::Instrumentation::Logger::NAME, OpenTelemetry::Instrumentation::Logger::VERSION ).emit( severity_text: severity, diff --git a/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec b/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec index 12d352d924..8d6581334a 100644 --- a/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec +++ b/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec @@ -6,10 +6,11 @@ lib = File.expand_path('lib', __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +require 'opentelemetry/instrumentation/logger' require 'opentelemetry/instrumentation/logger/version' Gem::Specification.new do |spec| - spec.name = 'opentelemetry-instrumentation-logger' + spec.name = OpenTelemetry::Instrumentation::Logger::NAME spec.version = OpenTelemetry::Instrumentation::Logger::VERSION spec.authors = ['OpenTelemetry Authors'] spec.email = ['cncf-opentelemetry-contributors@lists.cncf.io'] From 595ecc57b9a15d22565e511a8e04f3fa4108a099 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Tue, 3 Oct 2023 14:28:09 -0700 Subject: [PATCH 10/67] WIP test active support logger --- instrumentation/logger/Appraisals | 17 ++-- instrumentation/logger/Gemfile | 4 + .../instrumentation/logger/patches/logger.rb | 4 +- instrumentation/logger/log/development.log | 0 ...entelemetry-instrumentation-logger.gemspec | 2 +- .../patches/active_support_logger_test.rb | 37 +++++++ .../logger/patches/logger_test.rb | 66 +++++++++++++ instrumentation/logger/test/test_helper.rb | 23 ++++- .../logger/test/test_helpers/app_config.rb | 98 +++++++++++++++++++ 9 files changed, 236 insertions(+), 15 deletions(-) create mode 100644 instrumentation/logger/log/development.log create mode 100644 instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb create mode 100644 instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb create mode 100644 instrumentation/logger/test/test_helpers/app_config.rb diff --git a/instrumentation/logger/Appraisals b/instrumentation/logger/Appraisals index 0ee9909623..03f29edc97 100644 --- a/instrumentation/logger/Appraisals +++ b/instrumentation/logger/Appraisals @@ -4,13 +4,14 @@ # # SPDX-License-Identifier: Apache-2.0 -## TODO: Include the supported version to be tested here. -## Example: -# appraise 'rack-2.1' do -# gem 'rack', '~> 2.1.2' -# end +appraise 'rails-6.0' do + gem 'rails', '~> 6.0.0' +end -# appraise 'rack-2.0' do -# gem 'rack', '2.0.8' -# end +appraise 'rails-6.1' do + gem 'rails', '~> 6.1.0' +end +appraise 'rails-7.0' do + gem 'rails', '~> 7.0.0' +end diff --git a/instrumentation/logger/Gemfile b/instrumentation/logger/Gemfile index a952c5002d..af1ac79fef 100644 --- a/instrumentation/logger/Gemfile +++ b/instrumentation/logger/Gemfile @@ -14,4 +14,8 @@ gemspec group :test do gem 'opentelemetry-instrumentation-base', path: '../base' + gem 'opentelemetry-api', path: '/Users/kreopelle/dev/opentelemetry-ruby/api' + gem 'opentelemetry-logs-api', path: '/Users/kreopelle/dev/opentelemetry-ruby/logs_api' + gem 'opentelemetry-logs-sdk', path: '/Users/kreopelle/dev/opentelemetry-ruby/logs_sdk' + gem 'opentelemetry-sdk', path: '/Users/kreopelle/dev/opentelemetry-ruby/sdk' end diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb index 9e6a82229a..b1a46e45b4 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb @@ -23,7 +23,7 @@ def format_message(severity, datetime, progname, msg) severity_text: severity, severity_number: severity_number(severity), timestamp: datetime, - body: formatted_message + body: msg # New Relic uses formatted_message here. This also helps us with not recording progname, because it is included in the formatted message by default. Which seems more appropriate? ) formatted_message @@ -40,7 +40,7 @@ def skip_instrumenting? def severity_number(severity) ::Logger::Severity.const_get(severity) rescue NameError => e - OpenTelemetry.handle_error(message: "Unable to coerce severity text #{severity} into severity_number. Setting severity_number to nil.", exception: e) + OpenTelemetry.logger.warn(message: "Unable to coerce severity text #{severity} into severity_number. Setting severity_number to nil.", exception: e) nil end end diff --git a/instrumentation/logger/log/development.log b/instrumentation/logger/log/development.log new file mode 100644 index 0000000000..e69de29bb2 diff --git a/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec b/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec index 8d6581334a..44280e52c5 100644 --- a/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec +++ b/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec @@ -36,7 +36,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'opentelemetry-test-helpers', '~> 0.3' spec.add_development_dependency 'rake', '~> 13.0' spec.add_development_dependency 'rubocop', '~> 1.48.1' - spec.add_development_dependency 'simplecov', '~> 0.17.1' + spec.add_development_dependency 'simplecov' spec.add_development_dependency 'webmock', '~> 3.7.6' spec.add_development_dependency 'yard', '~> 0.9' spec.add_development_dependency 'yard-doctest', '~> 0.1.6' diff --git a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb new file mode 100644 index 0000000000..37f7f10db8 --- /dev/null +++ b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'test_helper' + +require_relative '../../../../../lib/opentelemetry/instrumentation/logger/patches/active_support_logger' + +describe OpenTelemetry::Instrumentation::Logger::Patches::ActiveSupportLogger do + let(:instrumentation) { OpenTelemetry::Instrumentation::Logger::Instrumentation.instance } + let(:exporter) { EXPORTER } + let(:log_record) { exporter.emitted_log_records.first } + let(:rails_logger) { Rails.logger } + let(:log_stream) { LOG_STREAM } + # let(:ruby_logger) { Logger.new(log_stream) } + let(:msg) { 'message' } + + before do + exporter.reset + instrumentation.install + end + + after { instrumentation.instance_variable_set(:@installed, false) } + + describe '#broadcast' do + it 'adds @skip_instrumenting to broadcasted loggers' do + rails_logger.debug(msg) + assert_match(/DEBUG -- : #{msg}/, log_stream.string) + end + + it 'does not add @skip_instrumenting to the initial logger' do + + end + end +end diff --git a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb new file mode 100644 index 0000000000..c03053d06a --- /dev/null +++ b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'test_helper' + +require_relative '../../../../../lib/opentelemetry/instrumentation/logger/patches/logger' + +describe OpenTelemetry::Instrumentation::Logger::Patches::Logger do + let(:instrumentation) { OpenTelemetry::Instrumentation::Logger::Instrumentation.instance } + let(:exporter) { EXPORTER } + let(:log_record) { exporter.emitted_log_records.first } + let(:log_stream) { StringIO.new } + let(:ruby_logger) { Logger.new(log_stream) } + let(:msg) { 'message' } + + before do + exporter.reset + instrumentation.install + end + + after { instrumentation.instance_variable_set(:@installed, false) } + + describe '#format_message' do + it 'logs the formatted message to the correct source' do + ruby_logger.debug(msg) + assert_match(/DEBUG -- : #{msg}/, log_stream.string) + end + + it 'sets the OTel logger instrumentation name and version' do + ruby_logger.debug(msg) + assert_equal(OpenTelemetry::Instrumentation::Logger::NAME, log_record.instrumentation_scope.name) + assert_equal(OpenTelemetry::Instrumentation::Logger::VERSION, log_record.instrumentation_scope.version) + end + + it 'sets log record attributes based on the Ruby log' do + timestamp = Time.now + Time.stub(:now, timestamp) do + ruby_logger.debug(msg) + assert_equal(msg, log_record.body) + assert_equal('DEBUG', log_record.severity_text) + assert_equal(0, log_record.severity_number) + assert_equal(timestamp, log_record.timestamp) + end + end + + it 'does not emit when @skip_instrumenting is true' do + ruby_logger.instance_variable_set(:@skip_instrumenting, true) + ruby_logger.debug(msg) + assert_nil(log_record) + end + + it 'turns the severity into a number' do + assert_equal(Logger::Severity::DEBUG, 0) + ruby_logger.debug(msg) + assert_equal(0, log_record.severity_number) + end + + it 'safely handles unknown severity number translations' do + ruby_logger.send(:format_message, 'CUSTOM_SEVERITY', Time.now, nil, msg) + assert_nil(log_record.severity_number) + end + end +end diff --git a/instrumentation/logger/test/test_helper.rb b/instrumentation/logger/test/test_helper.rb index aa05118d16..cc7badfb5e 100644 --- a/instrumentation/logger/test/test_helper.rb +++ b/instrumentation/logger/test/test_helper.rb @@ -7,15 +7,30 @@ require 'bundler/setup' Bundler.require(:default, :development, :test) +require 'simplecov' +SimpleCov.start do + enable_coverage :branch + add_filter '/test/' +end + +SimpleCov.minimum_coverage 85 + require 'minitest/autorun' require 'webmock/minitest' +require 'opentelemetry/sdk/logs' +require 'test_helpers/app_config' -# global opentelemetry-sdk setup: -EXPORTER = OpenTelemetry::SDK::Trace::Export::InMemorySpanExporter.new -span_processor = OpenTelemetry::SDK::Trace::Export::SimpleSpanProcessor.new(EXPORTER) +EXPORTER = OpenTelemetry::SDK::Logs::Export::InMemoryLogRecordExporter.new +log_record_processor = OpenTelemetry::SDK::Logs::Export::SimpleLogRecordProcessor.new(EXPORTER) +LOG_STREAM = StringIO.new OpenTelemetry::SDK.configure do |c| c.error_handler = ->(exception:, message:) { raise(exception || message) } c.logger = Logger.new($stderr, level: ENV.fetch('OTEL_LOG_LEVEL', 'fatal').to_sym) - c.add_span_processor span_processor + c.add_log_record_processor log_record_processor end + +# Create a globally available Rails app, this should be used in test unless +# specifically testing behaviour with different initialization configs. +DEFAULT_RAILS_APP = AppConfig.initialize_app +Rails.application = DEFAULT_RAILS_APP diff --git a/instrumentation/logger/test/test_helpers/app_config.rb b/instrumentation/logger/test/test_helpers/app_config.rb new file mode 100644 index 0000000000..97d5c39d16 --- /dev/null +++ b/instrumentation/logger/test/test_helpers/app_config.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +class Application < Rails::Application; end +require 'action_controller/railtie' +# require_relative 'middlewares' +# require_relative 'controllers' +# require_relative 'routes' + +module AppConfig + extend self + + def initialize_app(use_exceptions_app: false, remove_rack_tracer_middleware: false) + new_app = Application.new + new_app.config.secret_key_base = 'secret_key_base' + + # Ensure we don't see this Rails warning when testing + new_app.config.eager_load = false + + # Prevent tests from creating log/*.log + level = ENV.fetch('OTEL_LOG_LEVEL', 'fatal').to_sym + new_app.config.logger = Logger.new(LOG_STREAM, level: level) + new_app.config.log_level = level + + new_app.config.filter_parameters = [:param_to_be_filtered] + + case Rails.version + when /^6\.0/ + apply_rails_6_0_configs(new_app) + when /^6\.1/ + apply_rails_6_1_configs(new_app) + when /^7\./ + apply_rails_7_configs(new_app) + end + + # remove_rack_middleware(new_app) if remove_rack_tracer_middleware + # add_exceptions_app(new_app) if use_exceptions_app + # add_middlewares(new_app) + + new_app.initialize! + + # draw_routes(new_app) + + new_app + end + + private + + # def remove_rack_middleware(application) + # application.middleware.delete( + # OpenTelemetry::Instrumentation::Rack::Middlewares::TracerMiddleware + # ) + # end + + # def add_exceptions_app(application) + # application.config.exceptions_app = lambda do |env| + # ExceptionsController.action(:show).call(env) + # end + # end + + # def add_middlewares(application) + # application.middleware.insert_after( + # ActionDispatch::DebugExceptions, + # ExceptionRaisingMiddleware + # ) + + # application.middleware.insert_after( + # ActionDispatch::DebugExceptions, + # RedirectMiddleware + # ) + # end + + def apply_rails_6_0_configs(application) + # Required in Rails 6 + application.config.hosts << 'example.org' + # Creates a lot of deprecation warnings on subsequent app initializations if not explicitly set. + application.config.action_view.finalize_compiled_template_methods = ActionView::Railtie::NULL_OPTION + end + + def apply_rails_6_1_configs(application) + # Required in Rails 6 + application.config.hosts << 'example.org' + end + + def apply_rails_7_configs(application) + # Required in Rails 7 + application.config.hosts << 'example.org' + + # Unfreeze values which may have been frozen on previous initializations. + ActiveSupport::Dependencies.autoload_paths = + ActiveSupport::Dependencies.autoload_paths.dup + ActiveSupport::Dependencies.autoload_once_paths = + ActiveSupport::Dependencies.autoload_once_paths.dup + end +end From 58c82a710815b798db6015c3a1df14ccf07dee99 Mon Sep 17 00:00:00 2001 From: khushijain21 Date: Fri, 29 Mar 2024 14:32:38 +0530 Subject: [PATCH 11/67] Map logger level to OTel level --- .../instrumentation/logger/patches/logger.rb | 19 ++++++++++++++----- .../logger/patches/logger_test.rb | 2 +- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb index b1a46e45b4..9dbb3bbe20 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb @@ -25,7 +25,6 @@ def format_message(severity, datetime, progname, msg) timestamp: datetime, body: msg # New Relic uses formatted_message here. This also helps us with not recording progname, because it is included in the formatted message by default. Which seems more appropriate? ) - formatted_message end @@ -38,10 +37,20 @@ def skip_instrumenting? end def severity_number(severity) - ::Logger::Severity.const_get(severity) - rescue NameError => e - OpenTelemetry.logger.warn(message: "Unable to coerce severity text #{severity} into severity_number. Setting severity_number to nil.", exception: e) - nil + case severity.downcase + when 'debug' + Opentelemetry::Proto::Logs::V1::SeverityNumber::SEVERITY_NUMBER_DEBUG + when 'info' + Opentelemetry::Proto::Logs::V1::SeverityNumber::SEVERITY_NUMBER_INFO + when 'warn' + Opentelemetry::Proto::Logs::V1::SeverityNumber::SEVERITY_NUMBER_WARN + when 'error' + Opentelemetry::Proto::Logs::V1::SeverityNumber::SEVERITY_NUMBER_ERROR + when 'fatal' + Opentelemetry::Proto::Logs::V1::SeverityNumber::SEVERITY_NUMBER_FATAL + else + Opentelemetry::Proto::Logs::V1::SeverityNumber::SEVERITY_NUMBER_UNSPECIFIED + end end end end diff --git a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb index c03053d06a..42afe74bf8 100644 --- a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb +++ b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb @@ -41,7 +41,7 @@ ruby_logger.debug(msg) assert_equal(msg, log_record.body) assert_equal('DEBUG', log_record.severity_text) - assert_equal(0, log_record.severity_number) + assert_equal(5, log_record.severity_number) assert_equal(timestamp, log_record.timestamp) end end From c587212b357e1d70c327e497614c6f31afa92000 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Fri, 29 Mar 2024 16:53:45 -0700 Subject: [PATCH 12/67] Install WIP logs dependencies from git, not local --- instrumentation/logger/Gemfile | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/instrumentation/logger/Gemfile b/instrumentation/logger/Gemfile index af1ac79fef..fef6f87239 100644 --- a/instrumentation/logger/Gemfile +++ b/instrumentation/logger/Gemfile @@ -6,6 +6,7 @@ source 'https://rubygems.org' +### TODO: Ignore the below comment during development, fix before release ### # DO NOT ADD DEPENDENCIES HERE! # Please declare a minimum development dependency in the gemspec, # then target specific versions in the Appraisals file. @@ -14,8 +15,8 @@ gemspec group :test do gem 'opentelemetry-instrumentation-base', path: '../base' - gem 'opentelemetry-api', path: '/Users/kreopelle/dev/opentelemetry-ruby/api' - gem 'opentelemetry-logs-api', path: '/Users/kreopelle/dev/opentelemetry-ruby/logs_api' - gem 'opentelemetry-logs-sdk', path: '/Users/kreopelle/dev/opentelemetry-ruby/logs_sdk' - gem 'opentelemetry-sdk', path: '/Users/kreopelle/dev/opentelemetry-ruby/sdk' + gem 'opentelemetry-api', git: 'https://github.com/kaylareopelle/opentelemetry-ruby', branch: 'log-record-processor3', glob: 'api/*.gemspec' + gem 'opentelemetry-logs-api', git: 'https://github.com/kaylareopelle/opentelemetry-ruby', branch: 'log-record-processor3', glob: 'logs_api/*.gemspec' + gem 'opentelemetry-logs-sdk', git: 'https://github.com/kaylareopelle/opentelemetry-ruby', branch: 'log-record-processor3', glob: 'logs_sdk/*.gemspec' + gem 'opentelemetry-sdk', git: 'https://github.com/kaylareopelle/opentelemetry-ruby', branch: 'log-record-processor3', glob: 'sdk/*.gemspec' end From a4e980d164ebd0f3f183a4af8b5cf27bea5698c1 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Fri, 29 Mar 2024 16:53:54 -0700 Subject: [PATCH 13/67] Update rubocop dependencies --- .../logger/opentelemetry-instrumentation-logger.gemspec | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec b/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec index 44280e52c5..81684138cf 100644 --- a/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec +++ b/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec @@ -35,7 +35,8 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'opentelemetry-sdk', '~> 1.0' spec.add_development_dependency 'opentelemetry-test-helpers', '~> 0.3' spec.add_development_dependency 'rake', '~> 13.0' - spec.add_development_dependency 'rubocop', '~> 1.48.1' + spec.add_development_dependency 'rubocop', '~> 1.62.1' + spec.add_development_dependency 'rubocop-performance', '~> 1.20.2' spec.add_development_dependency 'simplecov' spec.add_development_dependency 'webmock', '~> 3.7.6' spec.add_development_dependency 'yard', '~> 0.9' From 4604a96f4118c4c37b4132cec66169ca9fb3f519 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Fri, 29 Mar 2024 16:56:40 -0700 Subject: [PATCH 14/67] Disable active_support_logger tests Appraisal can't install gems from a git source. Since the appraisal is only necessary for active_support_logger, disable those tests while working on other features. --- instrumentation/logger/Appraisals | 27 +-- .../patches/active_support_logger_test.rb | 56 ++--- instrumentation/logger/test/test_helper.rb | 5 +- .../logger/test/test_helpers/app_config.rb | 196 +++++++++--------- 4 files changed, 143 insertions(+), 141 deletions(-) diff --git a/instrumentation/logger/Appraisals b/instrumentation/logger/Appraisals index 03f29edc97..eeea916f07 100644 --- a/instrumentation/logger/Appraisals +++ b/instrumentation/logger/Appraisals @@ -1,17 +1,18 @@ -# frozen_string_literal: true +# # frozen_string_literal: true -# Copyright The OpenTelemetry Authors -# -# SPDX-License-Identifier: Apache-2.0 +# # Copyright The OpenTelemetry Authors +# # +# # SPDX-License-Identifier: Apache-2.0 -appraise 'rails-6.0' do - gem 'rails', '~> 6.0.0' -end +# # TOOD: Re-enable before release, along with active_support_logger tests +# appraise 'rails-6.0' do +# gem 'rails', '~> 6.0.0' +# end -appraise 'rails-6.1' do - gem 'rails', '~> 6.1.0' -end +# appraise 'rails-6.1' do +# gem 'rails', '~> 6.1.0' +# end -appraise 'rails-7.0' do - gem 'rails', '~> 7.0.0' -end +# appraise 'rails-7.0' do +# gem 'rails', '~> 7.0.0' +# end diff --git a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb index 37f7f10db8..16394cf805 100644 --- a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb +++ b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb @@ -1,37 +1,37 @@ -# frozen_string_literal: true +# # frozen_string_literal: true -# Copyright The OpenTelemetry Authors -# -# SPDX-License-Identifier: Apache-2.0 +# # Copyright The OpenTelemetry Authors +# # +# # SPDX-License-Identifier: Apache-2.0 -require 'test_helper' +# require 'test_helper' -require_relative '../../../../../lib/opentelemetry/instrumentation/logger/patches/active_support_logger' +# require_relative '../../../../../lib/opentelemetry/instrumentation/logger/patches/active_support_logger' -describe OpenTelemetry::Instrumentation::Logger::Patches::ActiveSupportLogger do - let(:instrumentation) { OpenTelemetry::Instrumentation::Logger::Instrumentation.instance } - let(:exporter) { EXPORTER } - let(:log_record) { exporter.emitted_log_records.first } - let(:rails_logger) { Rails.logger } - let(:log_stream) { LOG_STREAM } - # let(:ruby_logger) { Logger.new(log_stream) } - let(:msg) { 'message' } +# describe OpenTelemetry::Instrumentation::Logger::Patches::ActiveSupportLogger do +# let(:instrumentation) { OpenTelemetry::Instrumentation::Logger::Instrumentation.instance } +# let(:exporter) { EXPORTER } +# let(:log_record) { exporter.emitted_log_records.first } +# let(:rails_logger) { Rails.logger } +# let(:log_stream) { LOG_STREAM } +# # let(:ruby_logger) { Logger.new(log_stream) } +# let(:msg) { 'message' } - before do - exporter.reset - instrumentation.install - end +# before do +# exporter.reset +# instrumentation.install +# end - after { instrumentation.instance_variable_set(:@installed, false) } +# after { instrumentation.instance_variable_set(:@installed, false) } - describe '#broadcast' do - it 'adds @skip_instrumenting to broadcasted loggers' do - rails_logger.debug(msg) - assert_match(/DEBUG -- : #{msg}/, log_stream.string) - end +# describe '#broadcast' do +# it 'adds @skip_instrumenting to broadcasted loggers' do +# rails_logger.debug(msg) +# assert_match(/DEBUG -- : #{msg}/, log_stream.string) +# end - it 'does not add @skip_instrumenting to the initial logger' do +# it 'does not add @skip_instrumenting to the initial logger' do - end - end -end +# end +# end +# end diff --git a/instrumentation/logger/test/test_helper.rb b/instrumentation/logger/test/test_helper.rb index cc7badfb5e..b293cf0542 100644 --- a/instrumentation/logger/test/test_helper.rb +++ b/instrumentation/logger/test/test_helper.rb @@ -30,7 +30,8 @@ c.add_log_record_processor log_record_processor end +# TODO: Re-enable Rails app and active_support_logger testing # Create a globally available Rails app, this should be used in test unless # specifically testing behaviour with different initialization configs. -DEFAULT_RAILS_APP = AppConfig.initialize_app -Rails.application = DEFAULT_RAILS_APP +# DEFAULT_RAILS_APP = AppConfig.initialize_app +# Rails.application = DEFAULT_RAILS_APP diff --git a/instrumentation/logger/test/test_helpers/app_config.rb b/instrumentation/logger/test/test_helpers/app_config.rb index 97d5c39d16..9141376148 100644 --- a/instrumentation/logger/test/test_helpers/app_config.rb +++ b/instrumentation/logger/test/test_helpers/app_config.rb @@ -1,98 +1,98 @@ -# frozen_string_literal: true - -# Copyright The OpenTelemetry Authors -# -# SPDX-License-Identifier: Apache-2.0 - -class Application < Rails::Application; end -require 'action_controller/railtie' -# require_relative 'middlewares' -# require_relative 'controllers' -# require_relative 'routes' - -module AppConfig - extend self - - def initialize_app(use_exceptions_app: false, remove_rack_tracer_middleware: false) - new_app = Application.new - new_app.config.secret_key_base = 'secret_key_base' - - # Ensure we don't see this Rails warning when testing - new_app.config.eager_load = false - - # Prevent tests from creating log/*.log - level = ENV.fetch('OTEL_LOG_LEVEL', 'fatal').to_sym - new_app.config.logger = Logger.new(LOG_STREAM, level: level) - new_app.config.log_level = level - - new_app.config.filter_parameters = [:param_to_be_filtered] - - case Rails.version - when /^6\.0/ - apply_rails_6_0_configs(new_app) - when /^6\.1/ - apply_rails_6_1_configs(new_app) - when /^7\./ - apply_rails_7_configs(new_app) - end - - # remove_rack_middleware(new_app) if remove_rack_tracer_middleware - # add_exceptions_app(new_app) if use_exceptions_app - # add_middlewares(new_app) - - new_app.initialize! - - # draw_routes(new_app) - - new_app - end - - private - - # def remove_rack_middleware(application) - # application.middleware.delete( - # OpenTelemetry::Instrumentation::Rack::Middlewares::TracerMiddleware - # ) - # end - - # def add_exceptions_app(application) - # application.config.exceptions_app = lambda do |env| - # ExceptionsController.action(:show).call(env) - # end - # end - - # def add_middlewares(application) - # application.middleware.insert_after( - # ActionDispatch::DebugExceptions, - # ExceptionRaisingMiddleware - # ) - - # application.middleware.insert_after( - # ActionDispatch::DebugExceptions, - # RedirectMiddleware - # ) - # end - - def apply_rails_6_0_configs(application) - # Required in Rails 6 - application.config.hosts << 'example.org' - # Creates a lot of deprecation warnings on subsequent app initializations if not explicitly set. - application.config.action_view.finalize_compiled_template_methods = ActionView::Railtie::NULL_OPTION - end - - def apply_rails_6_1_configs(application) - # Required in Rails 6 - application.config.hosts << 'example.org' - end - - def apply_rails_7_configs(application) - # Required in Rails 7 - application.config.hosts << 'example.org' - - # Unfreeze values which may have been frozen on previous initializations. - ActiveSupport::Dependencies.autoload_paths = - ActiveSupport::Dependencies.autoload_paths.dup - ActiveSupport::Dependencies.autoload_once_paths = - ActiveSupport::Dependencies.autoload_once_paths.dup - end -end +# # frozen_string_literal: true + +# # Copyright The OpenTelemetry Authors +# # +# # SPDX-License-Identifier: Apache-2.0 + +# class Application < Rails::Application; end +# require 'action_controller/railtie' +# # require_relative 'middlewares' +# # require_relative 'controllers' +# # require_relative 'routes' + +# module AppConfig +# extend self + +# def initialize_app(use_exceptions_app: false, remove_rack_tracer_middleware: false) +# new_app = Application.new +# new_app.config.secret_key_base = 'secret_key_base' + +# # Ensure we don't see this Rails warning when testing +# new_app.config.eager_load = false + +# # Prevent tests from creating log/*.log +# level = ENV.fetch('OTEL_LOG_LEVEL', 'fatal').to_sym +# new_app.config.logger = Logger.new(LOG_STREAM, level: level) +# new_app.config.log_level = level + +# new_app.config.filter_parameters = [:param_to_be_filtered] + +# case Rails.version +# when /^6\.0/ +# apply_rails_6_0_configs(new_app) +# when /^6\.1/ +# apply_rails_6_1_configs(new_app) +# when /^7\./ +# apply_rails_7_configs(new_app) +# end + +# # remove_rack_middleware(new_app) if remove_rack_tracer_middleware +# # add_exceptions_app(new_app) if use_exceptions_app +# # add_middlewares(new_app) + +# new_app.initialize! + +# # draw_routes(new_app) + +# new_app +# end + +# private + +# # def remove_rack_middleware(application) +# # application.middleware.delete( +# # OpenTelemetry::Instrumentation::Rack::Middlewares::TracerMiddleware +# # ) +# # end + +# # def add_exceptions_app(application) +# # application.config.exceptions_app = lambda do |env| +# # ExceptionsController.action(:show).call(env) +# # end +# # end + +# # def add_middlewares(application) +# # application.middleware.insert_after( +# # ActionDispatch::DebugExceptions, +# # ExceptionRaisingMiddleware +# # ) + +# # application.middleware.insert_after( +# # ActionDispatch::DebugExceptions, +# # RedirectMiddleware +# # ) +# # end + +# def apply_rails_6_0_configs(application) +# # Required in Rails 6 +# application.config.hosts << 'example.org' +# # Creates a lot of deprecation warnings on subsequent app initializations if not explicitly set. +# application.config.action_view.finalize_compiled_template_methods = ActionView::Railtie::NULL_OPTION +# end + +# def apply_rails_6_1_configs(application) +# # Required in Rails 6 +# application.config.hosts << 'example.org' +# end + +# def apply_rails_7_configs(application) +# # Required in Rails 7 +# application.config.hosts << 'example.org' + +# # Unfreeze values which may have been frozen on previous initializations. +# ActiveSupport::Dependencies.autoload_paths = +# ActiveSupport::Dependencies.autoload_paths.dup +# ActiveSupport::Dependencies.autoload_once_paths = +# ActiveSupport::Dependencies.autoload_once_paths.dup +# end +# end From b8ae0d93b6c3d7068e84929f9f21fad841e852d0 Mon Sep 17 00:00:00 2001 From: khushijain21 Date: Thu, 4 Apr 2024 21:52:01 +0530 Subject: [PATCH 15/67] maps otel log level --- .../instrumentation/logger/patches/logger.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb index 9dbb3bbe20..504583f630 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb @@ -17,8 +17,8 @@ def format_message(severity, datetime, progname, msg) return formatted_message if skip_instrumenting? OpenTelemetry.logger_provider.logger( - OpenTelemetry::Instrumentation::Logger::NAME, - OpenTelemetry::Instrumentation::Logger::VERSION + name: OpenTelemetry::Instrumentation::Logger::NAME, + version: OpenTelemetry::Instrumentation::Logger::VERSION ).emit( severity_text: severity, severity_number: severity_number(severity), @@ -39,17 +39,17 @@ def skip_instrumenting? def severity_number(severity) case severity.downcase when 'debug' - Opentelemetry::Proto::Logs::V1::SeverityNumber::SEVERITY_NUMBER_DEBUG + OpenTelemetry::Logs::SeverityNumber::SEVERITY_NUMBER_DEBUG when 'info' - Opentelemetry::Proto::Logs::V1::SeverityNumber::SEVERITY_NUMBER_INFO + OpenTelemetry::Logs::SeverityNumber::SEVERITY_NUMBER_INFO when 'warn' - Opentelemetry::Proto::Logs::V1::SeverityNumber::SEVERITY_NUMBER_WARN + OpenTelemetry::Logs::SeverityNumber::SEVERITY_NUMBER_WARN when 'error' - Opentelemetry::Proto::Logs::V1::SeverityNumber::SEVERITY_NUMBER_ERROR + OpenTelemetry::Logs::SeverityNumber::SEVERITY_NUMBER_ERROR when 'fatal' - Opentelemetry::Proto::Logs::V1::SeverityNumber::SEVERITY_NUMBER_FATAL + OpenTelemetry::Logs::SeverityNumber::SEVERITY_NUMBER_FATAL else - Opentelemetry::Proto::Logs::V1::SeverityNumber::SEVERITY_NUMBER_UNSPECIFIED + OpenTelemetry::Logs::SeverityNumber::SEVERITY_NUMBER_UNSPECIFIED end end end From 97ea4c92089e86ed9fb781e7aac6f89a303b53df Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Mon, 15 Apr 2024 11:14:26 -0700 Subject: [PATCH 16/67] Use the updated method name to emit, `on_emit` --- .../lib/opentelemetry/instrumentation/logger/patches/logger.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb index 504583f630..129a64f10c 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb @@ -19,7 +19,7 @@ def format_message(severity, datetime, progname, msg) OpenTelemetry.logger_provider.logger( name: OpenTelemetry::Instrumentation::Logger::NAME, version: OpenTelemetry::Instrumentation::Logger::VERSION - ).emit( + ).on_emit( severity_text: severity, severity_number: severity_number(severity), timestamp: datetime, @@ -30,6 +30,7 @@ def format_message(severity, datetime, progname, msg) private + # Placeholder for now def instrumentation_config; end def skip_instrumenting? From f7646f8a7d016ca6d96ca657345577a4bb735493 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Mon, 15 Apr 2024 11:14:47 -0700 Subject: [PATCH 17/67] Update tests referencing severity numbers --- .../instrumentation/logger/patches/logger_test.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb index 42afe74bf8..33c70b4f10 100644 --- a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb +++ b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb @@ -53,14 +53,13 @@ end it 'turns the severity into a number' do - assert_equal(Logger::Severity::DEBUG, 0) ruby_logger.debug(msg) - assert_equal(0, log_record.severity_number) + assert_equal(5, log_record.severity_number) end it 'safely handles unknown severity number translations' do ruby_logger.send(:format_message, 'CUSTOM_SEVERITY', Time.now, nil, msg) - assert_nil(log_record.severity_number) + assert_equal(0, log_record.severity_number) end end end From 483343cee539a0be71497efce636847b3ed082d3 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Tue, 23 Apr 2024 11:13:06 -0700 Subject: [PATCH 18/67] Loosen dependency version restrictions --- .../logger/opentelemetry-instrumentation-logger.gemspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec b/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec index 81684138cf..6fdae80e45 100644 --- a/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec +++ b/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec @@ -26,8 +26,8 @@ Gem::Specification.new do |spec| spec.require_paths = ['lib'] spec.required_ruby_version = '>= 3.0' - spec.add_dependency 'opentelemetry-api', '~> 1.2.2' - spec.add_dependency 'opentelemetry-instrumentation-base', '~> 0.22.1' + spec.add_dependency 'opentelemetry-api', '~> 1.2' + spec.add_dependency 'opentelemetry-instrumentation-base', '~> 0.22' spec.add_development_dependency 'appraisal', '~> 2.2.0' spec.add_development_dependency 'bundler', '~> 2.4' From c35df18117e8afbe8831f1968c8f2b4694488723 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Tue, 23 Apr 2024 12:19:07 -0700 Subject: [PATCH 19/67] fix: Remove NAME constant from gemspec --- .../logger/lib/opentelemetry/instrumentation/logger.rb | 1 - .../logger/opentelemetry-instrumentation-logger.gemspec | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger.rb index cdfcfb47cd..b23717e344 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger.rb @@ -11,7 +11,6 @@ module OpenTelemetry module Instrumentation # Contains the OpenTelemetry instrumentation for the Logger gem module Logger - NAME = 'opentelemetry-instrumentation-logger' end end end diff --git a/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec b/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec index 6fdae80e45..56de613594 100644 --- a/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec +++ b/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec @@ -6,11 +6,10 @@ lib = File.expand_path('lib', __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) -require 'opentelemetry/instrumentation/logger' require 'opentelemetry/instrumentation/logger/version' Gem::Specification.new do |spec| - spec.name = OpenTelemetry::Instrumentation::Logger::NAME + spec.name = 'opentelemetry-instrumentation-logger' spec.version = OpenTelemetry::Instrumentation::Logger::VERSION spec.authors = ['OpenTelemetry Authors'] spec.email = ['cncf-opentelemetry-contributors@lists.cncf.io'] From 686f2d9c5d7c59336105b3618f85defbd3481a69 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Tue, 23 Apr 2024 12:19:28 -0700 Subject: [PATCH 20/67] chore: Address Rubocop require_relative linter --- .../logger/lib/opentelemetry-instrumentation-logger.rb | 2 +- instrumentation/logger/lib/opentelemetry/instrumentation.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/instrumentation/logger/lib/opentelemetry-instrumentation-logger.rb b/instrumentation/logger/lib/opentelemetry-instrumentation-logger.rb index baceb3cb76..c034f140f8 100644 --- a/instrumentation/logger/lib/opentelemetry-instrumentation-logger.rb +++ b/instrumentation/logger/lib/opentelemetry-instrumentation-logger.rb @@ -4,4 +4,4 @@ # # SPDX-License-Identifier: Apache-2.0 -require_relative './opentelemetry/instrumentation' +require_relative 'opentelemetry/instrumentation' diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation.rb b/instrumentation/logger/lib/opentelemetry/instrumentation.rb index 56bc743f74..eb80ddd2bb 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation.rb @@ -16,4 +16,4 @@ module Instrumentation end end -require_relative './instrumentation/logger' +require_relative 'instrumentation/logger' From 1bd3ead8d181869bb645a6fcf33097cbe2f2cdbc Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Tue, 23 Apr 2024 12:33:20 -0700 Subject: [PATCH 21/67] Rubocop --- instrumentation/all/opentelemetry-instrumentation-all.gemspec | 2 +- instrumentation/logger/Appraisals | 2 +- .../logger/patches/active_support_logger_test.rb | 2 +- instrumentation/logger/test/test_helpers/app_config.rb | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/instrumentation/all/opentelemetry-instrumentation-all.gemspec b/instrumentation/all/opentelemetry-instrumentation-all.gemspec index e3edba6986..96d7381c3c 100644 --- a/instrumentation/all/opentelemetry-instrumentation-all.gemspec +++ b/instrumentation/all/opentelemetry-instrumentation-all.gemspec @@ -26,7 +26,6 @@ Gem::Specification.new do |spec| spec.require_paths = ['lib'] spec.required_ruby_version = '>= 3.0' - spec.add_dependency 'opentelemetry-instrumentation-logger', '~> 0.0.0' spec.add_dependency 'opentelemetry-instrumentation-active_model_serializers', '~> 0.20.1' spec.add_dependency 'opentelemetry-instrumentation-aws_sdk', '~> 0.5.0' spec.add_dependency 'opentelemetry-instrumentation-bunny', '~> 0.21.0' @@ -43,6 +42,7 @@ Gem::Specification.new do |spec| spec.add_dependency 'opentelemetry-instrumentation-http_client', '~> 0.22.1' spec.add_dependency 'opentelemetry-instrumentation-koala', '~> 0.20.1' spec.add_dependency 'opentelemetry-instrumentation-lmdb', '~> 0.22.1' + spec.add_dependency 'opentelemetry-instrumentation-logger', '~> 0.0.0' spec.add_dependency 'opentelemetry-instrumentation-mongo', '~> 0.22.1' spec.add_dependency 'opentelemetry-instrumentation-mysql2', '~> 0.27.0' spec.add_dependency 'opentelemetry-instrumentation-net_http', '~> 0.22.1' diff --git a/instrumentation/logger/Appraisals b/instrumentation/logger/Appraisals index eeea916f07..41ef245592 100644 --- a/instrumentation/logger/Appraisals +++ b/instrumentation/logger/Appraisals @@ -1,4 +1,4 @@ -# # frozen_string_literal: true +# frozen_string_literal: true # # Copyright The OpenTelemetry Authors # # diff --git a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb index 16394cf805..b3e3d561f2 100644 --- a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb +++ b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb @@ -1,4 +1,4 @@ -# # frozen_string_literal: true +# frozen_string_literal: true # # Copyright The OpenTelemetry Authors # # diff --git a/instrumentation/logger/test/test_helpers/app_config.rb b/instrumentation/logger/test/test_helpers/app_config.rb index 9141376148..7db946da9a 100644 --- a/instrumentation/logger/test/test_helpers/app_config.rb +++ b/instrumentation/logger/test/test_helpers/app_config.rb @@ -1,4 +1,4 @@ -# # frozen_string_literal: true +# frozen_string_literal: true # # Copyright The OpenTelemetry Authors # # From 8b06c93ce00bd8209831f7c28d2aae0515aab59c Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Wed, 24 Apr 2024 08:41:48 -0700 Subject: [PATCH 22/67] fix: Bring back the NAME constant It's used in the instrumentation when on_emit is called --- .../logger/lib/opentelemetry/instrumentation/logger.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger.rb index b23717e344..cdfcfb47cd 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger.rb @@ -11,6 +11,7 @@ module OpenTelemetry module Instrumentation # Contains the OpenTelemetry instrumentation for the Logger gem module Logger + NAME = 'opentelemetry-instrumentation-logger' end end end From 66d3b3f8b39ebdf44e4e62c833ec8f353284579b Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Mon, 13 May 2024 17:03:56 -0700 Subject: [PATCH 23/67] chore: Correct version number, add TODOs in README --- instrumentation/logger/README.md | 6 ++++-- .../lib/opentelemetry/instrumentation/logger/version.rb | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/instrumentation/logger/README.md b/instrumentation/logger/README.md index ef15b2cfdb..a89fc74f2a 100644 --- a/instrumentation/logger/README.md +++ b/instrumentation/logger/README.md @@ -1,6 +1,6 @@ # OpenTelemetry Logger Instrumentation -Todo: Add a description. +TODO: Update description. This README is incomplete. ## How do I get started? @@ -30,9 +30,11 @@ OpenTelemetry::SDK.configure do |c| end ``` +TODO: Add documentation for config options. + ## Examples -Example usage can be seen in the `./example/trace_demonstration.rb` file [here](https://github.com/open-telemetry/opentelemetry-ruby-contrib/blob/main/instrumentation/logger/example/trace_demonstration.rb) +TODO: Create example ## How can I get involved? diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/version.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/version.rb index 8e0b8e97b3..4157e9b04b 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/version.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/version.rb @@ -7,7 +7,7 @@ module OpenTelemetry module Instrumentation module Logger - VERSION = '0.0.0' + VERSION = '0.1.0' end end end From 4c6d6f19cf6d0fdc8444f816165b5aa0e4f313ff Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Mon, 13 May 2024 18:11:41 -0700 Subject: [PATCH 24/67] fix: Revert version for now --- .../logger/lib/opentelemetry/instrumentation/logger/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/version.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/version.rb index 4157e9b04b..8e0b8e97b3 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/version.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/version.rb @@ -7,7 +7,7 @@ module OpenTelemetry module Instrumentation module Logger - VERSION = '0.1.0' + VERSION = '0.0.0' end end end From 05277264451f7d107cf6c8f3a9d66d0acb13cef8 Mon Sep 17 00:00:00 2001 From: khushijain21 Date: Tue, 7 May 2024 12:26:26 +0530 Subject: [PATCH 25/67] change --- .../instrumentation/logger/patches/logger.rb | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb index 129a64f10c..634312e579 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb @@ -16,25 +16,37 @@ def format_message(severity, datetime, progname, msg) formatted_message = super(severity, datetime, progname, msg) return formatted_message if skip_instrumenting? - OpenTelemetry.logger_provider.logger( - name: OpenTelemetry::Instrumentation::Logger::NAME, - version: OpenTelemetry::Instrumentation::Logger::VERSION + logger_provider.logger( + name: @config[:name], + version: @config[:version] ).on_emit( severity_text: severity, severity_number: severity_number(severity), timestamp: datetime, - body: msg # New Relic uses formatted_message here. This also helps us with not recording progname, because it is included in the formatted message by default. Which seems more appropriate? + body: msg, # New Relic uses formatted_message here. This also helps us with not recording progname, because it is included in the formatted message by default. Which seems more appropriate? + context: OpenTelemetry::Context.Current ) formatted_message end + option :name, default: OpenTelemetry::Instrumentation::Logger::NAME, validate: :string + option :version, default: OpenTelemetry::Instrumentation::Logger::VERSION, validate: :string + private - # Placeholder for now - def instrumentation_config; end + def logger_provider + @logger_provider ||= OpenTelemetry.logger_provider + end def skip_instrumenting? - instance_variable_get(:@skip_instrumenting) + @skip_instrumenting || false # Set to a default value + end + + def instrumentation_config + { + name: @config[:name] || OpenTelemetry::Instrumentation::Logger::DEFAULT_NAME, + version: @config[:version] || OpenTelemetry::Instrumentation::Logger::DEFAULT_VERSION + } end def severity_number(severity) From 7dfba216013489784e45b05b49eb4f3d525b7d11 Mon Sep 17 00:00:00 2001 From: khushijain21 Date: Thu, 9 May 2024 09:06:56 +0530 Subject: [PATCH 26/67] move options --- .../instrumentation/logger/instrumentation.rb | 3 +++ .../instrumentation/logger/patches/logger.rb | 5 +---- .../instrumentation/logger/patches/logger_test.rb | 11 ++++++++++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb index f87eef44aa..b89c6033a2 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb @@ -18,6 +18,9 @@ class Instrumentation < OpenTelemetry::Instrumentation::Base defined?(::Logger) end + option :name, default: OpenTelemetry::Instrumentation::Logger::NAME, validate: :string + option :version, default: OpenTelemetry::Instrumentation::Logger::VERSION, validate: :string + private def patch diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb index 634312e579..d96353796c 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb @@ -29,9 +29,6 @@ def format_message(severity, datetime, progname, msg) formatted_message end - option :name, default: OpenTelemetry::Instrumentation::Logger::NAME, validate: :string - option :version, default: OpenTelemetry::Instrumentation::Logger::VERSION, validate: :string - private def logger_provider @@ -39,7 +36,7 @@ def logger_provider end def skip_instrumenting? - @skip_instrumenting || false # Set to a default value + @skip_instrumenting || false end def instrumentation_config diff --git a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb index 33c70b4f10..96bc91cf40 100644 --- a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb +++ b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb @@ -29,12 +29,21 @@ assert_match(/DEBUG -- : #{msg}/, log_stream.string) end - it 'sets the OTel logger instrumentation name and version' do + it 'sets the OTel logger instrumentation name and version (default case)' do ruby_logger.debug(msg) assert_equal(OpenTelemetry::Instrumentation::Logger::NAME, log_record.instrumentation_scope.name) assert_equal(OpenTelemetry::Instrumentation::Logger::VERSION, log_record.instrumentation_scope.version) end + it 'sets the OTel logger instrumentation name and version (user defined)' do + ruby_logger.debug(msg) + skip 'TODO: write tests for configuration options' + end + + it 'accepts custom logger provider ' do + skip 'TODO: write tests for configuration options' + end + it 'sets log record attributes based on the Ruby log' do timestamp = Time.now Time.stub(:now, timestamp) do From 03868bcb310119c94f50122fe8cd1915d49e3984 Mon Sep 17 00:00:00 2001 From: khushijain21 Date: Mon, 13 May 2024 12:32:51 +0530 Subject: [PATCH 27/67] accomodated changes --- .../instrumentation/logger/patches/logger.rb | 19 ++++++++++--------- instrumentation/logger/test/test_helper.rb | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb index d96353796c..dbce6ed852 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb @@ -17,8 +17,8 @@ def format_message(severity, datetime, progname, msg) return formatted_message if skip_instrumenting? logger_provider.logger( - name: @config[:name], - version: @config[:version] + name: Instrumentation.instance.config[:name], + version: Instrumentation.instance.config[:version] ).on_emit( severity_text: severity, severity_number: severity_number(severity), @@ -29,6 +29,14 @@ def format_message(severity, datetime, progname, msg) formatted_message end + def logger_provider=(value) + @logger_provider = value + end + + def skip_instrumenting=(value) + @skip_instrumenting = value + end + private def logger_provider @@ -39,13 +47,6 @@ def skip_instrumenting? @skip_instrumenting || false end - def instrumentation_config - { - name: @config[:name] || OpenTelemetry::Instrumentation::Logger::DEFAULT_NAME, - version: @config[:version] || OpenTelemetry::Instrumentation::Logger::DEFAULT_VERSION - } - end - def severity_number(severity) case severity.downcase when 'debug' diff --git a/instrumentation/logger/test/test_helper.rb b/instrumentation/logger/test/test_helper.rb index b293cf0542..e3205a131f 100644 --- a/instrumentation/logger/test/test_helper.rb +++ b/instrumentation/logger/test/test_helper.rb @@ -9,7 +9,7 @@ require 'simplecov' SimpleCov.start do - enable_coverage :branch + # enable_coverage :branch add_filter '/test/' end From c96243bc9bbc2c9941ec165c44feb779f3d42c6b Mon Sep 17 00:00:00 2001 From: khushijain21 Date: Tue, 14 May 2024 09:02:22 +0530 Subject: [PATCH 28/67] removed configuring logger_provider --- .../instrumentation/logger/patches/logger.rb | 16 +++------------- instrumentation/logger/test/test_helper.rb | 2 +- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb index dbce6ed852..143fb013ce 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb @@ -10,13 +10,15 @@ module Logger module Patches # Instrumention for methods from Ruby's Logger class module Logger + attr_writer :skip_instrumenting + # TODO: Make sure OTel logs aren't instrumented # TODO: How to pass attributes? def format_message(severity, datetime, progname, msg) formatted_message = super(severity, datetime, progname, msg) return formatted_message if skip_instrumenting? - logger_provider.logger( + OpenTelemetry.logger_provider.logger( name: Instrumentation.instance.config[:name], version: Instrumentation.instance.config[:version] ).on_emit( @@ -29,20 +31,8 @@ def format_message(severity, datetime, progname, msg) formatted_message end - def logger_provider=(value) - @logger_provider = value - end - - def skip_instrumenting=(value) - @skip_instrumenting = value - end - private - def logger_provider - @logger_provider ||= OpenTelemetry.logger_provider - end - def skip_instrumenting? @skip_instrumenting || false end diff --git a/instrumentation/logger/test/test_helper.rb b/instrumentation/logger/test/test_helper.rb index e3205a131f..b293cf0542 100644 --- a/instrumentation/logger/test/test_helper.rb +++ b/instrumentation/logger/test/test_helper.rb @@ -9,7 +9,7 @@ require 'simplecov' SimpleCov.start do - # enable_coverage :branch + enable_coverage :branch add_filter '/test/' end From 2c9a3e3cf59cbd854591efd00e3ff2936c958c4a Mon Sep 17 00:00:00 2001 From: khushijain21 Date: Tue, 14 May 2024 09:10:19 +0530 Subject: [PATCH 29/67] removed corresponding test --- .../opentelemetry/instrumentation/logger/patches/logger.rb | 2 +- .../instrumentation/logger/patches/logger_test.rb | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb index 143fb013ce..0d399c84b8 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb @@ -26,7 +26,7 @@ def format_message(severity, datetime, progname, msg) severity_number: severity_number(severity), timestamp: datetime, body: msg, # New Relic uses formatted_message here. This also helps us with not recording progname, because it is included in the formatted message by default. Which seems more appropriate? - context: OpenTelemetry::Context.Current + context: OpenTelemetry::Context.current ) formatted_message end diff --git a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb index 96bc91cf40..a5ced9d99d 100644 --- a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb +++ b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb @@ -40,10 +40,6 @@ skip 'TODO: write tests for configuration options' end - it 'accepts custom logger provider ' do - skip 'TODO: write tests for configuration options' - end - it 'sets log record attributes based on the Ruby log' do timestamp = Time.now Time.stub(:now, timestamp) do From 236bf1fd1089d2eda9a7fbff2b959f2c364f2744 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Thu, 9 May 2024 19:27:02 -0700 Subject: [PATCH 30/67] chore: Add tests for name and version config --- .../logger/patches/logger_test.rb | 44 +++++++++++++++++-- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb index a5ced9d99d..a4872429bf 100644 --- a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb +++ b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb @@ -15,10 +15,11 @@ let(:log_stream) { StringIO.new } let(:ruby_logger) { Logger.new(log_stream) } let(:msg) { 'message' } + let(:config) { {} } before do exporter.reset - instrumentation.install + instrumentation.install(config) end after { instrumentation.instance_variable_set(:@installed, false) } @@ -35,9 +36,44 @@ assert_equal(OpenTelemetry::Instrumentation::Logger::VERSION, log_record.instrumentation_scope.version) end - it 'sets the OTel logger instrumentation name and version (user defined)' do - ruby_logger.debug(msg) - skip 'TODO: write tests for configuration options' + describe 'configuration options' do + describe 'when a user configures name' do + let(:config) { { name: 'custom_logger' } } + + it 'updates the logger name' do + ruby_logger.debug(msg) + assert_equal('custom_logger', log_record.instrumentation_scope.name) + end + + it 'uses the default version' do + ruby_logger.debug(msg) + assert_equal(OpenTelemetry::Instrumentation::Logger::VERSION, log_record.instrumentation_scope.version) + end + end + + describe 'when a user configures version' do + let(:config) { { version: '5000' } } + + it 'updates the logger version' do + ruby_logger.debug(msg) + assert_equal('5000', log_record.instrumentation_scope.version) + end + + it 'uses the default name' do + ruby_logger.debug(msg) + assert_equal(OpenTelemetry::Instrumentation::Logger::NAME, log_record.instrumentation_scope.name) + end + end + + describe 'when a user configures both name and version' do + let(:config) { { name: 'custom_logger', version: '5000'} } + + it 'updates both values' do + ruby_logger.debug(msg) + assert_equal('custom_logger', log_record.instrumentation_scope.name) + assert_equal('5000', log_record.instrumentation_scope.version) + end + end end it 'sets log record attributes based on the Ruby log' do From b0b301f93c6f58aff6ecdc087d46e6043e2629a9 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Wed, 15 May 2024 17:04:54 -0700 Subject: [PATCH 31/67] chore: Rubocop --- .../opentelemetry/instrumentation/logger/patches/logger_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb index a4872429bf..9d4438aec5 100644 --- a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb +++ b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb @@ -66,7 +66,7 @@ end describe 'when a user configures both name and version' do - let(:config) { { name: 'custom_logger', version: '5000'} } + let(:config) { { name: 'custom_logger', version: '5000' } } it 'updates both values' do ruby_logger.debug(msg) From e6799ff70ef857b6c9fdf65dda6678462a687f3e Mon Sep 17 00:00:00 2001 From: khushijain21 Date: Fri, 17 May 2024 12:54:55 +0530 Subject: [PATCH 32/67] add check for logs sdk --- .../lib/opentelemetry/instrumentation/logger/instrumentation.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb index b89c6033a2..a2516d410f 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb @@ -15,7 +15,7 @@ class Instrumentation < OpenTelemetry::Instrumentation::Base end present do - defined?(::Logger) + defined?(::Logger) && defined?(::OpenTelemetry::SDK::Logs) end option :name, default: OpenTelemetry::Instrumentation::Logger::NAME, validate: :string From 18a45d1c3122a95ec1836821ff765a39afc8ff01 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Fri, 12 Jul 2024 09:56:27 -0700 Subject: [PATCH 33/67] test: Reinstate active support logger tests --- instrumentation/logger/Appraisals | 2 + instrumentation/logger/Gemfile | 3 + .../logger/patches/active_support_logger.rb | 1 + .../patches/active_support_logger_test.rb | 72 +++++--- instrumentation/logger/test/test_helper.rb | 6 +- .../logger/test/test_helpers/app_config.rb | 158 +++++++----------- 6 files changed, 116 insertions(+), 126 deletions(-) diff --git a/instrumentation/logger/Appraisals b/instrumentation/logger/Appraisals index 41ef245592..fa165e3e17 100644 --- a/instrumentation/logger/Appraisals +++ b/instrumentation/logger/Appraisals @@ -5,6 +5,8 @@ # # SPDX-License-Identifier: Apache-2.0 # # TOOD: Re-enable before release, along with active_support_logger tests +# # Appraisals wont work with gems installed by a branch, so we can't +# # set this up until there's a release of the otel logs gems # appraise 'rails-6.0' do # gem 'rails', '~> 6.0.0' # end diff --git a/instrumentation/logger/Gemfile b/instrumentation/logger/Gemfile index fef6f87239..c292f236c9 100644 --- a/instrumentation/logger/Gemfile +++ b/instrumentation/logger/Gemfile @@ -20,3 +20,6 @@ group :test do gem 'opentelemetry-logs-sdk', git: 'https://github.com/kaylareopelle/opentelemetry-ruby', branch: 'log-record-processor3', glob: 'logs_sdk/*.gemspec' gem 'opentelemetry-sdk', git: 'https://github.com/kaylareopelle/opentelemetry-ruby', branch: 'log-record-processor3', glob: 'sdk/*.gemspec' end + +# Temporary for testing, Appraisal does not work with gems installed from git source +gem 'rails', '~> 7.0.0' diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/active_support_logger.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/active_support_logger.rb index 5efcdc866d..7d8bcff1f5 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/active_support_logger.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/active_support_logger.rb @@ -13,6 +13,7 @@ module ActiveSupportLogger # The ActiveSupport::Logger.broadcast method emits identical logs to # multiple destinations. This instance variable will prevent the broadcasted # destinations from generating OpenTelemetry log record objects. + # Available in Rails 7.0 and below def broadcast(logger) logger.instance_variable_set(:@skip_instrumenting, true) super diff --git a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb index b3e3d561f2..e675885693 100644 --- a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb +++ b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb @@ -1,37 +1,55 @@ # frozen_string_literal: true -# # Copyright The OpenTelemetry Authors -# # -# # SPDX-License-Identifier: Apache-2.0 +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 -# require 'test_helper' +require 'test_helper' -# require_relative '../../../../../lib/opentelemetry/instrumentation/logger/patches/active_support_logger' +require_relative '../../../../../lib/opentelemetry/instrumentation/logger/patches/active_support_logger' -# describe OpenTelemetry::Instrumentation::Logger::Patches::ActiveSupportLogger do -# let(:instrumentation) { OpenTelemetry::Instrumentation::Logger::Instrumentation.instance } -# let(:exporter) { EXPORTER } -# let(:log_record) { exporter.emitted_log_records.first } -# let(:rails_logger) { Rails.logger } -# let(:log_stream) { LOG_STREAM } -# # let(:ruby_logger) { Logger.new(log_stream) } -# let(:msg) { 'message' } +describe OpenTelemetry::Instrumentation::Logger::Patches::ActiveSupportLogger do + let(:instrumentation) { OpenTelemetry::Instrumentation::Logger::Instrumentation.instance } + let(:main_logger) { ActiveSupport::Logger.new(LOG_STREAM) } + let(:broadcasted_logger) { ActiveSupport::Logger.new(BROADCASTED_STREAM) } -# before do -# exporter.reset -# instrumentation.install -# end + before do + EXPORTER.reset + Rails.logger = main_logger.extend(ActiveSupport::Logger.broadcast(broadcasted_logger)) + instrumentation.install + end -# after { instrumentation.instance_variable_set(:@installed, false) } + after { instrumentation.instance_variable_set(:@installed, false) } -# describe '#broadcast' do -# it 'adds @skip_instrumenting to broadcasted loggers' do -# rails_logger.debug(msg) -# assert_match(/DEBUG -- : #{msg}/, log_stream.string) -# end + describe '#broadcast' do + it 'emits the log to the Rails.logger' do + msg = "spruce #{rand(6)}" + Rails.logger.debug(msg) -# it 'does not add @skip_instrumenting to the initial logger' do + assert_match(/#{msg}/, LOG_STREAM.string) + end -# end -# end -# end + it 'emits the broadcasted log' do + msg = "willow #{rand(6)}" + Rails.logger.debug(msg) + + assert_match(/#{msg}/, BROADCASTED_STREAM.string) + end + + it 'records the log record' do + msg = "hemlock #{rand(6)}" + Rails.logger.debug(msg) + log_record = EXPORTER.emitted_log_records.first + + assert_match(/#{msg}/, log_record.body) + end + + it 'does not add @skip_instrumenting to the initial logger' do + refute Rails.logger.instance_variable_defined?(:@skip_instrumenting) + end + + it 'adds @skip_instrumenting to broadcasted loggers' do + assert broadcasted_logger.instance_variable_defined?(:@skip_instrumenting) + end + end +end diff --git a/instrumentation/logger/test/test_helper.rb b/instrumentation/logger/test/test_helper.rb index b293cf0542..547f0209f3 100644 --- a/instrumentation/logger/test/test_helper.rb +++ b/instrumentation/logger/test/test_helper.rb @@ -23,6 +23,7 @@ EXPORTER = OpenTelemetry::SDK::Logs::Export::InMemoryLogRecordExporter.new log_record_processor = OpenTelemetry::SDK::Logs::Export::SimpleLogRecordProcessor.new(EXPORTER) LOG_STREAM = StringIO.new +BROADCASTED_STREAM = StringIO.new OpenTelemetry::SDK.configure do |c| c.error_handler = ->(exception:, message:) { raise(exception || message) } @@ -30,8 +31,7 @@ c.add_log_record_processor log_record_processor end -# TODO: Re-enable Rails app and active_support_logger testing # Create a globally available Rails app, this should be used in test unless # specifically testing behaviour with different initialization configs. -# DEFAULT_RAILS_APP = AppConfig.initialize_app -# Rails.application = DEFAULT_RAILS_APP +DEFAULT_RAILS_APP = AppConfig.initialize_app +Rails.application = DEFAULT_RAILS_APP diff --git a/instrumentation/logger/test/test_helpers/app_config.rb b/instrumentation/logger/test/test_helpers/app_config.rb index 7db946da9a..4e050324fe 100644 --- a/instrumentation/logger/test/test_helpers/app_config.rb +++ b/instrumentation/logger/test/test_helpers/app_config.rb @@ -1,98 +1,64 @@ # frozen_string_literal: true -# # Copyright The OpenTelemetry Authors -# # -# # SPDX-License-Identifier: Apache-2.0 - -# class Application < Rails::Application; end -# require 'action_controller/railtie' -# # require_relative 'middlewares' -# # require_relative 'controllers' -# # require_relative 'routes' - -# module AppConfig -# extend self - -# def initialize_app(use_exceptions_app: false, remove_rack_tracer_middleware: false) -# new_app = Application.new -# new_app.config.secret_key_base = 'secret_key_base' - -# # Ensure we don't see this Rails warning when testing -# new_app.config.eager_load = false - -# # Prevent tests from creating log/*.log -# level = ENV.fetch('OTEL_LOG_LEVEL', 'fatal').to_sym -# new_app.config.logger = Logger.new(LOG_STREAM, level: level) -# new_app.config.log_level = level - -# new_app.config.filter_parameters = [:param_to_be_filtered] - -# case Rails.version -# when /^6\.0/ -# apply_rails_6_0_configs(new_app) -# when /^6\.1/ -# apply_rails_6_1_configs(new_app) -# when /^7\./ -# apply_rails_7_configs(new_app) -# end - -# # remove_rack_middleware(new_app) if remove_rack_tracer_middleware -# # add_exceptions_app(new_app) if use_exceptions_app -# # add_middlewares(new_app) - -# new_app.initialize! - -# # draw_routes(new_app) - -# new_app -# end - -# private - -# # def remove_rack_middleware(application) -# # application.middleware.delete( -# # OpenTelemetry::Instrumentation::Rack::Middlewares::TracerMiddleware -# # ) -# # end - -# # def add_exceptions_app(application) -# # application.config.exceptions_app = lambda do |env| -# # ExceptionsController.action(:show).call(env) -# # end -# # end - -# # def add_middlewares(application) -# # application.middleware.insert_after( -# # ActionDispatch::DebugExceptions, -# # ExceptionRaisingMiddleware -# # ) - -# # application.middleware.insert_after( -# # ActionDispatch::DebugExceptions, -# # RedirectMiddleware -# # ) -# # end - -# def apply_rails_6_0_configs(application) -# # Required in Rails 6 -# application.config.hosts << 'example.org' -# # Creates a lot of deprecation warnings on subsequent app initializations if not explicitly set. -# application.config.action_view.finalize_compiled_template_methods = ActionView::Railtie::NULL_OPTION -# end - -# def apply_rails_6_1_configs(application) -# # Required in Rails 6 -# application.config.hosts << 'example.org' -# end - -# def apply_rails_7_configs(application) -# # Required in Rails 7 -# application.config.hosts << 'example.org' - -# # Unfreeze values which may have been frozen on previous initializations. -# ActiveSupport::Dependencies.autoload_paths = -# ActiveSupport::Dependencies.autoload_paths.dup -# ActiveSupport::Dependencies.autoload_once_paths = -# ActiveSupport::Dependencies.autoload_once_paths.dup -# end -# end +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +class Application < Rails::Application; end +require 'action_controller/railtie' + +module AppConfig + extend self + + def initialize_app(use_exceptions_app: false, remove_rack_tracer_middleware: false) + app = Application.new + app.config.secret_key_base = 'secret_key_base' + + # Ensure we don't see this Rails warning when testing + app.config.eager_load = false + + # Prevent tests from creating log/*.log + level = ENV.fetch('OTEL_LOG_LEVEL', 'fatal').to_sym + app.config.logger = ActiveSupport::Logger.new(LOG_STREAM, level: level) + app.config.log_level = level + app.config.filter_parameters = [:param_to_be_filtered] + + case Rails.version + when /^6\.0/ + apply_rails_6_0_configs(app) + when /^6\.1/ + apply_rails_6_1_configs(app) + when /^7\./ + apply_rails_7_configs(app) + end + + app.initialize! + + app + end + + private + + def apply_rails_6_0_configs(application) + # Required in Rails 6 + application.config.hosts << 'example.org' + # Creates a lot of deprecation warnings on subsequent app initializations if not explicitly set. + application.config.action_view.finalize_compiled_template_methods = ActionView::Railtie::NULL_OPTION + end + + def apply_rails_6_1_configs(application) + # Required in Rails 6 + application.config.hosts << 'example.org' + end + + def apply_rails_7_configs(application) + # Required in Rails 7 + application.config.hosts << 'example.org' + + # Unfreeze values which may have been frozen on previous initializations. + ActiveSupport::Dependencies.autoload_paths = + ActiveSupport::Dependencies.autoload_paths.dup + ActiveSupport::Dependencies.autoload_once_paths = + ActiveSupport::Dependencies.autoload_once_paths.dup + end +end From d00bc1da07fe5302d592773a4e769568c4cd985e Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Mon, 16 Sep 2024 17:15:48 -0700 Subject: [PATCH 34/67] style: Add language to code fence --- instrumentation/logger/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/logger/README.md b/instrumentation/logger/README.md index a89fc74f2a..8540d0f18b 100644 --- a/instrumentation/logger/README.md +++ b/instrumentation/logger/README.md @@ -6,7 +6,7 @@ TODO: Update description. This README is incomplete. Install the gem using: -``` +```shell gem install opentelemetry-instrumentation-logger ``` From d129337eadd1534c373dbbfee907447ad8979684 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle <87386821+kaylareopelle@users.noreply.github.com> Date: Tue, 17 Sep 2024 12:07:50 -0700 Subject: [PATCH 35/67] Update instrumentation/logger/lib/opentelemetry/instrumentation.rb --- instrumentation/logger/lib/opentelemetry/instrumentation.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation.rb b/instrumentation/logger/lib/opentelemetry/instrumentation.rb index eb80ddd2bb..001b363af0 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation.rb @@ -11,7 +11,6 @@ # The OpenTelemetry module provides global accessors for telemetry objects. # See the documentation for the `opentelemetry-api` gem for details. module OpenTelemetry - # Instrumentation should be able to handle the case when the library is not installed on a user's system. module Instrumentation end end From 97b42deff5eafba3d97093c0f8ab51e5ede20c15 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Tue, 17 Sep 2024 15:15:40 -0700 Subject: [PATCH 36/67] docs: add some config info --- .../instrumentation/logger/instrumentation.rb | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb index a2516d410f..b0c3f531de 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb @@ -7,7 +7,38 @@ module OpenTelemetry module Instrumentation module Logger - # The Instrumentation class contains logic to detect and install the Logger instrumentation + # The `OpenTelemetry::Instrumentation::Logger::Instrumentation` class contains logic to detect and install the + # Ruby Logger library instrumentation. + # + # Installation and configuration of this instrumentation is done within the + # {https://www.rubydoc.info/gems/opentelemetry-sdk/OpenTelemetry/SDK#configure-instance_method OpenTelemetry::SDK#configure} + # block, calling {https://www.rubydoc.info/gems/opentelemetry-sdk/OpenTelemetry%2FSDK%2FConfigurator:use use()} + # or {https://www.rubydoc.info/gems/opentelemetry-sdk/OpenTelemetry%2FSDK%2FConfigurator:use_all use_all()}. + # + # ## Configuration keys and options + # + # ### `:name` + # + # Sets the name of the OpenTelemetry Logger InstrumentationScope. + # + # - The name of this gem, `'opentelemetry-instrumentation-logger'` is the default. + # + # ### `:version` + # + # Sets the version of the OpenTelemetry Logger InstrumentationScope. + # + # - This gem's current version is the default. + # + # + # @example An explicit default configuration + # OpenTelemetry::SDK.configure do |c| + # c.use_all({ + # 'OpenTelemetry::Instrumentation::Sidekiq' => { + # name: 'opentelemetry-instrumentation-logger', + # version: '0.1.0' + # }, + # }) + # end class Instrumentation < OpenTelemetry::Instrumentation::Base install do |_config| require_dependencies From 6276944df4495d92717182f927c6b51444a1d84c Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Tue, 17 Sep 2024 15:17:35 -0700 Subject: [PATCH 37/67] feat: Remove config options --- .../instrumentation/logger/instrumentation.rb | 27 ------------------- .../instrumentation/logger/patches/logger.rb | 6 ++--- 2 files changed, 2 insertions(+), 31 deletions(-) diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb index b0c3f531de..70ed5479dd 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb @@ -15,30 +15,6 @@ module Logger # block, calling {https://www.rubydoc.info/gems/opentelemetry-sdk/OpenTelemetry%2FSDK%2FConfigurator:use use()} # or {https://www.rubydoc.info/gems/opentelemetry-sdk/OpenTelemetry%2FSDK%2FConfigurator:use_all use_all()}. # - # ## Configuration keys and options - # - # ### `:name` - # - # Sets the name of the OpenTelemetry Logger InstrumentationScope. - # - # - The name of this gem, `'opentelemetry-instrumentation-logger'` is the default. - # - # ### `:version` - # - # Sets the version of the OpenTelemetry Logger InstrumentationScope. - # - # - This gem's current version is the default. - # - # - # @example An explicit default configuration - # OpenTelemetry::SDK.configure do |c| - # c.use_all({ - # 'OpenTelemetry::Instrumentation::Sidekiq' => { - # name: 'opentelemetry-instrumentation-logger', - # version: '0.1.0' - # }, - # }) - # end class Instrumentation < OpenTelemetry::Instrumentation::Base install do |_config| require_dependencies @@ -49,9 +25,6 @@ class Instrumentation < OpenTelemetry::Instrumentation::Base defined?(::Logger) && defined?(::OpenTelemetry::SDK::Logs) end - option :name, default: OpenTelemetry::Instrumentation::Logger::NAME, validate: :string - option :version, default: OpenTelemetry::Instrumentation::Logger::VERSION, validate: :string - private def patch diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb index 0d399c84b8..e6c17ea896 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb @@ -12,15 +12,13 @@ module Patches module Logger attr_writer :skip_instrumenting - # TODO: Make sure OTel logs aren't instrumented - # TODO: How to pass attributes? def format_message(severity, datetime, progname, msg) formatted_message = super(severity, datetime, progname, msg) return formatted_message if skip_instrumenting? OpenTelemetry.logger_provider.logger( - name: Instrumentation.instance.config[:name], - version: Instrumentation.instance.config[:version] + name: OpenTelemetry::Instrumentation::Logger::NAME, + version: OpenTelemetry::Instrumentation::Logger::VERSION ).on_emit( severity_text: severity, severity_number: severity_number(severity), From ef88293afa4bc36cdc966115447644472fa0dad4 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Tue, 17 Sep 2024 15:18:15 -0700 Subject: [PATCH 38/67] chore: Add TODO for version number update --- .../logger/lib/opentelemetry/instrumentation/logger/version.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/version.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/version.rb index 8e0b8e97b3..316fc1d14d 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/version.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/version.rb @@ -7,6 +7,7 @@ module OpenTelemetry module Instrumentation module Logger + # TODO: Update me when ready to release VERSION = '0.0.0' end end From 00c8687913b7fe0f246fc4cdd5a8ad36ddba39b0 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Tue, 17 Sep 2024 15:27:46 -0700 Subject: [PATCH 39/67] feat: Update example and readme --- instrumentation/logger/README.md | 20 +++++++++++---- instrumentation/logger/example/logger.rb | 32 ++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 instrumentation/logger/example/logger.rb diff --git a/instrumentation/logger/README.md b/instrumentation/logger/README.md index 8540d0f18b..819a5b9628 100644 --- a/instrumentation/logger/README.md +++ b/instrumentation/logger/README.md @@ -1,6 +1,6 @@ # OpenTelemetry Logger Instrumentation -TODO: Update description. This README is incomplete. +The Logger instrumentation is a community-maintained bridge for the Ruby [logger][logger-home] standard library. ## How do I get started? @@ -30,15 +30,23 @@ OpenTelemetry::SDK.configure do |c| end ``` -TODO: Add documentation for config options. - ## Examples -TODO: Create example +Example usage can be seen in the `./example/logger.rb` file [here](https://github.com/open-telemetry/opentelemetry-ruby-contrib/blob/main/instrumentation/logger/example/logger.rb) + +## Development + +The test suite leverages [appraisal][appraisal] to verify the integration across multiple Rails versions. To run the tests with appraisal: + +```shell +cd instrumentation/logger +bundle exec appraisal install +bundle exec appraisal rake test +``` ## How can I get involved? -The `opentelemetry-instrumentation-logger` gem source is [on github][repo-github], along with related gems including `opentelemetry-api` and `opentelemetry-sdk`. +The `opentelemetry-instrumentation-logger` gem source is [on github][repo-github], along with related gems including `opentelemetry-logs-api` and `opentelemetry-logs-sdk`. The OpenTelemetry Ruby gems are maintained by the OpenTelemetry-Ruby special interest group (SIG). You can get involved by joining us in [GitHub Discussions][discussions-url] 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]. @@ -46,7 +54,9 @@ The OpenTelemetry Ruby gems are maintained by the OpenTelemetry-Ruby special int The `opentelemetry-instrumentation-logger` gem is distributed under the Apache 2.0 license. See [LICENSE][license-github] for more information. +[appraisal]: https://github.com/thoughtbot/appraisal [bundler-home]: https://bundler.io +[logger-home]: https://github.com/ruby/logger [repo-github]: https://github.com/open-telemetry/opentelemetry-ruby [license-github]: https://github.com/open-telemetry/opentelemetry-ruby-contrib/blob/main/LICENSE [ruby-sig]: https://github.com/open-telemetry/community#ruby-sig diff --git a/instrumentation/logger/example/logger.rb b/instrumentation/logger/example/logger.rb new file mode 100644 index 0000000000..59369653c2 --- /dev/null +++ b/instrumentation/logger/example/logger.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true +# TODO: Test me once the logs API/SDK gems have been released +# Since the gems aren't released, this is broken atm +require 'bundler/inline' + +gemfile(true) do + source 'https://rubygems.org' + + gem 'logger' + gem 'opentelemetry-sdk' + gem 'opentelemetry-logs-api' + gem 'opentelemetry-logs-sdk' + gem 'opentelemetry-instrumentation-logger', path: '../' +end + +require 'opentelemetry-logs-sdk' +require 'opentelemetry-instrumentation-logger' +require 'logger' + +# Don't attempt to export traces, Logger instrumentation only emits logs. +ENV['OTEL_TRACES_EXPORTER'] ||= 'none' + +OpenTelemetry::SDK.configure do |c| + c.use OpenTelemetry::Instrumentation::Logger +end + +at_exit do + OpenTelemetry.logger_provider.shutdown +end + +logger = Logger.new +logger.debug('emerald ash borer') From 7f8dce1448a715d227e8815c0bb919b50fb62de5 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Tue, 17 Sep 2024 16:55:28 -0700 Subject: [PATCH 40/67] test: remove appraisals for eol'd rails versions --- instrumentation/logger/Appraisals | 14 ++++---- .../logger/test/test_helpers/app_config.rb | 36 ++----------------- 2 files changed, 10 insertions(+), 40 deletions(-) diff --git a/instrumentation/logger/Appraisals b/instrumentation/logger/Appraisals index fa165e3e17..80e00d6df0 100644 --- a/instrumentation/logger/Appraisals +++ b/instrumentation/logger/Appraisals @@ -7,14 +7,16 @@ # # TOOD: Re-enable before release, along with active_support_logger tests # # Appraisals wont work with gems installed by a branch, so we can't # # set this up until there's a release of the otel logs gems -# appraise 'rails-6.0' do -# gem 'rails', '~> 6.0.0' +# # Only test Rails versions that are still in support + +# appraise 'rails-7.0' do +# gem 'rails', '~> 7.0.0' # end -# appraise 'rails-6.1' do -# gem 'rails', '~> 6.1.0' +# appraise 'rails-7.1' do +# gem 'rails', '~> 7.1.0' # end -# appraise 'rails-7.0' do -# gem 'rails', '~> 7.0.0' +# appraise 'rails-7.2' do +# gem 'rails', '~> 7.2.0' # end diff --git a/instrumentation/logger/test/test_helpers/app_config.rb b/instrumentation/logger/test/test_helpers/app_config.rb index 4e050324fe..710c6de615 100644 --- a/instrumentation/logger/test/test_helpers/app_config.rb +++ b/instrumentation/logger/test/test_helpers/app_config.rb @@ -16,49 +16,17 @@ def initialize_app(use_exceptions_app: false, remove_rack_tracer_middleware: fal # Ensure we don't see this Rails warning when testing app.config.eager_load = false + app.config.enable_reloading = false # Prevent tests from creating log/*.log level = ENV.fetch('OTEL_LOG_LEVEL', 'fatal').to_sym app.config.logger = ActiveSupport::Logger.new(LOG_STREAM, level: level) app.config.log_level = level app.config.filter_parameters = [:param_to_be_filtered] - - case Rails.version - when /^6\.0/ - apply_rails_6_0_configs(app) - when /^6\.1/ - apply_rails_6_1_configs(app) - when /^7\./ - apply_rails_7_configs(app) - end + app.config.load_defaults([Rails::VERSION::MAJOR, Rails::VERSION::MINOR].compact.join('.')) app.initialize! app end - - private - - def apply_rails_6_0_configs(application) - # Required in Rails 6 - application.config.hosts << 'example.org' - # Creates a lot of deprecation warnings on subsequent app initializations if not explicitly set. - application.config.action_view.finalize_compiled_template_methods = ActionView::Railtie::NULL_OPTION - end - - def apply_rails_6_1_configs(application) - # Required in Rails 6 - application.config.hosts << 'example.org' - end - - def apply_rails_7_configs(application) - # Required in Rails 7 - application.config.hosts << 'example.org' - - # Unfreeze values which may have been frozen on previous initializations. - ActiveSupport::Dependencies.autoload_paths = - ActiveSupport::Dependencies.autoload_paths.dup - ActiveSupport::Dependencies.autoload_once_paths = - ActiveSupport::Dependencies.autoload_once_paths.dup - end end From e5cc573541172b59bcfac9e73c46e6882e559afc Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Tue, 17 Sep 2024 16:58:50 -0700 Subject: [PATCH 41/67] feat: Support ActiveSupport::BroadcastLogger Rails 7.1+ uses ActiveSupport::BroadcastLogger. This needs to protect against emitting duplicate logs in a different way than ActiveSupport::Logger.broadcast. Emits the log record for the first logger in the broadcast, skip the others. Reset everything at the end of the method call. --- instrumentation/logger/Gemfile | 2 +- .../instrumentation/logger/instrumentation.rb | 10 ++- .../active_support_broadcast_logger.rb | 55 ++++++++++++ .../active_support_broadcast_logger_test.rb | 88 +++++++++++++++++++ .../patches/active_support_logger_test.rb | 1 + 5 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/active_support_broadcast_logger.rb create mode 100644 instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_broadcast_logger_test.rb diff --git a/instrumentation/logger/Gemfile b/instrumentation/logger/Gemfile index c292f236c9..82e2567312 100644 --- a/instrumentation/logger/Gemfile +++ b/instrumentation/logger/Gemfile @@ -22,4 +22,4 @@ group :test do end # Temporary for testing, Appraisal does not work with gems installed from git source -gem 'rails', '~> 7.0.0' +gem 'rails', '~> 7.1.0' diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb index 70ed5479dd..44fc83d8c7 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/instrumentation.rb @@ -29,6 +29,7 @@ class Instrumentation < OpenTelemetry::Instrumentation::Base def patch ::Logger.prepend(Patches::Logger) + active_support_broadcast_logger_patch active_support_patch end @@ -37,11 +38,18 @@ def require_dependencies end def active_support_patch - return unless defined?(::ActiveSupport::Logger) + return unless defined?(::ActiveSupport::Logger) && !defined?(::ActiveSupport::BroadcastLogger) require_relative 'patches/active_support_logger' ::ActiveSupport::Logger.singleton_class.prepend(Patches::ActiveSupportLogger) end + + def active_support_broadcast_logger_patch + return unless defined?(::ActiveSupport::BroadcastLogger) + + require_relative 'patches/active_support_broadcast_logger' + ::ActiveSupport::BroadcastLogger.prepend(Patches::ActiveSupportBroadcastLogger) + end end end end diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/active_support_broadcast_logger.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/active_support_broadcast_logger.rb new file mode 100644 index 0000000000..c5623ebca3 --- /dev/null +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/active_support_broadcast_logger.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module Instrumentation + module Logger + module Patches + # Patches for the ActiveSupport::BroadcastLogger class included in Rails 7.1+ + module ActiveSupportBroadcastLogger + def add(*args) + emit_one_broadcast(*args) { super } + end + + def debug(*args) + emit_one_broadcast(*args) { super } + end + + def info(*args) + emit_one_broadcast(*args) { super } + end + + def warn(*args) + emit_one_broadcast(*args) { super } + end + + def error(*args) + emit_one_broadcast(*args) { super } + end + + def fatal(*args) + emit_one_broadcast(*args) { super } + end + + def unknown(*args) + emit_one_broadcast(*args) { super } + end + + private + + # Emit logs from only one of the loggers in the broadcast. + # Set @skip_instrumenting to `true` to the rest of the loggers before emitting the logs. + # Set @skip_instrumenting to `false` after the log is emitted. + def emit_one_broadcast(*args) + broadcasts[1..-1].each { |broadcasted_logger| broadcasted_logger.instance_variable_set(:@skip_instrumenting, true) } + yield + broadcasts.each { |broadcasted_logger| broadcasted_logger.instance_variable_set(:@skip_instrumenting, false) } + end + end + end + end + end +end diff --git a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_broadcast_logger_test.rb b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_broadcast_logger_test.rb new file mode 100644 index 0000000000..25e9c27382 --- /dev/null +++ b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_broadcast_logger_test.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'test_helper' + +require_relative '../../../../../lib/opentelemetry/instrumentation/logger/patches/active_support_broadcast_logger' + +describe OpenTelemetry::Instrumentation::Logger::Patches::ActiveSupportBroadcastLogger do + let(:instrumentation) { OpenTelemetry::Instrumentation::Logger::Instrumentation.instance } + let(:logger) { Logger.new(LOG_STREAM) } + let(:logger2) { Logger.new(BROADCASTED_STREAM) } + let(:broadcast) { ActiveSupport::BroadcastLogger.new(logger, logger2) } + + before do + skip unless defined?(::ActiveSupport::BroadcastLogger) + EXPORTER.reset + instrumentation.install + end + + after { instrumentation.instance_variable_set(:@installed, false) } + + describe '#add' do + it 'emits the log to the broadcasted loggers' do + body = "Ground control to Major Tom" + broadcast.add(Logger::DEBUG, body) + + assert_includes(LOG_STREAM.string, body) + assert_includes(BROADCASTED_STREAM.string, body) + end + + it 'emits only one OpenTelemetry log record' do + body = "Wake up, you sleepyhead" + broadcast.add(Logger::DEBUG, body) + log_records = EXPORTER.emitted_log_records + + assert_equal 1, log_records.size + assert_equal 'DEBUG', log_records.first.severity_text + assert_equal body, log_records.first.body + end + end + + describe '#unknown' do + it 'emits the log to the broadcasted loggers' do + body = "I know when to go out" + broadcast.unknown(body) + + assert_includes(LOG_STREAM.string, body) + assert_includes(BROADCASTED_STREAM.string, body) + end + + it 'emits only one OpenTelemetry log record' do + body = "You've got your mother in a whirl" + broadcast.unknown(body) + + log_records = EXPORTER.emitted_log_records + + assert_equal 1, log_records.size + assert_equal 'ANY', log_records.first.severity_text + assert_equal body, log_records.first.body + end + end + + %w[debug info warn error fatal].each do |severity| + describe "##{severity}" do + it 'emits the log to the broadcasted loggers' do + body = "Still don't know what I was waiting for...#{rand(7)}" + broadcast.send(severity.to_sym, body) + + assert_includes(LOG_STREAM.string, body) + assert_includes(BROADCASTED_STREAM.string, body) + end + + it 'emits only one OpenTelemetry log record' do + body = "They pulled in just behind the bridge...#{rand(7)}" + broadcast.send(severity.to_sym, body) + + log_records = EXPORTER.emitted_log_records + + assert_equal 1, log_records.size + assert_equal severity.upcase, log_records.first.severity_text + assert_equal body, log_records.first.body + end + end + end +end diff --git a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb index e675885693..9bd96ff3ef 100644 --- a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb +++ b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb @@ -14,6 +14,7 @@ let(:broadcasted_logger) { ActiveSupport::Logger.new(BROADCASTED_STREAM) } before do + skip unless defined?(::ActiveSupport::Logger) && !defined?(::ActiveSupport::BroadcastLogger) EXPORTER.reset Rails.logger = main_logger.extend(ActiveSupport::Logger.broadcast(broadcasted_logger)) instrumentation.install From ea3351e80ff47cdef16749643b4a0d149b29e824 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Tue, 17 Sep 2024 16:59:10 -0700 Subject: [PATCH 42/67] test: Remove config option tests --- .../logger/patches/logger_test.rb | 40 ------------------- 1 file changed, 40 deletions(-) diff --git a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb index 9d4438aec5..f0c5b9460d 100644 --- a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb +++ b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb @@ -36,46 +36,6 @@ assert_equal(OpenTelemetry::Instrumentation::Logger::VERSION, log_record.instrumentation_scope.version) end - describe 'configuration options' do - describe 'when a user configures name' do - let(:config) { { name: 'custom_logger' } } - - it 'updates the logger name' do - ruby_logger.debug(msg) - assert_equal('custom_logger', log_record.instrumentation_scope.name) - end - - it 'uses the default version' do - ruby_logger.debug(msg) - assert_equal(OpenTelemetry::Instrumentation::Logger::VERSION, log_record.instrumentation_scope.version) - end - end - - describe 'when a user configures version' do - let(:config) { { version: '5000' } } - - it 'updates the logger version' do - ruby_logger.debug(msg) - assert_equal('5000', log_record.instrumentation_scope.version) - end - - it 'uses the default name' do - ruby_logger.debug(msg) - assert_equal(OpenTelemetry::Instrumentation::Logger::NAME, log_record.instrumentation_scope.name) - end - end - - describe 'when a user configures both name and version' do - let(:config) { { name: 'custom_logger', version: '5000' } } - - it 'updates both values' do - ruby_logger.debug(msg) - assert_equal('custom_logger', log_record.instrumentation_scope.name) - assert_equal('5000', log_record.instrumentation_scope.version) - end - end - end - it 'sets log record attributes based on the Ruby log' do timestamp = Time.now Time.stub(:now, timestamp) do From c9f47c6f8bbc654eca73219f787ad7febe06ac69 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Wed, 4 Dec 2024 16:57:53 -0800 Subject: [PATCH 43/67] chore: Prepare bridge for review * Add references to released logs gems * Test Rails 7.0 - 8.0 * Rubocop * Set gem version --- instrumentation/logger/Appraisals | 33 ++++++++----------- instrumentation/logger/Gemfile | 8 ----- instrumentation/logger/README.md | 3 +- instrumentation/logger/example/logger.rb | 10 +++--- .../lib/opentelemetry/instrumentation.rb | 1 + .../instrumentation/logger/patches/logger.rb | 4 +-- .../instrumentation/logger/version.rb | 3 +- instrumentation/logger/log/development.log | 0 ...entelemetry-instrumentation-logger.gemspec | 11 +++---- .../active_support_broadcast_logger_test.rb | 8 ++--- .../patches/active_support_logger_test.rb | 2 +- .../logger/patches/logger_test.rb | 4 ++- instrumentation/logger/test/test_helper.rb | 2 -- 13 files changed, 37 insertions(+), 52 deletions(-) delete mode 100644 instrumentation/logger/log/development.log diff --git a/instrumentation/logger/Appraisals b/instrumentation/logger/Appraisals index 80e00d6df0..52806e8adb 100644 --- a/instrumentation/logger/Appraisals +++ b/instrumentation/logger/Appraisals @@ -1,22 +1,15 @@ # frozen_string_literal: true -# # Copyright The OpenTelemetry Authors -# # -# # SPDX-License-Identifier: Apache-2.0 - -# # TOOD: Re-enable before release, along with active_support_logger tests -# # Appraisals wont work with gems installed by a branch, so we can't -# # set this up until there's a release of the otel logs gems -# # Only test Rails versions that are still in support - -# appraise 'rails-7.0' do -# gem 'rails', '~> 7.0.0' -# end - -# appraise 'rails-7.1' do -# gem 'rails', '~> 7.1.0' -# end - -# appraise 'rails-7.2' do -# gem 'rails', '~> 7.2.0' -# end +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +%w[7.0.0 7.1.0].each do |version| + appraise "rails-#{version}" do + gem 'rails', "~> #{version}" + end +end + +appraise 'rails-latest' do + gem 'rails' +end diff --git a/instrumentation/logger/Gemfile b/instrumentation/logger/Gemfile index 82e2567312..a952c5002d 100644 --- a/instrumentation/logger/Gemfile +++ b/instrumentation/logger/Gemfile @@ -6,7 +6,6 @@ source 'https://rubygems.org' -### TODO: Ignore the below comment during development, fix before release ### # DO NOT ADD DEPENDENCIES HERE! # Please declare a minimum development dependency in the gemspec, # then target specific versions in the Appraisals file. @@ -15,11 +14,4 @@ gemspec group :test do gem 'opentelemetry-instrumentation-base', path: '../base' - gem 'opentelemetry-api', git: 'https://github.com/kaylareopelle/opentelemetry-ruby', branch: 'log-record-processor3', glob: 'api/*.gemspec' - gem 'opentelemetry-logs-api', git: 'https://github.com/kaylareopelle/opentelemetry-ruby', branch: 'log-record-processor3', glob: 'logs_api/*.gemspec' - gem 'opentelemetry-logs-sdk', git: 'https://github.com/kaylareopelle/opentelemetry-ruby', branch: 'log-record-processor3', glob: 'logs_sdk/*.gemspec' - gem 'opentelemetry-sdk', git: 'https://github.com/kaylareopelle/opentelemetry-ruby', branch: 'log-record-processor3', glob: 'sdk/*.gemspec' end - -# Temporary for testing, Appraisal does not work with gems installed from git source -gem 'rails', '~> 7.1.0' diff --git a/instrumentation/logger/README.md b/instrumentation/logger/README.md index 819a5b9628..73231dd515 100644 --- a/instrumentation/logger/README.md +++ b/instrumentation/logger/README.md @@ -22,7 +22,7 @@ OpenTelemetry::SDK.configure do |c| end ``` -Alternatively, you can also call `use_all` to install all the available instrumentation. +Alternatively, you can call `use_all` to install all the available instrumentation. ```ruby OpenTelemetry::SDK.configure do |c| @@ -40,6 +40,7 @@ The test suite leverages [appraisal][appraisal] to verify the integration across ```shell cd instrumentation/logger +bundle exec appraisal generate bundle exec appraisal install bundle exec appraisal rake test ``` diff --git a/instrumentation/logger/example/logger.rb b/instrumentation/logger/example/logger.rb index 59369653c2..6a8901e93d 100644 --- a/instrumentation/logger/example/logger.rb +++ b/instrumentation/logger/example/logger.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -# TODO: Test me once the logs API/SDK gems have been released -# Since the gems aren't released, this is broken atm + require 'bundler/inline' gemfile(true) do @@ -8,25 +7,26 @@ gem 'logger' gem 'opentelemetry-sdk' - gem 'opentelemetry-logs-api' gem 'opentelemetry-logs-sdk' gem 'opentelemetry-instrumentation-logger', path: '../' end +require 'opentelemetry/sdk' require 'opentelemetry-logs-sdk' require 'opentelemetry-instrumentation-logger' require 'logger' # Don't attempt to export traces, Logger instrumentation only emits logs. ENV['OTEL_TRACES_EXPORTER'] ||= 'none' +ENV['OTEL_LOGS_EXPORTER'] ||= 'console' OpenTelemetry::SDK.configure do |c| - c.use OpenTelemetry::Instrumentation::Logger + c.use 'OpenTelemetry::Instrumentation::Logger' end at_exit do OpenTelemetry.logger_provider.shutdown end -logger = Logger.new +logger = Logger.new(STDOUT) logger.debug('emerald ash borer') diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation.rb b/instrumentation/logger/lib/opentelemetry/instrumentation.rb index 001b363af0..99cd544da7 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation.rb @@ -11,6 +11,7 @@ # The OpenTelemetry module provides global accessors for telemetry objects. # See the documentation for the `opentelemetry-api` gem for details. module OpenTelemetry + # The Instrumentation module is a namespace for all OpenTelemetry Instrumentation libraries module Instrumentation end end diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb index e6c17ea896..5194b8b4d5 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb @@ -13,7 +13,7 @@ module Logger attr_writer :skip_instrumenting def format_message(severity, datetime, progname, msg) - formatted_message = super(severity, datetime, progname, msg) + formatted_message = super return formatted_message if skip_instrumenting? OpenTelemetry.logger_provider.logger( @@ -23,7 +23,7 @@ def format_message(severity, datetime, progname, msg) severity_text: severity, severity_number: severity_number(severity), timestamp: datetime, - body: msg, # New Relic uses formatted_message here. This also helps us with not recording progname, because it is included in the formatted message by default. Which seems more appropriate? + body: msg, context: OpenTelemetry::Context.current ) formatted_message diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/version.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/version.rb index 316fc1d14d..4157e9b04b 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/version.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/version.rb @@ -7,8 +7,7 @@ module OpenTelemetry module Instrumentation module Logger - # TODO: Update me when ready to release - VERSION = '0.0.0' + VERSION = '0.1.0' end end end diff --git a/instrumentation/logger/log/development.log b/instrumentation/logger/log/development.log deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec b/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec index 56de613594..82088daa29 100644 --- a/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec +++ b/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec @@ -25,19 +25,18 @@ Gem::Specification.new do |spec| spec.require_paths = ['lib'] spec.required_ruby_version = '>= 3.0' - spec.add_dependency 'opentelemetry-api', '~> 1.2' spec.add_dependency 'opentelemetry-instrumentation-base', '~> 0.22' + spec.add_dependency 'opentelemetry-logs-api', '~> 0.1' - spec.add_development_dependency 'appraisal', '~> 2.2.0' + spec.add_development_dependency 'appraisal', '~> 2.5.0' spec.add_development_dependency 'bundler', '~> 2.4' spec.add_development_dependency 'minitest', '~> 5.0' - spec.add_development_dependency 'opentelemetry-sdk', '~> 1.0' + spec.add_development_dependency 'opentelemetry-logs-sdk', '~> 0.1' spec.add_development_dependency 'opentelemetry-test-helpers', '~> 0.3' spec.add_development_dependency 'rake', '~> 13.0' - spec.add_development_dependency 'rubocop', '~> 1.62.1' - spec.add_development_dependency 'rubocop-performance', '~> 1.20.2' + spec.add_development_dependency 'rubocop', '~> 1.69.1' + spec.add_development_dependency 'rubocop-performance', '~> 1.23.0' spec.add_development_dependency 'simplecov' - spec.add_development_dependency 'webmock', '~> 3.7.6' spec.add_development_dependency 'yard', '~> 0.9' spec.add_development_dependency 'yard-doctest', '~> 0.1.6' diff --git a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_broadcast_logger_test.rb b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_broadcast_logger_test.rb index 25e9c27382..449b4e5632 100644 --- a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_broadcast_logger_test.rb +++ b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_broadcast_logger_test.rb @@ -15,7 +15,7 @@ let(:broadcast) { ActiveSupport::BroadcastLogger.new(logger, logger2) } before do - skip unless defined?(::ActiveSupport::BroadcastLogger) + skip unless defined?(ActiveSupport::BroadcastLogger) EXPORTER.reset instrumentation.install end @@ -24,7 +24,7 @@ describe '#add' do it 'emits the log to the broadcasted loggers' do - body = "Ground control to Major Tom" + body = 'Ground control to Major Tom' broadcast.add(Logger::DEBUG, body) assert_includes(LOG_STREAM.string, body) @@ -32,7 +32,7 @@ end it 'emits only one OpenTelemetry log record' do - body = "Wake up, you sleepyhead" + body = 'Wake up, you sleepyhead' broadcast.add(Logger::DEBUG, body) log_records = EXPORTER.emitted_log_records @@ -44,7 +44,7 @@ describe '#unknown' do it 'emits the log to the broadcasted loggers' do - body = "I know when to go out" + body = 'I know when to go out' broadcast.unknown(body) assert_includes(LOG_STREAM.string, body) diff --git a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb index 9bd96ff3ef..6cd58ecced 100644 --- a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb +++ b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb @@ -14,7 +14,7 @@ let(:broadcasted_logger) { ActiveSupport::Logger.new(BROADCASTED_STREAM) } before do - skip unless defined?(::ActiveSupport::Logger) && !defined?(::ActiveSupport::BroadcastLogger) + skip unless defined?(ActiveSupport::Logger) && !defined?(ActiveSupport::BroadcastLogger) EXPORTER.reset Rails.logger = main_logger.extend(ActiveSupport::Logger.broadcast(broadcasted_logger)) instrumentation.install diff --git a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb index f0c5b9460d..112e1ce624 100644 --- a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb +++ b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb @@ -38,12 +38,14 @@ it 'sets log record attributes based on the Ruby log' do timestamp = Time.now + nano_timestamp = OpenTelemetry::SDK::Logs::LogRecord.new.send(:to_integer_nanoseconds, timestamp) + Time.stub(:now, timestamp) do ruby_logger.debug(msg) assert_equal(msg, log_record.body) assert_equal('DEBUG', log_record.severity_text) assert_equal(5, log_record.severity_number) - assert_equal(timestamp, log_record.timestamp) + assert_equal(nano_timestamp, log_record.timestamp) end end diff --git a/instrumentation/logger/test/test_helper.rb b/instrumentation/logger/test/test_helper.rb index 547f0209f3..423c8b522e 100644 --- a/instrumentation/logger/test/test_helper.rb +++ b/instrumentation/logger/test/test_helper.rb @@ -16,8 +16,6 @@ SimpleCov.minimum_coverage 85 require 'minitest/autorun' -require 'webmock/minitest' -require 'opentelemetry/sdk/logs' require 'test_helpers/app_config' EXPORTER = OpenTelemetry::SDK::Logs::Export::InMemoryLogRecordExporter.new From 40f4b9a14dbe9564627d3e4c19ec72c7ef2c8579 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Wed, 4 Dec 2024 17:01:22 -0800 Subject: [PATCH 44/67] chore: Rename skip_instrumenting to skip_otel_emit --- .../logger/patches/active_support_broadcast_logger.rb | 8 ++++---- .../logger/patches/active_support_logger.rb | 2 +- .../instrumentation/logger/patches/logger.rb | 8 ++++---- .../logger/patches/active_support_logger_test.rb | 8 ++++---- .../instrumentation/logger/patches/logger_test.rb | 4 ++-- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/active_support_broadcast_logger.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/active_support_broadcast_logger.rb index c5623ebca3..8d310832ea 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/active_support_broadcast_logger.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/active_support_broadcast_logger.rb @@ -41,12 +41,12 @@ def unknown(*args) private # Emit logs from only one of the loggers in the broadcast. - # Set @skip_instrumenting to `true` to the rest of the loggers before emitting the logs. - # Set @skip_instrumenting to `false` after the log is emitted. + # Set @skip_otel_emit to `true` to the rest of the loggers before emitting the logs. + # Set @skip_otel_emit to `false` after the log is emitted. def emit_one_broadcast(*args) - broadcasts[1..-1].each { |broadcasted_logger| broadcasted_logger.instance_variable_set(:@skip_instrumenting, true) } + broadcasts[1..-1].each { |broadcasted_logger| broadcasted_logger.instance_variable_set(:@skip_otel_emit, true) } yield - broadcasts.each { |broadcasted_logger| broadcasted_logger.instance_variable_set(:@skip_instrumenting, false) } + broadcasts.each { |broadcasted_logger| broadcasted_logger.instance_variable_set(:@skip_otel_emit, false) } end end end diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/active_support_logger.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/active_support_logger.rb index 7d8bcff1f5..6d67072bb2 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/active_support_logger.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/active_support_logger.rb @@ -15,7 +15,7 @@ module ActiveSupportLogger # destinations from generating OpenTelemetry log record objects. # Available in Rails 7.0 and below def broadcast(logger) - logger.instance_variable_set(:@skip_instrumenting, true) + logger.instance_variable_set(:@skip_otel_emit, true) super end end diff --git a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb index 5194b8b4d5..63827882c3 100644 --- a/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb +++ b/instrumentation/logger/lib/opentelemetry/instrumentation/logger/patches/logger.rb @@ -10,11 +10,11 @@ module Logger module Patches # Instrumention for methods from Ruby's Logger class module Logger - attr_writer :skip_instrumenting + attr_writer :skip_otel_emit def format_message(severity, datetime, progname, msg) formatted_message = super - return formatted_message if skip_instrumenting? + return formatted_message if skip_otel_emit? OpenTelemetry.logger_provider.logger( name: OpenTelemetry::Instrumentation::Logger::NAME, @@ -31,8 +31,8 @@ def format_message(severity, datetime, progname, msg) private - def skip_instrumenting? - @skip_instrumenting || false + def skip_otel_emit? + @skip_otel_emit || false end def severity_number(severity) diff --git a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb index 6cd58ecced..8241596dc4 100644 --- a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb +++ b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb @@ -45,12 +45,12 @@ assert_match(/#{msg}/, log_record.body) end - it 'does not add @skip_instrumenting to the initial logger' do - refute Rails.logger.instance_variable_defined?(:@skip_instrumenting) + it 'does not add @skip_otel_emit to the initial logger' do + refute Rails.logger.instance_variable_defined?(:@skip_otel_emit) end - it 'adds @skip_instrumenting to broadcasted loggers' do - assert broadcasted_logger.instance_variable_defined?(:@skip_instrumenting) + it 'adds @skip_otel_emit to broadcasted loggers' do + assert broadcasted_logger.instance_variable_defined?(:@skip_otel_emit) end end end diff --git a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb index 112e1ce624..7c91621601 100644 --- a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb +++ b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/logger_test.rb @@ -49,8 +49,8 @@ end end - it 'does not emit when @skip_instrumenting is true' do - ruby_logger.instance_variable_set(:@skip_instrumenting, true) + it 'does not emit when @skip_otel_emit is true' do + ruby_logger.instance_variable_set(:@skip_otel_emit, true) ruby_logger.debug(msg) assert_nil(log_record) end From b0cace23725585d2b2a0d17f1f9c3f083ebf4add Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Wed, 4 Dec 2024 17:30:45 -0800 Subject: [PATCH 45/67] chore: Bump instrumentation-logger version in all --- instrumentation/all/opentelemetry-instrumentation-all.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/all/opentelemetry-instrumentation-all.gemspec b/instrumentation/all/opentelemetry-instrumentation-all.gemspec index 92f855ba92..dc1a88e404 100644 --- a/instrumentation/all/opentelemetry-instrumentation-all.gemspec +++ b/instrumentation/all/opentelemetry-instrumentation-all.gemspec @@ -43,7 +43,7 @@ Gem::Specification.new do |spec| spec.add_dependency 'opentelemetry-instrumentation-http_client', '~> 0.22.1' spec.add_dependency 'opentelemetry-instrumentation-koala', '~> 0.20.1' spec.add_dependency 'opentelemetry-instrumentation-lmdb', '~> 0.22.1' - spec.add_dependency 'opentelemetry-instrumentation-logger', '~> 0.0.0' + spec.add_dependency 'opentelemetry-instrumentation-logger', '~> 0.1.0' spec.add_dependency 'opentelemetry-instrumentation-mongo', '~> 0.22.1' spec.add_dependency 'opentelemetry-instrumentation-mysql2', '~> 0.28.0' spec.add_dependency 'opentelemetry-instrumentation-net_http', '~> 0.22.1' From eda40db184d182faf29a4b7891df116ffc1b6d57 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Thu, 5 Dec 2024 16:32:46 -0800 Subject: [PATCH 46/67] chore: Remove simplecov --- .../logger/opentelemetry-instrumentation-logger.gemspec | 1 - instrumentation/logger/test/test_helper.rb | 8 -------- 2 files changed, 9 deletions(-) diff --git a/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec b/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec index 82088daa29..2e35b241f5 100644 --- a/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec +++ b/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec @@ -36,7 +36,6 @@ Gem::Specification.new do |spec| 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' spec.add_development_dependency 'yard', '~> 0.9' spec.add_development_dependency 'yard-doctest', '~> 0.1.6' diff --git a/instrumentation/logger/test/test_helper.rb b/instrumentation/logger/test/test_helper.rb index 423c8b522e..1857900f62 100644 --- a/instrumentation/logger/test/test_helper.rb +++ b/instrumentation/logger/test/test_helper.rb @@ -7,14 +7,6 @@ require 'bundler/setup' Bundler.require(:default, :development, :test) -require 'simplecov' -SimpleCov.start do - enable_coverage :branch - add_filter '/test/' -end - -SimpleCov.minimum_coverage 85 - require 'minitest/autorun' require 'test_helpers/app_config' From d7ea817ec029f8282c8eddf373d21c944b29db65 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Thu, 5 Dec 2024 16:33:29 -0800 Subject: [PATCH 47/67] test: Update ActiveSupportLogger tests Test the outcome rather than the presence of variables --- .../logger/patches/active_support_logger_test.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb index 8241596dc4..8b9a1d4f07 100644 --- a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb +++ b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb @@ -23,21 +23,21 @@ after { instrumentation.instance_variable_set(:@installed, false) } describe '#broadcast' do - it 'emits the log to the Rails.logger' do + it 'streams the log to the Rails.logger' do msg = "spruce #{rand(6)}" Rails.logger.debug(msg) assert_match(/#{msg}/, LOG_STREAM.string) end - it 'emits the broadcasted log' do + it 'streams the broadcasted log' do msg = "willow #{rand(6)}" Rails.logger.debug(msg) assert_match(/#{msg}/, BROADCASTED_STREAM.string) end - it 'records the log record' do + it 'emits the log record' do msg = "hemlock #{rand(6)}" Rails.logger.debug(msg) log_record = EXPORTER.emitted_log_records.first @@ -45,12 +45,12 @@ assert_match(/#{msg}/, log_record.body) end - it 'does not add @skip_otel_emit to the initial logger' do - refute Rails.logger.instance_variable_defined?(:@skip_otel_emit) - end + it 'emits the log record only once' do + msg = "juniper #{rand(6)}" + Rails.logger.debug(msg) - it 'adds @skip_otel_emit to broadcasted loggers' do - assert broadcasted_logger.instance_variable_defined?(:@skip_otel_emit) + assert_equal 1, EXPORTER.emitted_log_records.size + assert_match(/#{msg}/, log_record.body) end end end From 59276c3778298fd470ad66c7b0ac5aa7e3cee149 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Thu, 5 Dec 2024 16:38:37 -0800 Subject: [PATCH 48/67] test: Add missing variable --- .../logger/patches/active_support_logger_test.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb index 8b9a1d4f07..9eff49e674 100644 --- a/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb +++ b/instrumentation/logger/test/opentelemetry/instrumentation/logger/patches/active_support_logger_test.rb @@ -49,8 +49,9 @@ msg = "juniper #{rand(6)}" Rails.logger.debug(msg) - assert_equal 1, EXPORTER.emitted_log_records.size - assert_match(/#{msg}/, log_record.body) + log_records = EXPORTER.emitted_log_records + assert_equal 1, log_records.size + assert_match(/#{msg}/, log_records.first.body) end end end From 837b2f687b3e2d2f7c67c0c403b1a1786db7e289 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Mon, 10 Mar 2025 16:23:21 -0700 Subject: [PATCH 49/67] chore: Update gem to include changes from main * move dev dependencies to Gemfile * require 'logger' in test helper * set min version to Ruby 3.1 --- instrumentation/logger/Gemfile | 15 +++++++++++++++ .../opentelemetry-instrumentation-logger.gemspec | 15 ++------------- instrumentation/logger/test/test_helper.rb | 1 + 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/instrumentation/logger/Gemfile b/instrumentation/logger/Gemfile index a952c5002d..78548d6544 100644 --- a/instrumentation/logger/Gemfile +++ b/instrumentation/logger/Gemfile @@ -13,5 +13,20 @@ source 'https://rubygems.org' gemspec group :test do + gem 'appraisal', '~> 2.5' + gem 'bundler', '~> 2.4' + gem 'minitest', '~> 5.0' + gem 'opentelemetry-sdk', '~> 1.0' + gem 'opentelemetry-logs-sdk', '~> 0.1' + gem 'opentelemetry-test-helpers', '~> 0.3' + gem 'rubocop', '~> 1.72.0' + gem 'rubocop-performance', '~> 1.23.0' + gem 'simplecov', '~> 0.22.0' + gem 'webmock', '~> 3.24' + gem 'yard', '~> 0.9' gem 'opentelemetry-instrumentation-base', path: '../base' + if RUBY_VERSION >= '3.4' + gem 'base64' + gem 'mutex_m' + end end diff --git a/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec b/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec index 2e35b241f5..b915ee13cb 100644 --- a/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec +++ b/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec @@ -23,22 +23,11 @@ Gem::Specification.new do |spec| Dir.glob('*.md') + ['LICENSE', '.yardopts'] spec.require_paths = ['lib'] - spec.required_ruby_version = '>= 3.0' + spec.required_ruby_version = '>= 3.1' - spec.add_dependency 'opentelemetry-instrumentation-base', '~> 0.22' + spec.add_dependency 'opentelemetry-instrumentation-base', '~> 0.23.0' spec.add_dependency 'opentelemetry-logs-api', '~> 0.1' - spec.add_development_dependency 'appraisal', '~> 2.5.0' - spec.add_development_dependency 'bundler', '~> 2.4' - spec.add_development_dependency 'minitest', '~> 5.0' - spec.add_development_dependency 'opentelemetry-logs-sdk', '~> 0.1' - spec.add_development_dependency 'opentelemetry-test-helpers', '~> 0.3' - 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 'yard', '~> 0.9' - spec.add_development_dependency 'yard-doctest', '~> 0.1.6' - 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/main/instrumentation/logger' diff --git a/instrumentation/logger/test/test_helper.rb b/instrumentation/logger/test/test_helper.rb index 1857900f62..34e3f0b51b 100644 --- a/instrumentation/logger/test/test_helper.rb +++ b/instrumentation/logger/test/test_helper.rb @@ -4,6 +4,7 @@ # # SPDX-License-Identifier: Apache-2.0 +require 'logger' require 'bundler/setup' Bundler.require(:default, :development, :test) From 4b9a3f47281a5d51e49d3d0a767ea8a3e82e4df9 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Fri, 15 Aug 2025 15:37:29 -0700 Subject: [PATCH 50/67] chore: Raise rubocop, rubocop-performance versions --- instrumentation/logger/Gemfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/instrumentation/logger/Gemfile b/instrumentation/logger/Gemfile index 78548d6544..071237ca92 100644 --- a/instrumentation/logger/Gemfile +++ b/instrumentation/logger/Gemfile @@ -19,8 +19,8 @@ group :test do gem 'opentelemetry-sdk', '~> 1.0' gem 'opentelemetry-logs-sdk', '~> 0.1' gem 'opentelemetry-test-helpers', '~> 0.3' - gem 'rubocop', '~> 1.72.0' - gem 'rubocop-performance', '~> 1.23.0' + gem 'rubocop', '~> 1.79.0' + gem 'rubocop-performance', '~> 1.25.0' gem 'simplecov', '~> 0.22.0' gem 'webmock', '~> 3.24' gem 'yard', '~> 0.9' From e1b0400b574128ebb2159f8b1d25d314b1532e8c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 12:38:56 -0700 Subject: [PATCH 51/67] chore: bump ossf/scorecard-action from 2.4.2 to 2.4.3 (#1709) Bumps [ossf/scorecard-action](https://github.com/ossf/scorecard-action) from 2.4.2 to 2.4.3. - [Release notes](https://github.com/ossf/scorecard-action/releases) - [Changelog](https://github.com/ossf/scorecard-action/blob/main/RELEASE.md) - [Commits](https://github.com/ossf/scorecard-action/compare/05b42c624433fc40578a4040d5cf5e36ddca8cde...4eaacf0543bb3f2c246792bd56e8cdeffafb205a) --- updated-dependencies: - dependency-name: ossf/scorecard-action dependency-version: 2.4.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Kayla Reopelle <87386821+kaylareopelle@users.noreply.github.com> --- .github/workflows/ossf-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index be630bf506..88bf71c505 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -24,7 +24,7 @@ jobs: with: persist-credentials: false - - uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2 + - uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 with: results_file: results.sarif results_format: sarif From 9fda92a18df9acfd18dc928ab279788fa326c074 Mon Sep 17 00:00:00 2001 From: Jonathan Lee <107072447+jj22ee@users.noreply.github.com> Date: Wed, 1 Oct 2025 12:43:50 -0700 Subject: [PATCH 52/67] feat: AWS X-Ray Remote Sampler Part 3 - Add Rate Limiter and Sampling Targets Poller Logic (#1536) * Add Rate Limiter and Sampling Targets Poller Logic * fix test cases related to rate limiter and timecop usage * add example for x-ray sampling on rails * address comments * address comments --------- Co-authored-by: Kayla Reopelle <87386821+kaylareopelle@users.noreply.github.com> --- .../xray_sampling_on_rails_demonstration.ru | 102 +++++++++++ .../sampler/xray/aws_xray_remote_sampler.rb | 59 +++++- .../sampler/xray/fallback_sampler.rb | 9 +- .../sampler/xray/rate_limiter.rb | 48 +++++ .../sampler/xray/rate_limiting_sampler.rb | 43 +++++ .../opentelemetry/sampler/xray/rule_cache.rb | 46 +++++ .../sampler/xray/sampling_rule_applier.rb | 60 +++++- .../xray/test/aws_xray_remote_sampler_test.rb | 94 +++++++++- sampler/xray/test/fallback_sampler_test.rb | 111 ++++++++++- sampler/xray/test/rate_limiter_test.rb | 64 +++++++ .../xray/test/rate_limiting_sampler_test.rb | 173 ++++++++++++++++++ sampler/xray/test/rule_cache_test.rb | 93 +++++++++- 12 files changed, 886 insertions(+), 16 deletions(-) create mode 100644 sampler/xray/example/xray_sampling_on_rails_demonstration.ru create mode 100644 sampler/xray/lib/opentelemetry/sampler/xray/rate_limiter.rb create mode 100644 sampler/xray/lib/opentelemetry/sampler/xray/rate_limiting_sampler.rb create mode 100644 sampler/xray/test/rate_limiter_test.rb create mode 100644 sampler/xray/test/rate_limiting_sampler_test.rb diff --git a/sampler/xray/example/xray_sampling_on_rails_demonstration.ru b/sampler/xray/example/xray_sampling_on_rails_demonstration.ru new file mode 100644 index 0000000000..b3c203b3e0 --- /dev/null +++ b/sampler/xray/example/xray_sampling_on_rails_demonstration.ru @@ -0,0 +1,102 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'bundler/inline' + +gemfile(true) do + source 'https://rubygems.org' + + gem 'concurrent-ruby', '1.3.4' + gem 'rails', '~> 7.0.4' + gem 'puma' + + gem 'opentelemetry-sdk' + gem 'opentelemetry-instrumentation-rails' + gem 'opentelemetry-sampler-xray', path: './../' # Use local version of the X-Ray Sampler + # gem 'opentelemetry-sampler-xray' # Use RubyGems version of the X-Ray Sampler +end + +require "action_controller/railtie" +require "action_mailer/railtie" +require "rails/test_unit/railtie" + +class App < Rails::Application + config.root = __dir__ + config.consider_all_requests_local = true + + routes.append do + root to: 'welcome#index' + get "/test" => 'welcome#test' + end +end + +class WelcomeController < ActionController::Base + def index + render inline: 'Successfully called "/" endpoint' + end + + def test + render inline: 'Successfully called "/test" endpoint' + end +end + +ENV['OTEL_TRACES_EXPORTER'] ||= 'console' +ENV['OTEL_SERVICE_NAME'] ||= 'xray-sampler-on-rails-service' + +OpenTelemetry::SDK.configure do |c| + c.use_all +end + +OpenTelemetry.tracer_provider.sampler = OpenTelemetry::Sampler::XRay::AWSXRayRemoteSampler.new(resource:OpenTelemetry::SDK::Resources::Resource.create({ + "service.name"=>"xray-sampler-on-rails-service" +})) + +App.initialize! + +run App + +#### Running and using the Sample App +# To run this example run the `rackup` command with this file +# Example: rackup xray_sampling_on_rails_demonstration.ru +# Navigate to http://localhost:9292/ +# Spans for any requests sampled by the X-Ray Sampler will appear in the console + +#### Required configuration in the OpenTelemetry Collector +# In order for sampling rules to be obtained from AWS X-Ray, the awsproxy extension +# must be configured in the OpenTelemetry Collector, which will use your AWS credentials. +# - https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/extension/awsproxy#aws-proxy +# Without the awsproxy extension, the X-Ray Sampler will use a fallback sampler +# with a sampling strategy of "1 request/second, plus 5% of any additional requests" + +#### Testing out configurable X-Ray Sampling Rules against the "service.name" resource attribute. +# Create a new Sampling Rule with the following matching criteria in AWS CloudWatch Settings for X-Ray Traces. +# - https://console.aws.amazon.com/cloudwatch/home#xray:settings/sampling-rules + # Matching Criteria + # ServiceName = xray-sampler-on-rails-service + # ServiceType = * + # Host = * + # ResourceARN = * + # HTTPMethod = * + # URLPath = * +# For the above matching criteria, try out the following settings to sample or not sample requests +# - Limit to 0r/sec then 0 fixed rate +# - Limit to 1r/sec then 0 fixed rate (May take 30 seconds for this setting to apply) +# - Limit to 0r/sec then 100% fixed rate + +#### Testing out configurable X-Ray Sampling Rules against the "/test" endpoint in this sample app. +# Create a new Sampling Rule with the following matching criteria in AWS CloudWatch Settings for X-Ray Traces. +# - https://console.aws.amazon.com/cloudwatch/home#xray:settings/sampling-rules + # Matching Criteria + # ServiceName = * + # ServiceType = * + # Host = * + # ResourceARN = * + # HTTPMethod = * + # URLPath = /test +# For the above matching criteria, try out the following settings to sample or not sample requests +# - Limit to 0r/sec then 0 fixed rate +# - Limit to 1r/sec then 0 fixed rate (May take 30 seconds for this setting to apply) +# - Limit to 0r/sec then 100% fixed rate \ No newline at end of file diff --git a/sampler/xray/lib/opentelemetry/sampler/xray/aws_xray_remote_sampler.rb b/sampler/xray/lib/opentelemetry/sampler/xray/aws_xray_remote_sampler.rb index 41f051ca2f..3705f9c5ef 100644 --- a/sampler/xray/lib/opentelemetry/sampler/xray/aws_xray_remote_sampler.rb +++ b/sampler/xray/lib/opentelemetry/sampler/xray/aws_xray_remote_sampler.rb @@ -4,14 +4,13 @@ # # SPDX-License-Identifier: Apache-2.0 -require 'net/http' require 'json' require 'opentelemetry/sdk' -require_relative 'sampling_rule' +require_relative 'aws_xray_sampling_client' require_relative 'fallback_sampler' -require_relative 'sampling_rule_applier' require_relative 'rule_cache' -require_relative 'aws_xray_sampling_client' +require_relative 'sampling_rule' +require_relative 'sampling_rule_applier' module OpenTelemetry module Sampler @@ -68,7 +67,8 @@ def initialize(endpoint: '127.0.0.1:2000', polling_interval: DEFAULT_RULES_POLLI # Start the Sampling Rules poller start_sampling_rules_poller - # TODO: Start the Sampling Targets poller + # Start the Sampling Targets poller + start_sampling_targets_poller end def should_sample?(trace_id:, parent_context:, links:, name:, kind:, attributes:) @@ -113,6 +113,15 @@ def start_sampling_rules_poller end end + def start_sampling_targets_poller + @target_poller = Thread.new do + loop do + sleep(((@target_polling_interval * 1000) + @target_polling_jitter_millis) / 1000.0) + retrieve_and_update_sampling_targets + end + end + end + def retrieve_and_update_sampling_rules sampling_rules_response = @sampling_client.fetch_sampling_rules if sampling_rules_response&.body && sampling_rules_response.body != '' @@ -125,6 +134,19 @@ def retrieve_and_update_sampling_rules OpenTelemetry.handle_error(exception: e, message: 'Error occurred when retrieving or updating Sampling Rules') end + def retrieve_and_update_sampling_targets + request_body = { + SamplingStatisticsDocuments: @rule_cache.create_sampling_statistics_documents(@client_id) + } + sampling_targets_response = @sampling_client.fetch_sampling_targets(request_body) + if sampling_targets_response&.body && sampling_targets_response.body != '' + response_body = JSON.parse(sampling_targets_response.body) + update_sampling_targets(response_body) + else + OpenTelemetry.logger.debug('SamplingTargets Response is falsy') + end + end + def update_sampling_rules(response_object) sampling_rules = [] if response_object && response_object['SamplingRuleRecords'] @@ -140,6 +162,33 @@ def update_sampling_rules(response_object) end end + def update_sampling_targets(response_object) + if response_object && response_object['SamplingTargetDocuments'] + target_documents = {} + + response_object['SamplingTargetDocuments'].each do |new_target| + target_documents[new_target['RuleName']] = new_target + end + + refresh_sampling_rules, next_polling_interval = @rule_cache.update_targets( + target_documents, + response_object['LastRuleModification'] + ) + + @target_polling_interval = next_polling_interval + + if refresh_sampling_rules + OpenTelemetry.logger.debug('Performing out-of-band sampling rule polling to fetch updated rules.') + @rule_poller&.kill + start_sampling_rules_poller + end + else + OpenTelemetry.logger.debug('SamplingTargetDocuments from SamplingTargets request is not defined') + end + rescue StandardError => e + OpenTelemetry.logger.debug("Error occurred when updating Sampling Targets: #{e}") + end + class << self def generate_client_id hex_chars = ('0'..'9').to_a + ('a'..'f').to_a diff --git a/sampler/xray/lib/opentelemetry/sampler/xray/fallback_sampler.rb b/sampler/xray/lib/opentelemetry/sampler/xray/fallback_sampler.rb index f25b08052c..75174150a8 100644 --- a/sampler/xray/lib/opentelemetry/sampler/xray/fallback_sampler.rb +++ b/sampler/xray/lib/opentelemetry/sampler/xray/fallback_sampler.rb @@ -4,6 +4,8 @@ # # SPDX-License-Identifier: Apache-2.0 +require_relative 'rate_limiting_sampler' + module OpenTelemetry module Sampler module XRay @@ -11,10 +13,15 @@ module XRay class FallbackSampler def initialize @fixed_rate_sampler = OpenTelemetry::SDK::Trace::Samplers::TraceIdRatioBased.new(0.05) + @rate_limiting_sampler = RateLimitingSampler.new(1) end def should_sample?(trace_id:, parent_context:, links:, name:, kind:, attributes:) - # TODO: implement and use Rate Limiting Sampler + sampling_result = @rate_limiting_sampler.should_sample?( + trace_id: trace_id, parent_context: parent_context, links: links, name: name, kind: kind, attributes: attributes + ) + + return sampling_result if sampling_result.instance_variable_get(:@decision) != OpenTelemetry::SDK::Trace::Samplers::Decision::DROP @fixed_rate_sampler.should_sample?(trace_id: trace_id, parent_context: parent_context, links: links, name: name, kind: kind, attributes: attributes) end diff --git a/sampler/xray/lib/opentelemetry/sampler/xray/rate_limiter.rb b/sampler/xray/lib/opentelemetry/sampler/xray/rate_limiter.rb new file mode 100644 index 0000000000..6baee1dc0a --- /dev/null +++ b/sampler/xray/lib/opentelemetry/sampler/xray/rate_limiter.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +# Copyright OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module Sampler + module XRay + # RateLimiter keeps track of the current reservoir quota balance available (measured via available time) + # If enough time has elapsed, the RateLimiter will allow quota balance to be consumed/taken (decrease available time) + # A RateLimitingSampler uses this RateLimiter to determine if it should sample or not based on the quota balance available. + class RateLimiter + def initialize(quota, max_balance_in_seconds = 1) + @max_balance_millis = max_balance_in_seconds * 1000.0 + @quota = quota + @wallet_floor_millis = Time.now.to_f * 1000 + # current "balance" would be `ceiling - floor` + @lock = Mutex.new + end + + def take(cost = 1) + return false if @quota <= 0 + + quota_per_millis = @quota / 1000.0 + + # assume divide by zero not possible + cost_in_millis = cost / quota_per_millis + + @lock.synchronize do + wallet_ceiling_millis = Time.now.to_f * 1000 + current_balance_millis = wallet_ceiling_millis - @wallet_floor_millis + current_balance_millis = [current_balance_millis, @max_balance_millis].min + pending_remaining_balance_millis = current_balance_millis - cost_in_millis + + if pending_remaining_balance_millis >= 0 + @wallet_floor_millis = wallet_ceiling_millis - pending_remaining_balance_millis + return true + end + + # No changes to the wallet state + false + end + end + end + end + end +end diff --git a/sampler/xray/lib/opentelemetry/sampler/xray/rate_limiting_sampler.rb b/sampler/xray/lib/opentelemetry/sampler/xray/rate_limiting_sampler.rb new file mode 100644 index 0000000000..d5dd093b4f --- /dev/null +++ b/sampler/xray/lib/opentelemetry/sampler/xray/rate_limiting_sampler.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +# Copyright OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require_relative 'rate_limiter' + +module OpenTelemetry + module Sampler + module XRay + # RateLimitingSampler is a Sampler that uses a RateLimiter to determine + # if it should sample or not based on the quota balance available. + class RateLimitingSampler + def initialize(quota) + @quota = quota + @reservoir = RateLimiter.new(quota) + end + + def should_sample?(trace_id:, parent_context:, links:, name:, kind:, attributes:) + tracestate = OpenTelemetry::Trace.current_span(parent_context).context.tracestate + if @reservoir.take(1) + OpenTelemetry::SDK::Trace::Samplers::Result.new( + decision: OpenTelemetry::SDK::Trace::Samplers::Decision::RECORD_AND_SAMPLE, + tracestate: tracestate, + attributes: attributes + ) + else + OpenTelemetry::SDK::Trace::Samplers::Result.new( + decision: OpenTelemetry::SDK::Trace::Samplers::Decision::DROP, + tracestate: tracestate, + attributes: attributes + ) + end + end + + def to_s + "RateLimitingSampler{rate limiting sampling with sampling config of #{@quota} req/sec and 0% of additional requests}" + end + end + end + end +end diff --git a/sampler/xray/lib/opentelemetry/sampler/xray/rule_cache.rb b/sampler/xray/lib/opentelemetry/sampler/xray/rule_cache.rb index 4583fe8397..b1b13f5b41 100644 --- a/sampler/xray/lib/opentelemetry/sampler/xray/rule_cache.rb +++ b/sampler/xray/lib/opentelemetry/sampler/xray/rule_cache.rb @@ -53,6 +53,52 @@ def update_rules(new_rule_appliers) end end + def create_sampling_statistics_documents(client_id) + statistics_documents = [] + + @cache_lock.synchronize do + @rule_appliers.each do |rule| + statistics = rule.snapshot_statistics + now_in_seconds = Time.now.to_i + + sampling_statistics_doc = { + ClientID: client_id, + RuleName: rule.sampling_rule.rule_name, + Timestamp: now_in_seconds, + RequestCount: statistics.request_count, + BorrowCount: statistics.borrow_count, + SampledCount: statistics.sample_count + } + + statistics_documents << sampling_statistics_doc + end + end + + statistics_documents + end + + def update_targets(target_documents, last_rule_modification) + min_polling_interval = nil + next_polling_interval = DEFAULT_TARGET_POLLING_INTERVAL_SECONDS + + @cache_lock.synchronize do + @rule_appliers.each_with_index do |rule, index| + target = target_documents[rule.sampling_rule.rule_name] + if target + @rule_appliers[index] = rule.with_target(target) + min_polling_interval = target['Interval'] if target['Interval'] && (min_polling_interval.nil? || min_polling_interval > target['Interval']) + else + OpenTelemetry.logger.debug('Invalid sampling target: missing rule name') + end + end + + next_polling_interval = min_polling_interval if min_polling_interval + + refresh_sampling_rules = last_rule_modification * 1000 > @last_updated_epoch_millis + return [refresh_sampling_rules, next_polling_interval] + end + end + private def sort_rules_by_priority diff --git a/sampler/xray/lib/opentelemetry/sampler/xray/sampling_rule_applier.rb b/sampler/xray/lib/opentelemetry/sampler/xray/sampling_rule_applier.rb index 0863d738c5..7ace9a6aa0 100644 --- a/sampler/xray/lib/opentelemetry/sampler/xray/sampling_rule_applier.rb +++ b/sampler/xray/lib/opentelemetry/sampler/xray/sampling_rule_applier.rb @@ -9,6 +9,7 @@ require 'date' require_relative 'sampling_rule' require_relative 'statistics' +require_relative 'rate_limiting_sampler' require_relative 'utils' module OpenTelemetry @@ -26,10 +27,24 @@ def initialize(sampling_rule, statistics = OpenTelemetry::Sampler::XRay::Statist @sampling_rule = sampling_rule @fixed_rate_sampler = OpenTelemetry::SDK::Trace::Samplers::TraceIdRatioBased.new(@sampling_rule.fixed_rate) - # TODO: Add Reservoir Sampler (Rate Limiting Sampler) + @reservoir_sampler = if @sampling_rule.reservoir_size.positive? + OpenTelemetry::Sampler::XRay::RateLimitingSampler.new(1) + else + OpenTelemetry::Sampler::XRay::RateLimitingSampler.new(0) + end @reservoir_expiry_time = MAX_DATE_TIME_SECONDS @statistics = statistics + @statistics_lock = Mutex.new + + @statistics.reset_statistics + @borrowing_enabled = true + + apply_target(target) if target + end + + def with_target(target) + self.class.new(@sampling_rule, @statistics, target) end def matches?(attributes, resource) @@ -78,14 +93,21 @@ def matches?(attributes, resource) end def should_sample?(trace_id:, parent_context:, links:, name:, kind:, attributes:) - # TODO: Record Sampling Statistics - + has_borrowed = false result = OpenTelemetry::SDK::Trace::Samplers::Result.new( decision: OpenTelemetry::SDK::Trace::Samplers::Decision::DROP, tracestate: OpenTelemetry::Trace::Tracestate::DEFAULT ) - # TODO: Apply Reservoir Sampling + now = Time.now + reservoir_expired = now >= @reservoir_expiry_time + + unless reservoir_expired + result = @reservoir_sampler.should_sample?( + trace_id: trace_id, parent_context: parent_context, links: links, name: name, kind: kind, attributes: attributes + ) + has_borrowed = @borrowing_enabled && result.instance_variable_get(:@decision) != OpenTelemetry::SDK::Trace::Samplers::Decision::DROP + end if result.instance_variable_get(:@decision) == OpenTelemetry::SDK::Trace::Samplers::Decision::DROP result = @fixed_rate_sampler.should_sample?( @@ -93,11 +115,41 @@ def should_sample?(trace_id:, parent_context:, links:, name:, kind:, attributes: ) end + @statistics_lock.synchronize do + @statistics.sample_count += result.instance_variable_get(:@decision) == OpenTelemetry::SDK::Trace::Samplers::Decision::DROP ? 0 : 1 + @statistics.borrow_count += has_borrowed ? 1 : 0 + @statistics.request_count += 1 + end + result end + def snapshot_statistics + @statistics_lock.synchronize do + statistics_copy = @statistics.dup + @statistics.reset_statistics + return statistics_copy + end + end + private + def apply_target(target) + @borrowing_enabled = false + + @reservoir_sampler = OpenTelemetry::Sampler::XRay::RateLimitingSampler.new(target['ReservoirQuota']) if target['ReservoirQuota'] + + @reservoir_expiry_time = if target['ReservoirQuotaTTL'] + Time.at(target['ReservoirQuotaTTL']) + else + Time.now + end + + return unless target['FixedRate'] + + @fixed_rate_sampler = OpenTelemetry::SDK::Trace::Samplers::TraceIdRatioBased.new(target['FixedRate']) + end + def get_arn(resource, attributes) resource_hash = resource.attribute_enumerator.to_h arn = resource_hash[SEMCONV::Resource::AWS_ECS_CONTAINER_ARN] || diff --git a/sampler/xray/test/aws_xray_remote_sampler_test.rb b/sampler/xray/test/aws_xray_remote_sampler_test.rb index 3de4299cb5..e745b27a5c 100644 --- a/sampler/xray/test/aws_xray_remote_sampler_test.rb +++ b/sampler/xray/test/aws_xray_remote_sampler_test.rb @@ -91,7 +91,14 @@ assert_equal OpenTelemetry::SDK::Trace::Samplers::Decision::DROP, rs.should_sample?(parent_context: nil, trace_id: '3759e988bd862e3fe1be46a994272793', name: 'name', kind: OpenTelemetry::Trace::SpanKind::SERVER, attributes: attributes, links: []).instance_variable_get(:@decision) - # TODO: Run more tests after updating Sampling Targets + rs.instance_variable_get(:@root).instance_variable_get(:@root).send(:retrieve_and_update_sampling_targets) + + assert_equal OpenTelemetry::SDK::Trace::Samplers::Decision::RECORD_AND_SAMPLE, + rs.should_sample?(parent_context: nil, trace_id: '3759e988bd862e3fe1be46a994272793', name: 'name', kind: OpenTelemetry::Trace::SpanKind::SERVER, attributes: attributes, links: []).instance_variable_get(:@decision) + assert_equal OpenTelemetry::SDK::Trace::Samplers::Decision::RECORD_AND_SAMPLE, + rs.should_sample?(parent_context: nil, trace_id: '3759e988bd862e3fe1be46a994272793', name: 'name', kind: OpenTelemetry::Trace::SpanKind::SERVER, attributes: attributes, links: []).instance_variable_get(:@decision) + assert_equal OpenTelemetry::SDK::Trace::Samplers::Decision::RECORD_AND_SAMPLE, + rs.should_sample?(parent_context: nil, trace_id: '3759e988bd862e3fe1be46a994272793', name: 'name', kind: OpenTelemetry::Trace::SpanKind::SERVER, attributes: attributes, links: []).instance_variable_get(:@decision) end it 'generates valid client id' do @@ -119,5 +126,88 @@ def create_spans(sampled_array, thread_id, span_attributes, remote_sampler, numb sampled_array[thread_id] = sampled end - # TODO: Run tests for Reservoir Sampling + it 'test_multithreading_with_large_reservoir' do + stub_request(:post, "http://#{TEST_URL}/GetSamplingRules") + .to_return(status: 200, body: File.read(DATA_DIR_SAMPLING_RULES)) + stub_request(:post, "http://#{TEST_URL}/SamplingTargets") + .to_return(status: 200, body: File.read(DATA_DIR_SAMPLING_TARGETS)) + + rs = OpenTelemetry::Sampler::XRay::AWSXRayRemoteSampler.new( + resource: OpenTelemetry::SDK::Resources::Resource.create({ + 'service.name' => 'test-service-name', + 'cloud.platform' => 'test-cloud-platform' + }) + ) + + attributes = { 'abc' => '1234' } + assert_equal OpenTelemetry::SDK::Trace::Samplers::Decision::DROP, + rs.should_sample?(parent_context: nil, trace_id: '3759e988bd862e3fe1be46a994272793', name: 'name', kind: OpenTelemetry::Trace::SpanKind::SERVER, attributes: attributes, links: []).instance_variable_get(:@decision) + rs.instance_variable_get(:@root).instance_variable_get(:@root).send(:retrieve_and_update_sampling_targets) + + number_of_spans = 100 + thread_count = 100 + sampled_array = Array.new(thread_count, 0) + threads = [] + + thread_count.times do |idx| + threads << Thread.new do + create_spans(sampled_array, idx, attributes, rs, number_of_spans) + end + end + + threads.each(&:join) + sum_sampled = sampled_array.sum + + test_rule_applier = rs.instance_variable_get(:@root).instance_variable_get(:@root).instance_variable_get(:@rule_cache).instance_variable_get(:@rule_appliers)[0] + assert_equal 100_000, test_rule_applier.instance_variable_get(:@reservoir_sampler).instance_variable_get(:@quota) + assert_equal 10_000, sum_sampled + end + + it 'test_multithreading_with_some_reservoir' do + stub_request(:post, "http://#{TEST_URL}/GetSamplingRules") + .to_return(status: 200, body: File.read(DATA_DIR_SAMPLING_RULES)) + stub_request(:post, "http://#{TEST_URL}/SamplingTargets") + .to_return(status: 200, body: File.read(DATA_DIR_SAMPLING_TARGETS)) + + rs = OpenTelemetry::Sampler::XRay::AWSXRayRemoteSampler.new( + resource: OpenTelemetry::SDK::Resources::Resource.create({ + 'service.name' => 'test-service-name', + 'cloud.platform' => 'test-cloud-platform' + }) + ) + + attributes = { 'abc' => 'non-matching attribute value, use default rule' } + assert_equal OpenTelemetry::SDK::Trace::Samplers::Decision::RECORD_AND_SAMPLE, + rs.should_sample?(parent_context: nil, trace_id: '3759e988bd862e3fe1be46a994272793', name: 'name', kind: OpenTelemetry::Trace::SpanKind::SERVER, attributes: attributes, links: []).instance_variable_get(:@decision) + + rs.instance_variable_get(:@root).instance_variable_get(:@root).send(:retrieve_and_update_sampling_targets) + + # Freeze time 1.5 seconds later in the future, but there should only be 1 second worth + # of reservoir available, which amounts to 100 sampled spans in this test. + # Here we will freeze time and pretend all thread jobs start and end at the exact same time, + # given exactly 1 second of available reservoir (100 quota) only. + current_time = Time.now + Timecop.freeze(current_time + 1.5) + + number_of_spans = 100 + thread_count = 100 + sampled_array = Array.new(thread_count, 0) + threads = [] + + thread_count.times do |idx| + threads << Thread.new do + create_spans(sampled_array, idx, attributes, rs, number_of_spans) + end + end + + threads.each(&:join) + sum_sampled = sampled_array.sum + + test_rule_applier = rs.instance_variable_get(:@root).instance_variable_get(:@root).instance_variable_get(:@rule_cache).instance_variable_get(:@rule_appliers)[1] + assert_equal 100, test_rule_applier.instance_variable_get(:@reservoir_sampler).instance_variable_get(:@quota) + assert_equal 100, sum_sampled + + # Return to normal time. + Timecop.return + end end diff --git a/sampler/xray/test/fallback_sampler_test.rb b/sampler/xray/test/fallback_sampler_test.rb index 01f5224b71..a1282fb0d6 100644 --- a/sampler/xray/test/fallback_sampler_test.rb +++ b/sampler/xray/test/fallback_sampler_test.rb @@ -7,7 +7,116 @@ require 'test_helper' describe OpenTelemetry::Sampler::XRay::FallbackSampler do - # TODO: Add tests for Fallback sampler when Rate Limiter is implemented + before do + # Freeze time at the current moment + @current_time = Time.now + end + + after do + # Return to normal time + Timecop.return + end + + it 'test_should_sample' do + Timecop.freeze(@current_time) + sampler = OpenTelemetry::Sampler::XRay::FallbackSampler.new + + sampler.should_sample?(parent_context: nil, trace_id: '3759e988bd862e3fe1be46a994272793', name: 'name', kind: OpenTelemetry::Trace::SpanKind::SERVER, attributes: {}, links: []) + + # 0 seconds passed, 0 quota available + sampled = 0 + 30.times do + if sampler.should_sample?(parent_context: nil, trace_id: '3759e988bd862e3fe1be46a994272793', name: 'name', kind: OpenTelemetry::Trace::SpanKind::SERVER, attributes: {}, + links: []).instance_variable_get(:@decision) != OpenTelemetry::SDK::Trace::Samplers::Decision::DROP + sampled += 1 + end + end + assert_equal 0, sampled + + # 0.4 seconds passed, 0.4 quota available + sampled = 0 + @current_time += 0.4 + Timecop.freeze(@current_time) + 30.times do + if sampler.should_sample?(parent_context: nil, trace_id: '3759e988bd862e3fe1be46a994272793', name: 'name', kind: OpenTelemetry::Trace::SpanKind::SERVER, attributes: {}, + links: []).instance_variable_get(:@decision) != OpenTelemetry::SDK::Trace::Samplers::Decision::DROP + sampled += 1 + end + end + assert_equal 0, sampled + + # Another 0.8 seconds passed, 1 quota available (1.2 quota capped at 1 quota), 1 quota consumed + sampled = 0 + @current_time += 0.8 + Timecop.freeze(@current_time) + 30.times do + if sampler.should_sample?(parent_context: nil, trace_id: '3759e988bd862e3fe1be46a994272793', name: 'name', kind: OpenTelemetry::Trace::SpanKind::SERVER, attributes: {}, + links: []).instance_variable_get(:@decision) != OpenTelemetry::SDK::Trace::Samplers::Decision::DROP + sampled += 1 + end + end + assert_equal 1, sampled + + # Another 1.9 seconds passed, 1 quota available (1.9 quota capped at 1 quota), 1 quota consumed + sampled = 0 + @current_time += 1.9 + Timecop.freeze(@current_time) + 30.times do + if sampler.should_sample?(parent_context: nil, trace_id: '3759e988bd862e3fe1be46a994272793', name: 'name', kind: OpenTelemetry::Trace::SpanKind::SERVER, attributes: {}, + links: []).instance_variable_get(:@decision) != OpenTelemetry::SDK::Trace::Samplers::Decision::DROP + sampled += 1 + end + end + assert_equal 1, sampled + + # Another 0.9 seconds passed, 0.9 quota available, 0 quota consumed + sampled = 0 + @current_time += 0.9 + Timecop.freeze(@current_time) + 30.times do + if sampler.should_sample?(parent_context: nil, trace_id: '3759e988bd862e3fe1be46a994272793', name: 'name', kind: OpenTelemetry::Trace::SpanKind::SERVER, attributes: {}, + links: []).instance_variable_get(:@decision) != OpenTelemetry::SDK::Trace::Samplers::Decision::DROP + sampled += 1 + end + end + assert_equal 0, sampled + + # Another 2.0 seconds passed, 1 quota available (2.0 quota capped at 1 quota), 1 quota consumed + sampled = 0 + @current_time += 2.0 + Timecop.freeze(@current_time) + 30.times do + if sampler.should_sample?(parent_context: nil, trace_id: '3759e988bd862e3fe1be46a994272793', name: 'name', kind: OpenTelemetry::Trace::SpanKind::SERVER, attributes: {}, + links: []).instance_variable_get(:@decision) != OpenTelemetry::SDK::Trace::Samplers::Decision::DROP + sampled += 1 + end + end + assert_equal 1, sampled + + # Another 2.4 seconds passed, 1 quota available (2.4 quota capped at 1 quota), 1 quota consumed + sampled = 0 + @current_time += 2.4 + Timecop.freeze(@current_time) + 30.times do + if sampler.should_sample?(parent_context: nil, trace_id: '3759e988bd862e3fe1be46a994272793', name: 'name', kind: OpenTelemetry::Trace::SpanKind::SERVER, attributes: {}, + links: []).instance_variable_get(:@decision) != OpenTelemetry::SDK::Trace::Samplers::Decision::DROP + sampled += 1 + end + end + assert_equal 1, sampled + + # Another 100 seconds passed, 1 quota available (100 quota capped at 1 quota), 1 quota consumed + sampled = 0 + @current_time += 100 + Timecop.freeze(@current_time) + 30.times do + if sampler.should_sample?(parent_context: nil, trace_id: '3759e988bd862e3fe1be46a994272793', name: 'name', kind: OpenTelemetry::Trace::SpanKind::SERVER, attributes: {}, + links: []).instance_variable_get(:@decision) != OpenTelemetry::SDK::Trace::Samplers::Decision::DROP + sampled += 1 + end + end + assert_equal 1, sampled + end it 'test_to_string' do assert_equal( diff --git a/sampler/xray/test/rate_limiter_test.rb b/sampler/xray/test/rate_limiter_test.rb new file mode 100644 index 0000000000..51d3266fa5 --- /dev/null +++ b/sampler/xray/test/rate_limiter_test.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +# Copyright OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'test_helper' +require 'timecop' + +describe OpenTelemetry::Sampler::XRay::RateLimiter do + before do + # Freeze time at the current moment + @current_time = Time.now + end + + after do + # Return to normal time + Timecop.return + end + + it 'test_take' do + Timecop.freeze(@current_time) + limiter = OpenTelemetry::Sampler::XRay::RateLimiter.new(30, 1) + + # First batch - no quota is available + spent = 0 + 100.times do + spent += 1 if limiter.take(1) + end + assert_equal 0, spent + + # Second batch - should get half the available quota after 0.5 seconds + @current_time += 0.5 + Timecop.freeze(@current_time) + spent = 0 + 100.times do + spent += 1 if limiter.take(1) + end + assert_equal 15, spent + + # Third batch - should get all the available quota after 1 second + @current_time += 1 + Timecop.freeze(@current_time) + spent = 0 + 100.times do + spent += 1 if limiter.take(1) + end + assert_equal 30, spent + end + + it 'test_take_with_zero_quota' do + limiter = OpenTelemetry::Sampler::XRay::RateLimiter.new(0, 1) + + # Zero quota should always return false + refute limiter.take(1) + end + + it 'test_take_with_negative_quota' do + limiter = OpenTelemetry::Sampler::XRay::RateLimiter.new(-5, 1) + + # Negative quota should always return false + refute limiter.take(1) + end +end diff --git a/sampler/xray/test/rate_limiting_sampler_test.rb b/sampler/xray/test/rate_limiting_sampler_test.rb new file mode 100644 index 0000000000..b24bc862f1 --- /dev/null +++ b/sampler/xray/test/rate_limiting_sampler_test.rb @@ -0,0 +1,173 @@ +# frozen_string_literal: true + +# Copyright OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'test_helper' + +describe OpenTelemetry::Sampler::XRay::RateLimitingSampler do + before do + # Freeze time at the current moment + @current_time = Time.now + Timecop.freeze(@current_time) + end + + after do + # Return to normal time + Timecop.return + end + + it 'test_should_sample' do + sampler = OpenTelemetry::Sampler::XRay::RateLimitingSampler.new(30) + + sampled = 0 + 100.times do + next unless sampler.should_sample?(parent_context: nil, + trace_id: '3759e988bd862e3fe1be46a994272793', + name: 'name', + kind: OpenTelemetry::Trace::SpanKind::SERVER, + attributes: {}, + links: []).instance_variable_get(:@decision) != OpenTelemetry::SDK::Trace::Samplers::Decision::DROP + + sampled += 1 + end + assert_equal 0, sampled + + @current_time += 0.5 + Timecop.freeze(@current_time) # Move forward half a second + + sampled = 0 + 100.times do + next unless sampler.should_sample?(parent_context: nil, + trace_id: '3759e988bd862e3fe1be46a994272793', + name: 'name', + kind: OpenTelemetry::Trace::SpanKind::SERVER, + attributes: {}, + links: []).instance_variable_get(:@decision) != OpenTelemetry::SDK::Trace::Samplers::Decision::DROP + + sampled += 1 + end + assert_equal 15, sampled + + @current_time += 1 + Timecop.freeze(@current_time) # Move forward 1 second + + sampled = 0 + 100.times do + next unless sampler.should_sample?(parent_context: nil, + trace_id: '3759e988bd862e3fe1be46a994272793', + name: 'name', + kind: OpenTelemetry::Trace::SpanKind::SERVER, + attributes: {}, + links: []).instance_variable_get(:@decision) != OpenTelemetry::SDK::Trace::Samplers::Decision::DROP + + sampled += 1 + end + assert_equal 30, sampled + + @current_time += 2.5 + Timecop.freeze(@current_time) # Move forward 2.5 more seconds + + sampled = 0 + 100.times do + next unless sampler.should_sample?(parent_context: nil, + trace_id: '3759e988bd862e3fe1be46a994272793', + name: 'name', + kind: OpenTelemetry::Trace::SpanKind::SERVER, + attributes: {}, + links: []).instance_variable_get(:@decision) != OpenTelemetry::SDK::Trace::Samplers::Decision::DROP + + sampled += 1 + end + assert_equal 30, sampled + + @current_time += 1000 + Timecop.freeze(@current_time) # Move forward 1000 seconds + + sampled = 0 + 100.times do + next unless sampler.should_sample?(parent_context: nil, + trace_id: '3759e988bd862e3fe1be46a994272793', + name: 'name', + kind: OpenTelemetry::Trace::SpanKind::SERVER, + attributes: {}, + links: []).instance_variable_get(:@decision) != OpenTelemetry::SDK::Trace::Samplers::Decision::DROP + + sampled += 1 + end + assert_equal 30, sampled + end + + it 'test_should_sample_with_quota_of_one' do + sampler = OpenTelemetry::Sampler::XRay::RateLimitingSampler.new(1) + + sampled = 0 + 50.times do + next unless sampler.should_sample?(parent_context: nil, + trace_id: '3759e988bd862e3fe1be46a994272793', + name: 'name', + kind: OpenTelemetry::Trace::SpanKind::SERVER, + attributes: {}, + links: []).instance_variable_get(:@decision) != OpenTelemetry::SDK::Trace::Samplers::Decision::DROP + + sampled += 1 + end + assert_equal 0, sampled + + @current_time += 0.5 + Timecop.freeze(@current_time) # Move forward half a second + + sampled = 0 + 50.times do + next unless sampler.should_sample?(parent_context: nil, + trace_id: '3759e988bd862e3fe1be46a994272793', + name: 'name', + kind: OpenTelemetry::Trace::SpanKind::SERVER, + attributes: {}, + links: []).instance_variable_get(:@decision) != OpenTelemetry::SDK::Trace::Samplers::Decision::DROP + + sampled += 1 + end + assert_equal 0, sampled + + @current_time += 0.5 + Timecop.freeze(@current_time) # Move forward another half second + + sampled = 0 + 50.times do + next unless sampler.should_sample?(parent_context: nil, + trace_id: '3759e988bd862e3fe1be46a994272793', + name: 'name', + kind: OpenTelemetry::Trace::SpanKind::SERVER, + attributes: {}, + links: []).instance_variable_get(:@decision) != OpenTelemetry::SDK::Trace::Samplers::Decision::DROP + + sampled += 1 + end + assert_equal 1, sampled + + @current_time += 1000 + Timecop.freeze(@current_time) # Move forward 1000 seconds + + sampled = 0 + 50.times do + next unless sampler.should_sample?(parent_context: nil, + trace_id: '3759e988bd862e3fe1be46a994272793', + name: 'name', + kind: OpenTelemetry::Trace::SpanKind::SERVER, + attributes: {}, + links: []).instance_variable_get(:@decision) != OpenTelemetry::SDK::Trace::Samplers::Decision::DROP + + sampled += 1 + end + assert_equal 1, sampled + end + + it 'test_to_string' do + assert_equal( + 'RateLimitingSampler{rate limiting sampling with sampling config of 123 req/sec and 0% of additional requests}', + OpenTelemetry::Sampler::XRay::RateLimitingSampler.new(123).to_s + ) + end +end diff --git a/sampler/xray/test/rule_cache_test.rb b/sampler/xray/test/rule_cache_test.rb index bad80e3fc0..cc7ac1c1fa 100644 --- a/sampler/xray/test/rule_cache_test.rb +++ b/sampler/xray/test/rule_cache_test.rb @@ -24,6 +24,11 @@ def create_rule(name, priority, reservoir_size, fixed_rate) OpenTelemetry::Sampler::XRay::SamplingRuleApplier.new(OpenTelemetry::Sampler::XRay::SamplingRule.new(test_sampling_rule)) end + after do + # Return to normal time + Timecop.return + end + it 'test_cache_updates_and_sorts_rules' do # Set up default rule in rule cache default_rule = create_rule('Default', 10_000, 1, 0.05) @@ -55,12 +60,13 @@ def create_rule(name, priority, reservoir_size, fixed_rate) end it 'test_rule_cache_expiration_logic' do - Timecop.freeze(Time.now) do + current_time = Time.now + Timecop.freeze(current_time) do default_rule = create_rule('Default', 10_000, 1, 0.05) cache = OpenTelemetry::Sampler::XRay::RuleCache.new(OpenTelemetry::SDK::Resources::Resource.create({})) cache.update_rules([default_rule]) - Timecop.travel(2 * 60 * 60) # Travel 2 hours into the future + Timecop.freeze(current_time + (2 * 60 * 60)) # Travel 2 hours into the future assert cache.expired? end end @@ -109,5 +115,86 @@ def create_rule(name, priority, reservoir_size, fixed_rate) assert_equal 'second_rule', rule_appliers[0].sampling_rule.rule_name end - # TODO: Add tests for updating Sampling Targets and getting statistics + it 'test_update_sampling_targets' do + rule1 = create_rule('default', 10_000, 1, 0.05) + rule2 = create_rule('test', 20, 10, 0.2) + cache = OpenTelemetry::Sampler::XRay::RuleCache.new(OpenTelemetry::SDK::Resources::Resource.create({})) + cache.update_rules([rule1, rule2]) + + time = Time.now.to_i + target1 = { + 'FixedRate' => 0.05, + 'Interval' => 15, + 'ReservoirQuota' => 1, + 'ReservoirQuotaTTL' => time + 10, + 'RuleName' => 'default' + } + target2 = { + 'FixedRate' => 0.15, + 'Interval' => 12, + 'ReservoirQuota' => 5, + 'ReservoirQuotaTTL' => time + 10, + 'RuleName' => 'test' + } + target3 = { + 'FixedRate' => 0.15, + 'Interval' => 3, + 'ReservoirQuota' => 5, + 'ReservoirQuotaTTL' => time + 10, + 'RuleName' => 'associated rule does not exist' + } + + target_map = { + 'default' => target1, + 'test' => target2, + 'associated rule does not exist' => target3 + } + + refresh_sampling_rules, next_polling_interval = cache.update_targets(target_map, time - 10) + refute refresh_sampling_rules + assert_equal target2['Interval'], next_polling_interval + + rule_appliers = cache.instance_variable_get(:@rule_appliers) + assert_equal 2, rule_appliers.length + + refresh_sampling_rules_after, = cache.update_targets(target_map, time + 1) + assert refresh_sampling_rules_after + end + + it 'test_get_all_statistics' do + current_time = Time.now + Timecop.freeze(current_time) do + rule1 = create_rule('test', 4, 2, 2.0) + rule2 = create_rule('default', 5, 5, 5.0) + + cache = OpenTelemetry::Sampler::XRay::RuleCache.new(OpenTelemetry::SDK::Resources::Resource.create) + cache.update_rules([rule1, rule2]) + + Timecop.freeze(current_time + 0.001) # Travel 1ms into the future + + client_id = '12345678901234567890abcd' + statistics = cache.create_sampling_statistics_documents(client_id) + + expected_statistics = [ + { + ClientID: client_id, + RuleName: 'test', + Timestamp: Time.now.to_i, + RequestCount: 0, + BorrowCount: 0, + SampledCount: 0 + }, + { + ClientID: client_id, + RuleName: 'default', + Timestamp: Time.now.to_i, + RequestCount: 0, + BorrowCount: 0, + SampledCount: 0 + } + ] + + assert_equal expected_statistics, statistics + end + end end From 8a96a783c3d4f45fbf00bd95bca31f855e1f5768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Silva?= Date: Wed, 1 Oct 2025 23:44:55 +0100 Subject: [PATCH 53/67] fix: unify rack middleware_args (#1660) * refactor: unify rack middleware_args Ensure rack middleware_args are selected consistently based on `OTEL_SEMCONV_STABILITY_OPT_IN` environment variable. * use only middleware_args in sinatra tests --------- Co-authored-by: Ariel Valentin Co-authored-by: Kayla Reopelle <87386821+kaylareopelle@users.noreply.github.com> --- .../instrumentation/action_pack/railtie.rb | 14 +------------- .../instrumentation/rack/instrumentation.rb | 9 ++++++--- .../instrumentation/sinatra/instrumentation.rb | 14 +------------- .../instrumentation/sinatra_dup_http_test.rb | 2 +- .../instrumentation/sinatra_stable_http_test.rb | 2 +- 5 files changed, 10 insertions(+), 31 deletions(-) diff --git a/instrumentation/action_pack/lib/opentelemetry/instrumentation/action_pack/railtie.rb b/instrumentation/action_pack/lib/opentelemetry/instrumentation/action_pack/railtie.rb index 54c47658a5..630aa1e11e 100644 --- a/instrumentation/action_pack/lib/opentelemetry/instrumentation/action_pack/railtie.rb +++ b/instrumentation/action_pack/lib/opentelemetry/instrumentation/action_pack/railtie.rb @@ -11,21 +11,9 @@ module ActionPack class Railtie < ::Rails::Railtie config.before_initialize do |app| OpenTelemetry::Instrumentation::Rack::Instrumentation.instance.install({}) - - stability_opt_in = ENV.fetch('OTEL_SEMCONV_STABILITY_OPT_IN', '') - values = stability_opt_in.split(',').map(&:strip) - - rack_middleware_args = if values.include?('http/dup') - OpenTelemetry::Instrumentation::Rack::Instrumentation.instance.middleware_args_dup - elsif values.include?('http') - OpenTelemetry::Instrumentation::Rack::Instrumentation.instance.middleware_args_stable - else - OpenTelemetry::Instrumentation::Rack::Instrumentation.instance.middleware_args - end - app.middleware.insert_before( 0, - *rack_middleware_args + *OpenTelemetry::Instrumentation::Rack::Instrumentation.instance.middleware_args ) end end diff --git a/instrumentation/rack/lib/opentelemetry/instrumentation/rack/instrumentation.rb b/instrumentation/rack/lib/opentelemetry/instrumentation/rack/instrumentation.rb index 9c3dc10b81..2089db4284 100644 --- a/instrumentation/rack/lib/opentelemetry/instrumentation/rack/instrumentation.rb +++ b/instrumentation/rack/lib/opentelemetry/instrumentation/rack/instrumentation.rb @@ -32,6 +32,11 @@ class Instrumentation < OpenTelemetry::Instrumentation::Base # This option is only valid for applications using Rack 2.0 or greater option :use_rack_events, default: true, validate: :boolean + def middleware_args + patch_type = determine_semconv + send(:"middleware_args_#{patch_type}") + end + # Temporary Helper for Sinatra and ActionPack middleware to use during installation # # @example Default usage @@ -40,7 +45,7 @@ class Instrumentation < OpenTelemetry::Instrumentation::Base # run lambda { |_arg| [200, { 'Content-Type' => 'text/plain' }, body] } # end # @return [Array] consisting of a middleware and arguments used in rack builders - def middleware_args + def middleware_args_old if config.fetch(:use_rack_events, false) == true && defined?(OpenTelemetry::Instrumentation::Rack::Middlewares::Old::EventHandler) [::Rack::Events, [OpenTelemetry::Instrumentation::Rack::Middlewares::Old::EventHandler.new]] else @@ -48,8 +53,6 @@ def middleware_args end end - alias middleware_args_old middleware_args - def middleware_args_dup if config.fetch(:use_rack_events, false) == true && defined?(OpenTelemetry::Instrumentation::Rack::Middlewares::Dup::EventHandler) [::Rack::Events, [OpenTelemetry::Instrumentation::Rack::Middlewares::Dup::EventHandler.new]] diff --git a/instrumentation/sinatra/lib/opentelemetry/instrumentation/sinatra/instrumentation.rb b/instrumentation/sinatra/lib/opentelemetry/instrumentation/sinatra/instrumentation.rb index b1ed53e7a7..bd7b85ad60 100644 --- a/instrumentation/sinatra/lib/opentelemetry/instrumentation/sinatra/instrumentation.rb +++ b/instrumentation/sinatra/lib/opentelemetry/instrumentation/sinatra/instrumentation.rb @@ -51,19 +51,7 @@ class Instrumentation < OpenTelemetry::Instrumentation::Base end def install_middleware(app) - if config[:install_rack] - stability_opt_in = ENV.fetch('OTEL_SEMCONV_STABILITY_OPT_IN', '') - values = stability_opt_in.split(',').map(&:strip) - - if values.include?('http/dup') - app.use(*OpenTelemetry::Instrumentation::Rack::Instrumentation.instance.middleware_args_dup) - elsif values.include?('http') - app.use(*OpenTelemetry::Instrumentation::Rack::Instrumentation.instance.middleware_args_stable) - else - app.use(*OpenTelemetry::Instrumentation::Rack::Instrumentation.instance.middleware_args) - end - end - + app.use(*OpenTelemetry::Instrumentation::Rack::Instrumentation.instance.middleware_args) if config[:install_rack] app.use(Middlewares::TracerMiddleware) end end diff --git a/instrumentation/sinatra/test/opentelemetry/instrumentation/sinatra_dup_http_test.rb b/instrumentation/sinatra/test/opentelemetry/instrumentation/sinatra_dup_http_test.rb index 332edc4762..bf75b7265a 100644 --- a/instrumentation/sinatra/test/opentelemetry/instrumentation/sinatra_dup_http_test.rb +++ b/instrumentation/sinatra/test/opentelemetry/instrumentation/sinatra_dup_http_test.rb @@ -219,7 +219,7 @@ class CustomError < StandardError; end let(:app) do apps_to_build = apps Rack::Builder.new do - use(*OpenTelemetry::Instrumentation::Rack::Instrumentation.instance.middleware_args_dup) + use(*OpenTelemetry::Instrumentation::Rack::Instrumentation.instance.middleware_args) apps_to_build.each do |root, app| map root do diff --git a/instrumentation/sinatra/test/opentelemetry/instrumentation/sinatra_stable_http_test.rb b/instrumentation/sinatra/test/opentelemetry/instrumentation/sinatra_stable_http_test.rb index 0d0313e395..150ab5d427 100644 --- a/instrumentation/sinatra/test/opentelemetry/instrumentation/sinatra_stable_http_test.rb +++ b/instrumentation/sinatra/test/opentelemetry/instrumentation/sinatra_stable_http_test.rb @@ -199,7 +199,7 @@ class CustomError < StandardError; end let(:app) do apps_to_build = apps Rack::Builder.new do - use(*OpenTelemetry::Instrumentation::Rack::Instrumentation.instance.middleware_args_stable) + use(*OpenTelemetry::Instrumentation::Rack::Instrumentation.instance.middleware_args) apps_to_build.each do |root, app| map root do From cf123179809808c52fb3606b88757c98e41d7799 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Oct 2025 18:56:01 -0500 Subject: [PATCH 54/67] chore: bump github/codeql-action from 3.30.5 to 3.30.6 (#1714) --- .github/workflows/ossf-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index 88bf71c505..a938221d1b 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -43,6 +43,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard (optional). # Commenting out will disable upload of results to your repo's Code Scanning dashboard - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5 + uses: github/codeql-action/upload-sarif@64d10c13136e1c5bce3e5fbde8d4906eeaafc885 # v3.30.6 with: sarif_file: results.sarif From b1fe9681a1ea563f409737e0a00c3bdae49876b2 Mon Sep 17 00:00:00 2001 From: Dan Corneanu Date: Sun, 5 Oct 2025 03:41:54 +1300 Subject: [PATCH 55/67] docs: Enhance README (#1711) feat: Enhance README Document configuration options for ActiveJob instrumentation. Co-authored-by: Kayla Reopelle <87386821+kaylareopelle@users.noreply.github.com> Co-authored-by: Ariel Valentin --- instrumentation/active_job/README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/instrumentation/active_job/README.md b/instrumentation/active_job/README.md index 52ad445101..fde00053e5 100644 --- a/instrumentation/active_job/README.md +++ b/instrumentation/active_job/README.md @@ -30,6 +30,27 @@ OpenTelemetry::SDK.configure do |c| end ``` +## Configuration Options + +The instrumentation supports the following configuration options: + +- **span_naming:** Determines how span names are generated. + - `:job_class` – Span names are set to ` `. + - `:queue` – Span names are set to ` `. + - Default: `:queue` +- **force_flush:** If enabled, all completed spans are synchronously flushed at + the end of each job execution. This is recommended for job systems that fork + worker processes, such as Resque. + - Default: `false` +- **propagation_style:** Controls how job execution traces are related to the + trace where the job was enqueued. + - `:link` – The job runs in a separate trace, with its initial span linked to + the enqueuing span via a Span Link. + - `:child` – The job runs in the same trace, as a direct child of the + enqueuing span. + - `:none` – No explicit link between the job execution and the enqueuing span. + - Default: `:link` + ## Active Support Instrumentation Earlier versions of this instrumentation relied on registering custom `around_perform` hooks in order to deal with limitations From 2db52ff76daf86ab6963cc1dcf02d7402fa28909 Mon Sep 17 00:00:00 2001 From: Ariel Valentin Date: Mon, 6 Oct 2025 18:28:53 -0500 Subject: [PATCH 56/67] chore: Configure Dependabot to update recursively (#1718) The gemspecs dependencies are currently not being updated automatically by dependabot. This change uses recursive directories instead of only updating the root Gemfile. https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/controlling-dependencies-updated#defining-multiple-locations-for-manifest-files --- .github/dependabot.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b2b3977782..bfa7966420 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,10 +6,6 @@ updates: schedule: interval: daily - package-ecosystem: bundler - directory: "/" + directories: "**/*" schedule: interval: weekly - groups: - all-gems: - patterns: - - "**/Gemfile" From 60a630ce3b73887ed1438edcec6c1ba73101a731 Mon Sep 17 00:00:00 2001 From: Ariel Valentin Date: Mon, 6 Oct 2025 18:50:37 -0500 Subject: [PATCH 57/67] chore: Fix dependabot syntax error (#1719) --- .github/dependabot.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index bfa7966420..7efa35d8d9 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,6 +6,7 @@ updates: schedule: interval: daily - package-ecosystem: bundler - directories: "**/*" + directories: + - "**/*" schedule: interval: weekly From 3e66199e2a2bb9f126609e2a08db3664707c348b Mon Sep 17 00:00:00 2001 From: Ariel Valentin Date: Mon, 6 Oct 2025 19:06:39 -0500 Subject: [PATCH 58/67] revert: Bad dependabot config (#1720) * revert: Fix dependabot syntax error (#1719) This reverts commit 60a630ce3b73887ed1438edcec6c1ba73101a731. * revert: Configure Dependabot to update recursively (#1718) This reverts commit 2db52ff76daf86ab6963cc1dcf02d7402fa28909. --- .github/dependabot.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 7efa35d8d9..b2b3977782 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,7 +6,10 @@ updates: schedule: interval: daily - package-ecosystem: bundler - directories: - - "**/*" + directory: "/" schedule: interval: weekly + groups: + all-gems: + patterns: + - "**/Gemfile" From 653ccd5017cdaa71fbf3cf43b9f8becf35c62003 Mon Sep 17 00:00:00 2001 From: "otelbot-ruby-contrib[bot]" <227239399+otelbot-ruby-contrib[bot]@users.noreply.github.com> Date: Tue, 7 Oct 2025 11:58:48 -0500 Subject: [PATCH 59/67] release: Release 4 gems (#1722) * opentelemetry-instrumentation-action_pack 0.14.1 (was 0.14.0) * opentelemetry-instrumentation-active_job 0.9.2 (was 0.9.1) * opentelemetry-instrumentation-rack 0.28.2 (was 0.28.1) * opentelemetry-instrumentation-sinatra 0.27.1 (was 0.27.0) Co-authored-by: otelbot <197425009+otelbot@users.noreply.github.com> --- instrumentation/action_pack/CHANGELOG.md | 4 ++++ .../lib/opentelemetry/instrumentation/action_pack/version.rb | 2 +- instrumentation/active_job/CHANGELOG.md | 4 ++++ .../lib/opentelemetry/instrumentation/active_job/version.rb | 2 +- instrumentation/rack/CHANGELOG.md | 4 ++++ .../rack/lib/opentelemetry/instrumentation/rack/version.rb | 2 +- instrumentation/sinatra/CHANGELOG.md | 4 ++++ .../lib/opentelemetry/instrumentation/sinatra/version.rb | 2 +- 8 files changed, 20 insertions(+), 4 deletions(-) diff --git a/instrumentation/action_pack/CHANGELOG.md b/instrumentation/action_pack/CHANGELOG.md index 675a46834c..ce8a012fd8 100644 --- a/instrumentation/action_pack/CHANGELOG.md +++ b/instrumentation/action_pack/CHANGELOG.md @@ -1,5 +1,9 @@ # Release History: opentelemetry-instrumentation-action_pack +### v0.14.1 / 2025-10-07 + +* FIXED: Unify rack middleware_args + ### v0.14.0 / 2025-09-30 * ADDED: Bump minimum API Version to 1.7 diff --git a/instrumentation/action_pack/lib/opentelemetry/instrumentation/action_pack/version.rb b/instrumentation/action_pack/lib/opentelemetry/instrumentation/action_pack/version.rb index d7a58852eb..f5ab8ff3ca 100644 --- a/instrumentation/action_pack/lib/opentelemetry/instrumentation/action_pack/version.rb +++ b/instrumentation/action_pack/lib/opentelemetry/instrumentation/action_pack/version.rb @@ -7,7 +7,7 @@ module OpenTelemetry module Instrumentation module ActionPack - VERSION = '0.14.0' + VERSION = '0.14.1' end end end diff --git a/instrumentation/active_job/CHANGELOG.md b/instrumentation/active_job/CHANGELOG.md index 4bc353affa..f7aa25300b 100644 --- a/instrumentation/active_job/CHANGELOG.md +++ b/instrumentation/active_job/CHANGELOG.md @@ -1,5 +1,9 @@ # Release History: opentelemetry-instrumentation-active_job +### v0.9.2 / 2025-10-07 + +* DOCS: Enhance README + ### v0.9.1 / 2025-09-30 * FIXED: Min OTel Ruby API 1.7 diff --git a/instrumentation/active_job/lib/opentelemetry/instrumentation/active_job/version.rb b/instrumentation/active_job/lib/opentelemetry/instrumentation/active_job/version.rb index e116bf0d00..82d1c283a0 100644 --- a/instrumentation/active_job/lib/opentelemetry/instrumentation/active_job/version.rb +++ b/instrumentation/active_job/lib/opentelemetry/instrumentation/active_job/version.rb @@ -7,7 +7,7 @@ module OpenTelemetry module Instrumentation module ActiveJob - VERSION = '0.9.1' + VERSION = '0.9.2' end end end diff --git a/instrumentation/rack/CHANGELOG.md b/instrumentation/rack/CHANGELOG.md index f170f3e604..189fd073b7 100644 --- a/instrumentation/rack/CHANGELOG.md +++ b/instrumentation/rack/CHANGELOG.md @@ -1,5 +1,9 @@ # Release History: opentelemetry-instrumentation-rack +### v0.28.2 / 2025-10-07 + +* FIXED: Unify rack middleware_args + ### v0.28.1 / 2025-09-30 * FIXED: Min OTel Ruby API 1.7 diff --git a/instrumentation/rack/lib/opentelemetry/instrumentation/rack/version.rb b/instrumentation/rack/lib/opentelemetry/instrumentation/rack/version.rb index f53023981c..bed6a41cb7 100644 --- a/instrumentation/rack/lib/opentelemetry/instrumentation/rack/version.rb +++ b/instrumentation/rack/lib/opentelemetry/instrumentation/rack/version.rb @@ -7,7 +7,7 @@ module OpenTelemetry module Instrumentation module Rack - VERSION = '0.28.1' + VERSION = '0.28.2' end end end diff --git a/instrumentation/sinatra/CHANGELOG.md b/instrumentation/sinatra/CHANGELOG.md index 24a31f4985..a01205a8cf 100644 --- a/instrumentation/sinatra/CHANGELOG.md +++ b/instrumentation/sinatra/CHANGELOG.md @@ -1,5 +1,9 @@ # Release History: opentelemetry-instrumentation-sinatra +### v0.27.1 / 2025-10-07 + +* FIXED: Unify rack middleware_args + ### v0.27.0 / 2025-09-30 * ADDED: Bump minimum API Version to 1.7 diff --git a/instrumentation/sinatra/lib/opentelemetry/instrumentation/sinatra/version.rb b/instrumentation/sinatra/lib/opentelemetry/instrumentation/sinatra/version.rb index ddbf6bda49..6538d1342a 100644 --- a/instrumentation/sinatra/lib/opentelemetry/instrumentation/sinatra/version.rb +++ b/instrumentation/sinatra/lib/opentelemetry/instrumentation/sinatra/version.rb @@ -7,7 +7,7 @@ module OpenTelemetry module Instrumentation module Sinatra - VERSION = '0.27.0' + VERSION = '0.27.1' end end end From 9c0a33179f3127c3e5c80acc6d891d41bd9f0df1 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Tue, 7 Oct 2025 13:25:54 -0700 Subject: [PATCH 60/67] Remove logger instrumentation from all --- instrumentation/all/lib/opentelemetry/instrumentation/all.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/instrumentation/all/lib/opentelemetry/instrumentation/all.rb b/instrumentation/all/lib/opentelemetry/instrumentation/all.rb index 4e1c620146..02423f7488 100644 --- a/instrumentation/all/lib/opentelemetry/instrumentation/all.rb +++ b/instrumentation/all/lib/opentelemetry/instrumentation/all.rb @@ -33,7 +33,6 @@ require 'opentelemetry-instrumentation-grpc' require 'opentelemetry-instrumentation-gruf' require 'opentelemetry-instrumentation-http_client' -require 'opentelemetry-instrumentation-logger' require 'opentelemetry-instrumentation-mongo' require 'opentelemetry-instrumentation-mysql2' require 'opentelemetry-instrumentation-net_http' From 9dfcde52f9a46e955a447e00b140fb60ca5c31d2 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Tue, 7 Oct 2025 13:36:46 -0700 Subject: [PATCH 61/67] test: Exclude logger from all Gemfile/tests --- instrumentation/all/Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/all/Gemfile b/instrumentation/all/Gemfile index 4c1159e105..bc9f6d0400 100644 --- a/instrumentation/all/Gemfile +++ b/instrumentation/all/Gemfile @@ -29,7 +29,7 @@ group :test do .sort .each { |dir| gem "opentelemetry-helpers-#{dir}", path: "../../helpers/#{dir}" } - excluded_instrumentations = %w[. .. all] + excluded_instrumentations = %w[. .. all logger] Dir.entries('../') .select { |entry| File.directory?(File.join('../', entry)) } .reject { |entry| excluded_instrumentations.include?(entry) } From 341eec2a57d4d6af804d2a01f7c796ae2f0f26dc Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Tue, 7 Oct 2025 13:39:27 -0700 Subject: [PATCH 62/67] chore: Match dependency-related files with repo * raise min tested Rails version * use constants for Ruby version and post-install message * update internal dep versions for consistency --- instrumentation/logger/Appraisals | 2 +- instrumentation/logger/Gemfile | 4 ++-- .../logger/opentelemetry-instrumentation-logger.gemspec | 6 ++++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/instrumentation/logger/Appraisals b/instrumentation/logger/Appraisals index 52806e8adb..abe715a1c2 100644 --- a/instrumentation/logger/Appraisals +++ b/instrumentation/logger/Appraisals @@ -4,7 +4,7 @@ # # SPDX-License-Identifier: Apache-2.0 -%w[7.0.0 7.1.0].each do |version| +%w[7.1.0 7.2.0].each do |version| appraise "rails-#{version}" do gem 'rails', "~> #{version}" end diff --git a/instrumentation/logger/Gemfile b/instrumentation/logger/Gemfile index 071237ca92..ef7549e066 100644 --- a/instrumentation/logger/Gemfile +++ b/instrumentation/logger/Gemfile @@ -19,8 +19,8 @@ group :test do gem 'opentelemetry-sdk', '~> 1.0' gem 'opentelemetry-logs-sdk', '~> 0.1' gem 'opentelemetry-test-helpers', '~> 0.3' - gem 'rubocop', '~> 1.79.0' - gem 'rubocop-performance', '~> 1.25.0' + gem 'rubocop', '~> 1.80.2' + gem 'rubocop-performance', '~> 1.26.0' gem 'simplecov', '~> 0.22.0' gem 'webmock', '~> 3.24' gem 'yard', '~> 0.9' diff --git a/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec b/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec index b915ee13cb..c0d4e8d669 100644 --- a/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec +++ b/instrumentation/logger/opentelemetry-instrumentation-logger.gemspec @@ -23,9 +23,9 @@ Gem::Specification.new do |spec| Dir.glob('*.md') + ['LICENSE', '.yardopts'] spec.require_paths = ['lib'] - spec.required_ruby_version = '>= 3.1' + spec.required_ruby_version = ">= #{File.read(File.expand_path('../../gemspecs/RUBY_REQUIREMENT', __dir__))}" - spec.add_dependency 'opentelemetry-instrumentation-base', '~> 0.23.0' + spec.add_dependency 'opentelemetry-instrumentation-base', '~> 0.24' spec.add_dependency 'opentelemetry-logs-api', '~> 0.1' if spec.respond_to?(:metadata) @@ -34,4 +34,6 @@ Gem::Specification.new do |spec| 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('../../gemspecs/POST_INSTALL_MESSAGE', __dir__)) end From c1ea64ed09c531876fe28949f2cb405afe5a286a Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Tue, 7 Oct 2025 13:41:17 -0700 Subject: [PATCH 63/67] chore: Remove extra requires from all --- instrumentation/all/lib/opentelemetry/instrumentation/all.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/instrumentation/all/lib/opentelemetry/instrumentation/all.rb b/instrumentation/all/lib/opentelemetry/instrumentation/all.rb index 02423f7488..966ed967df 100644 --- a/instrumentation/all/lib/opentelemetry/instrumentation/all.rb +++ b/instrumentation/all/lib/opentelemetry/instrumentation/all.rb @@ -5,8 +5,6 @@ # SPDX-License-Identifier: Apache-2.0 require 'opentelemetry-instrumentation-anthropic' -require 'opentelemetry-instrumentation-gruf' -require 'opentelemetry-instrumentation-trilogy' require 'opentelemetry-instrumentation-active_support' require 'opentelemetry-instrumentation-action_pack' require 'opentelemetry-instrumentation-active_job' From 29faa2b47cc54ed7856a7db8a95d6501bba635b8 Mon Sep 17 00:00:00 2001 From: Hannah Ramadan <76922290+hannahramadan@users.noreply.github.com> Date: Wed, 8 Oct 2025 10:39:13 -0700 Subject: [PATCH 64/67] feat: introduce sql-processor gem (#1673) * Add files for sql-processor gem * fix: filename change * Update helpers/sql-processor/CHANGELOG.md * Add gem to build matrix and release config --------- Co-authored-by: Kayla Reopelle <87386821+kaylareopelle@users.noreply.github.com> --- .github/workflows/ci-contrib.yml | 1 + .toys/.data/releases.yml | 5 + helpers/sql-processor/.rubocop.yml | 4 + helpers/sql-processor/.yardopts | 9 + helpers/sql-processor/CHANGELOG.md | 1 + helpers/sql-processor/Gemfile | 25 + helpers/sql-processor/LICENSE | 201 +++++ helpers/sql-processor/README.md | 65 ++ helpers/sql-processor/Rakefile | 28 + .../opentelemetry-helpers-sql-processor.rb | 7 + .../lib/opentelemetry/helpers.rb | 14 + .../opentelemetry/helpers/sql_obfuscation.rb | 130 ++++ .../helpers/sql_processor/version.rb | 13 + ...pentelemetry-helpers-sql-processor.gemspec | 38 + .../test/fixtures/sql_obfuscation.json | 685 ++++++++++++++++++ .../test/helpers/sql_obfuscation_test.rb | 100 +++ helpers/sql-processor/test/test_helper.rb | 12 + 17 files changed, 1338 insertions(+) create mode 100644 helpers/sql-processor/.rubocop.yml create mode 100644 helpers/sql-processor/.yardopts create mode 100644 helpers/sql-processor/CHANGELOG.md create mode 100644 helpers/sql-processor/Gemfile create mode 100644 helpers/sql-processor/LICENSE create mode 100644 helpers/sql-processor/README.md create mode 100644 helpers/sql-processor/Rakefile create mode 100644 helpers/sql-processor/lib/opentelemetry-helpers-sql-processor.rb create mode 100644 helpers/sql-processor/lib/opentelemetry/helpers.rb create mode 100644 helpers/sql-processor/lib/opentelemetry/helpers/sql_obfuscation.rb create mode 100644 helpers/sql-processor/lib/opentelemetry/helpers/sql_processor/version.rb create mode 100644 helpers/sql-processor/opentelemetry-helpers-sql-processor.gemspec create mode 100644 helpers/sql-processor/test/fixtures/sql_obfuscation.json create mode 100644 helpers/sql-processor/test/helpers/sql_obfuscation_test.rb create mode 100644 helpers/sql-processor/test/test_helper.rb diff --git a/.github/workflows/ci-contrib.yml b/.github/workflows/ci-contrib.yml index 6bbc510cc6..4ff4d97cc4 100644 --- a/.github/workflows/ci-contrib.yml +++ b/.github/workflows/ci-contrib.yml @@ -27,6 +27,7 @@ jobs: - sql - mysql - sql-obfuscation + - sql-processor os: - ubuntu-latest name: "helpers-${{ matrix.gem }} / ${{ matrix.os }}" diff --git a/.toys/.data/releases.yml b/.toys/.data/releases.yml index 1182c128ac..c3f5bfba4f 100644 --- a/.toys/.data/releases.yml +++ b/.toys/.data/releases.yml @@ -55,6 +55,11 @@ gems: version_rb_path: lib/opentelemetry/helpers/sql_obfuscation/version.rb version_constant: [OpenTelemetry, Helpers, SqlObfuscation, VERSION] + - name: opentelemetry-helpers-sql-processor + directory: helpers/sql-processor + version_rb_path: lib/opentelemetry/helpers/sql_processor/version.rb + version_constant: [OpenTelemetry, Helpers, SqlProcessor, VERSION] + - name: opentelemetry-instrumentation-grape directory: instrumentation/grape version_constant: [OpenTelemetry, Instrumentation, Grape, VERSION] diff --git a/helpers/sql-processor/.rubocop.yml b/helpers/sql-processor/.rubocop.yml new file mode 100644 index 0000000000..4b31975de1 --- /dev/null +++ b/helpers/sql-processor/.rubocop.yml @@ -0,0 +1,4 @@ +inherit_from: ../../.rubocop.yml + +Gemspec/DevelopmentDependencies: + Enabled: false diff --git a/helpers/sql-processor/.yardopts b/helpers/sql-processor/.yardopts new file mode 100644 index 0000000000..6c38f31551 --- /dev/null +++ b/helpers/sql-processor/.yardopts @@ -0,0 +1,9 @@ +--no-private +--title=OpenTelemetry SQL Processor Instrumentation Helpers +--markup=markdown +--main=README.md +./lib/opentelemetry/helpers/**/*.rb +./lib/opentelemetry/helpers.rb +- +README.md +CHANGELOG.md diff --git a/helpers/sql-processor/CHANGELOG.md b/helpers/sql-processor/CHANGELOG.md new file mode 100644 index 0000000000..6e05cdd3d1 --- /dev/null +++ b/helpers/sql-processor/CHANGELOG.md @@ -0,0 +1 @@ +# Release History: opentelemetry-helpers-sql-processor diff --git a/helpers/sql-processor/Gemfile b/helpers/sql-processor/Gemfile new file mode 100644 index 0000000000..f77eaf288e --- /dev/null +++ b/helpers/sql-processor/Gemfile @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +source 'https://rubygems.org' + +gemspec + +group :test do + gem 'bundler', '~> 2.4' + gem 'minitest', '~> 5.0' + gem 'opentelemetry-test-helpers', '~> 0.3' + gem 'rake', '~> 13.0' + gem 'rubocop', '~> 1.79.1' + gem 'rubocop-performance', '~> 1.25.0' + gem 'simplecov', '~> 0.22.0' + gem 'yard', '~> 0.9' + gem 'yard-doctest', '~> 0.1.6' + if RUBY_VERSION >= '3.4' + gem 'base64' + gem 'mutex_m' + end +end diff --git a/helpers/sql-processor/LICENSE b/helpers/sql-processor/LICENSE new file mode 100644 index 0000000000..1ef7dad2c5 --- /dev/null +++ b/helpers/sql-processor/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/helpers/sql-processor/README.md b/helpers/sql-processor/README.md new file mode 100644 index 0000000000..e869796009 --- /dev/null +++ b/helpers/sql-processor/README.md @@ -0,0 +1,65 @@ +# OpenTelemetry Instrumentation Helpers: SQL Processor + +This Ruby gem contains logic to process SQL, including obfuscation. It's intended for use by by gem authors instrumenting SQL adapter libraries, such as mysql2, pg, and trilogy. + +Obfuscation logic is largely drawn from the [New Relic Ruby agent's SQL Obfuscation Helpers module][new-relic-obfuscation-helpers]. + +## Usage + +Add the gem to your instrumentation's gemspec file: + +```ruby +# opentelemetry-instrumentation-your-gem.gemspec + spec.add_dependency 'opentelemetry-helpers-sql-processor' +``` + +Add the gem to your instrumentation's Gemfile: + +```ruby +# Gemfile + +group :test do + gem 'opentelemetry-helpers-sql-processor', path: '../../helpers/sql-processor' +end +``` +## Obfuscation + +Make sure the `Instrumentation` class for your gem contains configuration options for: + +- `:obfuscation_limit`: the length at which the SQL string will not be obfuscated + Example: `option :obfuscation_limit, default: 2000, validate: :integer` + +If you want to add support for a new adapter, update the following constants to include keys for your adapter: + +- `DIALECT_COMPONENTS` +- `CLEANUP_REGEX` + +You must also add a new constant that calls the `generate_regex` method with your adapter's DIALECT_COMPONENTS that is named like `_COMPONENTS_REGEX`, such as: `MYSQL_COMPONENTS_REGEX`. + +Check [New Relic's SQL Obfuscation Helpers module][new-relic-obfuscation-helpers] to see if regular expressions for your adapter already exist. + +### Examples + +To obfuscate sql in your library: + +```ruby +OpenTelemetry::Helpers::SqlObfuscation.obfuscate_sql(sql, obfuscation_limit: config[:obfuscation_limit], adapter: :postgres) +``` + +## How can I get involved? + +The `opentelemetry-helpers-sql-processor` gem source is [on github][repo-github], along with related gems including `opentelemetry-instrumentation-pg` and `opentelemetry-instrumentation-trilogy`. + +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-helpers-sql-processor` gem is distributed under the Apache 2.0 license. See [LICENSE][license-github] for more information. + +[new-relic-obfuscation-helpers]: https://github.com/newrelic/newrelic-ruby-agent/blob/96e7aca22c1c873c0f5fe704a2b3bb19652db68e/lib/new_relic/agent/database/obfuscation_helpers.rb +[repo-github]: https://github.com/open-telemetry/opentelemetry-ruby +[license-github]: https://github.com/open-telemetry/opentelemetry-ruby-contrib/blob/main/LICENSE +[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 diff --git a/helpers/sql-processor/Rakefile b/helpers/sql-processor/Rakefile new file mode 100644 index 0000000000..1a64ba842e --- /dev/null +++ b/helpers/sql-processor/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/helpers/sql-processor/lib/opentelemetry-helpers-sql-processor.rb b/helpers/sql-processor/lib/opentelemetry-helpers-sql-processor.rb new file mode 100644 index 0000000000..a839b7e58d --- /dev/null +++ b/helpers/sql-processor/lib/opentelemetry-helpers-sql-processor.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require_relative 'opentelemetry/helpers' diff --git a/helpers/sql-processor/lib/opentelemetry/helpers.rb b/helpers/sql-processor/lib/opentelemetry/helpers.rb new file mode 100644 index 0000000000..671d2b6e1a --- /dev/null +++ b/helpers/sql-processor/lib/opentelemetry/helpers.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'opentelemetry/helpers/sql_obfuscation' + +module OpenTelemetry + # The helpers module contains functionality shared across multiple + # instrumentation libraries + module Helpers + end +end diff --git a/helpers/sql-processor/lib/opentelemetry/helpers/sql_obfuscation.rb b/helpers/sql-processor/lib/opentelemetry/helpers/sql_obfuscation.rb new file mode 100644 index 0000000000..debb2b7e5c --- /dev/null +++ b/helpers/sql-processor/lib/opentelemetry/helpers/sql_obfuscation.rb @@ -0,0 +1,130 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0module OpenTelemetry + +require 'opentelemetry-common' + +module OpenTelemetry + module Helpers + # + # This module contains SQL obfuscation behavior to share with + # instrumentation for specific database adapters. + # The class uses code from: https://github.com/newrelic/newrelic-ruby-agent/blob/1fca78cc7a087421ad58088d8bea72c0362bc62f/lib/new_relic/agent/database/obfuscation_helpers.rb + # + # To use this in your instrumentation, the `Instrumentation` class for + # your gem must contain configuration options for: + # * `:db_statement` + # Example: + # `option :db_statement, default: :include, validate: %I[omit include obfuscate]` + # * `:obfuscation_limit` + # Example: + # `option :obfuscation_limit, default: 2000, validate: :integer` + # + # If you want to add support for a new adapter, update the following + # constants to include keys for your adapter: + # * DIALECT_COMPONENTS + # * CLEANUP_REGEX + # You must also add a new constant that uses `generate_regex` with your + # adapter's dialect components that is named like + # `_COMPONENTS_REGEX`, such as: `MYSQL_COMPONENTS_REGEX`. + # + # @api public + module SqlObfuscation + module_function + + # From: https://github.com/newrelic/newrelic-ruby-agent/blob/1fca78cc7a087421ad58088d8bea72c0362bc62f/lib/new_relic/agent/database/obfuscation_helpers.rb + COMPONENTS_REGEX_MAP = { + single_quotes: /'(?:[^']|'')*?(?:\\'.*|'(?!'))/, + double_quotes: /"(?:[^"]|"")*?(?:\\".*|"(?!"))/, + dollar_quotes: /(\$(?!\d)[^$]*?\$).*?(?:\1|$)/, + uuids: /\{?(?:[0-9a-fA-F]\-*){32}\}?/, + numeric_literals: /-?\b(?:[0-9]+\.)?[0-9]+([eE][+-]?[0-9]+)?\b/, + boolean_literals: /\b(?:true|false|null)\b/i, + hexadecimal_literals: /0x[0-9a-fA-F]+/, + comments: /(?:#|--).*?(?=\r|\n|$)/i, + multi_line_comments: %r{(?:\/\*.*?\*\/)}m, + oracle_quoted_strings: /q'\[.*?(?:\]'|$)|q'\{.*?(?:\}'|$)|q'\<.*?(?:\>'|$)|q'\(.*?(?:\)'|$)/ + }.freeze + + DIALECT_COMPONENTS = { + default: COMPONENTS_REGEX_MAP.keys, + mysql: %i[single_quotes double_quotes numeric_literals boolean_literals + hexadecimal_literals comments multi_line_comments], + postgres: %i[single_quotes dollar_quotes uuids numeric_literals + boolean_literals comments multi_line_comments], + sqlite: %i[single_quotes numeric_literals boolean_literals hexadecimal_literals + comments multi_line_comments], + oracle: %i[single_quotes oracle_quoted_strings numeric_literals comments + multi_line_comments], + cassandra: %i[single_quotes uuids numeric_literals boolean_literals + hexadecimal_literals comments multi_line_comments] + }.freeze + + PLACEHOLDER = '?' + + # We use these to check whether the query contains any quote characters + # after obfuscation. If so, that's a good indication that the original + # query was malformed, and so our obfuscation can't reliably find + # literals. In such a case, we'll replace the entire query with a + # placeholder. + CLEANUP_REGEX = { + default: %r{'|"|\/\*|\*\/}, + mysql: %r{'|"|\/\*|\*\//}, + postgres: %r{'|\/\*|\*\/|\$(?!\?)/}, + sqlite: %r{'|\/\*|\*\//}, + cassandra: %r{'|\/\*|\*\//}, + oracle: %r{'|\/\*|\*\//} + }.freeze + + # @api private + def generate_regex(dialect) + components = DIALECT_COMPONENTS[dialect] + Regexp.union(components.map { |component| COMPONENTS_REGEX_MAP[component] }) + end + + DEFAULT_COMPONENTS_REGEX = generate_regex(:default) + MYSQL_COMPONENTS_REGEX = generate_regex(:mysql) + POSTGRES_COMPONENTS_REGEX = generate_regex(:postgres) + SQLITE_COMPONENTS_REGEX = generate_regex(:sqlite) + CASSANDRA_COMPONENTS_REGEX = generate_regex(:cassandra) + ORACLE_COMPONENTS_REGEX = generate_regex(:oracle) + + # This is a SQL obfuscation utility intended for use in database adapter instrumentation. + # + # @param sql [String] The SQL to obfuscate. + # @param obfuscation_limit [optional Integer] the length at which the SQL string will not be obfuscated + # @param adapter [optional Symbol] the type of database adapter calling the method. `:default`, `:mysql` and `:postgres` are supported. + # @return [String] The SQL query string where the values are replaced with "?". When the sql statement exceeds the obufscation limit + # the first matched pair from the SQL statement will be returned, with an appended truncation message. If trunaction is unsuccessful, + # a string describing the error will be returned. + # + # @api public + def obfuscate_sql(sql, obfuscation_limit: 2000, adapter: :default) + return "SQL not obfuscated, query exceeds #{obfuscation_limit} characters" if sql.size > obfuscation_limit + + regex = case adapter + when :mysql + MYSQL_COMPONENTS_REGEX + when :postgres + POSTGRES_COMPONENTS_REGEX + else + DEFAULT_COMPONENTS_REGEX + end + + # Original MySQL UTF-8 Encoding Fixes: + # https://github.com/open-telemetry/opentelemetry-ruby-contrib/pull/160 + # https://github.com/open-telemetry/opentelemetry-ruby-contrib/pull/345 + sql = OpenTelemetry::Common::Utilities.utf8_encode(sql, binary: true) + + sql = sql.gsub(regex, PLACEHOLDER) + return 'Failed to obfuscate SQL query - quote characters remained after obfuscation' if CLEANUP_REGEX[adapter].match(sql) + + sql + rescue StandardError => e + OpenTelemetry.handle_error(message: 'Failed to obfuscate SQL', exception: e) + end + end + end +end diff --git a/helpers/sql-processor/lib/opentelemetry/helpers/sql_processor/version.rb b/helpers/sql-processor/lib/opentelemetry/helpers/sql_processor/version.rb new file mode 100644 index 0000000000..825394bc6a --- /dev/null +++ b/helpers/sql-processor/lib/opentelemetry/helpers/sql_processor/version.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module Helpers + module SqlProcessor + VERSION = '0.0.0' + end + end +end diff --git a/helpers/sql-processor/opentelemetry-helpers-sql-processor.gemspec b/helpers/sql-processor/opentelemetry-helpers-sql-processor.gemspec new file mode 100644 index 0000000000..dd3c039a6e --- /dev/null +++ b/helpers/sql-processor/opentelemetry-helpers-sql-processor.gemspec @@ -0,0 +1,38 @@ +# 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 'opentelemetry/helpers/sql_processor/version' + +Gem::Specification.new do |spec| + spec.name = 'opentelemetry-helpers-sql-processor' + spec.version = OpenTelemetry::Helpers::SqlProcessor::VERSION + spec.authors = ['OpenTelemetry Authors'] + spec.email = ['cncf-opentelemetry-contributors@lists.cncf.io'] + + spec.summary = 'SQL Processing Instrumentation Helpers for the OpenTelemetry framework' + spec.description = 'SQL Processing Instrumentation Helpers for the OpenTelemetry framework' + 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', '.yardopts'] + spec.require_paths = ['lib'] + spec.required_ruby_version = ">= #{File.read(File.expand_path('../../gemspecs/RUBY_REQUIREMENT', __dir__))}" + + spec.add_dependency 'opentelemetry-common', '~> 0.21' + + 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/main/helpers/sql-processor' + 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('../../gemspecs/POST_INSTALL_MESSAGE', __dir__)) +end diff --git a/helpers/sql-processor/test/fixtures/sql_obfuscation.json b/helpers/sql-processor/test/fixtures/sql_obfuscation.json new file mode 100644 index 0000000000..9c75b8f168 --- /dev/null +++ b/helpers/sql-processor/test/fixtures/sql_obfuscation.json @@ -0,0 +1,685 @@ +[ + { + "name": "back_quoted_identifiers.mysql", + "obfuscated": [ + "SELECT `t001`.`c2` FROM `t001` WHERE `t001`.`c2` = ? AND c3=? LIMIT ?" + ], + "dialects": [ + "mysql" + ], + "sql": "SELECT `t001`.`c2` FROM `t001` WHERE `t001`.`c2` = 'value' AND c3=\"othervalue\" LIMIT ?" + }, + { + "name": "comment_delimiters_in_double_quoted_strings", + "obfuscated": [ + "SELECT * FROM t WHERE foo=? AND baz=?" + ], + "dialects": [ + "mysql" + ], + "sql": "SELECT * FROM t WHERE foo=\"bar/*\" AND baz=\"whatever */qux\"" + }, + { + "name": "comment_delimiters_in_single_quoted_strings", + "obfuscated": [ + "SELECT * FROM t WHERE foo=? AND baz=?" + ], + "dialects": [ + "mysql", + "postgres", + "oracle", + "cassandra", + "sqlite" + ], + "sql": "SELECT * FROM t WHERE foo='bar/*' AND baz='whatever */qux'" + }, + { + "name": "double_quoted_identifiers.postgres", + "obfuscated": [ + "SELECT \"t001\".\"c2\" FROM \"t001\" WHERE \"t001\".\"c2\" = ? AND c3=? LIMIT ?" + ], + "dialects": [ + "postgres" + ], + "sql": "SELECT \"t001\".\"c2\" FROM \"t001\" WHERE \"t001\".\"c2\" = 'value' AND c3=1234 LIMIT 1" + }, + { + "name": "end_of_line_comment_in_double_quoted_string", + "obfuscated": [ + "SELECT * FROM t WHERE foo=? AND\n baz=?" + ], + "dialects": [ + "mysql" + ], + "sql": "SELECT * FROM t WHERE foo=\"bar--\" AND\n baz=\"qux--\"" + }, + { + "name": "end_of_line_comment_in_single_quoted_string", + "obfuscated": [ + "SELECT * FROM t WHERE foo=? AND\n baz=?" + ], + "dialects": [ + "mysql", + "postgres", + "oracle", + "cassandra", + "sqlite" + ], + "sql": "SELECT * FROM t WHERE foo='bar--' AND\n baz='qux--'" + }, + { + "name": "end_of_query_comment_cstyle", + "obfuscated": [ + "SELECT * FROM foo WHERE bar=? ?", + "SELECT * FROM foo WHERE bar=? " + ], + "dialects": [ + "mysql", + "postgres", + "oracle", + "cassandra", + "sqlite" + ], + "sql": "SELECT * FROM foo WHERE bar='baz' /* Hide Me */" + }, + { + "name": "end_of_query_comment_doubledash", + "obfuscated": [ + "SELECT * FROM foobar WHERE password=?\n?", + "SELECT * FROM foobar WHERE password=?\n" + ], + "dialects": [ + "mysql", + "postgres", + "oracle", + "cassandra", + "sqlite" + ], + "sql": "SELECT * FROM foobar WHERE password='secret2'\n-- No peeking!" + }, + { + "name": "end_of_query_comment_hash", + "obfuscated": [ + "SELECT foo, bar FROM baz WHERE password=? ?", + "SELECT foo, bar FROM baz WHERE password=? " + ], + "dialects": [ + "mysql", + "postgres", + "oracle", + "cassandra", + "sqlite" + ], + "sql": "SELECT foo, bar FROM baz WHERE password='secret2' # Secret" + }, + { + "name": "escape_string_constants.postgres", + "sql": "SELECT \"col1\", \"col2\" from \"table\" WHERE \"col3\"=E'foo\\'bar\\\\baz' AND country=e'foo\\'bar\\\\baz'", + "obfuscated": [ + "SELECT \"col1\", \"col2\" from \"table\" WHERE \"col3\"=E?", + "SELECT \"col1\", \"col2\" from \"table\" WHERE \"col3\"=E? AND country=e?" + ], + "dialects": [ + "postgres" + ], + "comments": [ + "PostgreSQL supports an alternate string quoting mode where backslash escape", + "sequences are interpreted.", + "See: http://www.postgresql.org/docs/9.3/static/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS-ESCAPE" + ] + }, + { + "name": "multiple_literal_types.mysql", + "obfuscated": [ + "INSERT INTO `X` values(?,?, ? , ?, ?)" + ], + "dialects": [ + "mysql" + ], + "sql": "INSERT INTO `X` values(\"test\",0, 1 , 2, 'test')" + }, + { + "name": "numbers_in_identifiers", + "obfuscated": [ + "SELECT c11.col1, c22.col2 FROM table c11, table c22 WHERE value=?" + ], + "dialects": [ + "mysql", + "postgres", + "oracle", + "cassandra", + "sqlite" + ], + "sql": "SELECT c11.col1, c22.col2 FROM table c11, table c22 WHERE value='nothing'" + }, + { + "name": "numeric_literals", + "sql": "INSERT INTO X VALUES(1, 23456, 123.456, 99+100)", + "obfuscated": [ + "INSERT INTO X VALUES(?, ?, ?, ?+?)", + "INSERT INTO X VALUES(?, ?, ?.?, ?+?)" + ], + "dialects": [ + "mysql", + "postgres", + "oracle", + "cassandra", + "sqlite" + ] + }, + { + "name": "string_double_quoted.mysql", + "obfuscated": [ + "SELECT * FROM table WHERE name=? AND value=?" + ], + "dialects": [ + "mysql" + ], + "sql": "SELECT * FROM table WHERE name=\"foo\" AND value=\"don't\"" + }, + { + "name": "string_single_quoted", + "obfuscated": [ + "SELECT * FROM table WHERE name=? AND value = ?" + ], + "dialects": [ + "mysql", + "postgres", + "oracle", + "cassandra", + "sqlite" + ], + "sql": "SELECT * FROM table WHERE name='foo' AND value = 'bar'" + }, + { + "name": "string_with_backslash_and_twin_single_quotes", + "obfuscated": [ + "SELECT * FROM table WHERE col=?" + ], + "dialects": [ + "mysql", + "postgres", + "oracle", + "cassandra", + "sqlite" + ], + "sql": "SELECT * FROM table WHERE col='foo\\''bar'", + "comments": [ + "If backslashes are being ignored in single-quoted strings", + "(standard_conforming_strings=on in PostgreSQL, or NO_BACKSLASH_ESCAPES is on", + "in MySQL), then this is valid SQL." + ] + }, + { + "name": "string_with_embedded_double_quote", + "obfuscated": [ + "SELECT * FROM table WHERE col1=? AND col2=?" + ], + "dialects": [ + "mysql", + "postgres", + "oracle", + "cassandra", + "sqlite" + ], + "sql": "SELECT * FROM table WHERE col1='foo\"bar' AND col2='what\"ever'" + }, + { + "name": "string_with_embedded_newline", + "obfuscated": [ + "select * from accounts where accounts.name != ? order by accounts.name" + ], + "dialects": [ + "mysql", + "postgres", + "oracle", + "cassandra", + "sqlite" + ], + "sql": "select * from accounts where accounts.name != 'dude \n newline' order by accounts.name" + }, + { + "name": "string_with_embedded_single_quote.mysql", + "obfuscated": [ + "SELECT * FROM table WHERE col1=? AND col2=?" + ], + "dialects": [ + "mysql" + ], + "sql": "SELECT * FROM table WHERE col1=\"don't\" AND col2=\"won't\"" + }, + { + "name": "string_with_escaped_quotes.mysql", + "sql": "INSERT INTO X values('', 'jim''s ssn',0, 1 , 'jim''s son''s son', \"\"\"jim''s\"\" hat\", \"\\\"jim''s secret\\\"\")", + "obfuscated": [ + "INSERT INTO X values(?, ?,?, ? , ?, ?, ?", + "INSERT INTO X values(?, ?,?, ? , ?, ?, ?)" + ], + "dialects": [ + "mysql" + ] + }, + { + "name": "string_with_trailing_backslash", + "sql": "SELECT * FROM table WHERE name='foo\\' AND color='blue'", + "obfuscated": [ + "SELECT * FROM table WHERE name=?", + "SELECT * FROM table WHERE name=? AND color=?" + ], + "dialects": [ + "mysql", + "postgres", + "oracle", + "cassandra", + "sqlite" + ], + "comments": [ + "If backslashes are being ignored in single-quoted strings", + "(standard_conforming_strings=on in PostgreSQL, or NO_BACKSLASH_ESCAPES is on", + "in MySQL), then this is valid SQL." + ] + }, + { + "name": "string_with_trailing_escaped_backslash.mysql", + "obfuscated": [ + "SELECT * FROM table WHERE foo=?" + ], + "dialects": [ + "mysql" + ], + "sql": "SELECT * FROM table WHERE foo=\"this string ends with a backslash\\\\\"" + }, + { + "name": "string_with_trailing_escaped_backslash_single_quoted", + "obfuscated": [ + "SELECT * FROM table WHERE foo=?" + ], + "dialects": [ + "mysql", + "postgres", + "oracle", + "cassandra", + "sqlite" + ], + "sql": "SELECT * FROM table WHERE foo='this string ends with a backslash\\\\'" + }, + { + "name": "string_with_trailing_escaped_quote", + "sql": "SELECT * FROM table WHERE name='foo\\'' AND color='blue'", + "obfuscated": [ + "SELECT * FROM table WHERE name=?", + "SELECT * FROM table WHERE name=? AND color=?" + ], + "dialects": [ + "mysql", + "postgres", + "oracle", + "cassandra", + "sqlite" + ] + }, + { + "name": "string_with_twin_single_quotes", + "obfuscated": [ + "INSERT INTO X values(?, ?,?, ? , ?)" + ], + "dialects": [ + "mysql", + "postgres", + "oracle", + "cassandra", + "sqlite" + ], + "sql": "INSERT INTO X values('', 'a''b c',0, 1 , 'd''e f''s h')" + }, + { + "name": "end_of_line_comments_with_quotes", + "sql": "SELECT * FROM t WHERE -- '\n bar='baz' -- '", + "obfuscated": [ + "SELECT * FROM t WHERE ?\n bar=? ?", + "SELECT * FROM t WHERE ?" + ], + "dialects": [ + "mysql", + "postgres", + "oracle", + "cassandra", + "sqlite" + ] + }, + { + "name": "mixed_comments_and_quotes", + "sql": "SELECT * FROM t WHERE /* ' */ \n bar='baz' -- '", + "obfuscated": [ + "SELECT * FROM t WHERE ? \n bar=? ?", + "SELECT * FROM t WHERE ?" + ], + "dialects": [ + "mysql", + "postgres", + "oracle", + "cassandra", + "sqlite" + ] + }, + { + "name": "mixed_quotes_comments_and_newlines", + "sql": "SELECT * FROM t WHERE -- '\n /* ' */ c2='xxx' /* ' */\n c='x\n xx' -- '", + "obfuscated": [ + "SELECT * FROM t WHERE ?\n ? c2=? ?\n c=? ?", + "SELECT * FROM t WHERE ?" + ], + "dialects": [ + "mysql", + "postgres", + "oracle", + "cassandra", + "sqlite" + ] + }, + { + "name": "mixed_quotes_end_of_line_comments", + "sql": "SELECT * FROM t WHERE -- '\n c='x\n xx' -- '", + "obfuscated": [ + "SELECT * FROM t WHERE ?\n c=? ?", + "SELECT * FROM t WHERE ?" + ], + "dialects": [ + "mysql", + "postgres", + "oracle", + "cassandra", + "sqlite" + ] + }, + { + "name": "quote_delimiters_in_comments", + "sql": "SELECT * FROM foo WHERE col='value1' AND /* don't */ col2='value1' /* won't */", + "obfuscated": [ + "SELECT * FROM foo WHERE col=? AND ? col2=? ?", + "SELECT * FROM foo WHERE col=? AND ?" + ], + "dialects": [ + "mysql", + "postgres", + "oracle", + "cassandra", + "sqlite" + ] + }, + { + "name": "malformed/unterminated_double_quoted_string.mysql", + "sql": "SELECT * FROM table WHERE foo='bar' AND baz=\"nothing to see here'", + "dialects": [ + "mysql" + ], + "obfuscated": [ + "?" + ], + "malformed": true + }, + { + "name": "malformed/unterminated_single_quoted_string", + "sql": "SELECT * FROM table WHERE foo='bar' AND baz='nothing to see here", + "dialects": [ + "mysql", + "postgres", + "oracle", + "cassandra", + "sqlite" + ], + "obfuscated": [ + "?" + ], + "malformed": true + }, + { + "name": "dollar_quotes", + "sql": "SELECT * FROM \"foo\" WHERE \"foo\" = $a$dollar quotes can be $b$nested$b$$a$ and bar = 'baz'", + "obfuscated": [ + "SELECT * FROM \"foo\" WHERE \"foo\" = ? and bar = ?" + ], + "dialects": [ + "postgres" + ] + }, + { + "name": "variable_substitution_not_mistaken_for_dollar_quotes", + "sql": "INSERT INTO \"foo\" (\"bar\", \"baz\", \"qux\") VALUES ($1, $2, $3) RETURNING \"id\"", + "obfuscated": [ + "INSERT INTO \"foo\" (\"bar\", \"baz\", \"qux\") VALUES ($?, $?, $?) RETURNING \"id\"" + ], + "dialects": [ + "postgres" + ] + }, + { + "name": "non_quote_escape", + "sql": "select * from foo where bar = 'some\\tthing' and baz = 10", + "obfuscated": [ + "select * from foo where bar = ? and baz = ?" + ], + "dialects": [ + "mysql", + "postgres", + "oracle", + "cassandra", + "sqlite" + ] + }, + { + "name": "end_of_string_backslash_and_line_comment_with_quite", + "sql": "select * from users where user = 'user1\\' password = 'secret 2' -- ->don't count this quote", + "obfuscated": [ + "select * from users where user = ?" + ], + "dialects": [ + "mysql", + "postgres", + "oracle", + "cassandra", + "sqlite" + ] + }, + { + "name": "oracle_bracket_quote", + "sql": "select * from foo where bar=q'[baz's]' and x=5", + "obfuscated": [ + "select * from foo where bar=? and x=?" + ], + "dialects": [ + "oracle" + ] + }, + { + "name": "oracle_brace_quote", + "sql": "select * from foo where bar=q'{baz's}' and x=5", + "obfuscated": [ + "select * from foo where bar=? and x=?" + ], + "dialects": [ + "oracle" + ] + }, + { + "name": "oracle_angle_quote", + "sql": "select * from foo where bar=q'' and x=5", + "obfuscated": [ + "select * from foo where bar=? and x=?" + ], + "dialects": [ + "oracle" + ] + }, + { + "name": "oracle_paren_quote", + "sql": "select * from foo where bar=q'(baz's)' and x=5", + "obfuscated": [ + "select * from foo where bar=? and x=?" + ], + "dialects": [ + "oracle" + ] + }, + { + "name": "cassandra_blobs", + "sql": "select * from foo where bar=0xabcdef123 and x=5", + "obfuscated": [ + "select * from foo where bar=? and x=?" + ], + "dialects": [ + "cassandra", + "sqlite" + ] + }, + { + "name": "hex_literals", + "sql": "select * from foo where bar=0x2F and x=5", + "obfuscated": [ + "select * from foo where bar=? and x=?" + ], + "dialects": [ + "mysql", + "cassandra", + "sqlite" + ] + }, + { + "name": "exponential_literals", + "sql": "select * from foo where bar=1.234e-5 and x=5", + "obfuscated": [ + "select * from foo where bar=? and x=?" + ], + "dialects": [ + "mysql", + "postgres", + "oracle", + "cassandra", + "sqlite" + ] + }, + { + "name": "negative_integer_literals", + "sql": "select * from foo where bar=-1.234e-5 and x=-5", + "obfuscated": [ + "select * from foo where bar=? and x=?" + ], + "dialects": [ + "mysql", + "postgres", + "oracle", + "cassandra", + "sqlite" + ] + }, + { + "name": "uuid", + "sql": "select * from foo where bar=01234567-89ab-cdef-0123-456789abcdef and x=5", + "obfuscated": [ + "select * from foo where bar=? and x=?" + ], + "dialects": [ + "postgres", + "cassandra" + ] + }, + { + "name": "uuid_with_braces", + "sql": "select * from foo where bar={01234567-89ab-cdef-0123-456789abcdef} and x=5", + "obfuscated": [ + "select * from foo where bar=? and x=?" + ], + "dialects": [ + "postgres" + ] + }, + { + "name": "uuid_no_dashes", + "sql": "select * from foo where bar=0123456789abcdef0123456789abcdef and x=5", + "obfuscated": [ + "select * from foo where bar=? and x=?" + ], + "dialects": [ + "postgres" + ] + }, + { + "name": "uuid_random_dashes", + "sql": "select * from foo where bar={012-345678-9abc-def012345678-9abcdef} and x=5", + "obfuscated": [ + "select * from foo where bar=? and x=?" + ], + "dialects": [ + "postgres" + ] + }, + { + "name": "booleans", + "sql": "select * from truestory where bar=true and x=FALSE", + "obfuscated": [ + "select * from truestory where bar=? and x=?" + ], + "dialects": [ + "mysql", + "postgres", + "cassandra", + "sqlite" + ] + }, + { + "name": "in_clause_digits", + "sql": "select * from foo where bar IN (123, 456, 789)", + "obfuscated": [ + "select * from foo where bar IN (?, ?, ?)" + ], + "dialects": [ + "mysql", + "postgres", + "oracle", + "cassandra" + ] + }, + { + "name": "in_clause_strings", + "sql": "select * from foo where bar IN ('asdf', 'fdsa')", + "obfuscated": [ + "select * from foo where bar IN (?, ?)" + ], + "dialects": [ + "mysql", + "postgres", + "oracle", + "cassandra" + ] + }, + { + "name": "prepended_comments_with_quotes.postgres", + "sql": "/*application:Demo,controller:posts,action:update*/ UPDATE \"posts\" SET \"updated_at\" = '2023-11-01 19:02:34.795909' WHERE \"posts\".\"id\" = 3", + "obfuscated": [ + "? UPDATE \"posts\" SET \"updated_at\" = ? WHERE \"posts\".\"id\" = ?" + ], + "dialects": [ + "postgres" + ] + }, + { + "name": "prepended_comments_with_quotes.mysql", + "sql": "/*action='show',application='TrilogyTest',controller='users'*/ SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1", + "obfuscated": [ + "? SELECT `users`.* FROM `users` WHERE `users`.`id` = ? LIMIT ?" + ], + "dialects": [ + "mysql" + ] + }, + { + "name": "prepended_multiline_comments_with_quotes.mysql", + "sql": "/*action='show',\napplication='TrilogyTest',controller='users'*/\nSELECT `users`.*\nFROM `users`\nWHERE `users`.`id` = 1 LIMIT 1", + "obfuscated": [ + "?\nSELECT `users`.*\nFROM `users`\nWHERE `users`.`id` = ? LIMIT ?" + ], + "dialects": [ + "mysql" + ] + } +] diff --git a/helpers/sql-processor/test/helpers/sql_obfuscation_test.rb b/helpers/sql-processor/test/helpers/sql_obfuscation_test.rb new file mode 100644 index 0000000000..39229d47f1 --- /dev/null +++ b/helpers/sql-processor/test/helpers/sql_obfuscation_test.rb @@ -0,0 +1,100 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +# This file is distributed under New Relic's license terms. +# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. + +require_relative '../test_helper' + +class SqlObfuscationTest < Minitest::Test + def test_named_arg_defaults_obfuscates + sql = "SELECT * from users where users.id = 1 and users.email = 'test@test.com'" + expected = 'SELECT * from users where users.id = ? and users.email = ?' + result = OpenTelemetry::Helpers::SqlObfuscation.obfuscate_sql(sql) + + assert_equal(expected, result) + end + + def test_obfuscation_returns_message_when_limit_is_reached + sql = "SELECT * from users where users.id = 1 and users.email = 'test@test.com'" + expected = 'SQL not obfuscated, query exceeds 42 characters' + result = OpenTelemetry::Helpers::SqlObfuscation.obfuscate_sql(sql, obfuscation_limit: 42) + + assert_equal(expected, result) + end + + def test_non_utf_8_encoded_string_obfuscates_with_mysql + sql = "SELECT * from users where users.id = 1 and users.email = 'test@test.com\255'" + expected = 'SELECT * from users where users.id = ? and users.email = ?' + result = OpenTelemetry::Helpers::SqlObfuscation.obfuscate_sql(sql, adapter: :mysql) + + assert_equal(expected, result) + end + + def test_non_utf_8_encoded_string_obfuscates_with_postgres + sql = "SELECT * from users where users.id = 1 and users.email = 'test@test.com\255'" + expected = 'SELECT * from users where users.id = ? and users.email = ?' + result = OpenTelemetry::Helpers::SqlObfuscation.obfuscate_sql(sql, adapter: :postgres) + + assert_equal(expected, result) + end + + def test_statement_with_emoji_encodes_utf_8_and_obfuscates + sql = "SELECT * from users where users.id = 1 and users.email = 'test@😄.com'" + expected = 'SELECT * from users where users.id = ? and users.email = ?' + result = OpenTelemetry::Helpers::SqlObfuscation.obfuscate_sql(sql) + + assert_equal(expected, result) + end + + # The following tests and their corresponding fixture are based on code from + # the New Relic Ruby agent. + # source: https://github.com/newrelic/newrelic-ruby-agent/blob/cb72bb5fab3fb318613421c86863a5ccdd2ff250/test/new_relic/agent/database/sql_obfuscation_test.rb + + FAILED_TO_OBFUSCATE_MESSAGE = 'Failed to obfuscate SQL query - quote characters remained after obfuscation' + + def build_failure_message(statement, dialect, acceptable_outputs, actual_output) + msg = "Failed to obfuscate #{dialect} query correctly.\n" + msg << "Input: #{statement}\n" + if acceptable_outputs.size == 1 + msg << "Expected: #{acceptable_outputs.first}\n" + else + msg << "Acceptable outputs:\n" + acceptable_outputs.each do |output| + msg << " #{output}\n" + end + end + msg << "Actual: #{actual_output}\n" + msg + end + + def self.load_fixture + data = File.read("#{Dir.pwd}/test/fixtures/sql_obfuscation.json") + JSON.parse(data) + end + + load_fixture.each do |test_case| + name = test_case['name'] + query = test_case['sql'] + acceptable_outputs = test_case['obfuscated'] + dialects = test_case['dialects'] + + # If the entire query is obfuscated because it's malformed, we use a + # placeholder message instead of just '?', so add that to the acceptable + # outputs. + acceptable_outputs << FAILED_TO_OBFUSCATE_MESSAGE if test_case['malformed'] + + dialects.each do |dialect| + define_method(:"test_sql_obfuscation_#{name}_#{dialect}") do + actual_obfuscated = OpenTelemetry::Helpers::SqlObfuscation.obfuscate_sql(query, adapter: dialect.to_sym) + message = build_failure_message(query, dialect, acceptable_outputs, actual_obfuscated) + + assert_includes(acceptable_outputs, actual_obfuscated, message) + end + end + end + ## End New Relic tests +end diff --git a/helpers/sql-processor/test/test_helper.rb b/helpers/sql-processor/test/test_helper.rb new file mode 100644 index 0000000000..419b09eb04 --- /dev/null +++ b/helpers/sql-processor/test/test_helper.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'simplecov' +require 'bundler/setup' +Bundler.require(:default, :development, :test) + +require 'minitest/autorun' +require 'opentelemetry-helpers-sql-processor' From 3203be6237c2c99b5f574fe394174b31a3e28512 Mon Sep 17 00:00:00 2001 From: Hannah Ramadan <76922290+hannahramadan@users.noreply.github.com> Date: Wed, 8 Oct 2025 11:04:26 -0700 Subject: [PATCH 65/67] feat!: deprecate sql-obfuscation gem (#1674) * feat!: deprecate gem * Update message * Only one heading for README * Feedback updates * Revert version * Update helpers/sql-obfuscation/CHANGELOG.md Co-authored-by: Kayla Reopelle <87386821+kaylareopelle@users.noreply.github.com> * Update readme * Update helpers/sql-obfuscation/README.md * Update gemspec description --------- Co-authored-by: Kayla Reopelle <87386821+kaylareopelle@users.noreply.github.com> --- helpers/sql-obfuscation/CHANGELOG.md | 6 ++++++ helpers/sql-obfuscation/README.md | 10 +++++++++- .../lib/opentelemetry-helpers-sql-obfuscation.rb | 4 ++++ .../opentelemetry-helpers-sql-obfuscation.gemspec | 4 ++-- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/helpers/sql-obfuscation/CHANGELOG.md b/helpers/sql-obfuscation/CHANGELOG.md index 75d1c3a194..f6365c53a9 100644 --- a/helpers/sql-obfuscation/CHANGELOG.md +++ b/helpers/sql-obfuscation/CHANGELOG.md @@ -1,5 +1,11 @@ # Release History: opentelemetry-helpers-sql-obfuscation +### Deprecation Notice / 2025-10-13 + +* **DEPRECATED:** This gem, `opentelemetry-helpers-sql-obfuscation`, has been replaced by `opentelemetry-helpers-sql-processor`. This is the final release and serves as a transitional package. +* **ACTION REQUIRED:** No action is needed unless you use this gem directly. If you use this gem directly, update your `Gemfile` to use `gem 'opentelemetry-helpers-sql-processor'` instead. +* **SUPPORT ENDING:** `opentelemetry-helpers-sql-obfuscation` will no longer receive updates. + ### v0.3.0 / 2025-01-16 * BREAKING CHANGE: Set minimum supported version to Ruby 3.1 diff --git a/helpers/sql-obfuscation/README.md b/helpers/sql-obfuscation/README.md index de4c436d11..9e9ff223e4 100644 --- a/helpers/sql-obfuscation/README.md +++ b/helpers/sql-obfuscation/README.md @@ -1,4 +1,12 @@ -# OpenTelemetry Instrumentation Helpers: SQL Obfuscation +# Deprecation Notice + +**This gem (`opentelemetry-helpers-sql-obfuscation`) is deprecated and no longer maintained.** + +It has been replaced by **`opentelemetry-helpers-sql-processor`**. + +All future development, bug fixes, and feature releases will occur in the new gem. + +## OpenTelemetry Instrumentation Helpers: SQL Obfuscation This Ruby gem contains logic to obfuscate SQL. It's intended for use by by gem authors instrumenting SQL adapter libraries, such as mysql2, pg, and trilogy. diff --git a/helpers/sql-obfuscation/lib/opentelemetry-helpers-sql-obfuscation.rb b/helpers/sql-obfuscation/lib/opentelemetry-helpers-sql-obfuscation.rb index a839b7e58d..cdbb025bd9 100644 --- a/helpers/sql-obfuscation/lib/opentelemetry-helpers-sql-obfuscation.rb +++ b/helpers/sql-obfuscation/lib/opentelemetry-helpers-sql-obfuscation.rb @@ -5,3 +5,7 @@ # SPDX-License-Identifier: Apache-2.0 require_relative 'opentelemetry/helpers' + +OpenTelemetry.logger.warn <<~WARNING + [DEPRECATION] The 'opentelemetry-helpers-sql-obfuscation' gem has been renamed to 'opentelemetry-helpers-sql-processor'. No action is needed unless you use this gem directly. +WARNING diff --git a/helpers/sql-obfuscation/opentelemetry-helpers-sql-obfuscation.gemspec b/helpers/sql-obfuscation/opentelemetry-helpers-sql-obfuscation.gemspec index 734e507747..3a17a8b369 100644 --- a/helpers/sql-obfuscation/opentelemetry-helpers-sql-obfuscation.gemspec +++ b/helpers/sql-obfuscation/opentelemetry-helpers-sql-obfuscation.gemspec @@ -14,8 +14,8 @@ Gem::Specification.new do |spec| spec.authors = ['OpenTelemetry Authors'] spec.email = ['cncf-opentelemetry-contributors@lists.cncf.io'] - spec.summary = 'SQL Obfuscation Instrumentation Helpers for the OpenTelemetry framework' - spec.description = 'SQL Obfuscation Instrumentation Helpers for the OpenTelemetry framework' + spec.summary = 'This gem is deprecated and no longer maintained. It has been replaced by opentelemetry-helpers-sql-processor.' + spec.description = 'This gem is deprecated and no longer maintained. It has been replaced by opentelemetry-helpers-sql-processor.' spec.homepage = 'https://github.com/open-telemetry/opentelemetry-ruby-contrib' spec.license = 'Apache-2.0' From b2e1cbb3389ce34f8e6b8c390d352c8b9612245c Mon Sep 17 00:00:00 2001 From: Hannah Ramadan <76922290+hannahramadan@users.noreply.github.com> Date: Wed, 8 Oct 2025 11:23:54 -0700 Subject: [PATCH 66/67] fix: yml spacing (#1726) Fix spacing for releases.yml file to release opentelemetry-helpers-sql-processor --- .toys/.data/releases.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.toys/.data/releases.yml b/.toys/.data/releases.yml index c3f5bfba4f..b99c210a65 100644 --- a/.toys/.data/releases.yml +++ b/.toys/.data/releases.yml @@ -55,7 +55,7 @@ gems: version_rb_path: lib/opentelemetry/helpers/sql_obfuscation/version.rb version_constant: [OpenTelemetry, Helpers, SqlObfuscation, VERSION] - - name: opentelemetry-helpers-sql-processor + - name: opentelemetry-helpers-sql-processor directory: helpers/sql-processor version_rb_path: lib/opentelemetry/helpers/sql_processor/version.rb version_constant: [OpenTelemetry, Helpers, SqlProcessor, VERSION] From f65d4e327e801dc66d53fe96fef7f69fac0a24aa Mon Sep 17 00:00:00 2001 From: "otelbot-ruby-contrib[bot]" <227239399+otelbot-ruby-contrib[bot]@users.noreply.github.com> Date: Wed, 8 Oct 2025 12:13:38 -0700 Subject: [PATCH 67/67] release: Release 2 gems (#1727) * release: Release 2 gems * opentelemetry-helpers-sql-obfuscation 0.4.0 (was 0.3.0) * opentelemetry-helpers-sql-processor 0.1.0 (initial release) * chore: Update entry for deprecation notice --------- Co-authored-by: otelbot <197425009+otelbot@users.noreply.github.com> Co-authored-by: Kayla Reopelle --- helpers/sql-obfuscation/CHANGELOG.md | 6 ++++-- .../lib/opentelemetry/helpers/sql_obfuscation/version.rb | 2 +- helpers/sql-processor/CHANGELOG.md | 4 ++++ .../lib/opentelemetry/helpers/sql_processor/version.rb | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/helpers/sql-obfuscation/CHANGELOG.md b/helpers/sql-obfuscation/CHANGELOG.md index f6365c53a9..4fa997dc04 100644 --- a/helpers/sql-obfuscation/CHANGELOG.md +++ b/helpers/sql-obfuscation/CHANGELOG.md @@ -1,8 +1,10 @@ # Release History: opentelemetry-helpers-sql-obfuscation -### Deprecation Notice / 2025-10-13 +### v0.4.0 / 2025-10-08 -* **DEPRECATED:** This gem, `opentelemetry-helpers-sql-obfuscation`, has been replaced by `opentelemetry-helpers-sql-processor`. This is the final release and serves as a transitional package. +## Deprecation Notice + +* **DEPRECATED:** This gem, `opentelemetry-helpers-sql-obfuscation`, has been replaced by `opentelemetry-helpers-sql-processor`. This is the final release and serves as a transitional package. * **ACTION REQUIRED:** No action is needed unless you use this gem directly. If you use this gem directly, update your `Gemfile` to use `gem 'opentelemetry-helpers-sql-processor'` instead. * **SUPPORT ENDING:** `opentelemetry-helpers-sql-obfuscation` will no longer receive updates. diff --git a/helpers/sql-obfuscation/lib/opentelemetry/helpers/sql_obfuscation/version.rb b/helpers/sql-obfuscation/lib/opentelemetry/helpers/sql_obfuscation/version.rb index a8a1fc54a1..3d90c2db07 100644 --- a/helpers/sql-obfuscation/lib/opentelemetry/helpers/sql_obfuscation/version.rb +++ b/helpers/sql-obfuscation/lib/opentelemetry/helpers/sql_obfuscation/version.rb @@ -7,7 +7,7 @@ module OpenTelemetry module Helpers module SqlObfuscation - VERSION = '0.3.0' + VERSION = '0.4.0' end end end diff --git a/helpers/sql-processor/CHANGELOG.md b/helpers/sql-processor/CHANGELOG.md index 6e05cdd3d1..2627104a61 100644 --- a/helpers/sql-processor/CHANGELOG.md +++ b/helpers/sql-processor/CHANGELOG.md @@ -1 +1,5 @@ # Release History: opentelemetry-helpers-sql-processor + +### v0.1.0 / 2025-10-08 + +Initial release. diff --git a/helpers/sql-processor/lib/opentelemetry/helpers/sql_processor/version.rb b/helpers/sql-processor/lib/opentelemetry/helpers/sql_processor/version.rb index 825394bc6a..d54b7c5127 100644 --- a/helpers/sql-processor/lib/opentelemetry/helpers/sql_processor/version.rb +++ b/helpers/sql-processor/lib/opentelemetry/helpers/sql_processor/version.rb @@ -7,7 +7,7 @@ module OpenTelemetry module Helpers module SqlProcessor - VERSION = '0.0.0' + VERSION = '0.1.0' end end end