From 83efa6744da93d042c0e1c46c7509fa59ef57d88 Mon Sep 17 00:00:00 2001 From: James Thompson Date: Mon, 13 Apr 2026 13:34:30 +1000 Subject: [PATCH 1/3] First pass at docs --- CONTRIBUTING.md | 2 +- DEVELOPER.md | 276 ++++++++++++++++++++++++++++++++ instrumentation/CONTRIBUTING.md | 254 ----------------------------- 3 files changed, 277 insertions(+), 255 deletions(-) create mode 100644 DEVELOPER.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7862d24739..4a9e958fa1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -191,7 +191,7 @@ bundle exec rake yard ## Instrumentation author's guide -Please make sure that you review the [Instrumentation author's guide](instrumentation/CONTRIBUTING.md) before submitting a new instrumentation. +Please make sure that you review the [Instrumentation author's guide](instrumentation/CONTRIBUTING.md) & [Developer's guide](DEVELOPER.md) before submitting a new instrumentation. ## Create a pull request diff --git a/DEVELOPER.md b/DEVELOPER.md new file mode 100644 index 0000000000..7e597c1c2c --- /dev/null +++ b/DEVELOPER.md @@ -0,0 +1,276 @@ +# Developer Guide + +## Scaffolding new Gem + +### Using Instrumentation Generator + +This repository contains a script to generate a new instrumentation library. + +The snippet below demonstrates how to generate an instrumentation for the `werewolf` gem, starting from the repository root. + +```console + +$bash opentelemetry-ruby-contrib> bin/instrumentation_generator werewolf + +``` + +The output of the generator shows that it creates a new directory in the `instrumentation` directory using the name of the gem: + +```console + +🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨 + + WARNING: Your gem will *NOT* be tested until you add it to the CI workflows in `.github/workflows/ci.yml`!! + +🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨 + + create instrumentation/werewolf/.rubocop.yml + create instrumentation/werewolf/.yardopts + create instrumentation/werewolf/Appraisals + create instrumentation/werewolf/CHANGELOG.md + create instrumentation/werewolf/Gemfile + create instrumentation/werewolf/LICENSE + create instrumentation/werewolf/opentelemetry-instrumentation-werewolf.gemspec + create instrumentation/werewolf/Rakefile + create instrumentation/werewolf/README.md + create instrumentation/werewolf/lib/opentelemetry-instrumentation-werewolf.rb + create instrumentation/werewolf/lib/opentelemetry/instrumentation.rb + create instrumentation/werewolf/lib/opentelemetry/instrumentation/werewolf.rb + create instrumentation/werewolf/lib/opentelemetry/instrumentation/werewolf/instrumentation.rb + create instrumentation/werewolf/lib/opentelemetry/instrumentation/werewolf/version.rb + create instrumentation/werewolf/test/test_helper.rb + create instrumentation/werewolf/test/opentelemetry/instrumentation/werewolf/instrumentation_test.rb + insert .toys/.data/releases.yml + insert instrumentation/all/Gemfile + insert instrumentation/all/opentelemetry-instrumentation-all.gemspec + insert instrumentation/all/lib/opentelemetry/instrumentation/all.rb + +``` + +### Manually + +TBC + +## Adding gem to processes + +### CI + +To add the gem to the CI processes the gem needs to be added to the `matrix.gem` object of the `.github/workflows/ci-instrumentation.yml` when not using services and if it requires services it goes in `.github/workflows/ci-instrumentation-with-services.yml`: + +```yaml +jobs: + instrumentation: + strategy: + fail-fast: false + matrix: + gem: + - action_pack + - action_view + - active_job + # ... + - werewolf + os: + - ubuntu-latest +``` + +### Labeler + +TBC + +### Component owners + +TBC + +## Testing with Services + +### Adding new Services + +Assuming that the required external service is not already supported, you will need to define it as a infra service in the `/.docker/infra/{{service_name}}` directory. +This directory will contain a single file `compose.yml` which acts as the service definition. + +An example of this service is: + +```yaml +services: + rabbitmq: + image: rabbitmq:4.2.5-alpine@sha256:53e51f8469ef13d0a1a2b03c0d36dcf4efbf13089f552440ec5620ca07f2c64e + ports: + - "5672:5672" +``` + +This compose file when run using docker will produce a single runnable service. +This service needs to be included in the root compose file `docker-compose.yml`. + +The final step in adding a new service is to define a renovate package rule to `.github/renovate.json5` which manages the min major version of the service tested against. + +```json + { + description: "Wait until current major postgres is EoL before updating", + dependencyDashboardCategory: "Min Docker service", + matchUpdateTypes: ["major"], + matchDepNames: ["postgres"], + minimumReleaseAge: "1460 days", + }, +``` + +The `minimumReleaseAge` days value should be calculated based on the expected age of the major version when it becomes the lowest major version which is not end of life. +In the above example, we wait until a major version has been available for 1460 days (4 years) which is calculated based on each major version of Postgres being supported for 5 years with a new major each year. +Hence 5 years - 1 year = 4 years which works out to be the 1460 days. + +### Using service to test + +Once the required infra services have been defined as described in [Adding new services](#adding-new-services), a `compose.yml` needs to be defined within the test folder. + +This `compose.yml` file in most cases will just be: + +```yaml +include: + - ../../../.docker/infra/rabbitmq/docker-compose.yml +``` + +Lastly to ensure that pr's are labelled with the gems affected by changes an infra service, the path to the to infra service needs to be added labeler setup (`.github/labeler.yml`) for the gem as per below. + +```yaml +instrumentation-warewolf: + - changed-files: + - any-glob-to-any-file: + - "instrumentation/warewolf/**" + - ".docker/infra/rabbitmq/**" +``` + +## Showcasing functionality via examples + +Executable examples should be included in the `examples` directory that demonstrate how to use the instrumentation in a real-world scenario. + +We recommend using [Bundler's inline gemfile](https://bundler.io/guides/bundler_in_a_single_file_ruby_script.html) to run the examples. Here is an example from the `grape` instrumentation: + +```ruby + +# frozen_string_literal: true + +require 'bundler/inline' + +gemfile(true) do + source 'https://rubygems.org' + gem 'grape', '~> 1.2' + gem 'opentelemetry-sdk' + gem 'opentelemetry-instrumentation-rack' + gem 'opentelemetry-instrumentation-grape' +end + +# Export traces to console +ENV['OTEL_TRACES_EXPORTER'] ||= 'console' + +OpenTelemetry::SDK.configure do |c| + c.service_name = 'trace_demonstration' + c.use_all # this will only require instrumentation gems it finds that are installed by bundler. +end + +at_exit do + OpenTelemetry.tracer_provider.shutdown +end + +# A basic Grape API example +class ExampleAPI < Grape::API + format :json + + desc 'Return a greeting message' + get :hello do + { message: 'Hello, world!' } + end + + desc 'Return information about a user' + params do + requires :id, type: Integer, desc: 'User ID' + end + get 'users/:id' do + { id: params[:id], name: 'John Doe', email: 'johndoe@example.com' } + end +end + +# Set up fake Rack application +builder = Rack::Builder.app do + # Integration is automatic in web frameworks but plain Rack applications require this line. + # Enable it in your config.ru. + use *OpenTelemetry::Instrumentation::Rack::Instrumentation.instance.middleware_args + run ExampleAPI +end +app = Rack::MockRequest.new(builder) + +app.get('/hello') +app.get('/users/1') + +``` + +## Freezing Gem Development + +### Adding notice to Readme + +#### Upstream abbandoned + +When the upstream library has not seen activity for an extended period of time XYZ months, we will freeze our development efforts on this gem. +When this occurs we are to add the folloing notice to the readme of this gem: + +> [!IMPORTANT] +> +> **Deprecation Notice:** +> This gem is deprecated due to the library ({{library}}) it relies on no longer being maintained. The code remains available for reference, but no further updates or fixes will be provided. Users should consider migrating to actively supported alternatives. + +#### Upstream added native support + +When the upstream library adds native instrumentation, this is an indicator that we can freeze our development efforts on this gem. +When this occurs we are to add the following notice to the readme: + +> [!NOTE] +> +> **Development Frozen:** +> +> {{library}} {{version}}+ includes native OpenTelemetry instrumentation. For the best experience and continued support, we recommend: +> +> - **{{library}} < {{version}}**: Use `{{GEM}}` gem +> - **{{library}} ≥ {{version}}**: Use {{library}}'s built-in OpenTelemetry support (remove `{{GEM}}` gem) +> +> Community instrumentation is compatible with {{library}} versions up to {{version}}. Development of this gem is frozen for newer {{library}} versions in favor of the native integration. + +Where gem is this gem and library is what we are instrumenting. + +### Blocking automated dependency updates + +Currently we use Renovate to provide automated pr's which update our dependencies, however in the scenario where a Gem development is frozen, +we do not want these updates just for this gem. + +To achieve this the path of the gem need to be added to the `ignorePaths` property of `.github/renovate.json5`. + +The key thing is that this will block renovate from updating that gem directory while still allowing all updates to proceed. + +### Pinning infra services + +Due to the nature of using shared service definitions it will be necessary to copy the shared service folder ie `.docker/rabbitmq` into a newly created `.docker` folder at the root of the gem. +Following this copying of the services, the references in the gem's compose files need to be updated to use the local copy. + +This processes ensures that if the service is updated the frozen gem remains using the last version supported/tested. + +### Adding deprecation label to pr's + +To help with PR review, the gem path should be added to the deprecated label block in `.github/labeler.yml` which ensures that if changes to deprecated gems occur, +they are labelled accordingly. + +## Removing Gems + +### Removal Checklist + +TBC + +### Steps to be performed + +When a gem is ready to be removed, there is a couple of additional steps to be performed so that we achieve a complete removal. +These steps are: + +- Remove the gem block from `.github/labeler.yml` +- Remove the gem path from the deprecated label path in `.github/labeler.yml` +- Remove the gem path from the `AllCops.Exclude` property in `.rubocop.yml` +- Remove the gem block from `.toys/.data/releases.yml` +- Remove the gem path from ignore path block in `.github/renovate.json5` +- Close any open issues for which only apply to the gem +- Remove the gem label from any issues which affect multiple gems +- Remove the component from `.github/component_owners.yml` diff --git a/instrumentation/CONTRIBUTING.md b/instrumentation/CONTRIBUTING.md index 843ae80cd7..85d7906916 100644 --- a/instrumentation/CONTRIBUTING.md +++ b/instrumentation/CONTRIBUTING.md @@ -45,51 +45,6 @@ The following steps are required to contribute a new instrumentation library: - Provide executable examples in an `examples` directory 5. Submit a pull request -## Generate the gem - -This repository contains a script to generate a new instrumentation library. - -The snippet below demonstrates how to generate an instrumentation for the `werewolf` gem, starting from the repository root. - -```console - -$bash opentelemetry-ruby-contrib> bin/instrumentation_generator werewolf - -``` - -The output of the generator shows that it creates a new directory in the `instrumentation` directory using the name of the gem: - -```console - -🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨 - - WARNING: Your gem will *NOT* be tested until you add it to the CI workflows in `.github/workflows/ci.yml`!! - -🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨 - - create instrumentation/werewolf/.rubocop.yml - create instrumentation/werewolf/.yardopts - create instrumentation/werewolf/Appraisals - create instrumentation/werewolf/CHANGELOG.md - create instrumentation/werewolf/Gemfile - create instrumentation/werewolf/LICENSE - create instrumentation/werewolf/opentelemetry-instrumentation-werewolf.gemspec - create instrumentation/werewolf/Rakefile - create instrumentation/werewolf/README.md - create instrumentation/werewolf/lib/opentelemetry-instrumentation-werewolf.rb - create instrumentation/werewolf/lib/opentelemetry/instrumentation.rb - create instrumentation/werewolf/lib/opentelemetry/instrumentation/werewolf.rb - create instrumentation/werewolf/lib/opentelemetry/instrumentation/werewolf/instrumentation.rb - create instrumentation/werewolf/lib/opentelemetry/instrumentation/werewolf/version.rb - create instrumentation/werewolf/test/test_helper.rb - create instrumentation/werewolf/test/opentelemetry/instrumentation/werewolf/instrumentation_test.rb - insert .toys/.data/releases.yml - insert instrumentation/all/Gemfile - insert instrumentation/all/opentelemetry-instrumentation-all.gemspec - insert instrumentation/all/lib/opentelemetry/instrumentation/all.rb - -``` - ## Implementation guidelines The original design and implementation of this project was heavily influenced by Datadog's `dd-trace-rb` project. You may refer to the [Datadog Porting Guide](datadog-porting-guide.md) as a reference for implementing instrumentations, however, the following guidelines are specific to OpenTelemetry Ruby: @@ -236,30 +191,6 @@ You should _avoid_ adding options that allow custom code blocks (`type: :callabl You should steer users towards post-processing as part of the [OTel Collector](https://opentelemetry.io/docs/collector/), which has a richer and more powerful toolset, and executes out of the application's critical code path. -## Enable CI - -This project contains multiple CI workflows that execute tests and ensure the gems are installable. - -### Standalone instrumentation - -For standalone instrumentation that does not have any external service dependencies, add the gem to the `/.github/workflows/ci-instrumentation.yml` file under `jobs/instrumentation/strategy/matrix/gem`: - -```yaml -jobs: - instrumentation: - strategy: - fail-fast: false - matrix: - gem: - - action_pack - - action_view - - active_job - # ... - - werewolf - os: - - ubuntu-latest -``` - #### JRuby Compatibility If your gem is incompatible with `JRuby`, you can exclude it from the matrix by adding an entry to the `/.github/workflows/ci-instrumentation-full.yml` file under `jobs/instrumentation/steps/[name="JRuby Filter"]`: @@ -277,127 +208,6 @@ If your gem is incompatible with `JRuby`, you can exclude it from the matrix by true ``` -### External service instrumentations - -Adding jobs for instrumentation with external service dependencies may be a bit more difficult if the job does not already have a similar service configured. - -#### Using Existing Services - -CI is currently configured to support the following services: - -- kafka -- memcached -- mongodb -- mysql -- postgresql -- rabbitmq -- redis - -If your gem depends on one of those services, then great! The next step is to add the gem to matrix in the `/.github/workflows/ci-service-instrumentation.yml` file under `jobs/instrumentation_*/strategy/matrix/gem`: - -```yaml -instrumentation_kafka: - strategy: - fail-fast: false - matrix: - gem: - - racecar - - rdkafka - - ruby_kafka - - werewolf - os: - - ubuntu-latest -``` - -The final step is adding the path to the docker-compose.yml snippet to the path list for your instrumentation in `.github/labeler.yml` which is necessary for the build system. - -#### Adding a New Service - -Assuming your external service is not supported, you may consider adding it as a new job in the `/.github/workflows/ci-service-instrumentation.yml` file, however we will accept new services on a case-by-case basis. - -Add the service container to `jobs/instrumentation_with_services/services` and add the gem to the matrix in `jobs/instrumentation_with_services/strategy/matrix/gem`: - -```yaml -instrumentation_with_services: - strategy: - fail-fast: false - matrix: - gem: - - dalli - - mongo - - werewolf - os: - - ubuntu-latest - services: - # ... - my_service: - image: my_service:latest - # ... -``` - -> :information_source: Please refer to the official [GitHub Actions Documentation](https://docs.github.com/en/actions/using-containerized-services/about-service-containers) for more information on how to add a service container. - -If we determine the service container slows down the test suite significantly, it may make sense to copy the matrix and steps stanzas from an existing instrumentation and update it to use the new service container as a dependency: - -```yaml -instrumentation_silver: - strategy: - fail-fast: false - matrix: - gem: - - werewolf - os: - - ubuntu-latest - name: other / ${{ matrix.gem }} / ${{ matrix.os }} - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v4 - - name: "Test Ruby 3.4" - uses: ./.github/actions/test_gem - with: - gem: "opentelemetry-helpers-${{ matrix.gem }}" - ruby: "3.4" - - name: "Test Ruby 3.3" - uses: ./.github/actions/test_gem - with: - gem: "opentelemetry-instrumentation-${{ matrix.gem }}" - ruby: "3.3" - - name: "Test Ruby 3.2" - uses: ./.github/actions/test_gem - with: - gem: "opentelemetry-instrumentation-${{ matrix.gem }}" - ruby: "3.2" - yard: true - rubocop: true - build: true - - name: "Test JRuby" - uses: ./.github/actions/test_gem - with: - gem: "opentelemetry-instrumentation-${{ matrix.gem }}" - ruby: "jruby-9.4.2.0" - services: - # ... - my_service: - image: my_service:latest - # ... -``` - -The final step in adding a new service is to define a renovate package rule to `.github/renovate.json5` which manages the min major version of the service tested against. - -```json - { - description: "Wait until current major postgres is EoL before updating", - dependencyDashboardCategory: "Min Docker service", - matchUpdateTypes: ["major"], - matchDepNames: ["postgres"], - minimumReleaseAge: "1460 days", - }, -``` - -The `minimumReleaseAge` days value should be calculated based on the expected age of the major version when it becomes the lowest major version which is not end of life. -In the above example, we wait until a major version has been available for 1460 days (4 years) which is calculated based on each major version of Postgres being supported for 5 years with a new major each year. -Hence 5 years - 1 year = 4 years which works out to be the 1460 days. - ## Documentation ### README and Yardoc @@ -415,70 +225,6 @@ In addition to that, there should also be redundant `yardoc` comments in the ent > :information_source: See the `Sidekiq::Instrumentation` [class description](./sidekiq/lib/opentelemetry/instrumentation/sidekiq/instrumentation.rb) for a comprehensive example. -### Examples - -Executable examples should be included in the `examples` directory that demonstrate how to use the instrumentation in a real-world scenario. - -We recommend using [Bundler's inline gemfile](https://bundler.io/guides/bundler_in_a_single_file_ruby_script.html) to run the examples. Here is an example from the `grape` instrumentation: - -```ruby - -# frozen_string_literal: true - -require 'bundler/inline' - -gemfile(true) do - source 'https://rubygems.org' - gem 'grape', '~> 1.2' - gem 'opentelemetry-sdk' - gem 'opentelemetry-instrumentation-rack' - gem 'opentelemetry-instrumentation-grape' -end - -# Export traces to console -ENV['OTEL_TRACES_EXPORTER'] ||= 'console' - -OpenTelemetry::SDK.configure do |c| - c.service_name = 'trace_demonstration' - c.use_all # this will only require instrumentation gems it finds that are installed by bundler. -end - -at_exit do - OpenTelemetry.tracer_provider.shutdown -end - -# A basic Grape API example -class ExampleAPI < Grape::API - format :json - - desc 'Return a greeting message' - get :hello do - { message: 'Hello, world!' } - end - - desc 'Return information about a user' - params do - requires :id, type: Integer, desc: 'User ID' - end - get 'users/:id' do - { id: params[:id], name: 'John Doe', email: 'johndoe@example.com' } - end -end - -# Set up fake Rack application -builder = Rack::Builder.app do - # Integration is automatic in web frameworks but plain Rack applications require this line. - # Enable it in your config.ru. - use *OpenTelemetry::Instrumentation::Rack::Instrumentation.instance.middleware_args - run ExampleAPI -end -app = Rack::MockRequest.new(builder) - -app.get('/hello') -app.get('/users/1') - -``` - ## Submit a pull request You are encouraged to submit a `draft` pull request early in the development process to get feedback from the maintainers, run your tests via CI, and ensure that your changes are in line with the project's goals. From 0b00caefe4e990f32671ffef8bdf3bfca538fe56 Mon Sep 17 00:00:00 2001 From: James Thompson Date: Mon, 13 Apr 2026 15:22:28 +1000 Subject: [PATCH 2/3] Linting --- DEVELOPER.md | 552 +++++++++++++++++++++++++-------------------------- 1 file changed, 276 insertions(+), 276 deletions(-) diff --git a/DEVELOPER.md b/DEVELOPER.md index 7e597c1c2c..a6ac6a066d 100644 --- a/DEVELOPER.md +++ b/DEVELOPER.md @@ -1,276 +1,276 @@ -# Developer Guide - -## Scaffolding new Gem - -### Using Instrumentation Generator - -This repository contains a script to generate a new instrumentation library. - -The snippet below demonstrates how to generate an instrumentation for the `werewolf` gem, starting from the repository root. - -```console - -$bash opentelemetry-ruby-contrib> bin/instrumentation_generator werewolf - -``` - -The output of the generator shows that it creates a new directory in the `instrumentation` directory using the name of the gem: - -```console - -🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨 - - WARNING: Your gem will *NOT* be tested until you add it to the CI workflows in `.github/workflows/ci.yml`!! - -🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨 - - create instrumentation/werewolf/.rubocop.yml - create instrumentation/werewolf/.yardopts - create instrumentation/werewolf/Appraisals - create instrumentation/werewolf/CHANGELOG.md - create instrumentation/werewolf/Gemfile - create instrumentation/werewolf/LICENSE - create instrumentation/werewolf/opentelemetry-instrumentation-werewolf.gemspec - create instrumentation/werewolf/Rakefile - create instrumentation/werewolf/README.md - create instrumentation/werewolf/lib/opentelemetry-instrumentation-werewolf.rb - create instrumentation/werewolf/lib/opentelemetry/instrumentation.rb - create instrumentation/werewolf/lib/opentelemetry/instrumentation/werewolf.rb - create instrumentation/werewolf/lib/opentelemetry/instrumentation/werewolf/instrumentation.rb - create instrumentation/werewolf/lib/opentelemetry/instrumentation/werewolf/version.rb - create instrumentation/werewolf/test/test_helper.rb - create instrumentation/werewolf/test/opentelemetry/instrumentation/werewolf/instrumentation_test.rb - insert .toys/.data/releases.yml - insert instrumentation/all/Gemfile - insert instrumentation/all/opentelemetry-instrumentation-all.gemspec - insert instrumentation/all/lib/opentelemetry/instrumentation/all.rb - -``` - -### Manually - -TBC - -## Adding gem to processes - -### CI - -To add the gem to the CI processes the gem needs to be added to the `matrix.gem` object of the `.github/workflows/ci-instrumentation.yml` when not using services and if it requires services it goes in `.github/workflows/ci-instrumentation-with-services.yml`: - -```yaml -jobs: - instrumentation: - strategy: - fail-fast: false - matrix: - gem: - - action_pack - - action_view - - active_job - # ... - - werewolf - os: - - ubuntu-latest -``` - -### Labeler - -TBC - -### Component owners - -TBC - -## Testing with Services - -### Adding new Services - -Assuming that the required external service is not already supported, you will need to define it as a infra service in the `/.docker/infra/{{service_name}}` directory. -This directory will contain a single file `compose.yml` which acts as the service definition. - -An example of this service is: - -```yaml -services: - rabbitmq: - image: rabbitmq:4.2.5-alpine@sha256:53e51f8469ef13d0a1a2b03c0d36dcf4efbf13089f552440ec5620ca07f2c64e - ports: - - "5672:5672" -``` - -This compose file when run using docker will produce a single runnable service. -This service needs to be included in the root compose file `docker-compose.yml`. - -The final step in adding a new service is to define a renovate package rule to `.github/renovate.json5` which manages the min major version of the service tested against. - -```json - { - description: "Wait until current major postgres is EoL before updating", - dependencyDashboardCategory: "Min Docker service", - matchUpdateTypes: ["major"], - matchDepNames: ["postgres"], - minimumReleaseAge: "1460 days", - }, -``` - -The `minimumReleaseAge` days value should be calculated based on the expected age of the major version when it becomes the lowest major version which is not end of life. -In the above example, we wait until a major version has been available for 1460 days (4 years) which is calculated based on each major version of Postgres being supported for 5 years with a new major each year. -Hence 5 years - 1 year = 4 years which works out to be the 1460 days. - -### Using service to test - -Once the required infra services have been defined as described in [Adding new services](#adding-new-services), a `compose.yml` needs to be defined within the test folder. - -This `compose.yml` file in most cases will just be: - -```yaml -include: - - ../../../.docker/infra/rabbitmq/docker-compose.yml -``` - -Lastly to ensure that pr's are labelled with the gems affected by changes an infra service, the path to the to infra service needs to be added labeler setup (`.github/labeler.yml`) for the gem as per below. - -```yaml -instrumentation-warewolf: - - changed-files: - - any-glob-to-any-file: - - "instrumentation/warewolf/**" - - ".docker/infra/rabbitmq/**" -``` - -## Showcasing functionality via examples - -Executable examples should be included in the `examples` directory that demonstrate how to use the instrumentation in a real-world scenario. - -We recommend using [Bundler's inline gemfile](https://bundler.io/guides/bundler_in_a_single_file_ruby_script.html) to run the examples. Here is an example from the `grape` instrumentation: - -```ruby - -# frozen_string_literal: true - -require 'bundler/inline' - -gemfile(true) do - source 'https://rubygems.org' - gem 'grape', '~> 1.2' - gem 'opentelemetry-sdk' - gem 'opentelemetry-instrumentation-rack' - gem 'opentelemetry-instrumentation-grape' -end - -# Export traces to console -ENV['OTEL_TRACES_EXPORTER'] ||= 'console' - -OpenTelemetry::SDK.configure do |c| - c.service_name = 'trace_demonstration' - c.use_all # this will only require instrumentation gems it finds that are installed by bundler. -end - -at_exit do - OpenTelemetry.tracer_provider.shutdown -end - -# A basic Grape API example -class ExampleAPI < Grape::API - format :json - - desc 'Return a greeting message' - get :hello do - { message: 'Hello, world!' } - end - - desc 'Return information about a user' - params do - requires :id, type: Integer, desc: 'User ID' - end - get 'users/:id' do - { id: params[:id], name: 'John Doe', email: 'johndoe@example.com' } - end -end - -# Set up fake Rack application -builder = Rack::Builder.app do - # Integration is automatic in web frameworks but plain Rack applications require this line. - # Enable it in your config.ru. - use *OpenTelemetry::Instrumentation::Rack::Instrumentation.instance.middleware_args - run ExampleAPI -end -app = Rack::MockRequest.new(builder) - -app.get('/hello') -app.get('/users/1') - -``` - -## Freezing Gem Development - -### Adding notice to Readme - -#### Upstream abbandoned - -When the upstream library has not seen activity for an extended period of time XYZ months, we will freeze our development efforts on this gem. -When this occurs we are to add the folloing notice to the readme of this gem: - -> [!IMPORTANT] -> -> **Deprecation Notice:** -> This gem is deprecated due to the library ({{library}}) it relies on no longer being maintained. The code remains available for reference, but no further updates or fixes will be provided. Users should consider migrating to actively supported alternatives. - -#### Upstream added native support - -When the upstream library adds native instrumentation, this is an indicator that we can freeze our development efforts on this gem. -When this occurs we are to add the following notice to the readme: - -> [!NOTE] -> -> **Development Frozen:** -> -> {{library}} {{version}}+ includes native OpenTelemetry instrumentation. For the best experience and continued support, we recommend: -> -> - **{{library}} < {{version}}**: Use `{{GEM}}` gem -> - **{{library}} ≥ {{version}}**: Use {{library}}'s built-in OpenTelemetry support (remove `{{GEM}}` gem) -> -> Community instrumentation is compatible with {{library}} versions up to {{version}}. Development of this gem is frozen for newer {{library}} versions in favor of the native integration. - -Where gem is this gem and library is what we are instrumenting. - -### Blocking automated dependency updates - -Currently we use Renovate to provide automated pr's which update our dependencies, however in the scenario where a Gem development is frozen, -we do not want these updates just for this gem. - -To achieve this the path of the gem need to be added to the `ignorePaths` property of `.github/renovate.json5`. - -The key thing is that this will block renovate from updating that gem directory while still allowing all updates to proceed. - -### Pinning infra services - -Due to the nature of using shared service definitions it will be necessary to copy the shared service folder ie `.docker/rabbitmq` into a newly created `.docker` folder at the root of the gem. -Following this copying of the services, the references in the gem's compose files need to be updated to use the local copy. - -This processes ensures that if the service is updated the frozen gem remains using the last version supported/tested. - -### Adding deprecation label to pr's - -To help with PR review, the gem path should be added to the deprecated label block in `.github/labeler.yml` which ensures that if changes to deprecated gems occur, -they are labelled accordingly. - -## Removing Gems - -### Removal Checklist - -TBC - -### Steps to be performed - -When a gem is ready to be removed, there is a couple of additional steps to be performed so that we achieve a complete removal. -These steps are: - -- Remove the gem block from `.github/labeler.yml` -- Remove the gem path from the deprecated label path in `.github/labeler.yml` -- Remove the gem path from the `AllCops.Exclude` property in `.rubocop.yml` -- Remove the gem block from `.toys/.data/releases.yml` -- Remove the gem path from ignore path block in `.github/renovate.json5` -- Close any open issues for which only apply to the gem -- Remove the gem label from any issues which affect multiple gems -- Remove the component from `.github/component_owners.yml` +# Developer Guide + +## Scaffolding new Gem + +### Using Instrumentation Generator + +This repository contains a script to generate a new instrumentation library. + +The snippet below demonstrates how to generate an instrumentation for the `werewolf` gem, starting from the repository root. + +```console + +$bash opentelemetry-ruby-contrib> bin/instrumentation_generator werewolf + +``` + +The output of the generator shows that it creates a new directory in the `instrumentation` directory using the name of the gem: + +```console + +🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨 + + WARNING: Your gem will *NOT* be tested until you add it to the CI workflows in `.github/workflows/ci.yml`!! + +🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨 + + create instrumentation/werewolf/.rubocop.yml + create instrumentation/werewolf/.yardopts + create instrumentation/werewolf/Appraisals + create instrumentation/werewolf/CHANGELOG.md + create instrumentation/werewolf/Gemfile + create instrumentation/werewolf/LICENSE + create instrumentation/werewolf/opentelemetry-instrumentation-werewolf.gemspec + create instrumentation/werewolf/Rakefile + create instrumentation/werewolf/README.md + create instrumentation/werewolf/lib/opentelemetry-instrumentation-werewolf.rb + create instrumentation/werewolf/lib/opentelemetry/instrumentation.rb + create instrumentation/werewolf/lib/opentelemetry/instrumentation/werewolf.rb + create instrumentation/werewolf/lib/opentelemetry/instrumentation/werewolf/instrumentation.rb + create instrumentation/werewolf/lib/opentelemetry/instrumentation/werewolf/version.rb + create instrumentation/werewolf/test/test_helper.rb + create instrumentation/werewolf/test/opentelemetry/instrumentation/werewolf/instrumentation_test.rb + insert .toys/.data/releases.yml + insert instrumentation/all/Gemfile + insert instrumentation/all/opentelemetry-instrumentation-all.gemspec + insert instrumentation/all/lib/opentelemetry/instrumentation/all.rb + +``` + +### Manually + +TBC + +## Adding gem to processes + +### CI + +To add the gem to the CI processes the gem needs to be added to the `matrix.gem` object of the `.github/workflows/ci-instrumentation.yml` when not using services and if it requires services it goes in `.github/workflows/ci-instrumentation-with-services.yml`: + +```yaml +jobs: + instrumentation: + strategy: + fail-fast: false + matrix: + gem: + - action_pack + - action_view + - active_job + # ... + - werewolf + os: + - ubuntu-latest +``` + +### Labeler + +TBC + +### Component owners + +TBC + +## Testing with Services + +### Adding new Services + +Assuming that the required external service is not already supported, you will need to define it as a infra service in the `/.docker/infra/{{service_name}}` directory. +This directory will contain a single file `compose.yml` which acts as the service definition. + +An example of this service is: + +```yaml +services: + rabbitmq: + image: rabbitmq:4.2.5-alpine@sha256:53e51f8469ef13d0a1a2b03c0d36dcf4efbf13089f552440ec5620ca07f2c64e + ports: + - "5672:5672" +``` + +This compose file when run using docker will produce a single runnable service. +This service needs to be included in the root compose file `docker-compose.yml`. + +The final step in adding a new service is to define a renovate package rule to `.github/renovate.json5` which manages the min major version of the service tested against. + +```json + { + description: "Wait until current major postgres is EoL before updating", + dependencyDashboardCategory: "Min Docker service", + matchUpdateTypes: ["major"], + matchDepNames: ["postgres"], + minimumReleaseAge: "1460 days", + }, +``` + +The `minimumReleaseAge` days value should be calculated based on the expected age of the major version when it becomes the lowest major version which is not end of life. +In the above example, we wait until a major version has been available for 1460 days (4 years) which is calculated based on each major version of Postgres being supported for 5 years with a new major each year. +Hence 5 years - 1 year = 4 years which works out to be the 1460 days. + +### Using service to test + +Once the required infra services have been defined as described in [Adding new services](#adding-new-services), a `compose.yml` needs to be defined within the test folder. + +This `compose.yml` file in most cases will just be: + +```yaml +include: + - ../../../.docker/infra/rabbitmq/docker-compose.yml +``` + +Lastly to ensure that pr's are labelled with the gems affected by changes an infra service, the path to the to infra service needs to be added labeler setup (`.github/labeler.yml`) for the gem as per below. + +```yaml +instrumentation-warewolf: + - changed-files: + - any-glob-to-any-file: + - "instrumentation/warewolf/**" + - ".docker/infra/rabbitmq/**" +``` + +## Showcasing functionality via examples + +Executable examples should be included in the `examples` directory that demonstrate how to use the instrumentation in a real-world scenario. + +We recommend using [Bundler's inline gemfile](https://bundler.io/guides/bundler_in_a_single_file_ruby_script.html) to run the examples. Here is an example from the `grape` instrumentation: + +```ruby + +# frozen_string_literal: true + +require 'bundler/inline' + +gemfile(true) do + source 'https://rubygems.org' + gem 'grape', '~> 1.2' + gem 'opentelemetry-sdk' + gem 'opentelemetry-instrumentation-rack' + gem 'opentelemetry-instrumentation-grape' +end + +# Export traces to console +ENV['OTEL_TRACES_EXPORTER'] ||= 'console' + +OpenTelemetry::SDK.configure do |c| + c.service_name = 'trace_demonstration' + c.use_all # this will only require instrumentation gems it finds that are installed by bundler. +end + +at_exit do + OpenTelemetry.tracer_provider.shutdown +end + +# A basic Grape API example +class ExampleAPI < Grape::API + format :json + + desc 'Return a greeting message' + get :hello do + { message: 'Hello, world!' } + end + + desc 'Return information about a user' + params do + requires :id, type: Integer, desc: 'User ID' + end + get 'users/:id' do + { id: params[:id], name: 'John Doe', email: 'johndoe@example.com' } + end +end + +# Set up fake Rack application +builder = Rack::Builder.app do + # Integration is automatic in web frameworks but plain Rack applications require this line. + # Enable it in your config.ru. + use *OpenTelemetry::Instrumentation::Rack::Instrumentation.instance.middleware_args + run ExampleAPI +end +app = Rack::MockRequest.new(builder) + +app.get('/hello') +app.get('/users/1') + +``` + +## Freezing Gem Development + +### Adding notice to Readme + +#### Upstream abbandoned + +When the upstream library has not seen activity for an extended period of time XYZ months, we will freeze our development efforts on this gem. +When this occurs we are to add the folloing notice to the readme of this gem: + +> [!IMPORTANT] +> +> **Deprecation Notice:** +> This gem is deprecated due to the library ({{library}}) it relies on no longer being maintained. The code remains available for reference, but no further updates or fixes will be provided. Users should consider migrating to actively supported alternatives. + +#### Upstream added native support + +When the upstream library adds native instrumentation, this is an indicator that we can freeze our development efforts on this gem. +When this occurs we are to add the following notice to the readme: + +> [!NOTE] +> +> **Development Frozen:** +> +> {{library}} {{version}}+ includes native OpenTelemetry instrumentation. For the best experience and continued support, we recommend: +> +> - **{{library}} < {{version}}**: Use `{{GEM}}` gem +> - **{{library}} ≥ {{version}}**: Use {{library}}'s built-in OpenTelemetry support (remove `{{GEM}}` gem) +> +> Community instrumentation is compatible with {{library}} versions up to {{version}}. Development of this gem is frozen for newer {{library}} versions in favor of the native integration. + +Where gem is this gem and library is what we are instrumenting. + +### Blocking automated dependency updates + +Currently we use Renovate to provide automated pr's which update our dependencies, however in the scenario where a Gem development is frozen, +we do not want these updates just for this gem. + +To achieve this the path of the gem need to be added to the `ignorePaths` property of `.github/renovate.json5`. + +The key thing is that this will block renovate from updating that gem directory while still allowing all updates to proceed. + +### Pinning infra services + +Due to the nature of using shared service definitions it will be necessary to copy the shared service folder ie `.docker/rabbitmq` into a newly created `.docker` folder at the root of the gem. +Following this copying of the services, the references in the gem's compose files need to be updated to use the local copy. + +This processes ensures that if the service is updated the frozen gem remains using the last version supported/tested. + +### Adding deprecation label to pr's + +To help with PR review, the gem path should be added to the deprecated label block in `.github/labeler.yml` which ensures that if changes to deprecated gems occur, +they are labelled accordingly. + +## Removing Gems + +### Removal Checklist + +TBC + +### Steps to be performed + +When a gem is ready to be removed, there is a couple of additional steps to be performed so that we achieve a complete removal. +These steps are: + +- Remove the gem block from `.github/labeler.yml` +- Remove the gem path from the deprecated label path in `.github/labeler.yml` +- Remove the gem path from the `AllCops.Exclude` property in `.rubocop.yml` +- Remove the gem block from `.toys/.data/releases.yml` +- Remove the gem path from ignore path block in `.github/renovate.json5` +- Close any open issues for which only apply to the gem +- Remove the gem label from any issues which affect multiple gems +- Remove the component from `.github/component_owners.yml` From de90ad62b735f82f0aa8f8362b4171b5c19935d4 Mon Sep 17 00:00:00 2001 From: James Thompson Date: Sun, 3 May 2026 15:09:56 +1000 Subject: [PATCH 3/3] Update DEVELOPER.md --- DEVELOPER.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/DEVELOPER.md b/DEVELOPER.md index a6ac6a066d..5feaabb391 100644 --- a/DEVELOPER.md +++ b/DEVELOPER.md @@ -255,6 +255,25 @@ This processes ensures that if the service is updated the frozen gem remains usi To help with PR review, the gem path should be added to the deprecated label block in `.github/labeler.yml` which ensures that if changes to deprecated gems occur, they are labelled accordingly. +### Disabling Rubocop + +To disable rubocop replace + +``` +inherit_from: ../../.rubocop.yml +``` + +with + +``` +AllCops: + DisabledByDefault: true + Exclude: + - "**/*" +``` + +This tells rubocop for this gem, disable all cops enabled by default, exclude all files within this folder and don't inherit any config. + ## Removing Gems ### Removal Checklist