├── log └── .keep ├── test ├── support │ ├── apps │ │ ├── rails │ │ │ ├── tmp │ │ │ │ └── .keep │ │ │ └── models │ │ │ │ ├── block.rb │ │ │ │ └── block6.rb │ │ ├── action_view │ │ │ └── views │ │ │ │ ├── blocks │ │ │ │ └── _block.html.erb │ │ │ │ ├── test_view │ │ │ │ ├── _message.html.erb │ │ │ │ ├── _syntax_error.html.erb │ │ │ │ ├── render_alternate_layout.html.erb │ │ │ │ ├── render_view.html.erb │ │ │ │ ├── render_partial.html.erb │ │ │ │ ├── render_partial_that_errors.html.erb │ │ │ │ └── render_collection.html.erb │ │ │ │ └── layouts │ │ │ │ └── mobile.html.erb │ │ ├── sidekiq │ │ │ ├── config.yaml │ │ │ ├── jobs │ │ │ │ ├── sidekiq_job_1.rb │ │ │ │ └── sidekiq_job_2.rb │ │ │ ├── boot.rb │ │ │ └── worker.rb │ │ ├── resque │ │ │ ├── boot.rb │ │ │ └── jobs │ │ │ │ ├── resque_error_job.rb │ │ │ │ └── resque_fast_job.rb │ │ ├── roda │ │ │ └── config.ru │ │ ├── sinatra │ │ │ └── config.ru │ │ ├── cuba │ │ │ └── config.ru │ │ ├── grpc │ │ │ └── boot.rb │ │ ├── active_record │ │ │ └── active_record.rb │ │ ├── rails_generic │ │ │ └── config.ru │ │ ├── http_endpoint │ │ │ └── boot.rb │ │ └── action_controller │ │ │ └── config.ru │ ├── proc │ │ ├── 0 │ │ │ ├── cpuset │ │ │ └── cmdline │ │ ├── 1 │ │ │ └── cpuset │ │ └── self │ │ │ ├── net │ │ │ └── route │ │ │ └── sched │ ├── mock_timer.rb │ ├── serverless │ │ └── cloudwatch_log.bin │ └── ecs │ │ └── container.json ├── backend │ ├── gc_snapshot_test.rb │ ├── request_client_test.rb │ ├── host_agent_lookup_test.rb │ └── agent_test.rb ├── util_test.rb ├── benchmarks │ ├── bench_id_generation.rb │ └── bench_opentracing.rb ├── snapshot │ ├── deltable_test.rb │ ├── ruby_process_test.rb │ ├── lambda_function_test.rb │ ├── google_cloud_run_process_test.rb │ ├── fargate_process_test.rb │ └── fargate_task_test.rb ├── instana_test.rb ├── config_test.rb ├── trace │ ├── instrumented_logger_test.rb │ ├── span_context_test.rb │ └── processor_test.rb ├── activator_test.rb ├── instrumentation │ ├── shoryuken_test.rb │ ├── rails_active_record_database_missing_test.rb │ ├── rails_action_mailer_test.rb │ ├── rails_active_job_test.rb │ └── mongo_test.rb ├── frameworks │ ├── roda_test.rb │ ├── cuba_test.rb │ └── sinatra_test.rb └── test_helper.rb ├── gemfiles ├── .bundle │ └── config ├── rubocop_162.gemfile ├── dalli_30.gemfile ├── dalli_32.gemfile ├── mongo_219.gemfile ├── sidekiq_70.gemfile ├── sinatra_40.gemfile ├── sinatra_22.gemfile ├── sinatra_30.gemfile ├── redis_51.gemfile ├── sidekiq_65.gemfile ├── cuba_40.gemfile ├── rack_30.gemfile ├── redis_50.gemfile ├── excon_100.gemfile ├── shoryuken_60.gemfile ├── excon_0100.gemfile ├── sidekiq_60.gemfile ├── bunny_223.gemfile ├── rack_16.gemfile ├── bunny_224.gemfile ├── rack_20.gemfile ├── sidekiq_42.gemfile ├── sidekiq_50.gemfile ├── dalli_20.gemfile ├── grpc_10.gemfile ├── mongo_216.gemfile ├── redis_40.gemfile ├── sinatra_14.gemfile ├── sequel_56.gemfile ├── sequel_57.gemfile ├── sequel_58.gemfile ├── roda_30.gemfile ├── graphql_10.gemfile ├── net_http_01.gemfile ├── resque_20.gemfile ├── cuba_30.gemfile ├── excon_021.gemfile ├── excon_079.gemfile ├── roda_20.gemfile ├── graphql_20.gemfile ├── resque_122.gemfile ├── shoryuken_50.gemfile ├── rest_client_16.gemfile ├── rest_client_20.gemfile ├── aws_60.gemfile ├── rails_71.gemfile ├── rails_80.gemfile ├── rails_42.gemfile ├── rails_60.gemfile ├── rails_50.gemfile ├── rails_52.gemfile ├── rails_70.gemfile ├── resque_1274_3scale.gemfile ├── rails_61.gemfile └── aws_30.gemfile ├── bin ├── aws-lambda │ ├── aws-regions │ │ ├── cn-regions.txt │ │ └── other_regions.txt │ ├── create_lambda_release_gh.rb │ └── Dockerfile ├── setup └── console ├── .tekton ├── .currency │ ├── resources │ │ └── requirements.txt │ └── docs │ │ └── report.md ├── github-interceptor-secret.yaml ├── pipelinerun.yaml ├── github-webhook-ingress.yaml ├── ruby-tracer-prepuller-cronjob.yaml ├── prepuller-restart-service-account.yaml ├── tekton-triggers-eventlistener-serviceaccount.yaml ├── github-pr-pipeline.yaml.part └── github-set-status-task.yaml ├── MAINTAINERS.md ├── lib ├── instana │ ├── version.rb │ ├── activators │ │ ├── rack.rb │ │ ├── rails.rb │ │ ├── sequel.rb │ │ ├── active_job.rb │ │ ├── cuba.rb │ │ ├── roda.rb │ │ ├── net_http.rb │ │ ├── aws_sdk_s3.rb │ │ ├── grpc_server.rb │ │ ├── aws_sdk_sns.rb │ │ ├── aws_sdk_sqs.rb │ │ ├── grpc_client.rb │ │ ├── resque_client.rb │ │ ├── action_controller_api.rb │ │ ├── action_controller_base.rb │ │ ├── rest_client.rb │ │ ├── aws_sdk_lambda.rb │ │ ├── aws_sdk_dynamodb.rb │ │ ├── active_record.rb │ │ ├── excon.rb │ │ ├── mongo.rb │ │ ├── action_mailer.rb │ │ ├── graphql.rb │ │ ├── bunny.rb │ │ ├── shoryuken.rb │ │ ├── action_cable.rb │ │ ├── sidekiq_client.rb │ │ ├── sidekiq_worker.rb │ │ ├── action_view.rb │ │ ├── sinatra.rb │ │ ├── redis.rb │ │ ├── resque_worker.rb │ │ └── dalli.rb │ ├── rack.rb │ ├── frameworks │ │ ├── sinatra.rb │ │ ├── cuba.rb │ │ └── roda.rb │ ├── instrumentation │ │ ├── action_mailer.rb │ │ ├── rest-client.rb │ │ ├── aws_sdk_sns.rb │ │ ├── mongo.rb │ │ ├── aws_sdk_dynamodb.rb │ │ ├── sequel.rb │ │ ├── shoryuken.rb │ │ ├── aws_sdk_s3.rb │ │ ├── aws_sdk_lambda.rb │ │ ├── action_view.rb │ │ ├── active_record.rb │ │ ├── action_cable.rb │ │ ├── sidekiq-client.rb │ │ ├── action_controller.rb │ │ ├── active_job.rb │ │ ├── sidekiq-worker.rb │ │ └── dalli.rb │ ├── snapshot │ │ ├── deltable.rb │ │ ├── lambda_function.rb │ │ ├── ruby_process.rb │ │ ├── google_cloud_run_process.rb │ │ ├── fargate_process.rb │ │ ├── google_cloud_run_instance.rb │ │ └── fargate_task.rb │ ├── instrumented_logger.rb │ ├── logger_delegator.rb │ ├── samplers │ │ └── result.rb │ ├── span_filtering │ │ └── filter_rule.rb │ ├── base.rb │ ├── backend │ │ ├── gc_snapshot.rb │ │ ├── process_info.rb │ │ ├── host_agent_lookup.rb │ │ ├── request_client.rb │ │ └── agent.rb │ ├── trace │ │ ├── export.rb │ │ └── span_kind.rb │ ├── activator.rb │ ├── secrets.rb │ ├── setup.rb │ └── span_filtering.rb └── instana.rb ├── .editorconfig ├── .gitignore ├── .codeclimate.yml ├── Gemfile ├── docker-compose.yml ├── .github ├── workflows │ ├── pr_commits_signed_off.yml │ └── release-notification-on-slack.yml └── ISSUE_TEMPLATE │ ├── config.yml │ └── bug.yml ├── sonar-project.properties ├── .rubocop.yml ├── .fasterer.yml ├── LICENSE ├── Rakefile ├── extras └── license_header.rb ├── instana.gemspec └── examples └── otel.rb /log/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/support/apps/rails/tmp/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/support/proc/0/cpuset: -------------------------------------------------------------------------------- 1 | / 2 | -------------------------------------------------------------------------------- /test/support/proc/0/cmdline: -------------------------------------------------------------------------------- 1 | test 2 | -------------------------------------------------------------------------------- /test/support/proc/1/cpuset: -------------------------------------------------------------------------------- 1 | not an empty file 2 | -------------------------------------------------------------------------------- /gemfiles/.bundle/config: -------------------------------------------------------------------------------- 1 | --- 2 | BUNDLE_RETRY: "1" 3 | -------------------------------------------------------------------------------- /test/support/apps/action_view/views/blocks/_block.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /bin/aws-lambda/aws-regions/cn-regions.txt: -------------------------------------------------------------------------------- 1 | cn-north-1 2 | cn-northwest-1 -------------------------------------------------------------------------------- /test/support/apps/action_view/views/test_view/_message.html.erb: -------------------------------------------------------------------------------- 1 | <%= @message %> 2 | -------------------------------------------------------------------------------- /test/support/apps/action_view/views/layouts/mobile.html.erb: -------------------------------------------------------------------------------- 1 |

Sample Mobile Layout

2 | -------------------------------------------------------------------------------- /.tekton/.currency/resources/requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | pandas 3 | beautifulsoup4 4 | tabulate 5 | -------------------------------------------------------------------------------- /test/support/apps/action_view/views/test_view/_syntax_error.html.erb: -------------------------------------------------------------------------------- 1 | <%= this does not compute! %> 2 | -------------------------------------------------------------------------------- /test/support/apps/action_view/views/test_view/render_alternate_layout.html.erb: -------------------------------------------------------------------------------- 1 | 2 | render_alternate_layout 3 | -------------------------------------------------------------------------------- /test/support/apps/sidekiq/config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | :concurrency: 2 3 | :timeout: 25 4 | :queues: 5 | - [important] 6 | -------------------------------------------------------------------------------- /test/support/apps/action_view/views/test_view/render_view.html.erb: -------------------------------------------------------------------------------- 1 | This is the template file. 2 | 3 |

<%= @message %>

4 | -------------------------------------------------------------------------------- /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | # MAINTAINERS 2 | 3 | [instana/eng-ruby](https://github.com/orgs/instana/teams/eng-ruby) can be reached with @instana/eng-ruby 4 | -------------------------------------------------------------------------------- /test/support/apps/action_view/views/test_view/render_partial.html.erb: -------------------------------------------------------------------------------- 1 | This is the template file. 2 | 3 | <%= render :partial => 'message' %> 4 | -------------------------------------------------------------------------------- /test/support/apps/action_view/views/test_view/render_partial_that_errors.html.erb: -------------------------------------------------------------------------------- 1 | This is the template file. 2 | 3 | <%= render :partial => 'syntax_error' %> 4 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /gemfiles/rubocop_162.gemfile: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2016 3 | 4 | source 'https://rubygems.org' 5 | 6 | gem 'rubocop', ">= 1.62.1" 7 | -------------------------------------------------------------------------------- /test/support/apps/action_view/views/test_view/render_collection.html.erb: -------------------------------------------------------------------------------- 1 | This is the template file. 2 | 3 |

<%= render partial: 'blocks/block', collection: @blocks %>

4 | -------------------------------------------------------------------------------- /lib/instana/version.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2016 3 | 4 | module Instana 5 | VERSION = "2.3.0" 6 | VERSION_FULL = "instana-#{VERSION}" 7 | end 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | 7 | [*.rb] 8 | indent_style = space 9 | indent_size = 2 10 | trim_trailing_whitespace = true 11 | -------------------------------------------------------------------------------- /test/support/apps/resque/boot.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | require_relative 'jobs/resque_error_job' 5 | require_relative 'jobs/resque_fast_job' 6 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2016 5 | 6 | require 'irb' 7 | 8 | require 'bundler/setup' 9 | require 'instana' 10 | 11 | IRB.start 12 | -------------------------------------------------------------------------------- /test/support/apps/sidekiq/jobs/sidekiq_job_1.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2017 3 | 4 | class SidekiqJobOne 5 | include Sidekiq::Worker 6 | 7 | def perform(a, b, c) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /.tekton/github-interceptor-secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: github-interceptor-secret 5 | type: Opaque 6 | stringData: 7 | # Always use a long, strong and random generated token 8 | secretToken: "<--- TOKEN GOES HERE --->" 9 | -------------------------------------------------------------------------------- /test/support/apps/sidekiq/jobs/sidekiq_job_2.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2017 3 | 4 | class SidekiqJobTwo 5 | include Sidekiq::Worker 6 | 7 | def perform(a, b, c) 8 | raise 'Fail to execute the job' 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /gemfiles/dalli_30.gemfile: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2023 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "minitest-reporters" 6 | gem "webmock" 7 | gem "puma" 8 | gem "rack-test" 9 | gem "simplecov", "~> 0.21.2" 10 | gem "dalli", "< 3.1.3" 11 | 12 | gemspec path: "../" 13 | -------------------------------------------------------------------------------- /gemfiles/dalli_32.gemfile: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2023 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "minitest-reporters" 6 | gem "webmock" 7 | gem "puma" 8 | gem "rack-test" 9 | gem "simplecov", "~> 0.21.2" 10 | gem "dalli", ">= 3.2.5" 11 | 12 | gemspec path: "../" 13 | -------------------------------------------------------------------------------- /gemfiles/mongo_219.gemfile: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2023 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "minitest-reporters" 6 | gem "webmock" 7 | gem "puma" 8 | gem "rack-test" 9 | gem "simplecov", "~> 0.21.2" 10 | gem "mongo", ">= 2.19" 11 | 12 | gemspec path: "../" 13 | -------------------------------------------------------------------------------- /gemfiles/sidekiq_70.gemfile: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2023 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "minitest-reporters" 6 | gem "webmock" 7 | gem "puma" 8 | gem "rack-test" 9 | gem "simplecov", "~> 0.21.2" 10 | gem "sidekiq", ">= 7.2.0" 11 | 12 | gemspec path: "../" 13 | -------------------------------------------------------------------------------- /gemfiles/sinatra_40.gemfile: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2023 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "minitest-reporters" 6 | gem "webmock" 7 | gem "puma" 8 | gem "rack-test" 9 | gem "simplecov", "~> 0.21.2" 10 | gem "sinatra", ">= 4.0.0" 11 | 12 | gemspec path: "../" 13 | -------------------------------------------------------------------------------- /gemfiles/sinatra_22.gemfile: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2023 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "minitest-reporters" 6 | gem "webmock" 7 | gem "puma" 8 | gem "rack-test" 9 | gem "simplecov", "~> 0.21.2" 10 | gem "sinatra", ">= 2.2.4", "< 3.0" 11 | 12 | gemspec path: "../" 13 | -------------------------------------------------------------------------------- /gemfiles/sinatra_30.gemfile: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2023 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "minitest-reporters" 6 | gem "webmock" 7 | gem "puma" 8 | gem "rack-test" 9 | gem "simplecov", "~> 0.21.2" 10 | gem "sinatra", ">= 3.0.6", "< 4.0" 11 | 12 | gemspec path: "../" 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /_yardoc/ 5 | /coverage/ 6 | /doc/ 7 | /pkg/ 8 | /spec/reports/ 9 | /tmp/ 10 | /log/ 11 | .ruby-version 12 | gemfiles/*.lock 13 | /test/tmp/*.pid 14 | .idea 15 | *.iml 16 | .DS_Store 17 | vendor/bundle 18 | gemfiles/vendor/bundle 19 | *.log -------------------------------------------------------------------------------- /gemfiles/redis_51.gemfile: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2024 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "minitest-reporters" 6 | gem "webmock" 7 | gem "puma" 8 | gem "rack-test" 9 | gem "simplecov", "~> 0.21.2" 10 | gem "redis", ">= 5.1.0" 11 | gem "debug" 12 | 13 | gemspec path: "../" 14 | -------------------------------------------------------------------------------- /gemfiles/sidekiq_65.gemfile: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2023 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "minitest-reporters" 6 | gem "webmock" 7 | gem "puma" 8 | gem "rack-test" 9 | gem "simplecov", "~> 0.21.2" 10 | gem "sidekiq", ">= 6.5.12", "< 7.0.0" 11 | 12 | gemspec path: "../" 13 | -------------------------------------------------------------------------------- /gemfiles/cuba_40.gemfile: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2023 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "rack", ">=3.0.0" 6 | gem "minitest-reporters" 7 | gem "webmock" 8 | gem "puma" 9 | gem "rack-test" 10 | gem "simplecov", "~> 0.21.2" 11 | gem "cuba", ">= 4.0.1" 12 | 13 | gemspec path: "../" 14 | -------------------------------------------------------------------------------- /gemfiles/rack_30.gemfile: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2023 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "minitest-reporters" 6 | gem "webmock" 7 | gem "puma" 8 | gem "rack-test" 9 | gem "simplecov", "~> 0.21.2" 10 | gem "rack", ">= 3.0.8" 11 | gem "rackup", ">= 2.1.0" 12 | 13 | gemspec path: "../" 14 | -------------------------------------------------------------------------------- /gemfiles/redis_50.gemfile: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2024 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "minitest-reporters" 6 | gem "webmock" 7 | gem "puma" 8 | gem "rack-test" 9 | gem "simplecov", "~> 0.21.2" 10 | gem "redis", ">= 5.0.0", "< 5.1.0" 11 | gem "debug" 12 | 13 | gemspec path: "../" 14 | -------------------------------------------------------------------------------- /gemfiles/excon_100.gemfile: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2024 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "rack" 6 | gem "rackup" 7 | gem "minitest-reporters" 8 | gem "webmock" 9 | gem "puma" 10 | gem "rack-test" 11 | gem "simplecov", "~> 0.21.2" 12 | gem "excon", ">= 1.0.0", "< 2.0" 13 | 14 | gemspec path: "../" 15 | -------------------------------------------------------------------------------- /gemfiles/shoryuken_60.gemfile: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2023 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "minitest-reporters" 6 | gem "webmock" 7 | gem "puma" 8 | gem "rack-test" 9 | gem "simplecov", "~> 0.21.2" 10 | gem "shoryuken", ">= 6.0" 11 | gem "aws-sdk-sqs", "~> 1.59" 12 | 13 | gemspec path: "../" 14 | -------------------------------------------------------------------------------- /test/backend/gc_snapshot_test.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | require 'test_helper' 5 | 6 | class GcSnapshotTest < Minitest::Test 7 | def test_report 8 | subject = Instana::Backend::GCSnapshot.instance 9 | assert subject.report.is_a?(Hash) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /gemfiles/excon_0100.gemfile: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2023 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "rack" 6 | gem "rackup" 7 | gem "minitest-reporters" 8 | gem "webmock" 9 | gem "puma" 10 | gem "rack-test" 11 | gem "simplecov", "~> 0.21.2" 12 | gem "excon", ">= 0.100.0", "< 1.0" 13 | 14 | gemspec path: "../" 15 | -------------------------------------------------------------------------------- /gemfiles/sidekiq_60.gemfile: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2023 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "minitest-reporters" 6 | gem "webmock" 7 | gem "puma" 8 | gem "rack-test" 9 | gem "simplecov", "~> 0.21.2" 10 | gem "sidekiq", "> 6.0.4", "< 6.5.0" 11 | gem "connection_pool", "<3.0.0" 12 | gemspec path: "../" 13 | -------------------------------------------------------------------------------- /test/support/proc/self/net/route: -------------------------------------------------------------------------------- 1 | Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT 2 | eth0 00000000 010012AC 0003 0 0 0 00000000 3 | eth0 000012AC 00000000 0001 0 0 0 0000FFF0 4 | -------------------------------------------------------------------------------- /test/util_test.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | require 'test_helper' 5 | 6 | class UtilTest < Minitest::Test 7 | def test_get_rb_source_error 8 | assert_equal({ error: "Only Ruby source files are allowed. (*.rb)" }, Instana::Util.get_rb_source('invalid.txt')) 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /gemfiles/bunny_223.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2025 4 | 5 | source "https://rubygems.org" 6 | 7 | gem "minitest-reporters" 8 | gem "webmock" 9 | gem "puma" 10 | gem "rack" 11 | gem "rackup" 12 | gem "rack-test" 13 | gem "simplecov", "~> 0.21.2" 14 | gem "bunny", "~> 2.23.0" 15 | 16 | gemspec path: "../" 17 | -------------------------------------------------------------------------------- /gemfiles/rack_16.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2021 5 | 6 | source "https://rubygems.org" 7 | 8 | gem "minitest-reporters" 9 | gem "webmock" 10 | gem "puma" 11 | gem "rack-test" 12 | gem "simplecov", "~> 0.21.2" 13 | gem "rack", "~> 1.6" 14 | 15 | gemspec path: "../" 16 | -------------------------------------------------------------------------------- /gemfiles/bunny_224.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2025 4 | 5 | source "https://rubygems.org" 6 | 7 | gem "minitest-reporters" 8 | gem "webmock" 9 | gem "puma" 10 | gem "rack" 11 | gem "rackup" 12 | gem "rack-test" 13 | gem "simplecov", "~> 0.21.2" 14 | gem "bunny", '>= 2.24', '< 3.0' 15 | 16 | gemspec path: "../" 17 | -------------------------------------------------------------------------------- /gemfiles/rack_20.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2021 5 | 6 | source "https://rubygems.org" 7 | 8 | gem "minitest-reporters" 9 | gem "webmock" 10 | gem "puma" 11 | gem "rack-test" 12 | gem "simplecov", "~> 0.21.2" 13 | gem "rack", ">= 2.0", "< 3.0" 14 | 15 | gemspec path: "../" 16 | -------------------------------------------------------------------------------- /gemfiles/sidekiq_42.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2021 5 | 6 | source "https://rubygems.org" 7 | 8 | gem "minitest-reporters" 9 | gem "webmock" 10 | gem "puma" 11 | gem "rack-test" 12 | gem "simplecov", "~> 0.21.2" 13 | gem "sidekiq", "~> 4.2.10" 14 | 15 | gemspec path: "../" 16 | -------------------------------------------------------------------------------- /gemfiles/sidekiq_50.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2021 5 | 6 | source "https://rubygems.org" 7 | 8 | gem "minitest-reporters" 9 | gem "webmock" 10 | gem "puma" 11 | gem "rack-test" 12 | gem "simplecov", "~> 0.21.2" 13 | gem "sidekiq", "< 6.0" 14 | 15 | gemspec path: "../" 16 | -------------------------------------------------------------------------------- /gemfiles/dalli_20.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2021 5 | 6 | source "https://rubygems.org" 7 | 8 | gem "minitest-reporters" 9 | gem "webmock" 10 | gem "puma" 11 | gem "rack-test" 12 | gem "simplecov", "~> 0.21.2" 13 | gem "dalli", ">= 2.0", "< 3.0" 14 | 15 | gemspec path: "../" 16 | -------------------------------------------------------------------------------- /gemfiles/grpc_10.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2021 5 | 6 | source "https://rubygems.org" 7 | 8 | gem "minitest-reporters" 9 | gem "webmock" 10 | gem "puma" 11 | gem "rack-test" 12 | gem "simplecov", "~> 0.21.2" 13 | gem "grpc", ">= 1.55.0", "< 2.0" 14 | 15 | gemspec path: "../" 16 | -------------------------------------------------------------------------------- /gemfiles/mongo_216.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2021 5 | 6 | source "https://rubygems.org" 7 | 8 | gem "minitest-reporters" 9 | gem "webmock" 10 | gem "puma" 11 | gem "rack-test" 12 | gem "simplecov", "~> 0.21.2" 13 | gem "mongo", ">= 2.16", "< 2.19" 14 | 15 | gemspec path: "../" 16 | -------------------------------------------------------------------------------- /gemfiles/redis_40.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2021 5 | 6 | source "https://rubygems.org" 7 | 8 | gem "minitest-reporters" 9 | gem "webmock" 10 | gem "puma" 11 | gem "rack-test" 12 | gem "simplecov", "~> 0.21.2" 13 | gem "redis", ">= 4.0.0", "< 5.0" 14 | 15 | gemspec path: "../" 16 | -------------------------------------------------------------------------------- /gemfiles/sinatra_14.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2021 5 | 6 | source "https://rubygems.org" 7 | 8 | gem "minitest-reporters" 9 | gem "webmock" 10 | gem "puma" 11 | gem "rack-test" 12 | gem "simplecov", "~> 0.21.2" 13 | gem "sinatra", ">= 1.4.8", "< 2.0" 14 | 15 | gemspec path: "../" 16 | -------------------------------------------------------------------------------- /gemfiles/sequel_56.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2024 4 | 5 | source "https://rubygems.org" 6 | 7 | gem "minitest-reporters" 8 | gem "webmock" 9 | gem "puma" 10 | gem "rack-test" 11 | gem "simplecov", "~> 0.21.2" 12 | gem "sequel", "~> 5.60.0" 13 | gem "sqlite3", "~> 1.4" 14 | gem "mysql2", "0.5.5" 15 | 16 | gemspec path: "../" 17 | -------------------------------------------------------------------------------- /gemfiles/sequel_57.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2024 4 | 5 | source "https://rubygems.org" 6 | 7 | gem "minitest-reporters" 8 | gem "webmock" 9 | gem "puma" 10 | gem "rack-test" 11 | gem "simplecov", "~> 0.21.2" 12 | gem "sequel", "~> 5.70.0" 13 | gem "sqlite3", "~> 1.4" 14 | gem "mysql2", "0.5.5" 15 | 16 | gemspec path: "../" 17 | -------------------------------------------------------------------------------- /gemfiles/sequel_58.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2024 4 | 5 | source "https://rubygems.org" 6 | 7 | gem "minitest-reporters" 8 | gem "webmock" 9 | gem "puma" 10 | gem "rack-test" 11 | gem "simplecov", "~> 0.21.2" 12 | gem "sequel", ">= 5.80" 13 | gem "sqlite3", "~> 1.4" 14 | gem "mysql2", "0.5.5" 15 | 16 | gemspec path: "../" 17 | -------------------------------------------------------------------------------- /lib/instana/activators/rack.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Activators 6 | class Rack < Activator 7 | def can_instrument? 8 | defined?(::Rack) 9 | end 10 | 11 | def instrument 12 | require 'instana/instrumentation/rack' 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /gemfiles/roda_30.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2021 5 | 6 | source "https://rubygems.org" 7 | 8 | gem "rack", "<3.0.0" 9 | gem "minitest-reporters" 10 | gem "webmock" 11 | gem "puma" 12 | gem "rack-test" 13 | gem "simplecov", "~> 0.21.2" 14 | gem "roda", ">= 3.69.0" 15 | 16 | gemspec path: "../" 17 | -------------------------------------------------------------------------------- /gemfiles/graphql_10.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2021 5 | 6 | source "https://rubygems.org" 7 | 8 | gem "minitest-reporters" 9 | gem "webmock" 10 | gem "puma" 11 | gem "rack-test" 12 | gem "racc" 13 | gem "simplecov", "~> 0.21.2" 14 | gem "graphql", ">= 1.0", "< 2.0" 15 | 16 | gemspec path: "../" 17 | -------------------------------------------------------------------------------- /gemfiles/net_http_01.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2021 5 | 6 | source "https://rubygems.org" 7 | 8 | gem "rack" 9 | gem "rackup" 10 | gem "minitest-reporters" 11 | gem "net-http" 12 | gem "webmock" 13 | gem "puma" 14 | gem "rack-test" 15 | gem "simplecov", "~> 0.21.2" 16 | 17 | gemspec path: "../" 18 | -------------------------------------------------------------------------------- /gemfiles/resque_20.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2021 5 | 6 | source "https://rubygems.org" 7 | 8 | gem "redis", "< 5.0.0" 9 | gem "minitest-reporters" 10 | gem "webmock" 11 | gem "puma" 12 | gem "rack-test" 13 | gem "simplecov", "~> 0.21.2" 14 | gem "resque", ">= 2.5.0" 15 | 16 | gemspec path: "../" 17 | -------------------------------------------------------------------------------- /gemfiles/cuba_30.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2021 5 | 6 | source "https://rubygems.org" 7 | 8 | gem "rack", "<3.0.0" 9 | gem "minitest-reporters" 10 | gem "webmock" 11 | gem "puma" 12 | gem "rack-test" 13 | gem "simplecov", "~> 0.21.2" 14 | gem "cuba", ">= 3.0", "< 4.0" 15 | 16 | gemspec path: "../" 17 | -------------------------------------------------------------------------------- /gemfiles/excon_021.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2021 5 | 6 | source "https://rubygems.org" 7 | 8 | gem "rack" 9 | gem "rackup" 10 | gem "minitest-reporters" 11 | gem "webmock" 12 | gem "puma" 13 | gem "rack-test" 14 | gem "simplecov", "~> 0.21.2" 15 | gem "excon", "0.21.0" 16 | 17 | gemspec path: "../" 18 | -------------------------------------------------------------------------------- /gemfiles/excon_079.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2021 5 | 6 | source "https://rubygems.org" 7 | 8 | gem "rack" 9 | gem "rackup" 10 | gem "minitest-reporters" 11 | gem "webmock" 12 | gem "puma" 13 | gem "rack-test" 14 | gem "simplecov", "~> 0.21.2" 15 | gem "excon", "0.79.0" 16 | 17 | gemspec path: "../" 18 | -------------------------------------------------------------------------------- /gemfiles/roda_20.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2021 5 | 6 | source "https://rubygems.org" 7 | 8 | gem "rack", "<3.0.0" 9 | gem "minitest-reporters" 10 | gem "webmock" 11 | gem "puma" 12 | gem "rack-test" 13 | gem "simplecov", "~> 0.21.2" 14 | gem "roda", ">= 2.0", "< 3.0" 15 | 16 | gemspec path: "../" 17 | -------------------------------------------------------------------------------- /gemfiles/graphql_20.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2021 5 | 6 | source "https://rubygems.org" 7 | 8 | gem "minitest-reporters" 9 | gem "webmock" 10 | gem "puma" 11 | gem "rack-test" 12 | gem "simplecov", "~> 0.21.2" 13 | gem "graphql", ">= 2.0.21", "< 3.0" 14 | gem "fiber-storage" 15 | 16 | gemspec path: "../" 17 | -------------------------------------------------------------------------------- /gemfiles/resque_122.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2021 5 | 6 | source "https://rubygems.org" 7 | 8 | gem "minitest-reporters" 9 | gem "webmock" 10 | gem "puma" 11 | gem "rack-test" 12 | gem "simplecov", "~> 0.21.2" 13 | gem "resque", ">= 1.22", "< 2.0" 14 | gem "redis", "~> 3.3", ">= 3.3.5" 15 | 16 | gemspec path: "../" 17 | -------------------------------------------------------------------------------- /gemfiles/shoryuken_50.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2021 5 | 6 | source "https://rubygems.org" 7 | 8 | gem "minitest-reporters" 9 | gem "webmock" 10 | gem "puma" 11 | gem "rack-test" 12 | gem "simplecov", "~> 0.21.2" 13 | gem "shoryuken", ">= 5.2", "< 6.0" 14 | gem "aws-sdk-sqs", "~> 1.36" 15 | 16 | gemspec path: "../" 17 | -------------------------------------------------------------------------------- /gemfiles/rest_client_16.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2021 5 | 6 | source "https://rubygems.org" 7 | 8 | gem "rack" 9 | gem "rackup" 10 | gem "minitest-reporters" 11 | gem "webmock" 12 | gem "puma" 13 | gem "rack-test" 14 | gem "simplecov", "~> 0.21.2" 15 | gem "rest-client", ">= 1.6", "< 2.0" 16 | 17 | gemspec path: "../" 18 | -------------------------------------------------------------------------------- /gemfiles/rest_client_20.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2021 5 | 6 | source "https://rubygems.org" 7 | 8 | gem "rack" 9 | gem "rackup" 10 | gem "minitest-reporters" 11 | gem "webmock" 12 | gem "puma" 13 | gem "rack-test" 14 | gem "simplecov", "~> 0.21.2" 15 | gem "rest-client", ">= 2.1.0", "< 3.0" 16 | 17 | gemspec path: "../" 18 | -------------------------------------------------------------------------------- /test/support/mock_timer.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | class MockTimer 5 | attr_reader :opts, :block, :running 6 | 7 | def initialize(*args, &blk) 8 | @opts = args.first 9 | @block = blk 10 | @running = false 11 | end 12 | 13 | def shutdown 14 | @running = false 15 | end 16 | 17 | def execute 18 | @running = true 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /gemfiles/aws_60.gemfile: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2023 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "minitest-reporters" 6 | gem "webmock" 7 | gem "puma" 8 | gem "rack-test" 9 | gem "simplecov", "~> 0.21.2" 10 | gem "aws-sdk-dynamodb", ">= 1.90" 11 | gem "aws-sdk-s3", ">= 1.127" 12 | gem "aws-sdk-sns", ">= 1.63" 13 | gem "aws-sdk-sqs", ">= 1.59" 14 | gem "aws-sdk-lambda", ">= 1.101" 15 | 16 | gemspec path: "../" 17 | -------------------------------------------------------------------------------- /test/benchmarks/bench_id_generation.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2017 3 | 4 | require 'test_helper' 5 | 6 | class BenchIDs < Minitest::Benchmark 7 | def bench_generate_id 8 | # TODO: This performs poorly on JRuby. 9 | assert_performance_constant do |input| 10 | 500_000.times do 11 | ::Instana::Util.generate_id 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /test/support/apps/roda/config.ru: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | class InstanaRodaApp < Roda 5 | route do |r| 6 | r.root do 7 | r.redirect '/hello' 8 | end 9 | r.get "hello" do 10 | "Hello Roda + Instana" 11 | end 12 | r.get "greet", String do |name| 13 | "Hello, #{name}!" 14 | end 15 | end 16 | end 17 | 18 | run InstanaRodaApp.freeze.app 19 | -------------------------------------------------------------------------------- /test/support/apps/sinatra/config.ru: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2016 3 | 4 | class InstanaSinatraApp < ::Sinatra::Base 5 | get '/' do 6 | "Hello Sinatra!" 7 | end 8 | 9 | get '/greet/:name' do 10 | "Hello, #{params[:name]}!" 11 | end 12 | 13 | configure do 14 | set :host_authorization, {permitted_hosts: "example.org"} 15 | end 16 | end 17 | 18 | run InstanaSinatraApp 19 | -------------------------------------------------------------------------------- /lib/instana/rack.rb: -------------------------------------------------------------------------------- 1 | # This file exists to just make the Instana::Rack require calls a bit more 2 | # user friendly. 3 | # 4 | # The real file is in the instrumentation subdirectory: 5 | # lib/instana/instrumentation/rack.rb 6 | # 7 | # require 'instana/rack' 8 | # config.middleware.use ::Instana::Rack 9 | # 10 | 11 | # (c) Copyright IBM Corp. 2021 12 | # (c) Copyright Instana Inc. 2016 13 | 14 | require 'instana/instrumentation/rack' 15 | -------------------------------------------------------------------------------- /gemfiles/rails_71.gemfile: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2023 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "minitest-reporters" 6 | gem "webmock" 7 | gem "puma" 8 | gem "rack-test" 9 | gem "simplecov", "~> 0.21.2" 10 | gem "mail", ">= 2.8.1" 11 | gem "mutex_m" # mutex_m is not part of the default gems since Ruby 3.4.0 12 | gem "rails", "~> 7.1" 13 | gem "mysql2", "0.5.5" 14 | gem "pg" 15 | gem "sqlite3", "~> 1.4" 16 | 17 | gemspec path: "../" 18 | -------------------------------------------------------------------------------- /gemfiles/rails_80.gemfile: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2024 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "minitest-reporters" 6 | gem "webmock" 7 | gem "puma" 8 | gem "rack-test" 9 | gem "simplecov", "~> 0.21.2" 10 | gem "mail", ">= 2.8.1" 11 | gem "mutex_m" # mutex_m is not part of the default gems since Ruby 3.4.0 12 | gem "rails", ">= 8.0" 13 | gem "mysql2", "0.5.5" 14 | gem "pg" 15 | gem "sqlite3", ">= 2.1" 16 | 17 | gemspec path: "../" 18 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | checks: 3 | complex-logic: 4 | config: 5 | threshold: 12 6 | file-lines: 7 | config: 8 | threshold: 325 9 | method-complexity: 10 | config: 11 | threshold: 12 12 | method-lines: 13 | config: 14 | threshold: 50 15 | method-count: 16 | config: 17 | threshold: 30 18 | 19 | plugins: 20 | fixme: 21 | enabled: true 22 | rubocop: 23 | enabled: true 24 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2016 3 | 4 | source 'https://rubygems.org' 5 | 6 | gem 'minitest-reporters' 7 | gem 'webmock' 8 | gem 'puma' 9 | 10 | gem 'rack' 11 | gem 'rackup' 12 | gem 'rack-test' 13 | 14 | gem 'simplecov', '~> 0.21.2' 15 | 16 | # instana.gemspec 17 | gemspec 18 | 19 | gem "opentelemetry-api", "~> 1.4" 20 | 21 | gem "rubocop", "~> 1.71" 22 | gem "opentelemetry-common", "~> 0.22.0" 23 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | sns: 4 | image: s12v/sns 5 | ports: 6 | - "9911:9911" 7 | s3: 8 | image: minio/minio:latest 9 | command: ["server", "/data"] 10 | ports: 11 | - "9000:9000" 12 | dynamodb: 13 | image: circleci/dynamodb 14 | ports: 15 | - "8000:8000" 16 | sqs: 17 | image: softwaremill/elasticmq-native 18 | ports: 19 | - "9324:9324" 20 | - "9325:9325" 21 | -------------------------------------------------------------------------------- /gemfiles/rails_42.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2021 5 | 6 | source "https://rubygems.org" 7 | 8 | gem "minitest-reporters" 9 | gem "webmock" 10 | gem "puma" 11 | gem "rack-test" 12 | gem "simplecov", "~> 0.21.2" 13 | gem "rails", ">= 4.2", "< 5.0" 14 | gem "mysql2", "0.4.10" 15 | gem "pg", "~> 0.21" 16 | gem "sqlite3", "~> 1.3", "< 1.4" 17 | 18 | gemspec path: "../" 19 | -------------------------------------------------------------------------------- /test/benchmarks/bench_opentracing.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2017 3 | 4 | require 'test_helper' 5 | 6 | class BenchOpenTracing < Minitest::Benchmark 7 | def bench_start_finish_span 8 | assert_performance_constant do |input| 9 | 10_000.times do 10 | span = ::Instana.tracer.start_span(:blah) 11 | span.set_tag(:zero, 0) 12 | span.finish 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /gemfiles/rails_60.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2021 5 | 6 | source "https://rubygems.org" 7 | 8 | gem "minitest-reporters" 9 | gem "webmock" 10 | gem "puma" 11 | gem "rack-test" 12 | gem "simplecov", "~> 0.21.2" 13 | gem "mail", "< 2.8.0" 14 | gem "rails", ">= 6.0", "< 6.1" 15 | gem "mysql2", "0.4.10" 16 | gem "pg" 17 | gem "sqlite3", "~> 1.4" 18 | 19 | gemspec path: "../" 20 | -------------------------------------------------------------------------------- /lib/instana/activators/rails.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Activators 6 | class Rails < Activator 7 | def can_instrument? 8 | defined?(::Instana::Rack) && 9 | defined?(::Rails) && 10 | defined?(::Rails::VERSION) 11 | end 12 | 13 | def instrument 14 | require 'instana/frameworks/rails' 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /.github/workflows/pr_commits_signed_off.yml: -------------------------------------------------------------------------------- 1 | name: Find signed commits 2 | on: 3 | pull_request_target: 4 | branches: 5 | - master # or the name of your main branch 6 | jobs: 7 | check-sign-off: 8 | name: Write comment if unsigned commits found 9 | env: 10 | FORCE_COLOR: 1 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: live627/check-pr-signoff-action@v1 15 | with: 16 | token: ${{ secrets.GITHUB_TOKEN }} 17 | -------------------------------------------------------------------------------- /gemfiles/rails_50.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2021 5 | 6 | source "https://rubygems.org" 7 | 8 | gem "minitest-reporters" 9 | gem "webmock" 10 | gem "puma" 11 | gem "rack-test" 12 | gem "simplecov", "~> 0.21.2" 13 | gem "mail", "< 2.8.0" 14 | gem "rails", ">= 5.0", "< 5.1" 15 | gem "mysql2", "0.4.10" 16 | gem "pg" 17 | gem "sqlite3", "~> 1.3", "< 1.4" 18 | 19 | gemspec path: "../" 20 | -------------------------------------------------------------------------------- /gemfiles/rails_52.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2021 5 | 6 | source "https://rubygems.org" 7 | 8 | gem "minitest-reporters" 9 | gem "webmock" 10 | gem "puma" 11 | gem "rack-test" 12 | gem "simplecov", "~> 0.21.2" 13 | gem "mail", "< 2.8.0" 14 | gem "rails", ">= 5.2", "< 6.0" 15 | gem "mysql2", "0.4.10" 16 | gem "pg" 17 | gem "sqlite3", "~> 1.3", "< 1.4" 18 | 19 | gemspec path: "../" 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Instana Support Portal 4 | url: https://www.ibm.com/mysupport 5 | about: Please ask questions related to your installation there. 6 | - name: Feature Requests 7 |  url: https://automation-management.ideas.ibm.com/?project=INSTANA 8 |  about: Please file feature requests there (or search for existing requests and vote for them). Do not use Github issues for feature requests. 9 | -------------------------------------------------------------------------------- /gemfiles/rails_70.gemfile: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2023 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "minitest-reporters" 6 | gem "webmock" 7 | gem "puma" 8 | gem "rack-test" 9 | gem "simplecov", "~> 0.21.2" 10 | gem "mail", ">= 2.8.1" 11 | gem "mutex_m" # mutex_m is not part of the default gems since Ruby 3.4.0 12 | gem "rails", "7.0.3" 13 | gem "mysql2", "0.5.5" 14 | gem "pg" 15 | gem "sqlite3", "~> 1.4" 16 | gem "concurrent-ruby", "1.3.4" 17 | 18 | gemspec path: "../" 19 | -------------------------------------------------------------------------------- /lib/instana/activators/sequel.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2024 2 | 3 | module Instana 4 | module Activators 5 | class Sequel < Activator 6 | def can_instrument? 7 | defined?(::Sequel::Database) 8 | end 9 | 10 | def instrument 11 | require 'instana/instrumentation/sequel' 12 | 13 | ::Sequel::Database 14 | .prepend(Instana::Instrumentation::Sequel) 15 | 16 | true 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /test/support/apps/cuba/config.ru: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2016 3 | 4 | require "cuba/safe" 5 | 6 | Cuba.plugin ::Cuba::Safe 7 | 8 | Cuba.define do 9 | on get do 10 | on "hello" do 11 | res.write "Hello Instana!" 12 | end 13 | 14 | on "greet/:name" do |name| 15 | res.write "Hello, #{name}" 16 | end 17 | 18 | on root do 19 | res.redirect '/hello' 20 | end 21 | end 22 | end 23 | 24 | run Cuba 25 | -------------------------------------------------------------------------------- /gemfiles/resque_1274_3scale.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2021 5 | 6 | source "https://rubygems.org" 7 | 8 | gem "debug" 9 | gem "minitest-reporters" 10 | gem "webmock" 11 | gem "puma" 12 | gem "rack-test" 13 | gem "simplecov", "~> 0.21.2" 14 | gem "resque", :git => "https://github.com/3scale/resque.git", :ref => "db327e389cf2fc572a503c47b19871ed899356d1" 15 | gem "redis", "~> 3.3", ">= 3.3.5" 16 | 17 | gemspec path: "../" 18 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.projectKey=instana_ruby-sensor 2 | sonar.organization=instana 3 | sonar.projectName=ruby-sensor 4 | sonar.sources=lib 5 | sonar.tests=test 6 | sonar.test.inclusions=test/**/* 7 | sonar.ruby.coverage.reportPaths=coverage/coverage.json 8 | sonar.coverage.exclusions=bin/*,examples/*,extras/* 9 | sonar.links.homepage=https://github.com/instana/ruby-sensor/ 10 | sonar.links.ci=https://circleci.com/gh/instana/ruby-sensor 11 | sonar.links.issue=https://github.com/instana/ruby-sensor/issues 12 | -------------------------------------------------------------------------------- /test/support/apps/rails/models/block.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2017 3 | 4 | class Block < ActiveRecord::Base 5 | def do_work(*args) 6 | block = Block.first 7 | block.name = "Charlie" 8 | block.color = "Black" 9 | block.save 10 | end 11 | end 12 | 13 | class CreateBlocks < ActiveRecord::Migration[4.2] 14 | def change 15 | create_table :blocks do |t| 16 | t.string :name 17 | t.string :color 18 | t.timestamps 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/instana/activators/active_job.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Activators 6 | class ActiveJob < Activator 7 | def can_instrument? 8 | defined?(::ActiveJob::Base) 9 | end 10 | 11 | def instrument 12 | require 'instana/instrumentation/active_job' 13 | 14 | ::ActiveJob::Base 15 | .prepend(Instana::Instrumentation::ActiveJob) 16 | 17 | true 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /test/support/apps/rails/models/block6.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2017 3 | 4 | class Block < ActiveRecord::Base 5 | def do_work(*args) 6 | block = Block.first 7 | block.name = "Charlie" 8 | block.color = "Black" 9 | block.save 10 | end 11 | end 12 | 13 | class CreateBlocks < ActiveRecord::Migration[6.0] 14 | def change 15 | create_table :blocks do |t| 16 | t.string :name 17 | t.string :color 18 | t.timestamps 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /.tekton/pipelinerun.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1 2 | kind: PipelineRun 3 | metadata: 4 | name: ruby-tracer-ci-pipeline-run 5 | spec: 6 | timeouts: 7 | pipeline: "2h" 8 | params: 9 | - name: revision 10 | value: "master" 11 | pipelineRef: 12 | name: ruby-tracer-ci-pipeline 13 | workspaces: 14 | - name: ruby-tracer-ci-pipeline-pvc 15 | volumeClaimTemplate: 16 | spec: 17 | accessModes: 18 | - ReadWriteOnce 19 | resources: 20 | requests: 21 | storage: 200Mi 22 | -------------------------------------------------------------------------------- /lib/instana/activators/cuba.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Activators 6 | class Cuba < Activator 7 | def can_instrument? 8 | defined?(::Instana::Rack) && defined?(::Cuba) 9 | end 10 | 11 | def instrument 12 | require 'instana/frameworks/cuba' 13 | 14 | ::Cuba.use ::Instana::Rack 15 | ::Cuba.prepend ::Instana::CubaPathTemplateExtractor 16 | 17 | true 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/instana/activators/roda.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Activators 6 | class Roda < Activator 7 | def can_instrument? 8 | defined?(::Instana::Rack) && defined?(::Roda) 9 | end 10 | 11 | def instrument 12 | require 'instana/frameworks/roda' 13 | 14 | ::Roda.use ::Instana::Rack 15 | ::Roda.plugin ::Instana::RodaPathTemplateExtractor 16 | 17 | true 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /test/snapshot/deltable_test.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | require 'test_helper' 5 | 6 | class DeltableTest < Minitest::Test 7 | include Instana::Snapshot::Deltable 8 | 9 | def test_delta 10 | subject = {a: {b: 5}} 11 | 12 | assert_equal 5, delta(:a, :b, obj: subject, compute: ->(o, n) { o + n }) 13 | assert_equal 10, delta(:a, :b, obj: subject, compute: ->(o, n) { o + n }) 14 | 15 | assert_nil delta(:a, :c, obj: subject, compute: ->(o, n) { o + n }) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/instana/activators/net_http.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Activators 6 | class NetHTTP < Activator 7 | def can_instrument? 8 | defined?(::Net::HTTP) && ::Instana.config[:nethttp][:enabled] 9 | end 10 | 11 | def instrument 12 | require 'instana/instrumentation/net-http' 13 | 14 | ::Net::HTTP.prepend(::Instana::Instrumentation::NetHTTPInstrumentation) 15 | 16 | true 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/instana/activators/aws_sdk_s3.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Activators 6 | class AwsS3 < Activator 7 | def can_instrument? 8 | defined?(::Aws::S3::Client) && ::Aws::S3::Client.respond_to?(:add_plugin) 9 | end 10 | 11 | def instrument 12 | require 'instana/instrumentation/aws_sdk_s3' 13 | 14 | ::Aws::S3::Client.add_plugin(Instana::Instrumentation::S3) 15 | 16 | true 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/instana/activators/grpc_server.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Activators 6 | class GrpcServer < Activator 7 | def can_instrument? 8 | defined?(::GRPC::RpcDesc) && ::Instana.config[:grpc][:enabled] 9 | end 10 | 11 | def instrument 12 | require 'instana/instrumentation/grpc' 13 | 14 | ::GRPC::RpcDesc.prepend(::Instana::Instrumentation::GRPCServerInstrumentation) 15 | 16 | true 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/instana/activators/aws_sdk_sns.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Activators 6 | class AwsSdkSns < Activator 7 | def can_instrument? 8 | defined?(::Aws::SNS::Client) && ::Aws::SNS::Client.respond_to?(:add_plugin) 9 | end 10 | 11 | def instrument 12 | require 'instana/instrumentation/aws_sdk_sns' 13 | 14 | ::Aws::SNS::Client.add_plugin(Instana::Instrumentation::SNS) 15 | 16 | true 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/instana/activators/aws_sdk_sqs.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Activators 6 | class AwsSdkSqs < Activator 7 | def can_instrument? 8 | defined?(::Aws::SQS::Client) && ::Aws::SQS::Client.respond_to?(:add_plugin) 9 | end 10 | 11 | def instrument 12 | require 'instana/instrumentation/aws_sdk_sqs' 13 | 14 | ::Aws::SQS::Client.add_plugin(Instana::Instrumentation::SQS) 15 | 16 | true 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/instana/activators/grpc_client.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Activators 6 | class GrpcClient < Activator 7 | def can_instrument? 8 | defined?(::GRPC::ActiveCall) && ::Instana.config[:grpc][:enabled] 9 | end 10 | 11 | def instrument 12 | require 'instana/instrumentation/grpc' 13 | 14 | ::GRPC::ClientStub.prepend(::Instana::Instrumentation::GRPCCientInstrumentation) 15 | 16 | true 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/instana/activators/resque_client.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Activators 6 | class ResqueClient < Activator 7 | def can_instrument? 8 | defined?(::Resque) && 9 | ::Instana.config[:'resque-client'][:enabled] 10 | end 11 | 12 | def instrument 13 | require 'instana/instrumentation/resque' 14 | 15 | ::Resque.prepend(::Instana::Instrumentation::ResqueClient) 16 | 17 | true 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /bin/aws-lambda/aws-regions/other_regions.txt: -------------------------------------------------------------------------------- 1 | af-south-1 2 | ap-east-1 3 | ap-east-2 4 | ap-northeast-1 5 | ap-northeast-2 6 | ap-northeast-3 7 | ap-south-1 8 | ap-south-2 9 | ap-southeast-1 10 | ap-southeast-2 11 | ap-southeast-3 12 | ap-southeast-4 13 | ap-southeast-5 14 | ap-southeast-7 15 | ca-central-1 16 | ca-west-1 17 | eu-central-1 18 | eu-central-2 19 | eu-north-1 20 | eu-south-1 21 | eu-south-2 22 | eu-west-1 23 | eu-west-2 24 | eu-west-3 25 | il-central-1 26 | me-central-1 27 | me-south-1 28 | sa-east-1 29 | us-east-1 30 | us-east-2 31 | us-west-1 32 | us-west-2 -------------------------------------------------------------------------------- /test/support/apps/resque/jobs/resque_error_job.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2018 3 | 4 | require "redis" 5 | require "net/http" 6 | 7 | class ErrorJob 8 | @queue = :critical 9 | 10 | def self.perform 11 | if ENV.key?('REDIS_URL') 12 | redis = Redis.new(:url => ENV['REDIS_URL']) 13 | else 14 | redis = Redis.new(:url => 'redis://localhost:6379') 15 | end 16 | 17 | dt = Time.now 18 | redis.set('ts', dt) 19 | 20 | raise Exception.new("Silly Rabbit, Trix are for kids.") 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /test/support/serverless/cloudwatch_log.bin: -------------------------------------------------------------------------------- 1 | eJxNUMtO3DAU3ecrLKtL0sSP+DG7QaSoEiOqSVgxI+RJriFVHtPYYUoR/47N 2 | INTtOefe83hN8ADOmUeoX46AVwhfrev1w6asqvV1iS8SPJ1GmCNBKOOFkErn 3 | hEainx6v52k5Ri4zJ5f1Zji0JoPmaUrHqYXf7lNW+RnMEHU0JzrLWUZYdv/t 4 | Zl2XVb3X3BolJBRMcs4I1cRI2xAO1BbCgIxP3HJwzdwdfTeNP7rew+zCu/sE 5 | 33x4ng0emn5a2pPxzVMwdR8ZcLI/ZyifYfTno9cEd21Mw7iglBGRay20VIrL 6 | XBQ811LkQilKVRG7CkFoyM2J4ExpWnARE/ku7ObNEOuToqAFUYyHm/zia9Fo 7 | sS1/3W5rtIU/S5D/bFdIhBkP1kJKuDbpQXCaKmJtSkEBk6JVxtqdv1pmE8uu 8 | EBffFUeD2/nLru+hRf9RMuBo5zcwTPMLqrp/EMJoijaXATR/0Sdx5yAYyzO+ 9 | G3HyluyTt3dtJ5Kr 10 | -------------------------------------------------------------------------------- /gemfiles/rails_61.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2021 5 | 6 | source "https://rubygems.org" 7 | 8 | gem "minitest-reporters" 9 | gem "webmock" 10 | gem "puma" 11 | gem "rack-test" 12 | gem "simplecov", "~> 0.21.2" 13 | gem "mail", ">= 2.8.1" 14 | gem "mutex_m" # mutex_m is not part of the default gems since Ruby 3.4.0 15 | gem "rails", ">= 6.1", "< 7.0" 16 | gem "mysql2", "0.5.5" 17 | gem "pg" 18 | gem "sqlite3", "~> 1.4" 19 | gem "concurrent-ruby", "1.3.4" 20 | 21 | gemspec path: "../" 22 | -------------------------------------------------------------------------------- /lib/instana/activators/action_controller_api.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Activators 6 | class ActionControllerAPI < Activator 7 | def can_instrument? 8 | defined?(::ActionController::API) 9 | end 10 | 11 | def instrument 12 | require 'instana/instrumentation/action_controller' 13 | 14 | ::ActionController::API 15 | .prepend(Instana::Instrumentation::ActionController) 16 | 17 | true 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/instana/activators/action_controller_base.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Activators 6 | class ActionControllerBase < Activator 7 | def can_instrument? 8 | defined?(::ActionController::Base) 9 | end 10 | 11 | def instrument 12 | require 'instana/instrumentation/action_controller' 13 | 14 | ::ActionController::Base 15 | .prepend(Instana::Instrumentation::ActionController) 16 | 17 | true 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/instana/activators/rest_client.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Activators 6 | class RestClient < Activator 7 | def can_instrument? 8 | defined?(::RestClient::Request) && ::Instana.config[:'rest-client'][:enabled] 9 | end 10 | 11 | def instrument 12 | require 'instana/instrumentation/rest-client' 13 | 14 | ::RestClient::Request.prepend ::Instana::Instrumentation::RestClientRequest 15 | 16 | true 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/instana/activators/aws_sdk_lambda.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Activators 6 | class AwsSdkLambda < Activator 7 | def can_instrument? 8 | defined?(::Aws::Lambda::Client) && ::Aws::Lambda::Client.respond_to?(:add_plugin) 9 | end 10 | 11 | def instrument 12 | require 'instana/instrumentation/aws_sdk_lambda' 13 | 14 | ::Aws::Lambda::Client.add_plugin(Instana::Instrumentation::Lambda) 15 | 16 | true 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /test/support/apps/resque/jobs/resque_fast_job.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2018 3 | 4 | require "redis" 5 | require "net/http" 6 | 7 | class FastJob 8 | @queue = :critical 9 | 10 | def self.perform(*args) 11 | raise 'Invalid Args' unless args.empty? 12 | 13 | if ENV.key?('REDIS_URL') 14 | redis = Redis.new(:url => ENV['REDIS_URL']) 15 | else 16 | redis = Redis.new(:url => 'redis://localhost:6379') 17 | end 18 | 19 | dt = Time.now 20 | redis.set('ts', dt) 21 | redis.set(:nb_id, 2) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/instana/activators/aws_sdk_dynamodb.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Activators 6 | class AwsDynamoDB < Activator 7 | def can_instrument? 8 | defined?(::Aws::DynamoDB::Client) && ::Aws::DynamoDB::Client.respond_to?(:add_plugin) 9 | end 10 | 11 | def instrument 12 | require 'instana/instrumentation/aws_sdk_dynamodb' 13 | 14 | ::Aws::DynamoDB::Client.add_plugin(Instana::Instrumentation::DynamoDB) 15 | 16 | true 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/instana/activators/active_record.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Activators 6 | class ActiveRecord < Activator 7 | def can_instrument? 8 | defined?(::ActiveRecord::ConnectionAdapters::AbstractAdapter) 9 | end 10 | 11 | def instrument 12 | require 'instana/instrumentation/active_record' 13 | 14 | ::ActiveRecord::ConnectionAdapters::AbstractAdapter 15 | .prepend(Instana::Instrumentation::ActiveRecord) 16 | 17 | true 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/instana/activators/excon.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Activators 6 | class Excon < Activator 7 | def can_instrument? 8 | defined?(::Excon) && defined?(::Excon::Middleware::Base) && ::Excon.respond_to?(:defaults) && Instana.config[:excon][:enabled] 9 | end 10 | 11 | def instrument 12 | require 'instana/instrumentation/excon' 13 | 14 | ::Excon.defaults[:middlewares].unshift(::Instana::Instrumentation::Excon) 15 | 16 | true 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/instana/activators/mongo.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Activators 6 | class Mongo < Activator 7 | def can_instrument? 8 | defined?(::Mongo::Client) && defined?(::Mongo::Monitoring::Global) 9 | end 10 | 11 | def instrument 12 | require 'instana/instrumentation/mongo' 13 | 14 | ::Mongo::Monitoring::Global.subscribe( 15 | ::Mongo::Monitoring::COMMAND, 16 | ::Instana::Mongo.new 17 | ) 18 | 19 | true 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /.tekton/github-webhook-ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: github-pr-ruby-webhook-ingress 5 | spec: 6 | ingressClassName: public-iks-k8s-nginx 7 | tls: 8 | - hosts: 9 | - 10 | rules: 11 | - host: 12 | http: 13 | paths: 14 | - path: /github-pr-ruby-hooks 15 | pathType: Exact 16 | backend: 17 | service: 18 | name: el-github-pr-ruby-eventlistener 19 | port: 20 | number: 8080 21 | -------------------------------------------------------------------------------- /gemfiles/aws_30.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2021 5 | 6 | source "https://rubygems.org" 7 | 8 | gem "minitest-reporters" 9 | gem "webmock" 10 | gem "webrick" 11 | gem "puma" 12 | gem "rack-test" 13 | gem "simplecov", "~> 0.21.2" 14 | gem "aws-sdk-core", "= 3.191.6" 15 | gem "aws-sdk-dynamodb", ">= 1.59", "< 1.99" 16 | gem "aws-sdk-s3", ">= 1.59", "< 1.60" 17 | gem "aws-sdk-sns", ">= 1.38", "< 1.59" 18 | gem "aws-sdk-sqs", ">= 1.36", "< 1.59" 19 | gem "aws-sdk-lambda", ">= 1.62", "< 1.80" 20 | 21 | gemspec path: "../" 22 | -------------------------------------------------------------------------------- /lib/instana/activators/action_mailer.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Activators 6 | class ActionMailer < Activator 7 | def can_instrument? 8 | defined?(::ActionMailer::Base) && defined?(ActiveSupport::Executor) 9 | end 10 | 11 | def instrument 12 | require 'instana/instrumentation/action_mailer' 13 | 14 | ::ActionMailer::Base 15 | .singleton_class 16 | .prepend(Instana::Instrumentation::ActionMailer) 17 | 18 | true 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/instana/activators/graphql.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Activators 6 | class Graphql < Activator 7 | def can_instrument? 8 | defined?(::GraphQL::Schema) && 9 | defined?(GraphQL::Tracing::PlatformTracing) && 10 | Instana.config[:graphql][:enabled] 11 | end 12 | 13 | def instrument 14 | require 'instana/instrumentation/graphql' 15 | 16 | ::GraphQL::Schema.use(::Instana::Instrumentation::GraphqlTracing) 17 | 18 | true 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/instana/frameworks/sinatra.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2016 3 | 4 | require "instana/rack" 5 | 6 | # This instrumentation will insert Rack into Sinatra _and_ Padrino since 7 | # the latter is based on Sinatra 8 | 9 | module Instana 10 | module SinatraPathTemplateExtractor 11 | def self.extended(base) 12 | base.store_path_template 13 | end 14 | 15 | def store_path_template 16 | after do 17 | @env["INSTANA_HTTP_PATH_TEMPLATE"] = @env["sinatra.route"] 18 | .sub("#{@request.request_method} ", '') 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /test/support/apps/grpc/boot.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2017 3 | 4 | require_relative 'grpc_server' 5 | 6 | ::Instana.logger.info "Booting instrumented gRPC server on port 50051 for tests." 7 | 8 | grpc_thread = Thread.new do 9 | s = GRPC::RpcServer.new 10 | Thread.current[:server] = s 11 | 12 | s.add_http2_port('127.0.0.1:50051', :this_port_is_insecure) 13 | s.handle(PingPongServer) 14 | s.run_till_terminated 15 | end 16 | 17 | Minitest.after_run do 18 | ::Instana.logger.info "Killing gRPC server" 19 | grpc_thread[:server].stop 20 | sleep 2 21 | end 22 | 23 | sleep 2 24 | -------------------------------------------------------------------------------- /.tekton/ruby-tracer-prepuller-cronjob.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: CronJob 3 | metadata: 4 | name: deploy-daemonset 5 | spec: 6 | schedule: "0 0 * * Mon-Fri" # Run every weekday at 12AM 7 | jobTemplate: 8 | spec: 9 | template: 10 | spec: 11 | serviceAccountName: prepuller-restart 12 | containers: 13 | - name: kubectl 14 | image: bitnami/kubectl 15 | command: 16 | - 'kubectl' 17 | - 'rollout' 18 | - 'restart' 19 | - 'daemonset/ruby-tracer-prepuller' 20 | restartPolicy: OnFailure -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | inherit_from: .rubocop_todo.yml 2 | 3 | require: 4 | - ./extras/license_header.rb 5 | 6 | # Remove when we remove .rubocop_todo.yml 7 | AllCops: 8 | NewCops: disable 9 | 10 | Metrics/PerceivedComplexity: 11 | Max: 20 12 | 13 | # Broken on 1.9 14 | Style/HashSyntax: 15 | Enabled: false 16 | 17 | Style/EvalWithLocation: 18 | Enabled: false 19 | 20 | Style/NilComparison: 21 | Enabled: false 22 | 23 | Style/SoleNestedConditional: 24 | Enabled: false 25 | 26 | # Ruby 2.1 compatibility 27 | Style/SafeNavigation: 28 | Enabled: false 29 | 30 | Style/SymbolProc: 31 | Enabled: false 32 | 33 | Style/MultilineBlockChain: 34 | Enabled: false -------------------------------------------------------------------------------- /test/support/apps/active_record/active_record.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | require 'rails' 5 | require 'active_record/railtie' 6 | 7 | migration_class = if ActiveRecord::Migration.respond_to?(:[]) 8 | ActiveRecord::Migration[4.2] 9 | else 10 | ActiveRecord::Migration 11 | end 12 | 13 | class CreateBlocks < migration_class 14 | def change 15 | create_table :blocks do |t| 16 | t.string :name 17 | t.string :color 18 | t.timestamps 19 | end 20 | end 21 | end 22 | 23 | class Block < ActiveRecord::Base 24 | end 25 | -------------------------------------------------------------------------------- /test/support/apps/sidekiq/boot.rb: -------------------------------------------------------------------------------- 1 | # Hook into sidekiq to control the current mode 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2021 5 | 6 | $sidekiq_mode = :client 7 | class << Sidekiq 8 | def server? 9 | $sidekiq_mode == :server 10 | end 11 | end 12 | 13 | # Configure redis for sidekiq client 14 | Sidekiq.configure_client do |config| 15 | config.redis = { url: ENV['REDIS_URL'] } 16 | end 17 | 18 | # Configure redis for sidekiq worker 19 | $sidekiq_mode = :server 20 | ::Sidekiq.configure_server do |config| 21 | config.redis = { url: ENV['REDIS_URL'] } 22 | end 23 | $sidekiq_mode = :client 24 | 25 | require_relative 'worker' 26 | -------------------------------------------------------------------------------- /lib/instana/activators/bunny.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2025 2 | 3 | module Instana 4 | module Activators 5 | class Bunny < Activator 6 | def can_instrument? 7 | defined?(::Bunny) && 8 | defined?(::Bunny::Queue) && 9 | defined?(::Bunny::Exchange) && 10 | ::Instana.config[:bunny][:enabled] 11 | end 12 | 13 | def instrument 14 | require 'instana/instrumentation/bunny' 15 | 16 | ::Bunny::Exchange.prepend(::Instana::Instrumentation::BunnyProducer) 17 | ::Bunny::Queue.prepend(::Instana::Instrumentation::BunnyConsumer) 18 | 19 | true 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/instana/activators/shoryuken.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Activators 6 | class Shoryuken < Activator 7 | def can_instrument? 8 | defined?(::Shoryuken) && ::Shoryuken.respond_to?(:configure_server) 9 | end 10 | 11 | def instrument 12 | require 'instana/instrumentation/shoryuken' 13 | 14 | ::Shoryuken.configure_server do |config| 15 | config.server_middleware do |chain| 16 | chain.add ::Instana::Instrumentation::Shoryuken 17 | end 18 | end 19 | 20 | true 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/instana/instrumentation/action_mailer.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Instrumentation 6 | module ActionMailer 7 | def method_missing(method_name, *args) # rubocop:disable Style/MissingRespondToMissing 8 | if action_methods.include?(method_name.to_s) 9 | tags = { 10 | actionmailer: { 11 | class: to_s, 12 | method: method_name.to_s 13 | } 14 | } 15 | Instana.tracer.in_span(:'mail.actionmailer', attributes: tags) { super } 16 | else 17 | super 18 | end 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/instana/snapshot/deltable.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Snapshot 6 | # @since 1.197.0 7 | module Deltable 8 | def delta(key, *rest, compute:, obj:, path: [key, *rest]) 9 | val = obj[key] 10 | return val if val == nil 11 | 12 | if rest.empty? 13 | @__delta ||= Hash.new(0) 14 | cache_key = path.join('.') 15 | old = @__delta[cache_key] 16 | @__delta[cache_key] = val 17 | 18 | return compute.call(old, val) 19 | end 20 | 21 | delta(*rest, compute: compute, obj: val, path: path) 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /.fasterer.yml: -------------------------------------------------------------------------------- 1 | speedups: 2 | rescue_vs_respond_to: true 3 | module_eval: true 4 | shuffle_first_vs_sample: true 5 | for_loop_vs_each: true 6 | each_with_index_vs_while: false 7 | map_flatten_vs_flat_map: true 8 | reverse_each_vs_reverse_each: true 9 | select_first_vs_detect: true 10 | sort_vs_sort_by: true 11 | fetch_with_argument_vs_block: true 12 | keys_each_vs_each_key: true 13 | hash_merge_bang_vs_hash_brackets: true 14 | block_vs_symbol_to_proc: true 15 | proc_call_vs_yield: true 16 | gsub_vs_tr: true 17 | select_last_vs_reverse_detect: true 18 | getter_vs_attr_reader: true 19 | setter_vs_attr_writer: true 20 | 21 | exclude_paths: 22 | - 'vendor/**/*.rb' 23 | - 'db/schema.rb' 24 | -------------------------------------------------------------------------------- /lib/instana/activators/action_cable.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Activators 6 | class ActionCable < Activator 7 | def can_instrument? 8 | defined?(::ActionCable::Connection::Base) && defined?(::ActionCable::Channel::Base) 9 | end 10 | 11 | def instrument 12 | require 'instana/instrumentation/action_cable' 13 | 14 | ::ActionCable::Connection::Base 15 | .prepend(Instana::Instrumentation::ActionCableConnection) 16 | 17 | ::ActionCable::Channel::Base 18 | .prepend(Instana::Instrumentation::ActionCableChannel) 19 | 20 | true 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/instana/activators/sidekiq_client.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Activators 6 | class SidekiqClient < Activator 7 | def can_instrument? 8 | defined?(::Sidekiq) && ::Sidekiq.respond_to?(:configure_client) && ::Instana.config[:'sidekiq-client'][:enabled] 9 | end 10 | 11 | def instrument 12 | require 'instana/instrumentation/sidekiq-client' 13 | 14 | ::Sidekiq.configure_client do |cfg| 15 | cfg.client_middleware do |chain| 16 | chain.add ::Instana::Instrumentation::SidekiqClient 17 | end 18 | end 19 | 20 | true 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/instana/activators/sidekiq_worker.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Activators 6 | class SidekiqWorker < Activator 7 | def can_instrument? 8 | defined?(::Sidekiq) && ::Sidekiq.respond_to?(:configure_server) && ::Instana.config[:'sidekiq-worker'][:enabled] 9 | end 10 | 11 | def instrument 12 | require 'instana/instrumentation/sidekiq-worker' 13 | 14 | ::Sidekiq.configure_server do |cfg| 15 | cfg.server_middleware do |chain| 16 | chain.add ::Instana::Instrumentation::SidekiqWorker 17 | end 18 | end 19 | 20 | true 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/instana/instrumentation/rest-client.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2016 3 | 4 | module Instana 5 | module Instrumentation 6 | module RestClientRequest 7 | def execute(&block) 8 | # Since RestClient uses net/http under the covers, we just 9 | # provide span visibility here. HTTP related KVs are reported 10 | # in the Net::HTTP instrumentation 11 | span = ::Instana.tracer.start_span(:'rest-client', with_parent: OpenTelemetry::Context.current) 12 | 13 | Trace.with_span(span) { super(&block) } 14 | rescue => e 15 | span.record_exception(e) 16 | raise 17 | ensure 18 | span.finish 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/instana/activators/action_view.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Activators 6 | class ActionView < Activator 7 | def can_instrument? 8 | defined?(::ActionView::PartialRenderer) 9 | end 10 | 11 | def instrument 12 | require 'instana/instrumentation/action_view' 13 | 14 | ::ActionView::PartialRenderer 15 | .prepend(Instana::Instrumentation::ActionView::PartialRenderer) 16 | if defined?(::ActionView::CollectionRenderer) 17 | ::ActionView::CollectionRenderer 18 | .prepend(Instrumentation::ActionView::CollectionRenderer) 19 | end 20 | true 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/instana/instrumented_logger.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | class InstrumentedLogger < Logger 6 | LEVEL_LABELS = %w[Debug Info Warn Error Fatal Any].freeze 7 | 8 | def instana_log_level 9 | WARN 10 | end 11 | 12 | def add(severity, message = nil, progname = nil) 13 | severity ||= UNKNOWN 14 | 15 | if severity >= instana_log_level && ::Instana.tracer.tracing? 16 | tags = { 17 | level: LEVEL_LABELS[severity], 18 | message: "#{message} #{progname}".strip 19 | } 20 | Instana.tracer.in_span(:log, attributes: {log: tags}) {} 21 | end 22 | 23 | super(severity, message, progname) 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/instana/activators/sinatra.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Activators 6 | class Sinatra < Activator 7 | def can_instrument? 8 | defined?(::Instana::Rack) && defined?(::Sinatra) && defined?(::Sinatra::Base) && !::Sinatra::Base.middleware.nil? 9 | end 10 | 11 | def instrument 12 | require 'instana/frameworks/sinatra' 13 | 14 | ::Sinatra::Base.use ::Instana::Rack 15 | unless ::Sinatra::Base.respond_to?(:mustermann_opts) 16 | ::Sinatra::Base.set :mustermann_opts, {} 17 | end 18 | ::Sinatra::Base.register ::Instana::SinatraPathTemplateExtractor 19 | 20 | true 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/instana/logger_delegator.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | class LoggerDelegator < SimpleDelegator 6 | def initialize(obj) 7 | obj.level = level_from_environment 8 | super(obj) 9 | end 10 | 11 | private 12 | 13 | def level_from_environment 14 | # :nocov: 15 | return Logger::FATAL if ENV.key?('INSTANA_TEST') || ENV.key?('RACK_TEST') 16 | return Logger::DEBUG if ENV.key?('INSTANA_DEBUG') 17 | 18 | case ENV['INSTANA_LOG_LEVEL'] 19 | when 'debug' 20 | Logger::DEBUG 21 | when 'warn' 22 | Logger::WARN 23 | when 'error' 24 | Logger::ERROR 25 | else 26 | Logger::INFO 27 | end 28 | # :nocov: 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/instana/snapshot/lambda_function.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Snapshot 6 | # @since 1.198.0 7 | class LambdaFunction 8 | ID = "com.instana.plugin.aws.lambda".freeze 9 | 10 | def entity_id 11 | Thread.current[:instana_function_arn] 12 | end 13 | 14 | def data 15 | {} 16 | end 17 | 18 | def snapshot 19 | { 20 | name: ID, 21 | entityId: entity_id, 22 | data: data 23 | } 24 | end 25 | 26 | def source 27 | { 28 | hl: true, 29 | cp: "aws", 30 | e: entity_id 31 | } 32 | end 33 | 34 | def host_name 35 | entity_id 36 | end 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /test/support/apps/rails_generic/config.ru: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2024 2 | 3 | require 'rails' 4 | require 'action_controller/railtie' 5 | 6 | class TestControllerApplication < Rails::Application 7 | config.eager_load = 'test' 8 | config.consider_all_requests_local = false 9 | config.secret_key_base = 'test_key' 10 | config.secret_token = 'test_token' 11 | 12 | if Rails::VERSION::MAJOR > 5 13 | config.hosts.clear 14 | end 15 | 16 | routes.append do 17 | get '/base/log_warning' => 'test_base#log_warning' 18 | end 19 | end 20 | 21 | class TestBaseController < ActionController::Base 22 | def log_warning 23 | Rails.logger.warn "This is a test warning" 24 | render plain: 'Test warning logged' 25 | end 26 | end 27 | 28 | TestControllerApplication.initialize! 29 | 30 | run TestControllerApplication 31 | -------------------------------------------------------------------------------- /lib/instana.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2016 3 | if ENV.fetch('INSTANA_DISABLE', false) && defined?(::Instana) 4 | Object.send(:remove_const, :Instana) 5 | end 6 | 7 | # Boot the instana agent background thread. If you wish to have greater 8 | # control on the where and which thread this is run in, instead use 9 | # 10 | # gem "instana", :require => "instana/setup" 11 | # 12 | # ...and override ::Instana::Agent.spawn_background_thread to boot 13 | # the thread of your choice. 14 | # 15 | 16 | # :nocov: 17 | unless ENV.fetch('INSTANA_DISABLE', false) 18 | require 'instana/setup' 19 | ::Instana::Activator.start 20 | ::Instana.agent.spawn_background_thread 21 | 22 | ::Instana.logger.info "Stan is on the scene. Starting Instana instrumentation version #{::Instana::VERSION}" 23 | end 24 | # :nocov: 25 | -------------------------------------------------------------------------------- /test/support/apps/http_endpoint/boot.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2016 3 | 4 | require 'rack/handler/puma' 5 | require 'rack/builder' 6 | require 'instana/rack' 7 | 8 | ::Instana.logger.info "Booting instrumented background Rackapp on port 6511 for tests." 9 | 10 | Thread.new do 11 | app = Rack::Builder.new { 12 | use ::Instana::Rack 13 | map "/" do 14 | run Proc.new { |env| 15 | [200, {"Content-Type" => "application/json"}, ["[\"Stan\",\"is\",\"on\",\"the\",\"scene!\"]"]] 16 | } 17 | end 18 | map "/error" do 19 | run Proc.new { |env| 20 | [500, {"Content-Type" => "application/json"}, ["[\"Stan\",\"is\",\"on\",\"the\",\"error!\"]"]] 21 | } 22 | end 23 | } 24 | 25 | Rackup::Handler::Puma.run(app, Host: '127.0.0.1', Port: 6511) 26 | end 27 | 28 | sleep(2) 29 | -------------------------------------------------------------------------------- /lib/instana/activators/redis.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Activators 6 | class Redis < Activator 7 | def can_instrument? 8 | defined?(::Redis) && defined?(::Redis::Client) && ::Instana.config[:redis][:enabled] && 9 | (Gem::Specification.find_by_name('redis').version < Gem::Version.new('5.0') || defined?(::RedisClient)) 10 | end 11 | 12 | def instrument 13 | require 'instana/instrumentation/redis' 14 | 15 | if Gem::Specification.find_by_name('redis').version >= Gem::Version.new('5.0') 16 | ::RedisClient.prepend(::Instana::RedisInstrumentation) 17 | else 18 | ::Redis::Client.prepend(::Instana::RedisInstrumentation) 19 | end 20 | 21 | true 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/instana/samplers/result.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2025 2 | 3 | module Instana 4 | module Trace 5 | module Samplers 6 | class Result 7 | EMPTY_HASH = {}.freeze 8 | attr_reader :tracestate, :attributes 9 | 10 | def initialize(decision:, tracestate:, attributes: nil) 11 | @decision = decision 12 | @attributes = attributes.freeze || EMPTY_HASH 13 | @tracestate = tracestate 14 | end 15 | 16 | # Returns true if this span should be sampled. 17 | # 18 | # @return FALSE always 19 | def sampled? 20 | false 21 | end 22 | 23 | # Returns true if this span should record events, attributes, status, etc. 24 | # 25 | # returns TRUE always 26 | def recording? 27 | true 28 | end 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /test/instana_test.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2016 3 | 4 | require 'test_helper' 5 | 6 | class InstanaTest < Minitest::Test 7 | def test_that_it_has_a_version_number 8 | refute_nil ::Instana::VERSION 9 | end 10 | 11 | def test_that_it_has_a_logger 12 | refute_nil ::Instana.logger 13 | end 14 | 15 | def test_that_it_has_an_agent 16 | refute_nil ::Instana.agent 17 | end 18 | 19 | def test_that_it_has_a_tracer 20 | refute_nil ::Instana.tracer 21 | end 22 | 23 | def test_that_it_has_a_config 24 | refute_nil ::Instana.config 25 | end 26 | 27 | def test_assign_logger 28 | mock = Minitest::Mock.new 29 | mock.expect(:info, true, [String]) 30 | 31 | ::Instana.logger = mock 32 | ::Instana.logger.info('test') 33 | ::Instana.logger = Logger.new('/dev/null') 34 | 35 | mock.verify 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /.tekton/prepuller-restart-service-account.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Service account which will be use to reset the daemonset, 3 | kind: ServiceAccount 4 | apiVersion: v1 5 | metadata: 6 | name: prepuller-restart 7 | --- 8 | # allow getting status and patching only the one daemonset you want 9 | # to restart 10 | apiVersion: rbac.authorization.k8s.io/v1 11 | kind: Role 12 | metadata: 13 | name: prepuller-restart 14 | rules: 15 | - apiGroups: ["apps", "extensions"] 16 | resources: ["daemonsets"] 17 | resourceNames: ["ruby-tracer-prepuller"] 18 | verbs: ["get", "patch"] 19 | --- 20 | # bind the role to the service account 21 | apiVersion: rbac.authorization.k8s.io/v1 22 | kind: RoleBinding 23 | metadata: 24 | name: prepuller-restart 25 | roleRef: 26 | apiGroup: rbac.authorization.k8s.io 27 | kind: Role 28 | name: prepuller-restart 29 | subjects: 30 | - kind: ServiceAccount 31 | name: prepuller-restart 32 | -------------------------------------------------------------------------------- /.tekton/tekton-triggers-eventlistener-serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: tekton-triggers-eventlistener-serviceaccount 5 | --- 6 | apiVersion: rbac.authorization.k8s.io/v1 7 | kind: RoleBinding 8 | metadata: 9 | name: tekton-triggers-eventlistener-serviceaccount-binding 10 | subjects: 11 | - kind: ServiceAccount 12 | name: tekton-triggers-eventlistener-serviceaccount 13 | roleRef: 14 | apiGroup: rbac.authorization.k8s.io 15 | kind: ClusterRole 16 | name: tekton-triggers-eventlistener-roles 17 | --- 18 | apiVersion: rbac.authorization.k8s.io/v1 19 | kind: ClusterRoleBinding 20 | metadata: 21 | name: tekton-triggers-eventlistener-serviceaccount-clusterbinding 22 | subjects: 23 | - kind: ServiceAccount 24 | name: tekton-triggers-eventlistener-serviceaccount 25 | namespace: default 26 | roleRef: 27 | apiGroup: rbac.authorization.k8s.io 28 | kind: ClusterRole 29 | name: tekton-triggers-eventlistener-clusterroles 30 | -------------------------------------------------------------------------------- /lib/instana/frameworks/cuba.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2016 3 | 4 | require "instana/rack" 5 | 6 | module Instana 7 | module CubaPathTemplateExtractor 8 | REPLACE_TARGET = /:(?[^\/]+)/i 9 | 10 | def self.prepended(base) 11 | ::Instana.logger.debug "#{base} prepended #{self}" 12 | end 13 | 14 | def on(*args, &blk) 15 | wrapper = lambda do |*caputres| 16 | env['INSTANA_PATH_TEMPLATE_FRAGMENTS'] << args 17 | .select { |a| a.is_a?(String) } 18 | .join('/') 19 | 20 | blk.call(*captures) 21 | end 22 | 23 | super(*args, &wrapper) 24 | end 25 | 26 | def call!(env) 27 | env['INSTANA_PATH_TEMPLATE_FRAGMENTS'] = [] 28 | response = super(env) 29 | env['INSTANA_HTTP_PATH_TEMPLATE'] = env['INSTANA_PATH_TEMPLATE_FRAGMENTS'] 30 | .join('/') 31 | .gsub(REPLACE_TARGET, '{\k}') 32 | response 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/instana/instrumentation/aws_sdk_sns.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Instrumentation 6 | class SNS < Seahorse::Client::Plugin 7 | class Handler < Seahorse::Client::Handler 8 | def call(context) 9 | sns_tags = { 10 | topic: context.params[:topic_arn], 11 | target: context.params[:target_arn], 12 | phone: context.params[:phone_number], 13 | subject: context.params[:subject] 14 | }.reject { |_, v| v.nil? } 15 | 16 | if context.operation_name == :publish 17 | ::Instana.tracer.in_span(:sns, attributes: {sns: sns_tags}) { @handler.call(context) } 18 | else 19 | @handler.call(context) 20 | end 21 | end 22 | end 23 | 24 | def add_handlers(handlers, _config) 25 | handlers.add(Handler, step: :initialize) 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/instana/activators/resque_worker.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Activators 6 | class ResqueWorker < Activator 7 | def can_instrument? 8 | defined?(::Resque::Worker) && 9 | defined?(::Resque::Job) && 10 | ::Instana.config[:'resque-worker'][:enabled] 11 | end 12 | 13 | def instrument 14 | require 'instana/instrumentation/resque' 15 | 16 | ::Resque::Worker.prepend(::Instana::Instrumentation::ResqueWorker) 17 | ::Resque::Job.prepend(::Instana::Instrumentation::ResqueJob) 18 | 19 | if ::Instana.config[:'resque-worker'][:'setup-fork'] 20 | ::Resque.after_fork do |_job| 21 | ::Instana.agent.after_fork 22 | end 23 | end 24 | 25 | # Set this so we assure that any remaining collected traces are reported at_exit 26 | ENV['RUN_AT_EXIT_HOOKS'] = "1" 27 | 28 | true 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/instana/span_filtering/filter_rule.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # (c) Copyright IBM Corp. 2025 4 | 5 | module Instana 6 | module SpanFiltering 7 | # Represents a filtering rule for spans 8 | # 9 | # A rule consists of: 10 | # - name: A human-readable identifier for the rule 11 | # - suppression: Whether child spans should be suppressed (only for exclude rules) 12 | # - conditions: A list of conditions that must all be satisfied (AND logic) 13 | class FilterRule 14 | attr_reader :name 15 | attr_accessor :suppression, :conditions 16 | 17 | def initialize(name, suppression, conditions) 18 | @name = name 19 | @suppression = suppression 20 | @conditions = conditions 21 | end 22 | 23 | # Check if a span matches this rule 24 | # @param span [Hash] The span to check 25 | # @return [Boolean] True if the span matches all conditions 26 | def matches?(span) 27 | @conditions.all? { |condition| condition.matches?(span) } 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /test/support/ecs/container.json: -------------------------------------------------------------------------------- 1 | { 2 | "DockerId": "43481a6ce4842eec8fe72fc28500c6b52edcc0917f105b83379f88cac1ff3946", 3 | "Name": "nginx-curl", 4 | "DockerName": "ecs-nginx-5-nginx-curl-ccccb9f49db0dfe0d901", 5 | "Image": "nrdlngr/nginx-curl", 6 | "ImageID": "sha256:2e00ae64383cfc865ba0a2ba37f61b50a120d2d9378559dcd458dc0de47bc165", 7 | "Labels": { 8 | "com.amazonaws.ecs.cluster": "default", 9 | "com.amazonaws.ecs.container-name": "nginx-curl", 10 | "com.amazonaws.ecs.task-arn": "arn:aws:ecs:us-east-2:012345678910:task/9781c248-0edd-4cdb-9a93-f63cb662a5d3", 11 | "com.amazonaws.ecs.task-definition-family": "nginx", 12 | "com.amazonaws.ecs.task-definition-version": "5" 13 | }, 14 | "DesiredStatus": "RUNNING", 15 | "KnownStatus": "RUNNING", 16 | "Limits": { 17 | "CPU": 512, 18 | "Memory": 512 19 | }, 20 | "CreatedAt": "2018-02-01T20:55:10.554941919Z", 21 | "StartedAt": "2018-02-01T20:55:11.064236631Z", 22 | "Type": "NORMAL", 23 | "Networks": [ 24 | { 25 | "NetworkMode": "awsvpc", 26 | "IPv4Addresses": [ 27 | "10.0.2.106" 28 | ] 29 | } 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /.tekton/github-pr-pipeline.yaml.part: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1 2 | kind: Pipeline 3 | metadata: 4 | name: github-pr-ruby-tracer-ci-pipeline 5 | spec: 6 | params: 7 | - name: revision 8 | type: string 9 | - name: git-commit-sha 10 | type: string 11 | workspaces: 12 | - name: ruby-tracer-ci-pipeline-pvc 13 | tasks: 14 | - name: github-set-check-status-to-pending 15 | taskRef: 16 | kind: Task 17 | name: github-set-status 18 | params: 19 | - name: SHA 20 | value: $(params.git-commit-sha) 21 | - name: STATE 22 | value: pending 23 | - name: REPO 24 | value: instana/ruby-sensor 25 | - name: github-set-check-status-to-success-or-failure 26 | runAfter: 27 | - github-set-check-status-to-pending 28 | - unittest-rails-postgres 29 | taskRef: 30 | kind: Task 31 | name: github-set-status 32 | params: 33 | - name: SHA 34 | value: $(params.git-commit-sha) 35 | - name: STATE 36 | value: success 37 | - name: REPO 38 | value: instana/ruby-sensor 39 | -------------------------------------------------------------------------------- /test/config_test.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2016 3 | 4 | require 'test_helper' 5 | 6 | class ConfigTest < Minitest::Test 7 | def test_that_config_exists 8 | refute_nil ::Instana.config 9 | assert_instance_of(::Instana::Config, ::Instana.config) 10 | end 11 | 12 | def test_that_it_has_defaults 13 | assert_equal '127.0.0.1', ::Instana.config[:agent_host] 14 | assert_equal 42699, ::Instana.config[:agent_port] 15 | 16 | assert ::Instana.config[:tracing][:enabled] 17 | assert ::Instana.config[:metrics][:enabled] 18 | 19 | ::Instana.config[:metrics].each do |k, v| 20 | next unless v.is_a? Hash 21 | assert_equal true, ::Instana.config[:metrics][k].key?(:enabled) 22 | end 23 | end 24 | 25 | def test_custom_agent_host 26 | subject = Instana::Config.new(logger: Logger.new('/dev/null'), agent_host: 'abc') 27 | assert_equal 'abc', subject[:agent_host] 28 | end 29 | 30 | def test_custom_agent_port 31 | subject = Instana::Config.new(logger: Logger.new('/dev/null'), agent_port: 'abc') 32 | assert_equal 'abc', subject[:agent_port] 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/instana/activators/dalli.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Activators 6 | class Dalli < Activator 7 | def can_instrument? 8 | defined?(::Dalli::Protocol::Base || defined?(::Dalli::Server)) && 9 | defined?(::Dalli::Client) && 10 | Instana.config[:dalli][:enabled] 11 | end 12 | 13 | def instrument 14 | require 'instana/instrumentation/dalli' 15 | dalli_version = Gem::Specification.find_by_name('dalli').version 16 | ::Dalli::Client.prepend ::Instana::Instrumentation::Dalli 17 | if dalli_version < Gem::Version.new('3.0') 18 | ::Dalli::Server.prepend ::Instana::Instrumentation::DalliRequestHandler 19 | elsif dalli_version >= Gem::Version.new('3.0') && dalli_version < Gem::Version.new('3.1.3') 20 | ::Dalli::Protocol::Binary.prepend ::Instana::Instrumentation::DalliRequestHandler 21 | else 22 | ::Dalli::Protocol::Base.prepend ::Instana::Instrumentation::DalliRequestHandler 23 | end 24 | 25 | true 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /test/snapshot/ruby_process_test.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | require 'test_helper' 5 | 6 | class RubyProcessTest < Minitest::Test 7 | def test_snapshot 8 | subject = Instana::Snapshot::RubyProcess.new 9 | snapshot = subject.snapshot 10 | 11 | assert_equal Instana::Snapshot::RubyProcess::ID, snapshot[:name] 12 | assert_equal Process.pid.to_s, snapshot[:entityId] 13 | assert_equal File.basename($PROGRAM_NAME), snapshot[:data][:name] 14 | end 15 | 16 | def test_snapshot_with_rails_defined_but_no_rails_application 17 | Object.send(:const_set, :Rails, 18 | Module.new do 19 | def respond_to? 20 | false 21 | end 22 | end) 23 | subject = Instana::Snapshot::RubyProcess.new 24 | snapshot = subject.snapshot 25 | 26 | assert_equal Instana::Snapshot::RubyProcess::ID, snapshot[:name] 27 | assert_equal Process.pid.to_s, snapshot[:entityId] 28 | assert_equal File.basename($PROGRAM_NAME), snapshot[:data][:name] 29 | ensure 30 | Object.send(:remove_const, :Rails) 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /test/trace/instrumented_logger_test.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | require 'test_helper' 5 | 6 | class InstrumentedLoggerTest < Minitest::Test 7 | def setup 8 | clear_all! 9 | end 10 | 11 | def test_log_warn_error 12 | subject = Instana::InstrumentedLogger.new('/dev/null') 13 | 14 | Instana::Tracer.in_span(:test_logging) do 15 | subject.warn('warn') 16 | subject.debug('test') 17 | subject.error('error') 18 | end 19 | 20 | spans = ::Instana.processor.queued_spans 21 | 22 | warn_span, error_span, = *spans 23 | 24 | assert_equal :log, warn_span[:n] 25 | assert_equal 'warn', warn_span[:data][:log][:message] 26 | assert_equal 'Warn', warn_span[:data][:log][:level] 27 | 28 | assert_equal :log, error_span[:n] 29 | assert_equal 'error', error_span[:data][:log][:message] 30 | assert_equal 'Error', error_span[:data][:log][:level] 31 | end 32 | 33 | def test_no_trace 34 | subject = Instana::InstrumentedLogger.new('/dev/null') 35 | subject.warn('warn') 36 | 37 | assert_equal [], ::Instana.processor.queued_spans 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/instana/base.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2016 3 | 4 | require "logger" 5 | require "instana/version" 6 | require "instana/util" 7 | 8 | module Instana 9 | class << self 10 | attr_accessor :agent 11 | attr_accessor :tracer 12 | attr_accessor :processor 13 | attr_accessor :config 14 | attr_accessor :pid 15 | attr_reader :secrets 16 | attr_reader :serverless 17 | attr_accessor :tracer_provider 18 | 19 | ## 20 | # setup 21 | # 22 | # Setup the Instana language agent to an informal "ready 23 | # to run" state. 24 | # 25 | def setup 26 | @agent = ::Instana::Backend::Agent.new 27 | @tracer_provider = ::Instana::Trace::TracerProvider.new 28 | @tracer = @tracer_provider.tracer('instana_tracer') 29 | @processor = ::Instana::Processor.new 30 | @secrets = ::Instana::Secrets.new 31 | @serverless = ::Instana::Serverless.new 32 | end 33 | 34 | def logger 35 | @logger ||= ::Instana::LoggerDelegator.new(Logger.new(STDOUT)) 36 | end 37 | 38 | def logger=(val) 39 | @logger.__setobj__(val) 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/instana/backend/gc_snapshot.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | require 'singleton' 5 | 6 | module Instana 7 | module Backend 8 | # Keeps track of garbage collector related metrics 9 | # @since 1.197.0 10 | class GCSnapshot 11 | include Singleton 12 | 13 | def initialize 14 | ::GC::Profiler.enable 15 | 16 | @last_major_count = 0 17 | @last_minor_count = 0 18 | end 19 | 20 | def report 21 | stats = ::GC.stat 22 | total_time = ::GC::Profiler.total_time * 1000 23 | 24 | ::GC::Profiler.clear 25 | 26 | payload = { 27 | totalTime: total_time, 28 | heap_live: stats[:heap_live_slots] || stats[:heap_live_num], 29 | heap_free: stats[:heap_free_slots] || stats[:heap_free_num], 30 | minorGcs: stats[:minor_gc_count] - @last_minor_count, 31 | majorGcs: stats[:major_gc_count] - @last_major_count 32 | } 33 | 34 | @last_major_count = stats[:major_gc_count] 35 | @last_minor_count = stats[:minor_gc_count] 36 | 37 | payload 38 | end 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /test/support/apps/sidekiq/worker.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2017 3 | 4 | require 'sidekiq/launcher' 5 | require 'sidekiq/cli' 6 | require 'sidekiq/api' 7 | require 'sidekiq/processor' 8 | 9 | require_relative 'jobs/sidekiq_job_1' 10 | require_relative 'jobs/sidekiq_job_2' 11 | 12 | ::Instana.logger.info "Booting instrumented sidekiq worker for tests." 13 | ::Sidekiq.logger.level = ::Logger::FATAL 14 | 15 | sidekiq_version = Gem::Specification.find_by_name('sidekiq').version 16 | cli = ::Sidekiq::CLI.instance 17 | cli.parse(['sidekiq', '-r', __FILE__, '-C', "#{File.dirname(__FILE__)}/config.yaml"]) 18 | 19 | config_or_options = if sidekiq_version >= Gem::Version.new('6.5.0') 20 | cli.config 21 | else 22 | cli.send :options 23 | end 24 | 25 | sidekiq_thread = Thread.new do 26 | launcher = ::Sidekiq::Launcher.new( 27 | config_or_options 28 | ) 29 | launcher.run 30 | Thread.current[:worker] = launcher 31 | end 32 | 33 | Minitest.after_run do 34 | ::Instana.logger.info "Killing Sidekiq worker" 35 | sidekiq_thread[:worker].stop 36 | sleep 1 37 | end 38 | -------------------------------------------------------------------------------- /test/snapshot/lambda_function_test.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | require 'test_helper' 5 | 6 | class LambdaFunctionTest < Minitest::Test 7 | def setup 8 | @subject = Instana::Snapshot::LambdaFunction.new 9 | end 10 | 11 | def test_snapshot 12 | Thread.current[:instana_function_arn] = 'test' 13 | 14 | assert_equal Instana::Snapshot::LambdaFunction::ID, @subject.snapshot[:name] 15 | assert_equal Thread.current[:instana_function_arn], @subject.snapshot[:entityId] 16 | ensure 17 | Thread.current[:instana_function_arn] = nil 18 | end 19 | 20 | def test_source 21 | Thread.current[:instana_function_arn] = 'test' 22 | 23 | assert @subject.source[:hl] 24 | assert_equal 'aws', @subject.source[:cp] 25 | assert_equal Thread.current[:instana_function_arn], @subject.source[:e] 26 | ensure 27 | Thread.current[:instana_function_arn] = nil 28 | end 29 | 30 | def test_host_name 31 | Thread.current[:instana_function_arn] = 'test' 32 | 33 | assert_equal Thread.current[:instana_function_arn], @subject.host_name 34 | ensure 35 | Thread.current[:instana_function_arn] = nil 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Copyright IBM Corp. 2021 4 | Copyright (c) 2017 Instana, Inc. https://www.instana.com/ 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /test/trace/span_context_test.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | require 'test_helper' 5 | 6 | class SpanContextTest < Minitest::Test 7 | def test_to_hash 8 | subject = Instana::SpanContext.new(trace_id: 'trace', span_id: 'span') 9 | assert_equal({trace_id: 'trace', span_id: 'span'}, subject.to_hash) 10 | end 11 | 12 | def test_invalid 13 | subject = Instana::SpanContext.new(trace_id: nil, span_id: nil) 14 | refute subject.valid? 15 | end 16 | 17 | def test_flags_level_zero 18 | subject = Instana::SpanContext.new(trace_id: 'trace', span_id: 'span', level: 0, baggage: {external_state: 'cn=test'}) 19 | assert_equal '00-000000000000000000000000000trace-000000000000span-02', subject.trace_parent_header 20 | assert_equal 'cn=test', subject.trace_state_header 21 | end 22 | 23 | def test_flags_level_zero_with_random_flag 24 | subject = Instana::SpanContext.new(trace_id: 'trace', span_id: 'span', level: 1, baggage: {external_state: 'cn=test'}) 25 | assert_equal '00-000000000000000000000000000trace-000000000000span-03', subject.trace_parent_header 26 | assert_equal 'in=trace;span,cn=test', subject.trace_state_header 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/instana/instrumentation/mongo.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | class Mongo 6 | REMOVED_COMMAND_ELEMENTS = %w[lsid $db documents].freeze 7 | 8 | def initialize 9 | @requests = {} 10 | end 11 | 12 | def started(event) 13 | tags = { 14 | namespace: event.database_name, 15 | command: event.command_name, 16 | peer: { 17 | hostname: event.address.host, 18 | port: event.address.port 19 | }, 20 | json: filter_statement(event.command) 21 | } 22 | 23 | @requests[event.request_id] = ::Instana.tracer.start_span(:mongo, attributes: {mongo: tags}) 24 | end 25 | 26 | def failed(event) 27 | span = @requests.delete(event.request_id) 28 | span.add_error(Exception.new(event.message)) 29 | 30 | span.finish 31 | end 32 | 33 | def succeeded(event) 34 | span = @requests.delete(event.request_id) 35 | span.finish 36 | end 37 | 38 | private 39 | 40 | def filter_statement(command) 41 | command.delete_if { |k, _| REMOVED_COMMAND_ELEMENTS.include?(k) } 42 | 43 | JSON.dump(command) 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/instana/snapshot/ruby_process.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Snapshot 6 | # Describes the current Ruby process 7 | # @since 1.197.0 8 | class RubyProcess 9 | ID = 'com.instana.plugin.ruby'.freeze 10 | 11 | def initialize(pid: Process.pid) 12 | @pid = pid 13 | end 14 | 15 | def entity_id 16 | @pid.to_s 17 | end 18 | 19 | def data 20 | metrics_data.merge(Util.take_snapshot) 21 | end 22 | 23 | def snapshot 24 | { 25 | name: ID, 26 | entityId: entity_id, 27 | data: data 28 | } 29 | end 30 | 31 | private 32 | 33 | def metrics_data 34 | proc_table = Sys::ProcTable.ps(pid: Process.pid) 35 | process = Backend::ProcessInfo.new(proc_table) 36 | 37 | { 38 | pid: @pid, 39 | name: Util.get_app_name, 40 | exec_args: process.arguments, 41 | gc: Backend::GCSnapshot.instance.report, 42 | thread: {count: ::Thread.list.count}, 43 | memory: {rss_size: proc_table.rss / 1024} # Bytes to Kilobytes 44 | } 45 | end 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /test/snapshot/google_cloud_run_process_test.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | require 'test_helper' 5 | 6 | class GoogleCloudRunProcessTest < Minitest::Test 7 | def test_snapshot 8 | ENV['K_REVISION'] = 'test' 9 | stub_request(:get, 'http://10.10.10.10//computeMetadata/v1/instance/id') 10 | .to_return(status: 200, body: 'test_instance_id') 11 | 12 | subject = Instana::Snapshot::GoogleCloudRunProcess.new(metadata_uri: 'http://10.10.10.10/') 13 | snapshot = subject.snapshot 14 | 15 | assert_equal Instana::Snapshot::GoogleCloudRunProcess::ID, snapshot[:name] 16 | assert_equal 'test_instance_id', snapshot[:data][:container] 17 | assert_equal 'gcpCloudRunInstance', snapshot[:data][:containerType] 18 | assert_equal 'gcp:cloud-run:revision:test', snapshot[:data][:'com.instana.plugin.host.name'] 19 | ensure 20 | ENV['K_REVISION'] = nil 21 | end 22 | 23 | def test_snapshot_error 24 | stub_request(:get, 'http://10.10.10.10//computeMetadata/v1/instance/id') 25 | .to_return(status: 500) 26 | 27 | subject = Instana::Snapshot::GoogleCloudRunProcess.new(metadata_uri: 'http://10.10.10.10/') 28 | 29 | assert_raises do 30 | subject.snapshot 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /test/backend/request_client_test.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | require 'test_helper' 5 | 6 | class RequestClientTest < Minitest::Test 7 | def test_send_request_simple 8 | stub_request(:get, 'http://example.com:9292/') 9 | .to_return(body: 'ok', status: '200') 10 | 11 | subject = Instana::Backend::RequestClient.new('example.com', 9292) 12 | response = subject.send_request('GET', '/') 13 | 14 | assert response.ok? 15 | assert 'ok', response.body 16 | end 17 | 18 | def test_send_request_json 19 | stub_request(:post, 'http://example.com:9292/') 20 | .with(body: '{"key":"value"}') 21 | .to_return(body: '{"ok": true}', status: '200') 22 | 23 | subject = Instana::Backend::RequestClient.new('example.com', 9292) 24 | response = subject.send_request('POST', '/', {key: 'value'}) 25 | 26 | assert response.ok? 27 | assert_equal({"ok" => true}, response.json) 28 | end 29 | 30 | def test_send_request_failure 31 | stub_request(:get, 'http://example.com:9292/') 32 | .to_return(status: '500') 33 | 34 | subject = Instana::Backend::RequestClient.new('example.com', 9292) 35 | response = subject.send_request('GET', '/') 36 | 37 | refute response.ok? 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/instana/frameworks/roda.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2016 3 | 4 | require "instana/rack" 5 | 6 | module Instana 7 | module RodaPathTemplateExtractor 8 | module RequestMethods 9 | TERM = defined?(::Roda) ? ::Roda::RodaPlugins::Base::RequestMethods::TERM : Object 10 | 11 | def if_match(args, &blk) 12 | path = @remaining_path 13 | captures = @captures.clear 14 | 15 | if match_all(args) 16 | (env['INSTANA_PATH_TEMPLATE_FRAGMENTS'] ||= []).concat(named_args(args, blk)) 17 | block_result(blk.(*captures)) 18 | env['INSTANA_HTTP_PATH_TEMPLATE'] = env['INSTANA_PATH_TEMPLATE_FRAGMENTS'] 19 | .join('/') 20 | .prepend('/') 21 | throw :halt, response.finish 22 | else 23 | @remaining_path = path 24 | false 25 | end 26 | end 27 | 28 | def named_args(args, blk) 29 | parameters = blk.parameters 30 | args.map do |a| 31 | case a 32 | when String 33 | a 34 | when TERM 35 | nil 36 | else 37 | _, name = parameters.pop 38 | "{#{name}}" 39 | end 40 | end.reject { |v| v.nil? } 41 | end 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/instana/trace/export.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2025 2 | 3 | module Instana 4 | module Trace 5 | # The Export module contains the built-in exporters and span processors for the OpenTelemetry 6 | # reference implementation. 7 | module Export 8 | # Raised when an export fails; spans are available via :spans accessor 9 | class ExportError < OpenTelemetry::Error 10 | # Returns the {Span} array for this exception 11 | # 12 | # @return [Array] 13 | attr_reader :spans 14 | 15 | # @param [Array] spans the array of spans that failed to export 16 | def initialize(spans) 17 | super("Unable to export #{spans.size} spans") 18 | @spans = spans 19 | end 20 | end 21 | 22 | # Result codes for the SpanExporter#export method and the SpanProcessor#force_flush and SpanProcessor#shutdown methods. 23 | 24 | # The operation finished successfully. 25 | SUCCESS = 0 26 | 27 | # The operation finished with an error. 28 | FAILURE = 1 29 | 30 | # Additional result code for the SpanProcessor#force_flush and SpanProcessor#shutdown methods. 31 | 32 | # The operation timed out. 33 | TIMEOUT = 2 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /test/activator_test.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | require 'test_helper' 5 | 6 | class ActivatorTest < Minitest::Test 7 | def test_start 8 | refute_nil Instana::Activator.trace_point 9 | assert Instana::Activator.trace_point.enabled? 10 | end 11 | 12 | def test_klass_call 13 | assert_equal [], Instana::Activator.call 14 | end 15 | 16 | def test_instance_call 17 | subject = Class.new(Instana::Activator) do 18 | def can_instrument? 19 | true 20 | end 21 | 22 | def instrument 23 | true 24 | end 25 | end 26 | 27 | assert_equal 1, Instana::Activator.call.length 28 | assert subject.call 29 | end 30 | 31 | def test_limited_activated_set 32 | ENV['INSTANA_ACTIVATE_SET'] = 'rack,rails' 33 | subject = activated_set 34 | assert_instance_of Set, subject 35 | assert_equal 2, subject.length 36 | assert_includes subject, 'rack' 37 | assert_includes subject, 'rails' 38 | ensure 39 | ENV.delete('INSTANA_ACTIVATE_SET') 40 | end 41 | 42 | def test_unlimited_activated_set 43 | ENV.delete('INSTANA_ACTIVATE_SET') 44 | subject = activated_set 45 | assert_instance_of Set, subject 46 | assert_equal 33, subject.length 47 | ensure 48 | ENV.delete('INSTANA_ACTIVATE_SET') 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /test/snapshot/fargate_process_test.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | require 'test_helper' 5 | 6 | class FargateProcessTest < Minitest::Test 7 | def setup 8 | @subject = Instana::Snapshot::FargateProcess.new(metadata_uri: 'https://10.10.10.10:8080/v3') 9 | end 10 | 11 | def test_snapshot 12 | stub_request(:get, 'https://10.10.10.10:8080/v3') 13 | .to_return(status: 200, body: File.read('test/support/ecs/container.json')) 14 | stub_request(:get, 'https://10.10.10.10:8080/v3/task') 15 | .to_return(status: 200, body: File.read('test/support/ecs/task.json')) 16 | 17 | snapshot = @subject.snapshot 18 | 19 | assert_equal Instana::Snapshot::FargateProcess::ID, snapshot[:name] 20 | assert_equal Process.pid.to_s, snapshot[:entityId] 21 | 22 | assert_equal 'docker', snapshot[:data][:containerType] 23 | assert_equal '43481a6ce4842eec8fe72fc28500c6b52edcc0917f105b83379f88cac1ff3946', snapshot[:data][:container] 24 | assert_equal 'arn:aws:ecs:us-east-2:012345678910:task/9781c248-0edd-4cdb-9a93-f63cb662a5d3', snapshot[:data][:'com.instana.plugin.host.name'] 25 | end 26 | 27 | def test_snapshot_error 28 | stub_request(:get, 'https://10.10.10.10:8080/v3') 29 | .to_return(status: 500) 30 | 31 | assert_raises do 32 | @subject.snapshot 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /.github/workflows/release-notification-on-slack.yml: -------------------------------------------------------------------------------- 1 | name: Slack Post 2 | on: 3 | workflow_dispatch: # Manual trigger 4 | inputs: 5 | github_ref: 6 | description: 'Manually provided value for GITHUB_RELEASE_TAG of a release' 7 | required: true 8 | type: string 9 | 10 | # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#release 11 | release: 12 | types: [published] 13 | jobs: 14 | build: 15 | name: Slack Post 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: 'Checkout' 19 | uses: actions/checkout@v3 20 | - run: | 21 | if [[ ${{ github.event_name == 'workflow_dispatch' }} == true ]]; then 22 | export GITHUB_RELEASE_TAG=${{ inputs.github_ref }} 23 | else # release event 24 | export GITHUB_RELEASE_TAG=$(basename ${GITHUB_REF}) 25 | fi 26 | echo "New release published ${GITHUB_RELEASE_TAG}" 27 | pip3 install httpx PyGithub 28 | echo $PWD 29 | ls -lah 30 | python ./.github/scripts/announce_release_on_slack.py 31 | env: 32 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 33 | SLACK_TOKEN: ${{ secrets.RUPY_TRACER_RELEASES_TOKEN }} 34 | SLACK_SERVICE: ${{ secrets.RUPY_TRACER_RELEASES_SERVICE_ID }} 35 | SLACK_TEAM: ${{ secrets.RUPY_TOWN_CRIER_TEAM_ID }} 36 | 37 | -------------------------------------------------------------------------------- /lib/instana/instrumentation/aws_sdk_dynamodb.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Instrumentation 6 | class DynamoDB < Seahorse::Client::Plugin 7 | class Handler < Seahorse::Client::Handler 8 | def call(context) 9 | dynamo_tags = { 10 | op: format_operation(context.operation_name), 11 | table: table_name_from(context) 12 | } 13 | 14 | ::Instana.tracer.in_span(:dynamodb, attributes: {dynamodb: dynamo_tags}) { @handler.call(context) } 15 | end 16 | 17 | private 18 | 19 | def table_name_from(context) 20 | context.params[:table_name] || context.params[:global_table_name] || 'Unknown' 21 | end 22 | 23 | def format_operation(name) 24 | case name 25 | when :create_table 26 | 'create' 27 | when :list_tables 28 | 'list' 29 | when :get_item 30 | 'get' 31 | when :put_item 32 | 'put' 33 | when :update_item 34 | 'update' 35 | when :delete_item 36 | 'delete' 37 | else 38 | name.to_s 39 | end 40 | end 41 | end 42 | 43 | def add_handlers(handlers, _config) 44 | handlers.add(Handler, step: :initialize) 45 | end 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /lib/instana/instrumentation/sequel.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2024 2 | 3 | module Instana 4 | module Instrumentation 5 | module Sequel 6 | IGNORED_SQL = %w[BEGIN COMMIT SET PRAGMA].freeze 7 | VERSION_SELECT_STATEMENT = "SELECT VERSION()".freeze 8 | SANITIZE_REGEXP = /('[\s\S][^']*'|\d*\.\d+|\d+|NULL)/i 9 | 10 | def log_connection_yield(sql, conn, *args) 11 | call_payload = { 12 | sequel: { 13 | adapter: opts[:adapter], 14 | host: opts[:host], 15 | username: opts[:user], 16 | db: opts[:database], 17 | sql: maybe_sanitize(sql) 18 | } 19 | } 20 | maybe_trace(call_payload) { super(sql, conn, *args) } 21 | end 22 | 23 | private 24 | 25 | def maybe_sanitize(sql) 26 | ::Instana.config[:sanitize_sql] ? sql.gsub(SANITIZE_REGEXP, '?') : sql 27 | end 28 | 29 | def maybe_trace(call_payload, &block) 30 | if ::Instana.tracer.tracing? && !ignored?(call_payload) 31 | ::Instana.tracer.in_span(:sequel, attributes: call_payload, &block) 32 | else 33 | yield 34 | end 35 | end 36 | 37 | def ignored?(call_payload) 38 | IGNORED_SQL.any? { |s| call_payload[:sequel][:sql].upcase.start_with?(s) } || call_payload[:sequel][:sql].upcase == VERSION_SELECT_STATEMENT 39 | end 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /test/instrumentation/shoryuken_test.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | require 'test_helper' 5 | require 'ostruct' 6 | 7 | class ShoryukenTest < Minitest::Test 8 | def setup 9 | clear_all! 10 | @middleware = Instana::Instrumentation::Shoryuken.new 11 | end 12 | 13 | def test_start_trace_with_context 14 | id = Instana::Util.generate_id 15 | message = OpenStruct.new( 16 | queue_url: 'http://example.com', 17 | message_attributes: { 18 | "X_INSTANA_T" => OpenStruct.new(string_value: id), 19 | "X_INSTANA_S" => OpenStruct.new(string_value: id), 20 | "X_INSTANA_L" => OpenStruct.new(string_value: '1') 21 | } 22 | ) 23 | 24 | @middleware.call(nil, nil, message, nil) {} 25 | 26 | span = ::Instana.processor.queued_spans.first 27 | 28 | assert_equal id, span[:t] 29 | assert_equal id, span[:p] 30 | assert_equal 'entry', span[:data][:sqs][:sort] 31 | assert_equal 'http://example.com', span[:data][:sqs][:queue] 32 | end 33 | 34 | def test_start_trace 35 | message = OpenStruct.new( 36 | queue_url: 'http://example.com' 37 | ) 38 | 39 | @middleware.call(nil, nil, message, nil) {} 40 | 41 | span = ::Instana.processor.queued_spans.first 42 | 43 | assert_nil span[:p] 44 | assert_equal 'entry', span[:data][:sqs][:sort] 45 | assert_equal 'http://example.com', span[:data][:sqs][:queue] 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2016 3 | 4 | require 'bundler/gem_tasks' 5 | require 'rake/testtask' 6 | 7 | require 'json' 8 | 9 | Rake::TestTask.new(:test) do |t| 10 | t.verbose = false 11 | t.warning = false 12 | t.ruby_opts = ["--parser=parse.y"] if Gem::Version.new(RUBY_VERSION) > Gem::Version.new('3.3') 13 | 14 | t.libs << "test" 15 | t.libs << "lib" 16 | 17 | if ENV['APPRAISAL_INITIALIZED'] 18 | appraised_group = File.basename(ENV['BUNDLE_GEMFILE']).split(/_[0-9]+\./).first 19 | suite_files = Dir['test/{instrumentation,frameworks}/*_test.rb'] 20 | 21 | t.test_files = suite_files.select { |f| File.basename(f).start_with?(appraised_group) } 22 | else 23 | t.test_files = Dir[ 24 | 'test/*_test.rb', 25 | 'test/{agent,trace,backend,snapshot,span_filtering}/*_test.rb' 26 | ] 27 | end 28 | end 29 | 30 | namespace :coverage do 31 | task :merge_reports do 32 | require 'simplecov' 33 | require 'simplecov_json_formatter' 34 | 35 | SimpleCov.start do 36 | enable_coverage :branch 37 | SimpleCov.collate Dir["partial_coverage_results/.resultset-*.json"] do 38 | formatter SimpleCov::Formatter::MultiFormatter.new( 39 | [ 40 | SimpleCov::Formatter::SimpleFormatter, 41 | SimpleCov::Formatter::JSONFormatter 42 | ] 43 | ) 44 | end 45 | end 46 | end 47 | end 48 | 49 | task :default => :test 50 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: File a bug report 3 | title: "[Bug]: " 4 | labels: [bug] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thank you for taking the time to fill out this report. Remember that these issues are public and if you need to discuss implementation specific issues securely, please [use our support portal](https://www.ibm.com/mysupport). 10 | - type: textarea 11 | id: problem-description 12 | attributes: 13 | label: Problem Description 14 | description: What was the issue that caused you to file this bug? 15 | validations: 16 | required: true 17 | - type: textarea 18 | id: mcve 19 | attributes: 20 | label: Minimal, Complete, Verifiable, Example 21 | description: Can you provide steps needed to reproduce this issue outside of your application? 22 | validations: 23 | required: false 24 | - type: textarea 25 | id: gemfile-lock 26 | attributes: 27 | label: Gemfile.lock 28 | description: Please paste the contents of the Gemfile.lock for the application that was affected by this bug. 29 | render: shell 30 | validations: 31 | required: true 32 | - type: textarea 33 | id: ruby-version 34 | attributes: 35 | label: Ruby Version 36 | description: What version of Ruby was the application running under when it encountered this bug? 37 | render: shell 38 | validations: 39 | required: true 40 | -------------------------------------------------------------------------------- /lib/instana/instrumentation/shoryuken.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Instrumentation 6 | class Shoryuken 7 | def call(_worker_instance, _queue, sqs_message, _body, &block) 8 | if sqs_message.is_a? Array 9 | return yield 10 | end 11 | 12 | sqs_tags = { 13 | sort: 'entry', 14 | queue: sqs_message.queue_url 15 | } 16 | 17 | context = incomming_context_from(sqs_message.message_attributes) 18 | instana_context = Instana::SpanContext.new(trace_id: context[:trace_id], span_id: context[:span_id], level: context[:level]) 19 | Trace.with_span(OpenTelemetry::Trace.non_recording_span(instana_context)) do 20 | ::Instana.tracer.in_span(:sqs, attributes: {sqs: sqs_tags}, &block) 21 | end 22 | end 23 | 24 | private 25 | 26 | def incomming_context_from(attributes) 27 | trace_id = read_message_header(attributes, 'X_INSTANA_T') 28 | span_id = read_message_header(attributes, 'X_INSTANA_S') 29 | level = read_message_header(attributes, 'X_INSTANA_L') 30 | 31 | { 32 | trace_id: trace_id, 33 | span_id: span_id, 34 | level: level 35 | }.reject { |_, v| v.nil? } 36 | end 37 | 38 | def read_message_header(attributes, key) 39 | attributes[key].string_value if attributes && attributes[key] && attributes[key].respond_to?(:string_value) 40 | end 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /test/trace/processor_test.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | require 'test_helper' 5 | 6 | class ProcessorTest < Minitest::Test 7 | def test_queued_spans_empty 8 | subject = Instana::Processor.new 9 | assert_equal [], subject.queued_spans 10 | end 11 | 12 | def test_queued_spans_valid_level 13 | clear_all! 14 | subject = Instana::Processor.new 15 | 16 | span_context = Instana::SpanContext.new(trace_id: '9', span_id: '8', level: 0) 17 | span = Instana::Span.new(:rack, span_context) 18 | span2 = Instana::Span.new(:"net-http") 19 | 20 | subject.on_finish(span) 21 | subject.on_finish(span2) 22 | 23 | spans = subject.queued_spans 24 | valid_span, = spans 25 | 26 | assert_equal 1, spans.length 27 | assert_equal :"net-http", valid_span[:n] 28 | end 29 | 30 | def test_queued_spans_invalid_type 31 | subject = Instana::Processor.new 32 | subject.on_finish(false) 33 | 34 | assert_equal [], subject.queued_spans 35 | end 36 | 37 | def test_send 38 | ENV['INSTANA_TEST'] = nil 39 | 40 | subject = Instana::Processor.new 41 | span = Instana::Span.new(:rack) 42 | subject.on_finish(span) 43 | 44 | was_invoked = false 45 | 46 | subject.send do |spans| 47 | was_invoked = true 48 | rack_span, = spans 49 | 50 | assert_equal 1, spans.length 51 | assert_equal :rack, rack_span[:n] 52 | end 53 | 54 | assert was_invoked 55 | ensure 56 | ENV['INSTANA_TEST'] = 'true' 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /.tekton/github-set-status-task.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: tekton.dev/v1 3 | kind: Task 4 | metadata: 5 | name: github-set-status 6 | spec: 7 | params: 8 | - name: SHA 9 | - name: STATE 10 | - name: REPO 11 | volumes: 12 | - name: githubtoken 13 | secret: 14 | secretName: githubtoken 15 | steps: 16 | - name: set-status 17 | # curlimages/curl:8.6.0 18 | image: curlimages/curl@sha256:f2237028bed58de91f62aea74260bb2a299cf12fbcabc23cfaf125fef276c884 19 | env: 20 | - name: SHA 21 | value: $(params.SHA) 22 | - name: STATE 23 | value: $(params.STATE) 24 | - name: REPO 25 | value: $(params.REPO) 26 | volumeMounts: 27 | - name: githubtoken 28 | mountPath: /etc/github-set-status 29 | script: | 30 | #!/bin/sh 31 | curl -L \ 32 | -X POST \ 33 | -H "Accept: application/vnd.github+json" \ 34 | -H "Authorization: Bearer $(cat /etc/github-set-status/token)" \ 35 | -H "Content-Type: application/json" \ 36 | -H "X-GitHub-Api-Version: 2022-11-28" \ 37 | "https://api.github.com/repos/${REPO}/statuses/${SHA}" \ 38 | -d '{ 39 | "state":"'${STATE}'", 40 | "target_url":"http://localhost:8001/api/v1/namespaces/tekton-pipelines/services/tekton-dashboard:http/proxy/#/namespaces/default/pipelineruns/", 41 | "description":"Tekton build is in state: '${STATE}'", 42 | "context":"Tekton" 43 | }' 44 | -------------------------------------------------------------------------------- /test/instrumentation/rails_active_record_database_missing_test.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | require 'test_helper' 5 | require 'support/apps/active_record/active_record' 6 | require 'fileutils' 7 | 8 | class RailsActiveRecordDatabaseMissingTest < Minitest::Test 9 | def setup 10 | skip unless ENV['DATABASE_URL'] 11 | clear_all! 12 | @old_url = ENV['DATABASE_URL'] 13 | SQLite3::Database.new('/tmp/test.db') 14 | ENV['DATABASE_URL'] = 'sqlite3:///tmp/test.db' 15 | 16 | @connection_pool = ActiveRecord::Base.establish_connection(ENV['DATABASE_URL']) 17 | c = ::ActiveRecord::Base.connection 18 | c.execute 'PRAGMA journal_mode=DELETE' 19 | c.execute 'PRAGMA locking_mode=NORMAL' 20 | ActiveRecord::Migration.suppress_messages do 21 | ActiveRecord::Migration.run(CreateBlocks, direction: :up) 22 | end 23 | end 24 | 25 | def teardown 26 | @connection_pool.disconnect 27 | ENV['DATABASE_URL'] = @old_url 28 | end 29 | 30 | def test_error_on_missing_database 31 | assert_raises(ActiveRecord::StatementInvalid) do 32 | Instana.tracer.in_span(:ar_test, attributes: {}) do 33 | b = Block.new 34 | FileUtils.rm('/tmp/test.db') 35 | b.save! 36 | end 37 | end 38 | spans = ::Instana.processor.queued_spans 39 | span = find_first_span_by_name(spans, :activerecord) 40 | 41 | assert_equal 1, span[:ec] 42 | assert span[:data][:activerecord][:error].include?("SQLite3::ReadOnlyException: attempt to write a readonly database") 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/instana/instrumentation/aws_sdk_s3.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Instrumentation 6 | class S3 < Seahorse::Client::Plugin 7 | class Handler < Seahorse::Client::Handler 8 | def call(context) 9 | s3_tags = { 10 | op: format_operation(context.operation_name), 11 | bucket: bucket_name_from(context), 12 | key: key_from_context(context) 13 | }.reject { |_, v| v.nil? } 14 | 15 | ::Instana.tracer.in_span(:s3, attributes: {s3: s3_tags}) { @handler.call(context) } 16 | end 17 | 18 | private 19 | 20 | def bucket_name_from(context) 21 | context.params[:bucket] || 'Unknown' 22 | end 23 | 24 | def key_from_context(context) 25 | context.params[:key] 26 | end 27 | 28 | def format_operation(name) 29 | case name 30 | when :create_bucket 31 | 'createBucket' 32 | when :delete_bucket 33 | 'deleteBucket' 34 | when :delete_object 35 | 'delete' 36 | when :get_object 37 | 'get' 38 | when :head_object 39 | 'metadata' 40 | when :list_objects 41 | 'list' 42 | when :put_object 43 | 'list' 44 | else 45 | name.to_s 46 | end 47 | end 48 | end 49 | 50 | def add_handlers(handlers, _config) 51 | handlers.add(Handler, step: :initialize) 52 | end 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /test/support/proc/self/sched: -------------------------------------------------------------------------------- 1 | cat (35, #threads: 1) 2 | ------------------------------------------------------------------- 3 | se.exec_start : 820865.307850 4 | se.vruntime : 805.150445 5 | se.sum_exec_runtime : 0.459800 6 | se.nr_migrations : 0 7 | nr_switches : 2 8 | nr_voluntary_switches : 2 9 | nr_involuntary_switches : 0 10 | se.load.weight : 1048576 11 | se.runnable_weight : 1048576 12 | se.avg.load_sum : 46068 13 | se.avg.runnable_load_sum : 46068 14 | se.avg.util_sum : 23811867 15 | se.avg.load_avg : 1006 16 | se.avg.runnable_load_avg : 1006 17 | se.avg.util_avg : 507 18 | se.avg.last_update_time : 820865307648 19 | se.avg.util_est.ewma : 128 20 | se.avg.util_est.enqueued : 512 21 | policy : 0 22 | prio : 120 23 | clock-delta : 17 24 | -------------------------------------------------------------------------------- /extras/license_header.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | require 'time' 5 | 6 | module RuboCop 7 | module Cop 8 | module Instana 9 | # Ensures the license header is present in each ruby file 10 | class LicenseHeader < Base 11 | extend AutoCorrector 12 | 13 | MSG = 'The license header should be present in each file.'.freeze 14 | HEADER = '(c) Copyright IBM Corp.'.freeze 15 | HEADER_TEMPLATE = <<~HERE.freeze 16 | # (c) Copyright IBM Corp. %d 17 | HERE 18 | 19 | def on_new_investigation 20 | first_statement = processed_source.tokens.detect { |t| t.type != :tCOMMENT } 21 | file_name = first_statement.pos.source_buffer.name 22 | header_comment = processed_source.comments.detect do |comment| 23 | first_statement_line = first_statement.pos.line 24 | comment_line = comment.loc.line 25 | 26 | (comment_line < first_statement_line) && comment.text.include?(HEADER) 27 | end 28 | 29 | return if header_comment 30 | 31 | add_offense(first_statement.pos) do |corrector| 32 | current_year = Time.now.year 33 | created_time = `git log --diff-filter=A --follow --format=%aD -1 -- #{file_name}` 34 | created_year = created_time.empty? ? current_year : Time.parse(created_time).year 35 | 36 | header_text = format(HEADER_TEMPLATE, current_year, created_year) 37 | corrector.insert_before(first_statement.pos, "\n#{header_text}\n") 38 | end 39 | end 40 | end 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/instana/instrumentation/aws_sdk_lambda.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | require 'base64' 4 | 5 | module Instana 6 | module Instrumentation 7 | class Lambda < Seahorse::Client::Plugin 8 | class Handler < Seahorse::Client::Handler 9 | def call(context) 10 | return @handler.call(context) unless [:invoke_async, :invoke].include?(context.operation_name) 11 | 12 | if context.params[:client_context].nil? && ::Instana.tracer.tracing? && context.operation_name == :invoke 13 | span_context = ::Instana.tracer.context 14 | payload = { 15 | 'X-INSTANA-T' => span_context.trace_id, 16 | 'X-INSTANA-S' => span_context.span_id, 17 | 'X-INSTANA-L' => span_context.level.to_s 18 | } 19 | 20 | context.params[:client_context] = Base64.strict_encode64(JSON.dump(payload)) 21 | end 22 | 23 | tags = { 24 | function: context.params[:function_name], 25 | type: context.params[:invocation_type] 26 | }.reject { |_, v| v.nil? } 27 | 28 | ::Instana.tracer.in_span(:"aws.lambda.invoke", attributes: {aws: {lambda: {invoke: tags}}}) do 29 | response = @handler.call(context) 30 | if response.respond_to? :status_code 31 | ::Instana.tracer.log_info(:http => {:status => response.status_code }) 32 | end 33 | response 34 | end 35 | end 36 | end 37 | 38 | def add_handlers(handlers, _config) 39 | handlers.add(Handler, step: :initialize) 40 | end 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /test/frameworks/roda_test.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2016 5 | 6 | require 'test_helper' 7 | require 'rack/test' 8 | 9 | class RodaTest < Minitest::Test 10 | include Rack::Test::Methods 11 | APP = Rack::Builder.parse_file('test/support/apps/roda/config.ru').first 12 | 13 | def app 14 | APP 15 | end 16 | 17 | def test_basic_get 18 | clear_all! 19 | 20 | r = get '/hello' 21 | assert last_response.ok? 22 | 23 | assert r.headers.key?("X-Instana-T") 24 | assert r.headers.key?("X-Instana-S") 25 | 26 | spans = ::Instana.processor.queued_spans 27 | assert_equal 1, spans.count 28 | 29 | first_span = spans.first 30 | assert_equal :rack, first_span[:n] 31 | assert first_span.key?(:data) 32 | assert first_span[:data].key?(:http) 33 | 34 | assert first_span[:data][:http].key?(:method) 35 | assert_equal "GET", first_span[:data][:http][:method] 36 | 37 | assert first_span[:data][:http].key?(:url) 38 | assert_equal "/hello", first_span[:data][:http][:url] 39 | 40 | assert first_span[:data][:http].key?(:status) 41 | assert_equal 200, first_span[:data][:http][:status] 42 | 43 | assert first_span[:data][:http].key?(:host) 44 | assert_equal "example.org", first_span[:data][:http][:host] 45 | end 46 | 47 | def test_path_template 48 | clear_all! 49 | 50 | r = get '/greet/instana' 51 | assert last_response.ok? 52 | 53 | spans = ::Instana.processor.queued_spans 54 | assert_equal 1, spans.count 55 | 56 | first_span = spans.first 57 | assert_equal :rack, first_span[:n] 58 | assert_equal '/greet/{name}', first_span[:data][:http][:path_tpl] 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /lib/instana/instrumentation/action_view.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Instrumentation 6 | module ActionView 7 | module PartialRenderer 8 | def render_partial(*args) 9 | call_payload = { 10 | render: { 11 | type: :partial, 12 | name: @options.is_a?(Hash) ? @options[:partial].to_s : 'Unknown' 13 | } 14 | } 15 | 16 | ::Instana.tracer.in_span(:render, attributes: call_payload) { super(*args) } 17 | end 18 | 19 | def render_collection(*args) 20 | call_payload = { 21 | render: { 22 | type: :collection, 23 | name: @path.to_s 24 | } 25 | } 26 | 27 | ::Instana.tracer.in_span(:render, attributes: call_payload) { super(*args) } 28 | end 29 | 30 | def render_partial_template(*args) 31 | call_payload = { 32 | render: { 33 | type: :partial, 34 | name: @options.is_a?(Hash) ? @options[:partial].to_s : 'Unknown' 35 | } 36 | } 37 | 38 | ::Instana.tracer.in_span(:render, attributes: call_payload) { super(*args) } 39 | end 40 | end 41 | 42 | module CollectionRenderer 43 | def render_collection(*args) 44 | call_payload = { 45 | render: { 46 | type: :collection, 47 | name: @options.is_a?(Hash) ? @options[:partial].to_s : 'Unknown' 48 | } 49 | } 50 | 51 | ::Instana.tracer.in_span(:render, attributes: call_payload) { super(*args) } 52 | end 53 | end 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /lib/instana/instrumentation/active_record.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Instrumentation 6 | module ActiveRecord 7 | IGNORED_NAMES = %w[SCHEMA EXPLAIN CACHE].freeze 8 | IGNORED_SQL = %w[BEGIN COMMIT SET].freeze 9 | SANITIZE_REGEXP = /('[\s\S][^']*'|\d*\.\d+|\d+|NULL)/i 10 | 11 | def log(sql, name = 'SQL', binds = [], *args, **kwargs, &block) 12 | call_payload = { 13 | activerecord: { 14 | adapter: @config[:adapter], 15 | host: @config[:host], 16 | username: @config[:username], 17 | db: @config[:database], 18 | sql: maybe_sanitize(sql) 19 | } 20 | } 21 | 22 | if binds.all? { |b| b.respond_to?(:value_before_type_cast) } && !::Instana.config[:sanitize_sql] 23 | mapped = binds.map(&:value_before_type_cast) 24 | call_payload[:activerecord][:binds] = mapped 25 | end 26 | 27 | maybe_trace(call_payload, name) { super(sql, name, binds, *args, **kwargs, &block) } 28 | end 29 | 30 | private 31 | 32 | def maybe_sanitize(sql) 33 | ::Instana.config[:sanitize_sql] ? sql.gsub(SANITIZE_REGEXP, '?') : sql 34 | end 35 | 36 | def maybe_trace(call_payload, name, &blk) 37 | if ::Instana.tracer.tracing? && !ignored?(call_payload, name) 38 | ::Instana.tracer.in_span(:activerecord, attributes: call_payload, &blk) 39 | else 40 | yield 41 | end 42 | end 43 | 44 | def ignored?(call_payload, name) 45 | IGNORED_NAMES.include?(name) || 46 | IGNORED_SQL.any? { |s| call_payload[:activerecord][:sql].upcase.start_with?(s) } 47 | end 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /test/frameworks/cuba_test.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2016 3 | 4 | require 'test_helper' 5 | require 'rack/test' 6 | 7 | class CubaTest < Minitest::Test 8 | include Rack::Test::Methods 9 | 10 | # rack < 3.0.0 returns a two long array `app, options` 11 | # rack >= 3.0.0 returns only the app 12 | APP, * = Rack::Builder.parse_file('test/support/apps/cuba/config.ru') 13 | 14 | def app 15 | APP 16 | end 17 | 18 | def test_basic_get 19 | clear_all! 20 | 21 | r = get '/hello' 22 | assert last_response.ok? 23 | 24 | assert r.headers.key?("X-Instana-T") 25 | assert r.headers.key?("X-Instana-S") 26 | 27 | spans = ::Instana.processor.queued_spans 28 | assert_equal 1, spans.count 29 | 30 | first_span = spans.first 31 | assert_equal :rack, first_span[:n] 32 | assert first_span.key?(:data) 33 | assert first_span[:data].key?(:http) 34 | 35 | assert first_span[:data][:http].key?(:method) 36 | assert_equal "GET", first_span[:data][:http][:method] 37 | 38 | assert first_span[:data][:http].key?(:url) 39 | assert_equal "/hello", first_span[:data][:http][:url] 40 | 41 | assert first_span[:data][:http].key?(:status) 42 | assert_equal 200, first_span[:data][:http][:status] 43 | 44 | assert first_span[:data][:http].key?(:host) 45 | assert_equal "example.org", first_span[:data][:http][:host] 46 | end 47 | 48 | def test_path_template 49 | clear_all! 50 | 51 | r = get '/greet/instana' 52 | assert last_response.ok? 53 | 54 | spans = ::Instana.processor.queued_spans 55 | assert_equal 1, spans.count 56 | 57 | first_span = spans.first 58 | assert_equal :rack, first_span[:n] 59 | assert_equal '/greet/{name}', first_span[:data][:http][:path_tpl] 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /lib/instana/instrumentation/action_cable.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Instrumentation 6 | module ActionCableConnection 7 | def instana_trace_context 8 | @instana_trace_context 9 | end 10 | 11 | def process 12 | @instana_trace_context ||= ::Instana.tracer.tracing? ? ::Instana.tracer.current_span.context : {} 13 | super 14 | end 15 | end 16 | 17 | module ActionCableChannel 18 | def transmit(data, via: nil) 19 | rpc_tags = { 20 | service: ::Instana::Util.get_app_name, 21 | rpc: { 22 | flavor: :actioncable, 23 | call: self.class.to_s, 24 | call_type: :transmit, 25 | host: Socket.gethostname 26 | } 27 | } 28 | 29 | context = connection.instana_trace_context 30 | Trace.with_span(OpenTelemetry::Trace.non_recording_span(context)) do 31 | ::Instana.tracer.in_span(:'rpc-server', attributes: rpc_tags) do 32 | super(data, via: via) 33 | end 34 | end 35 | end 36 | 37 | def dispatch_action(action, data) 38 | rpc_tags = { 39 | service: ::Instana::Util.get_app_name, 40 | rpc: { 41 | flavor: :actioncable, 42 | call: "#{self.class}##{action}", 43 | call_type: :action, 44 | host: Socket.gethostname 45 | } 46 | } 47 | 48 | context = connection.instana_trace_context 49 | Trace.with_span(OpenTelemetry::Trace.non_recording_span(context)) do 50 | ::Instana.tracer.in_span(:'rpc-server', attributes: rpc_tags) do 51 | super(action, data) 52 | end 53 | end 54 | end 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /lib/instana/activator.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | class Activator 6 | class << self 7 | attr_reader :trace_point, :activators 8 | 9 | def start 10 | # :nocov: 11 | @trace_point = TracePoint.new(:end) do 12 | activated = ::Instana::Activator.call 13 | ::Instana.logger.debug { "Activated #{activated.join(', ')}" } unless activated.empty? 14 | end 15 | 16 | @trace_point.enable if enabled? 17 | # :nocov: 18 | end 19 | 20 | def call 21 | @activators ||= [] 22 | activated, @activators = @activators.partition(&:call) 23 | activated 24 | end 25 | 26 | def inherited(subclass) 27 | super(subclass) 28 | 29 | @activators ||= [] 30 | @activators << subclass.new 31 | end 32 | 33 | private 34 | 35 | def enabled? 36 | ENV.fetch('INSTANA_DISABLE_AUTO_INSTR', 'false').eql?('false') || !ENV.key?('INSTANA_DISABLE') 37 | end 38 | end 39 | 40 | def call 41 | instrument if can_instrument? 42 | end 43 | end 44 | end 45 | 46 | DIRECTORY_OF_ACTIVATORS = "#{__dir__}/activators/".freeze 47 | 48 | def activated_set 49 | all_activators = Set.new( 50 | Dir["*.rb", base: DIRECTORY_OF_ACTIVATORS].map do |f| 51 | File.basename(f, '.rb') 52 | end 53 | ) 54 | 55 | if ENV['INSTANA_ACTIVATE_SET'] 56 | selected_activators = Set.new(ENV.fetch('INSTANA_ACTIVATE_SET', '').split(',')) 57 | all_activators & selected_activators 58 | else 59 | all_activators 60 | end 61 | end 62 | 63 | def require_selected_activator_files 64 | activated_set.each do |f| 65 | require("#{DIRECTORY_OF_ACTIVATORS}#{f}.rb") 66 | end 67 | end 68 | 69 | require_selected_activator_files 70 | -------------------------------------------------------------------------------- /lib/instana/backend/process_info.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Backend 6 | # Wrapper around {Sys::ProcTable} that adds support for reading the /proc 7 | # file system for extra information around containers 8 | # @since 1.197.0 9 | class ProcessInfo < SimpleDelegator 10 | def name 11 | cmdline 12 | .split(' ').first 13 | end 14 | 15 | def arguments 16 | _, *arguments = cmdline.split(' ') 17 | clean_arguments(arguments) 18 | end 19 | 20 | def parent_pid 21 | if in_container? && !sched_pid.nil? 22 | sched_pid 23 | else 24 | pid 25 | end 26 | end 27 | 28 | def from_parent_namespace 29 | !in_container? || in_container? && sched_pid != pid 30 | end 31 | 32 | def cpuset 33 | path = "/proc/#{pid}/cpuset" 34 | return unless File.exist?(path) 35 | 36 | File.read(path).strip 37 | end 38 | 39 | def in_container? 40 | !cpuset.nil? && cpuset != '/' 41 | end 42 | 43 | def sched_pid 44 | path = '/proc/self/sched' 45 | return unless File.exist?(path) 46 | 47 | File.read(path).match(/\d+/).to_s.to_i 48 | end 49 | 50 | def memory_used 51 | if RbConfig::CONFIG['host_os'].include?('darwin') 52 | rss 53 | else 54 | rss * 4096 55 | end 56 | end 57 | 58 | private 59 | 60 | def clean_arguments(arguments) 61 | return arguments unless RbConfig::CONFIG['host_os'].include?('darwin') 62 | 63 | arguments.reject do |a| 64 | if a.include?('=') 65 | k, = a.split('=', 2) 66 | ENV[k] 67 | end 68 | end 69 | end 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /lib/instana/snapshot/google_cloud_run_process.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Snapshot 6 | # @since 1.199.0 7 | class GoogleCloudRunProcess 8 | ID = 'com.instana.plugin.process'.freeze 9 | 10 | def initialize(metadata_uri: 'http://metadata.google.internal') 11 | @metadata_uri = URI(metadata_uri) 12 | @client = Backend::RequestClient.new(@metadata_uri.host, @metadata_uri.port, use_ssl: @metadata_uri.scheme == "https") 13 | @start_time = Time.now 14 | end 15 | 16 | def entity_id 17 | Process.pid.to_s 18 | end 19 | 20 | def data 21 | proc_table = Sys::ProcTable.ps(pid: Process.pid) 22 | process = Backend::ProcessInfo.new(proc_table) 23 | 24 | { 25 | pid: process.pid.to_i, 26 | env: ENV.to_h, 27 | exec: process.name, 28 | args: process.arguments, 29 | user: process.uid, 30 | group: process.gid, 31 | start: @start_time.to_i * 1000, 32 | containerType: 'gcpCloudRunInstance', 33 | container: lookup('/computeMetadata/v1/instance/id'), 34 | "com.instana.plugin.host.name": "gcp:cloud-run:revision:#{ENV['K_REVISION']}" 35 | } 36 | end 37 | 38 | def snapshot 39 | { 40 | name: ID, 41 | entityId: entity_id, 42 | data: data 43 | } 44 | end 45 | 46 | private 47 | 48 | def lookup(resource) 49 | path = @metadata_uri.path + resource 50 | response = @client.send_request('GET', path, nil, {'Metadata-Flavor' => 'Google'}) 51 | 52 | raise "Unable to get `#{path}`. Got `#{response.code}` `#{response['location']}`." unless response.ok? 53 | 54 | response.body 55 | end 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/instana/secrets.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | require 'uri' 5 | require 'cgi' 6 | 7 | module Instana 8 | class Secrets 9 | def initialize(logger: ::Instana.logger) 10 | @logger = logger 11 | end 12 | 13 | def remove_from_query(str, secret_values = Instana.agent.secret_values) 14 | return str unless secret_values && str 15 | 16 | begin 17 | url = URI(str) 18 | params = url.scheme ? CGI.parse(url.query || '') : CGI.parse(url.to_s) 19 | 20 | redacted = redact(params, secret_values) 21 | 22 | url.query = URI.encode_www_form(redacted) 23 | url.scheme ? CGI.unescape(url.to_s) : CGI.unescape(url.query) 24 | rescue URI::InvalidURIError => _e 25 | params = CGI.parse(str || '') 26 | redacted = redact(params, secret_values) 27 | CGI.unescape(URI.encode_www_form(redacted)) 28 | end 29 | end 30 | 31 | private 32 | 33 | def redact(params, secret_values) 34 | params.map do |k, v| 35 | needs_redaction = secret_values['list'] 36 | .any? { |t| matcher(secret_values['matcher']).(t,k) } 37 | [k, needs_redaction ? '' : v] 38 | end 39 | end 40 | 41 | def matcher(name) 42 | case name 43 | when 'equals-ignore-case' 44 | ->(expected, actual) { expected.casecmp(actual) == 0 } 45 | when 'equals' 46 | ->(expected, actual) { (expected <=> actual) == 0 } 47 | when 'contains-ignore-case' 48 | ->(expected, actual) { actual.downcase.include?(expected) } 49 | when 'contains' 50 | ->(expected, actual) { actual.include?(expected) } 51 | when 'regex' 52 | ->(expected, actual) { !Regexp.new(expected).match(actual).nil? } 53 | else 54 | @logger.warn("Matcher #{name} is not supported.") 55 | ->(_e, _a) { false } 56 | end 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /lib/instana/setup.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2016 3 | 4 | require 'logger' 5 | require 'concurrent' 6 | require 'sys-proctable' 7 | 8 | require 'instana/logger_delegator' 9 | require 'instana/instrumented_logger' 10 | 11 | require "instana/base" 12 | require "instana/config" 13 | require "instana/secrets" 14 | require "instana/trace/tracer" 15 | require "instana/trace/processor" 16 | 17 | require 'instana/serverless' 18 | 19 | require 'instana/activator' 20 | 21 | require 'instana/backend/request_client' 22 | require 'instana/backend/gc_snapshot' 23 | require 'instana/backend/process_info' 24 | 25 | require 'instana/snapshot/deltable' 26 | require 'instana/snapshot/ruby_process' 27 | require 'instana/snapshot/fargate_process' 28 | require 'instana/snapshot/fargate_task' 29 | require 'instana/snapshot/fargate_container' 30 | require 'instana/snapshot/docker_container' 31 | require 'instana/snapshot/lambda_function' 32 | require 'instana/snapshot/google_cloud_run_instance' 33 | require 'instana/snapshot/google_cloud_run_process' 34 | 35 | require 'instana/backend/host_agent_lookup' 36 | require 'instana/backend/host_agent_activation_observer' 37 | require 'instana/backend/host_agent_reporting_observer' 38 | 39 | require 'instana/backend/host_agent' 40 | require 'instana/backend/serverless_agent' 41 | require 'instana/backend/agent' 42 | require 'instana/trace' 43 | require 'instana/trace/tracer_provider' 44 | require 'instana/span_filtering' 45 | 46 | ::Instana.setup 47 | ::Instana.agent.setup 48 | 49 | # The Instana agent is now setup. The only remaining 50 | # task for a complete boot is to call 51 | # `Instana.agent.start` in the thread of your choice. 52 | # This can be in a simple `Thread.new` block or 53 | # any other thread system you may use (e.g. actor 54 | # threads). 55 | # 56 | # Note that `start` should only be called once per process. 57 | # 58 | # Thread.new do 59 | # ::Instana.agent.start 60 | # end 61 | -------------------------------------------------------------------------------- /test/support/apps/action_controller/config.ru: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | require 'rails' 5 | require 'action_controller/railtie' 6 | 7 | class TestControllerApplication < Rails::Application 8 | config.eager_load = 'test' 9 | config.consider_all_requests_local = false 10 | config.secret_key_base = 'test_key' 11 | config.secret_token = 'test_token' 12 | 13 | if Rails::VERSION::MAJOR > 5 14 | config.hosts.clear 15 | end 16 | 17 | routes.append do 18 | get '/base/world' => 'test_base#world' 19 | get '/base/raise_route_error' => 'test_base#raise_route_error' 20 | get '/base/error' => 'test_base#error' 21 | get '/base/log_warning' => 'test_base#log_warning' 22 | 23 | if defined?(::ActionController::API) 24 | get '/api/world' => 'test_api#world' 25 | get '/api/error' => 'test_api#error' 26 | get '/api/raise_route_error' => 'test_api#raise_route_error' 27 | end 28 | end 29 | end 30 | 31 | class TestBaseController < ActionController::Base 32 | def world 33 | render plain: 'Hello world!' 34 | end 35 | 36 | def raise_route_error 37 | raise ActionController::RoutingError, 'Simulated not found' 38 | end 39 | 40 | def error 41 | raise StandardError, "Warning: This is a simulated Error" 42 | end 43 | 44 | def log_warning 45 | Rails.logger.warn "This is a test warning" 46 | render plain: 'Test warning logged' 47 | end 48 | end 49 | 50 | if defined?(::ActionController::API) 51 | class TestApiController < ActionController::API 52 | def world 53 | render plain: 'Hello world!' 54 | end 55 | 56 | def raise_route_error 57 | raise ActionController::RoutingError, 'Simulated not found' 58 | end 59 | 60 | def error 61 | raise StandardError, "Warning: This is a simulated Socket API Error" 62 | end 63 | end 64 | end 65 | 66 | TestControllerApplication.initialize! 67 | 68 | run TestControllerApplication 69 | -------------------------------------------------------------------------------- /test/snapshot/fargate_task_test.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | require 'test_helper' 5 | 6 | class FargateTaskTest < Minitest::Test 7 | def setup 8 | @subject = Instana::Snapshot::FargateTask.new(metadata_uri: 'https://10.10.10.10:8080/v3') 9 | 10 | ENV['INSTANA_ZONE'] = 'test' 11 | ENV['INSTANA_TAGS'] = 'test=a,b,c' 12 | end 13 | 14 | def teardown 15 | ENV['INSTANA_ZONE'] = nil 16 | ENV['INSTANA_TAGS'] = nil 17 | end 18 | 19 | def test_snapshot 20 | stub_request(:get, 'https://10.10.10.10:8080/v3/task') 21 | .to_return(status: 200, body: File.read('test/support/ecs/task.json')) 22 | 23 | snapshot = @subject.snapshot 24 | 25 | assert_equal Instana::Snapshot::FargateTask::ID, snapshot[:name] 26 | assert_equal 'arn:aws:ecs:us-east-2:012345678910:task/9781c248-0edd-4cdb-9a93-f63cb662a5d3', snapshot[:entityId] 27 | 28 | assert_equal "arn:aws:ecs:us-east-2:012345678910:task/9781c248-0edd-4cdb-9a93-f63cb662a5d3", snapshot[:data][:taskArn] 29 | assert_equal "default", snapshot[:data][:clusterArn] 30 | assert_equal "nginx", snapshot[:data][:taskDefinition] 31 | assert_equal "5", snapshot[:data][:taskDefinitionVersion] 32 | assert_equal "us-east-2b", snapshot[:data][:availabilityZone] 33 | assert_equal "RUNNING", snapshot[:data][:desiredStatus] 34 | assert_equal "RUNNING", snapshot[:data][:knownStatus] 35 | assert_equal "2018-02-01T20:55:09.372495529Z", snapshot[:data][:pullStartedAt] 36 | assert_equal "2018-02-01T20:55:10.552018345Z", snapshot[:data][:pullStoppedAt] 37 | assert_equal "test", snapshot[:data][:instanaZone] 38 | assert_equal({"test" => "a", "b" => nil, "c" => nil}, snapshot[:data][:tags]) 39 | end 40 | 41 | def test_snapshot_error 42 | stub_request(:get, 'https://10.10.10.10:8080/v3/task') 43 | .to_return(status: 500) 44 | 45 | assert_raises do 46 | @subject.snapshot 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /bin/aws-lambda/create_lambda_release_gh.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # (c) Copyright IBM Corp. 2025 4 | 5 | # Script to make a new AWS Lambda Layer release on Github 6 | # Requires the Github CLI to be installed and configured: https://github.com/cli/cli 7 | 8 | require 'json' 9 | require 'open3' 10 | 11 | CHINA_REGIONS = File.readlines(File.join(File.dirname(__FILE__), 'aws-regions/cn-regions.txt')).map(&:chomp) 12 | OTHER_REGIONS = File.readlines(File.join(File.dirname(__FILE__), 'aws-regions/other_regions.txt')).map(&:chomp) 13 | 14 | if ARGV.length != 1 15 | raise ArgumentError, 'Please specify the layer version to release. e.g. "1"' 16 | end 17 | 18 | if ['-h', '--help'].include?(ARGV[0]) 19 | filename = File.basename(__FILE__) 20 | puts "Usage: #{filename} " 21 | puts "Example: #{filename} 14" 22 | puts "" 23 | puts "This will create a AWS Lambda release on Github such as:" 24 | puts "https://github.com/instana/ruby-sensor/releases/tag/v1" 25 | exit 0 26 | end 27 | 28 | # Check requirements first 29 | ["gh"].each do |cmd| 30 | if `which #{cmd}`.empty? 31 | puts "Can't find required tool: #{cmd}" 32 | exit 1 33 | end 34 | end 35 | 36 | regions = CHINA_REGIONS + OTHER_REGIONS 37 | version = ARGV[0] 38 | semantic_version = "v#{version}" 39 | title = "AWS Lambda Layer #{semantic_version}" 40 | 41 | body = "| AWS Region | ARN |\n" 42 | body += "| :-- | :-- |\n" 43 | regions.each do |region| 44 | body += "| #{region} | arn:aws:lambda:#{region}:410797082306:layer:instana-ruby:#{version} |\n" 45 | end 46 | 47 | stdout, stderr, status = Open3.capture3( 48 | "gh", "api", "repos/:owner/:repo/releases", "--method=POST", 49 | "-F", "tag_name=#{semantic_version}", 50 | "-F", "name=#{title}", 51 | "-F", "body=#{body}" 52 | ) 53 | 54 | if status.success? 55 | json_data = JSON.parse(stdout) 56 | puts "The release is available at:" 57 | puts json_data["html_url"] 58 | else 59 | puts "Error creating release: #{stderr}" 60 | exit 1 61 | end 62 | -------------------------------------------------------------------------------- /lib/instana/snapshot/fargate_process.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Snapshot 6 | # Describes the current process in terms of its existence inside of a Fargate container 7 | # @since 1.197.0 8 | class FargateProcess 9 | ID = 'com.instana.plugin.process'.freeze 10 | 11 | def initialize(metadata_uri: ENV['ECS_CONTAINER_METADATA_URI']) 12 | @metadata_uri = URI(metadata_uri) 13 | @client = Backend::RequestClient.new(@metadata_uri.host, @metadata_uri.port, use_ssl: @metadata_uri.scheme == "https") 14 | @start_time = Time.now 15 | end 16 | 17 | def entity_id 18 | Process.pid.to_s 19 | end 20 | 21 | def data 22 | proc_table = Sys::ProcTable.ps(pid: Process.pid) 23 | process = Backend::ProcessInfo.new(proc_table) 24 | 25 | { 26 | pid: process.pid.to_i, 27 | env: ENV.to_h, 28 | exec: process.name, 29 | args: process.arguments, 30 | user: process.uid, 31 | group: process.gid, 32 | start: @start_time.to_i * 1000, 33 | containerType: 'docker', 34 | container: container_id, 35 | "com.instana.plugin.host.name": task_id 36 | } 37 | end 38 | 39 | def snapshot 40 | { 41 | name: ID, 42 | entityId: entity_id, 43 | data: data 44 | } 45 | end 46 | 47 | private 48 | 49 | def lookup(resource = nil) 50 | path = resource ? @metadata_uri.path + resource : @metadata_uri.path 51 | response = @client.send_request('GET', path) 52 | 53 | raise "Unable to get `#{path}`. Got `#{response.code}`." unless response.ok? 54 | 55 | response.json 56 | end 57 | 58 | def container_id 59 | @container_id ||= lookup['DockerId'] 60 | end 61 | 62 | def task_id 63 | @task_id ||= lookup('/task')['TaskARN'] 64 | end 65 | end 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /bin/aws-lambda/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM public.ecr.aws/lambda/ruby:3.4 as ruby_dependencies_34 2 | 3 | # Install system dependencies for native extensions 4 | RUN dnf update -y && \ 5 | dnf install -y gcc gcc-c++ make 6 | 7 | # Configure bundler and install gems 8 | RUN echo "source 'https://rubygems.org'; gem 'instana'; gem 'get_process_mem'" > Gemfile && \ 9 | bundle config set --local path vendor/bundle && \ 10 | bundle install 11 | 12 | # Create the layer structure 13 | RUN mkdir -p ruby/gems/3.4.0 && \ 14 | cp -r vendor/bundle/ruby/3.4.0*/* ruby/gems/3.4.0/ 15 | 16 | FROM public.ecr.aws/lambda/ruby:3.3 as ruby_dependencies_33 17 | 18 | # Install system dependencies for native extensions 19 | RUN dnf update -y && \ 20 | dnf install -y gcc gcc-c++ make 21 | 22 | # Configure bundler and install gems 23 | RUN echo "source 'https://rubygems.org'; gem 'instana'; gem 'get_process_mem'" > Gemfile && \ 24 | bundle config set --local path vendor/bundle && \ 25 | bundle install 26 | 27 | # Create the layer structure 28 | RUN mkdir -p ruby/gems/3.3.0 && \ 29 | cp -r vendor/bundle/ruby/3.3.0*/* ruby/gems/3.3.0/ 30 | 31 | FROM public.ecr.aws/lambda/ruby:3.2 as ruby_dependencies_32 32 | 33 | # Install system dependencies for native extensions 34 | RUN yum update -y && \ 35 | yum install -y gcc gcc-c++ make && \ 36 | yum install -y zip 37 | 38 | # Configure bundler and install gems 39 | RUN echo "source 'https://rubygems.org'; gem 'instana'; gem 'get_process_mem'" > Gemfile && \ 40 | bundle config set --local path vendor/bundle && \ 41 | bundle install 42 | 43 | # Create the layer structure 44 | RUN mkdir -p ruby/gems/3.2.0 && \ 45 | cp -r vendor/bundle/ruby/3.2.0*/* ruby/gems/3.2.0/ 46 | 47 | FROM public.ecr.aws/docker/library/alpine:3.22 as final 48 | 49 | RUN apk add --no-cache zip unzip 50 | 51 | COPY --from=ruby_dependencies_34 /var/task/ruby/ ruby/ 52 | COPY --from=ruby_dependencies_33 /var/task/ruby/ ruby/ 53 | COPY --from=ruby_dependencies_32 /var/task/ruby/ ruby/ 54 | 55 | RUN zip -r /layer.zip ruby/ 56 | -------------------------------------------------------------------------------- /lib/instana/backend/host_agent_lookup.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | require 'csv' 5 | 6 | module Instana 7 | module Backend 8 | # Utility class to discover the agent that a given instance of the collector 9 | # needs to communicate with. 10 | # @since 1.197.0 11 | class HostAgentLookup 12 | def initialize(host = ::Instana.config[:agent_host], port = ::Instana.config[:agent_port], destination: '00000000') 13 | @host = host 14 | @port = port 15 | @destination = destination 16 | end 17 | 18 | # @return [RequestClient, NilClass] the request client to use to communicate with the agent or nil if no agent could be found 19 | def call 20 | host_listening?(@host, @port) || host_listening?(default_gateway, @port) 21 | end 22 | 23 | private 24 | 25 | # @return [RequestClient, nil] the request client if it responds to '/' with a success 26 | def host_listening?(host, port) 27 | client = RequestClient.new(host, port) 28 | client.send_request('GET', '/').ok? ? client : nil 29 | rescue Net::OpenTimeout, Errno::ECONNREFUSED => _e 30 | nil 31 | end 32 | 33 | # @return [String] the default gateway to attempt to connect to or the @host if a default gateway can not be identified 34 | def default_gateway 35 | return @host unless File.exist?('/proc/self/net/route') 36 | 37 | routes = CSV.read( 38 | '/proc/self/net/route', 39 | headers: :first_row, 40 | col_sep: "\t", 41 | header_converters: [->(v) { v.strip }], 42 | converters: [->(v) { v.strip }] 43 | ) 44 | 45 | route = routes.detect { |r| r['Destination'] == @destination } 46 | return @host unless route 47 | 48 | route['Gateway'] 49 | .split(/([0-9A-Z]{2})/) 50 | .reject(&:empty?) 51 | .reverse 52 | .map { |s| s.to_i(16) } 53 | .join('.') 54 | end 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /lib/instana/snapshot/google_cloud_run_instance.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Snapshot 6 | # @since 1.199 7 | class GoogleCloudRunInstance 8 | ID = 'com.instana.plugin.gcp.run.revision.instance'.freeze 9 | 10 | def initialize(metadata_uri: 'http://metadata.google.internal') 11 | @metadata_uri = URI(metadata_uri) 12 | @client = Backend::RequestClient.new(@metadata_uri.host, @metadata_uri.port, use_ssl: @metadata_uri.scheme == "https") 13 | end 14 | 15 | def entity_id 16 | lookup('/computeMetadata/v1/instance/id') 17 | end 18 | 19 | def data 20 | { 21 | runtime: 'ruby', 22 | region: gcp_region, 23 | service: ENV['K_SERVICE'], 24 | configuration: ENV['K_CONFIGURATION'], 25 | revision: ENV['K_REVISION'], 26 | instanceId: entity_id, 27 | port: ENV['PORT'], 28 | numericProjectId: lookup('/computeMetadata/v1/project/numeric-project-id'), 29 | projectId: lookup('/computeMetadata/v1/project/project-id') 30 | }.reject { |_, v| v.nil? } 31 | end 32 | 33 | def snapshot 34 | { 35 | name: ID, 36 | entityId: entity_id, 37 | data: data 38 | } 39 | end 40 | 41 | def source 42 | { 43 | hl: true, 44 | cp: 'gcp', 45 | e: entity_id 46 | } 47 | end 48 | 49 | def host_name 50 | "gcp:cloud-run:revision:#{ENV['K_REVISION']}" 51 | end 52 | 53 | private 54 | 55 | def gcp_region 56 | lookup('/computeMetadata/v1/instance/zone').split('/').last 57 | end 58 | 59 | def lookup(resource) 60 | path = @metadata_uri.path + resource 61 | response = @client.send_request('GET', path, nil, {'Metadata-Flavor' => 'Google'}) 62 | 63 | raise "Unable to get `#{path}`. Got `#{response.code}` `#{response['location']}`." unless response.ok? 64 | 65 | response.body 66 | end 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /test/instrumentation/rails_action_mailer_test.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | require 'test_helper' 5 | require 'action_mailer' 6 | 7 | class RailsActionMailerTest < Minitest::Test 8 | class TestMailer < ActionMailer::Base 9 | def sample_email 10 | mail_version = Gem::Specification.find_by_name('mail').version 11 | if mail_version >= Gem::Version.new('2.8.1') 12 | Mail.new do 13 | from 'test@example.com' 14 | to 'test@example.com' 15 | subject 'Test Email' 16 | body 'Hello' 17 | content_type "text/html" 18 | end 19 | else 20 | mail( 21 | from: 'test@example.com', 22 | to: 'test@example.com', 23 | subject: 'Test Email', 24 | body: 'Hello', 25 | content_type: "text/html" 26 | ) 27 | end 28 | end 29 | end 30 | 31 | def setup 32 | TestMailer.delivery_method = :sendmail 33 | 34 | clear_all! 35 | end 36 | 37 | def teardown 38 | ::Instana.config[:allow_exit_as_root] = false 39 | end 40 | 41 | def test_mailer 42 | Instana.tracer.in_span(:test) do 43 | TestMailer.sample_email.deliver_now 44 | end 45 | 46 | mail_span, = *::Instana.processor.queued_spans 47 | 48 | assert_equal :"mail.actionmailer", mail_span[:n] 49 | assert_equal 'RailsActionMailerTest::TestMailer', mail_span[:data][:actionmailer][:class] 50 | assert_equal 'sample_email', mail_span[:data][:actionmailer][:method] 51 | end 52 | 53 | def test_mailer_as_root_exit_span 54 | ::Instana.config[:allow_exit_as_root] = true 55 | TestMailer.sample_email.deliver_now 56 | ::Instana.config[:allow_exit_as_root] = false 57 | 58 | queued_spans = Instana.processor.queued_spans 59 | assert_equal 1, queued_spans.length 60 | mail_span = queued_spans[0] 61 | 62 | assert_equal :"mail.actionmailer", mail_span[:n] 63 | assert_equal 'RailsActionMailerTest::TestMailer', mail_span[:data][:actionmailer][:class] 64 | assert_equal 'sample_email', mail_span[:data][:actionmailer][:method] 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /lib/instana/instrumentation/sidekiq-client.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2017 3 | 4 | module Instana 5 | module Instrumentation 6 | class SidekiqClient 7 | def call(worker_class, msg, queue, _redis_pool) 8 | kvs = { :'sidekiq-client' => {} } 9 | kvs[:'sidekiq-client'][:queue] = queue 10 | kvs[:'sidekiq-client'][:job] = worker_class.to_s 11 | kvs[:'sidekiq-client'][:retry] = msg['retry'].to_s 12 | 13 | # Temporary until we move connection collection to redis 14 | # instrumentation 15 | Sidekiq.redis_pool.with do |client| 16 | sidekiq_version = Gem::Specification.find_by_name('sidekiq').version 17 | host, port = if sidekiq_version >= Gem::Version.new('7.0') && client.respond_to?(:config) && client.config.respond_to?(:host) && client.config.respond_to?(:port) 18 | [client.config.host, client.config.port] 19 | elsif client.respond_to?(:connection) 20 | [client.connection[:host], client.connection[:port]] 21 | elsif client.respond_to?(:client) && client.client.respond_to?(:options) 22 | [client.client.options[:host], client.client.options[:port]] 23 | else # Unexpected version, continue without recording any redis-url 24 | break 25 | end 26 | kvs[:'sidekiq-client'][:'redis-url'] = "#{host}:#{port}" 27 | end 28 | 29 | Instana.tracer.in_span(:'sidekiq-client', attributes: kvs) do |span| 30 | context = ::Instana.tracer.context 31 | if context 32 | msg['X-Instana-T'] = context.trace_id_header 33 | msg['X-Instana-S'] = context.span_id_header 34 | end 35 | 36 | result = yield 37 | 38 | if result && result['jid'] 39 | span.set_tag(:'sidekiq-client', { job_id: result['jid'] }) 40 | end 41 | 42 | result 43 | rescue => e 44 | span.record_exception(e) 45 | raise 46 | end 47 | end 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2016 3 | 4 | ENV['INSTANA_TEST'] = 'true' 5 | 6 | begin 7 | require 'simplecov' 8 | require 'simplecov_json_formatter' 9 | 10 | SimpleCov.start do 11 | enable_coverage :branch 12 | 13 | add_filter %r{^/test/} 14 | 15 | if ENV['CIRCLE_BUILD_NUM'] 16 | command_name "Job #{ENV['CIRCLE_BUILD_NUM']}" 17 | elsif ENV['COVERAGE_PATH'] 18 | coverage_dir ENV['COVERAGE_PATH'] 19 | end 20 | add_group( 21 | 'In Process Collector', 22 | [%r{lib/instana/(agent|backend|tracing|collectors|open_tracing|snapshot)}, %r{lib/instana/[^/]+\.rb}] 23 | ) 24 | 25 | if ENV['APPRAISAL_INITIALIZED'] 26 | add_group( 27 | 'Instrumentation', 28 | %r{lib/instana/(activators|frameworks|instrumentation)} 29 | ) 30 | else 31 | add_filter %r{lib/instana/(activators|frameworks|instrumentation)} 32 | end 33 | 34 | formatter SimpleCov::Formatter::MultiFormatter.new( 35 | [ 36 | SimpleCov::Formatter::HTMLFormatter, 37 | SimpleCov::Formatter::JSONFormatter 38 | ] 39 | ) 40 | end 41 | rescue LoadError => _e 42 | nil 43 | end 44 | 45 | require 'bundler/setup' 46 | Bundler.require 47 | 48 | require "minitest/spec" 49 | require "minitest/autorun" 50 | require "minitest/reporters" 51 | require 'fakefs/safe' 52 | 53 | require 'webmock/minitest' 54 | # Webmock: Whitelist local IPs 55 | WebMock.disable_net_connect!( 56 | allow: ->(uri) { %w[localhost 127.0.0.1 172.17.0.1 172.0.12.100].include?(uri.host) && ENV.key?('APPRAISAL_INITIALIZED') } 57 | ) 58 | 59 | Dir['test/support/*.rb'].each { |f| load(f) } 60 | 61 | minitest_reporters_to_use = [] 62 | if ENV['CIRCLE_BUILD_NUM'] 63 | minitest_reporters_to_use.append(Minitest::Reporters::JUnitReporter.new('_junit', false)) 64 | elsif ENV['COVERAGE_PATH'] 65 | minitest_reporters_to_use.append(Minitest::Reporters::JUnitReporter.new("#{ENV['COVERAGE_PATH']}/_junit", false)) 66 | end 67 | minitest_reporters_to_use.append(Minitest::Reporters::SpecReporter.new) 68 | Minitest::Reporters.use!(minitest_reporters_to_use) 69 | Minitest::Test.include(Instana::TestHelpers) 70 | -------------------------------------------------------------------------------- /lib/instana/span_filtering.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # (c) Copyright IBM Corp. 2025 4 | 5 | require 'instana/span_filtering/configuration' 6 | require 'instana/span_filtering/filter_rule' 7 | require 'instana/span_filtering/condition' 8 | 9 | module Instana 10 | # SpanFiltering module provides functionality to filter spans based on configured rules 11 | module SpanFiltering 12 | class << self 13 | attr_reader :configuration 14 | 15 | # Initialize the span filtering configuration 16 | # @return [Configuration] The span filtering configuration 17 | def initialize 18 | @configuration = Configuration.new 19 | end 20 | 21 | # Check if span filtering is deactivated 22 | # @return [Boolean] True if span filtering is deactivated 23 | def deactivated? 24 | @configuration&.deactivated || false 25 | end 26 | 27 | # Check if a span should be filtered out 28 | # @param span [Hash] The span to check 29 | # @return [Hash, nil] A result hash with filtered and suppression keys if filtered, nil if not filtered 30 | def filter_span(span) 31 | return nil if deactivated? 32 | return nil unless @configuration 33 | 34 | # Check include rules first (whitelist) 35 | if @configuration.include_rules.any? 36 | # If we have include rules, only keep spans that match at least one include rule 37 | unless @configuration.include_rules.any? { |rule| rule.matches?(span) } 38 | return { filtered: true, suppression: false } 39 | end 40 | # If it matches an include rule, continue to exclude rules 41 | end 42 | 43 | # Check exclude rules (blacklist) 44 | @configuration.exclude_rules.each do |rule| 45 | if rule.matches?(span) 46 | return { filtered: true, suppression: rule.suppression } 47 | end 48 | end 49 | 50 | nil # Keep the span if no rules match 51 | end 52 | 53 | # Reset the configuration (mainly for testing) 54 | def reset 55 | @configuration = nil 56 | end 57 | end 58 | 59 | # Initialize on module load 60 | initialize 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /test/backend/host_agent_lookup_test.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | require 'test_helper' 5 | 6 | class HostAgentLookupTest < Minitest::Test 7 | def test_lookup 8 | stub_request(:get, "http://10.10.10.10:42699/") 9 | .to_return(status: 200) 10 | 11 | subject = Instana::Backend::HostAgentLookup.new('10.10.10.10', 42699) 12 | client = subject.call 13 | 14 | assert client 15 | assert client.send_request('GET', '/').ok? 16 | end 17 | 18 | def test_lookup_no_agent 19 | stub_request(:get, "http://10.10.10.10:42699/") 20 | .to_timeout 21 | 22 | subject = Instana::Backend::HostAgentLookup.new('10.10.10.10', 42699) 23 | 24 | client = FakeFS.with_fresh do 25 | FakeFS::FileSystem.clone('test/support/ecs', '/proc') 26 | 27 | subject.call 28 | end 29 | 30 | assert_nil client 31 | end 32 | 33 | def test_lookup_agent_error 34 | stub_request(:get, "http://10.10.10.10:42699/") 35 | .to_return(status: 500) 36 | 37 | subject = Instana::Backend::HostAgentLookup.new('10.10.10.10', 42699) 38 | 39 | client = FakeFS.with_fresh do 40 | FakeFS::FileSystem.clone('test/support/ecs', '/proc') 41 | 42 | subject.call 43 | end 44 | 45 | assert_nil client 46 | end 47 | 48 | def test_lookup_with_gateway 49 | stub_request(:get, "http://10.10.10.10:42699/") 50 | .to_timeout 51 | stub_request(:get, "http://172.18.0.1:42699/") 52 | .to_return(status: 200) 53 | 54 | subject = Instana::Backend::HostAgentLookup.new('10.10.10.10', 42699) 55 | 56 | client = FakeFS do 57 | FakeFS::FileSystem.clone('test/support/proc', '/proc') 58 | subject.call 59 | end 60 | 61 | assert client 62 | assert client.send_request('GET', '/').ok? 63 | end 64 | 65 | def test_lookup_with_gateway_no_destination 66 | stub_request(:get, "http://10.10.10.10:42699/") 67 | .to_timeout 68 | 69 | subject = Instana::Backend::HostAgentLookup.new('10.10.10.10', 42699, destination: '11111111') 70 | 71 | client = FakeFS do 72 | FakeFS::FileSystem.clone('test/support/proc', '/proc') 73 | subject.call 74 | end 75 | 76 | assert_nil client 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /lib/instana/snapshot/fargate_task.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Snapshot 6 | # Describes the current process in terms of its existence inside of a Fargate task 7 | # @since 1.197.0 8 | class FargateTask 9 | ID = 'com.instana.plugin.aws.ecs.task'.freeze 10 | 11 | def initialize(metadata_uri: ENV['ECS_CONTAINER_METADATA_URI']) 12 | @metadata_uri = URI(metadata_uri) 13 | @client = Backend::RequestClient.new(@metadata_uri.host, @metadata_uri.port, use_ssl: @metadata_uri.scheme == "https") 14 | end 15 | 16 | def entity_id 17 | task_metadata['TaskARN'] 18 | end 19 | alias host_name entity_id 20 | 21 | def data 22 | { 23 | taskArn: task_metadata['TaskARN'], 24 | clusterArn: task_metadata['Cluster'], 25 | taskDefinition: task_metadata['Family'], 26 | taskDefinitionVersion: task_metadata['Revision'], 27 | availabilityZone: task_metadata['AvailabilityZone'], 28 | desiredStatus: task_metadata['DesiredStatus'], 29 | knownStatus: task_metadata['KnownStatus'], 30 | pullStartedAt: task_metadata['PullStartedAt'], 31 | pullStoppedAt: task_metadata['PullStoppedAt'], 32 | instanaZone: instana_zone, 33 | tags: instana_tags 34 | }.reject { |_, v| v.nil? } 35 | end 36 | 37 | def snapshot 38 | { 39 | name: ID, 40 | entityId: entity_id, 41 | data: data 42 | } 43 | end 44 | 45 | private 46 | 47 | def task_metadata 48 | lookup('/task') 49 | end 50 | 51 | def instana_zone 52 | ENV['INSTANA_ZONE'] 53 | end 54 | 55 | def instana_tags 56 | ENV.fetch('INSTANA_TAGS', '') 57 | .split(/,/) 58 | .map { |t| t.include?('=') ? t.split('=', 2) : [t, nil] } 59 | .to_h 60 | end 61 | 62 | def lookup(resource) 63 | path = @metadata_uri.path + resource 64 | response = @client.send_request('GET', path) 65 | 66 | raise "Unable to get `#{path}`. Got `#{response.code}`." unless response.ok? 67 | 68 | response.json 69 | end 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /test/frameworks/sinatra_test.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2016 3 | 4 | require 'test_helper' 5 | require 'rack/test' 6 | 7 | class SinatraTest < Minitest::Test 8 | include Rack::Test::Methods 9 | APP = Rack::Builder.parse_file('test/support/apps/sinatra/config.ru') 10 | 11 | sinatra_version = Gem::Specification.find_by_name('sinatra').version 12 | if sinatra_version < Gem::Version.new('4.0.0') 13 | APP = APP.first 14 | end 15 | 16 | def app 17 | APP 18 | end 19 | 20 | def test_basic_get 21 | clear_all! 22 | r = get '/' 23 | 24 | assert last_response.ok? 25 | 26 | 27 | spans = ::Instana.processor.queued_spans 28 | assert_equal 1, spans.count 29 | 30 | rack_span = spans.first 31 | assert_equal :rack, rack_span[:n] 32 | # ::Instana::Util.pry! 33 | 34 | assert r.headers.key?("X-Instana-T") 35 | assert r.headers["X-Instana-T"] == ::Instana::Util.id_to_header(rack_span[:t]) 36 | assert r.headers.key?("X-Instana-S") 37 | assert r.headers["X-Instana-S"] == ::Instana::Util.id_to_header(rack_span[:s]) 38 | assert r.headers.key?("X-Instana-L") 39 | assert r.headers["X-Instana-L"] == '1' 40 | assert r.headers.key?("Server-Timing") 41 | assert r.headers["Server-Timing"] == "intid;desc=#{::Instana::Util.id_to_header(rack_span[:t])}" 42 | 43 | assert rack_span.key?(:data) 44 | assert rack_span[:data].key?(:http) 45 | assert rack_span[:data][:http].key?(:method) 46 | assert_equal "GET", rack_span[:data][:http][:method] 47 | 48 | assert rack_span[:data][:http].key?(:url) 49 | assert_equal "/", rack_span[:data][:http][:url] 50 | 51 | assert rack_span[:data][:http].key?(:status) 52 | assert_equal 200, rack_span[:data][:http][:status] 53 | 54 | assert rack_span[:data][:http].key?(:host) 55 | assert_equal "example.org", rack_span[:data][:http][:host] 56 | end 57 | 58 | def test_path_template 59 | clear_all! 60 | 61 | r = get '/greet/instana' 62 | assert last_response.ok? 63 | 64 | spans = ::Instana.processor.queued_spans 65 | assert_equal 1, spans.count 66 | 67 | first_span = spans.first 68 | assert_equal :rack, first_span[:n] 69 | assert_equal '/greet/:name', first_span[:data][:http][:path_tpl] 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /test/instrumentation/rails_active_job_test.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | require 'test_helper' 5 | 6 | require 'rails' 7 | require 'active_job' 8 | 9 | class RailsActiveJobTest < Minitest::Test 10 | class SampleJob < ActiveJob::Base 11 | queue_as :test_queue 12 | 13 | def perform(*args); end 14 | end 15 | 16 | def setup 17 | @test_adapter = ActiveJob::Base.queue_adapter = ActiveJob::QueueAdapters::TestAdapter.new 18 | ActiveJob::Base.logger = Logger.new('/dev/null') 19 | 20 | clear_all! 21 | end 22 | 23 | def test_perform_now 24 | SampleJob.perform_now("test_perform_now") 25 | spans = ::Instana.processor.queued_spans 26 | 27 | server_span, *rest = spans 28 | assert_equal [], rest 29 | 30 | assert_equal :activejob, server_span[:n] 31 | assert_equal 'RailsActiveJobTest::SampleJob', server_span[:data][:activejob][:job] 32 | assert_equal :perform, server_span[:data][:activejob][:action] 33 | assert_equal 'test_queue', server_span[:data][:activejob][:queue] 34 | end 35 | 36 | def test_enqueue_perform 37 | # ActiveJob::QueueAdapters::TestAdapter.new doesn't work for this test on any version less than 6 38 | skip unless Rails::VERSION::MAJOR >= 6 39 | 40 | Instana.tracer.in_span(:peform_test) do 41 | SampleJob.perform_later("test_enqueue_perform") 42 | end 43 | 44 | job, *rest_jobs = @test_adapter.enqueued_jobs 45 | assert_equal [], rest_jobs 46 | 47 | ActiveJob::Base.execute(job) 48 | 49 | spans = ::Instana.processor.queued_spans 50 | client_span, _test_span, server_span, *rest = spans 51 | assert_equal [], rest 52 | 53 | assert_equal :activejob, server_span[:n] 54 | assert_equal 'RailsActiveJobTest::SampleJob', server_span[:data][:activejob][:job] 55 | assert_equal :perform, server_span[:data][:activejob][:action] 56 | 57 | assert_equal :activejob, client_span[:n] 58 | assert_equal 'RailsActiveJobTest::SampleJob', client_span[:data][:activejob][:job] 59 | assert_equal :enqueue, client_span[:data][:activejob][:action] 60 | assert_equal 'test_queue', server_span[:data][:activejob][:queue] 61 | 62 | assert_equal client_span[:t], server_span[:t] 63 | assert_equal client_span[:s], server_span[:p] 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /lib/instana/backend/request_client.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | require 'net/http' 5 | require 'delegate' 6 | require 'json' 7 | 8 | # :nocov: 9 | begin 10 | require 'oj' 11 | INSTANA_USE_OJ = true 12 | rescue LoadError => _e 13 | Instana.logger.warn("Unable to load Oj.") 14 | INSTANA_USE_OJ = false 15 | end 16 | # :nocov: 17 | 18 | module Instana 19 | module Backend 20 | # Convince wrapper around {Net::HTTP}. 21 | # @since 1.197.0 22 | class RequestClient 23 | attr_reader :host, :port 24 | 25 | class Response < SimpleDelegator 26 | # @return [Hash] the decoded json response 27 | def json 28 | JSON.parse(body) 29 | end 30 | 31 | # @return [Boolean] true if the request was successful 32 | def ok? 33 | __getobj__.is_a?(Net::HTTPSuccess) 34 | end 35 | end 36 | 37 | def initialize(host, port, use_ssl: false) 38 | timeout = Integer(ENV.fetch('INSTANA_TIMEOUT', 500)) 39 | @host = host 40 | @port = port 41 | @client = Net::HTTP.start(host, port, use_ssl: use_ssl, read_timeout: timeout) 42 | end 43 | 44 | # Send a request to the backend. If data is a {Hash}, 45 | # encode the object as JSON and set the proper headers. 46 | # 47 | # @param [String] method request method 48 | # @param [String] path request path 49 | # @param [Hash, String] data request body 50 | # @param [Hash] headers extra request headers to send 51 | def send_request(method, path, data = nil, headers = {}) 52 | body = if data.is_a?(Hash) || data.is_a?(Array) 53 | headers['Content-Type'] = 'application/json' 54 | headers['Accept'] = 'application/json' 55 | 56 | encode_body(data) 57 | else 58 | headers['Content-Type'] = 'application/octet-stream' 59 | 60 | data 61 | end 62 | 63 | response = @client.send_request(method, path, body, headers) 64 | Response.new(response) 65 | end 66 | 67 | private 68 | 69 | def encode_body(data) 70 | # :nocov: 71 | INSTANA_USE_OJ ? Oj.dump(data, mode: :strict) : JSON.dump(data) 72 | # :nocov: 73 | end 74 | end 75 | end 76 | end 77 | -------------------------------------------------------------------------------- /lib/instana/instrumentation/action_controller.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Instrumentation 6 | module ActionController 7 | def process_action(*args) 8 | call_payload = { 9 | actioncontroller: { 10 | controller: self.class.name, 11 | action: action_name 12 | } 13 | } 14 | request.env['INSTANA_HTTP_PATH_TEMPLATE'] = matched_path_template 15 | ::Instana.tracer.in_span(:actioncontroller, attributes: call_payload) { super(*args) } 16 | end 17 | 18 | def render(*args, &block) 19 | call_payload = { 20 | actionview: { 21 | name: describe_render_options(args.first) || 'Default' 22 | } 23 | } 24 | ::Instana.tracer.in_span(:actionview, attributes: call_payload) { super(*args, &block) } 25 | end 26 | 27 | private 28 | 29 | def matched_path_template 30 | Rails.application.routes.router.recognize(request) do |route, _, _| 31 | path = route.path 32 | return path.spec.to_s 33 | end 34 | 35 | nil 36 | end 37 | 38 | def describe_render_options(options) 39 | return unless options.is_a?(Hash) 40 | 41 | describe_layout(options[:layout]) || 42 | describe_direct(options) 43 | end 44 | 45 | def describe_layout(layout) 46 | return unless layout 47 | 48 | case layout 49 | when FalseClass 50 | 'Without layout' 51 | when String 52 | layout 53 | when Proc 54 | 'Proc' 55 | else 56 | 'Default' 57 | end 58 | end 59 | 60 | def describe_direct(options) 61 | case options 62 | when ->(o) { o.key?(:nothing) } 63 | 'Nothing' 64 | when ->(o) { o.key?(:plain) } 65 | 'Plaintext' 66 | when ->(o) { o.key?(:json) } 67 | 'JSON' 68 | when ->(o) { o.key?(:xml) } 69 | 'XML' 70 | when ->(o) { o.key?(:body) } 71 | 'Raw' 72 | when ->(o) { o.key?(:js) } 73 | 'Javascript' 74 | when ->(o) { o.key?(:template) } 75 | options[:template] 76 | when ->(o) { o.key?(:file) } 77 | options[:file] 78 | end 79 | end 80 | end 81 | end 82 | end 83 | -------------------------------------------------------------------------------- /instana.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # (c) Copyright IBM Corp. 2021 4 | # (c) Copyright Instana Inc. 2016 5 | 6 | lib = File.expand_path('../lib', __FILE__) 7 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 8 | require 'instana/version' 9 | 10 | Gem::Specification.new do |spec| 11 | spec.name = "instana" 12 | spec.version = Instana::VERSION 13 | spec.authors = ["Peter Giacomo Lombardo"] 14 | spec.email = ["pglombardo@gmail.com"] 15 | 16 | spec.summary = %q{Ruby Distributed Tracing & Metrics Sensor for Instana} 17 | spec.description = %q{The Instana gem is a zero configuration tool that will automatically collect key metrics and distributed traces from your Ruby processes. Just install and go.} 18 | spec.homepage = "https://www.instana.com/" 19 | 20 | spec.metadata = { 21 | "changelog_uri" => "https://github.com/instana/ruby-sensor/releases", 22 | "documentation_uri" => "https://docs.instana.io/ecosystem/ruby/", 23 | "homepage_uri" => "https://www.instana.com/", 24 | "source_code_uri" => "https://github.com/instana/ruby-sensor", 25 | } 26 | 27 | spec.licenses = ['MIT'] 28 | spec.files = Dir.glob('lib/**/*.rb') + Dir.glob('README.md') + Dir.glob('bin/console') + Dir.glob('bin/setup') 29 | spec.bindir = "exe" 30 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 31 | spec.require_paths = ["lib"] 32 | 33 | spec.required_ruby_version = '>= 3.0' 34 | spec.platform = defined?(JRUBY_VERSION) ? 'java' : Gem::Platform::RUBY 35 | 36 | # spec.add_development_dependency "bundler", "=> 2.0" 37 | spec.add_development_dependency "rake", "~> 12.0" 38 | spec.add_development_dependency "minitest", "~> 5.20" 39 | spec.add_development_dependency "appraisal" 40 | spec.add_development_dependency "fakefs" 41 | spec.add_development_dependency "irb" 42 | spec.add_development_dependency "benchmark" 43 | 44 | spec.add_runtime_dependency('base64', '>= 0.1') 45 | spec.add_runtime_dependency('logger') 46 | spec.add_runtime_dependency('concurrent-ruby', '>= 1.1') 47 | spec.add_runtime_dependency('csv', '>= 0.1') 48 | spec.add_runtime_dependency('sys-proctable', '>= 1.2.2') 49 | spec.add_runtime_dependency('opentelemetry-api', '~> 1.4') 50 | spec.add_runtime_dependency('opentelemetry-common') 51 | spec.add_runtime_dependency('oj', '>=3.0.11') unless RUBY_PLATFORM =~ /java/i 52 | end 53 | -------------------------------------------------------------------------------- /lib/instana/instrumentation/active_job.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | module OpenTelemetry 4 | module Trace 5 | module Propagation 6 | module TraceContext 7 | # A TraceParent is an implementation of the W3C trace context specification 8 | # https://www.w3.org/TR/trace-context/ 9 | # {Trace::SpanContext} 10 | class TraceParent 11 | REGEXP = /^(?[A-Fa-f0-9]{2})-(?[A-Fa-f0-9]{32})-(?[A-Fa-f0-9]{32})-(?[A-Fa-f0-9]{2})(?-.*)?$/ 12 | end 13 | end 14 | end 15 | end 16 | end 17 | 18 | module Instana 19 | module Instrumentation 20 | module ActiveJob 21 | def self.prepended(target) 22 | target.around_enqueue do |job, block| 23 | tags = { 24 | activejob: { 25 | queue: job.queue_name, 26 | job: job.class.to_s, 27 | action: :enqueue, 28 | job_id: job.job_id 29 | } 30 | } 31 | 32 | ::Instana.tracer.in_span(:activejob, attributes: tags) do 33 | context = ::Instana.tracer.context 34 | job.arguments.append({ 35 | instana_context: context ? context.to_hash : nil 36 | }) 37 | 38 | block.call 39 | end 40 | end 41 | 42 | target.around_perform do |job, block| 43 | tags = { 44 | activejob: { 45 | queue: job.queue_name, 46 | job: job.class.to_s, 47 | action: :perform, 48 | job_id: job.job_id 49 | } 50 | } 51 | incoming_context = if job.arguments.is_a?(Array) && job.arguments.last.is_a?(Hash) && job.arguments.last.key?(:instana_context) 52 | instana_context = job.arguments.last[:instana_context] 53 | job.arguments.pop 54 | instana_context ? ::Instana::SpanContext.new(trace_id: instana_context[:trace_id], span_id: instana_context[:span_id]) : nil 55 | end 56 | Trace.with_span(OpenTelemetry::Trace.non_recording_span(incoming_context)) do 57 | ::Instana.tracer.in_span(:activejob, attributes: tags) do 58 | block.call 59 | end 60 | end 61 | end 62 | end 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /test/instrumentation/mongo_test.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | require 'test_helper' 5 | 6 | class MongoTest < Minitest::Test 7 | def setup 8 | clear_all! 9 | end 10 | 11 | def teardown 12 | ::Instana.config[:allow_exit_as_root] = false 13 | end 14 | 15 | def test_mongo 16 | Instana.tracer.in_span(:'mongo-test') do 17 | client = Mongo::Client.new('mongodb://127.0.0.1:27017/instana') 18 | client[:people].delete_many({ name: /$S*/ }) 19 | client[:people].insert_many([{ _id: 1, name: "Stan" }]) 20 | end 21 | 22 | spans = ::Instana.processor.queued_spans 23 | delete_span, insert_span, = spans 24 | 25 | delete_data = delete_span[:data][:mongo] 26 | insert_data = insert_span[:data][:mongo] 27 | 28 | assert_equal delete_span[:n], :mongo 29 | assert_equal insert_span[:n], :mongo 30 | 31 | assert_equal delete_data[:namespace], "instana" 32 | assert_equal delete_data[:command], "delete" 33 | assert_equal delete_data[:peer], {hostname: "127.0.0.1", port: 27017} 34 | assert delete_data[:json].include?("delete") 35 | 36 | assert_equal insert_data[:namespace], "instana" 37 | assert_equal insert_data[:command], "insert" 38 | assert_equal insert_data[:peer], {hostname: "127.0.0.1", port: 27017} 39 | assert insert_data[:json].include?("insert") 40 | end 41 | 42 | def test_mongo_as_root_exit_span 43 | ::Instana.config[:allow_exit_as_root] = true 44 | 45 | client = Mongo::Client.new('mongodb://127.0.0.1:27017/instana') 46 | client[:people].delete_many({ name: /$S*/ }) 47 | client[:people].insert_many([{ _id: 1, name: "Stan" }]) 48 | 49 | spans = ::Instana.processor.queued_spans 50 | delete_span, insert_span, = spans 51 | 52 | delete_data = delete_span[:data][:mongo] 53 | insert_data = insert_span[:data][:mongo] 54 | 55 | assert_equal delete_span[:n], :mongo 56 | assert_equal insert_span[:n], :mongo 57 | 58 | assert_equal delete_data[:namespace], "instana" 59 | assert_equal delete_data[:command], "delete" 60 | assert_equal delete_data[:peer], {hostname: "127.0.0.1", port: 27017} 61 | assert delete_data[:json].include?("delete") 62 | 63 | assert_equal insert_data[:namespace], "instana" 64 | assert_equal insert_data[:command], "insert" 65 | assert_equal insert_data[:peer], {hostname: "127.0.0.1", port: 27017} 66 | assert insert_data[:json].include?("insert") 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /lib/instana/instrumentation/sidekiq-worker.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2017 3 | 4 | module Instana 5 | module Instrumentation 6 | class SidekiqWorker 7 | def call(_worker, msg, _queue) 8 | kvs = { :'sidekiq-worker' => {} } 9 | kvs[:'sidekiq-worker'][:job_id] = msg['jid'] 10 | kvs[:'sidekiq-worker'][:queue] = msg['queue'] 11 | kvs[:'sidekiq-worker'][:job] = msg['class'].to_s 12 | kvs[:'sidekiq-worker'][:retry] = msg['retry'].to_s 13 | 14 | # Temporary until we move connection collection to redis 15 | # instrumentation 16 | Sidekiq.redis_pool.with do |client| 17 | sidekiq_version = Gem::Specification.find_by_name('sidekiq').version 18 | host, port = if sidekiq_version >= Gem::Version.new('7.0') && client.respond_to?(:config) && client.config.respond_to?(:host) && client.config.respond_to?(:port) 19 | [client.config.host, client.config.port] 20 | elsif client.respond_to?(:connection) 21 | [client.connection[:host], client.connection[:port]] 22 | elsif client.respond_to?(:client) && client.client.respond_to?(:options) 23 | [client.client.options[:host], client.client.options[:port]] 24 | else # Unexpected version, continue without recording any redis-url 25 | break 26 | end 27 | kvs[:'sidekiq-worker'][:'redis-url'] = "#{host}:#{port}" 28 | end 29 | 30 | trace_context = nil 31 | if msg.key?('X-Instana-T') 32 | trace_id = msg.delete('X-Instana-T') 33 | span_id = msg.delete('X-Instana-S') 34 | trace_context = ::Instana::SpanContext.new( 35 | trace_id: ::Instana::Util.header_to_id(trace_id), 36 | span_id: span_id ? ::Instana::Util.header_to_id(span_id) : nil 37 | ) 38 | end 39 | 40 | parent_non_recording_span = OpenTelemetry::Trace.non_recording_span(trace_context) if trace_context 41 | Trace.with_span(parent_non_recording_span) do 42 | Instana.tracer.in_span(:'sidekiq-worker', attributes: kvs) do |span| 43 | yield 44 | rescue => e 45 | kvs[:'sidekiq-worker'][:error] = true 46 | span.set_tags(kvs) 47 | span.record_exception(e) 48 | raise 49 | end 50 | end 51 | end 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /lib/instana/instrumentation/dalli.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2017 3 | 4 | module Instana 5 | module Instrumentation 6 | module Dalli 7 | def perform(*args, &blk) 8 | if !::Instana.tracer.tracing? || ::Instana.tracer.tracing_span?(:memcache) 9 | do_skip = true 10 | return super(*args, &blk) 11 | end 12 | 13 | op, key, *_opts = args 14 | 15 | entry_payload = { :memcache => {} } 16 | entry_payload[:memcache][:namespace] = @options[:namespace] if @options.key?(:namespace) 17 | entry_payload[:memcache][:command] = op 18 | entry_payload[:memcache][:key] = key 19 | 20 | ::Instana.tracer.start_span(:memcache, attributes: entry_payload) 21 | exit_payload = { :memcache => {} } 22 | 23 | result = super(*args, &blk) 24 | 25 | if op == :get 26 | exit_payload[:memcache][:hit] = result ? 1 : 0 27 | end 28 | result 29 | rescue => e 30 | exit_payload[:memcache][:error] = e.message rescue nil 31 | ::Instana.tracer.log_error(e) 32 | raise 33 | ensure 34 | ::Instana.tracer.log_exit(:memcache, exit_payload) unless do_skip 35 | end 36 | 37 | def get_multi(*keys) 38 | entry_payload = { :memcache => {} } 39 | entry_payload[:memcache][:namespace] = @options[:namespace] if @options.key?(:namespace) 40 | entry_payload[:memcache][:command] = :get_multi 41 | entry_payload[:memcache][:keys] = keys.flatten.join(", ") 42 | 43 | ::Instana.tracer.log_entry(:memcache, entry_payload) 44 | exit_payload = { :memcache => {} } 45 | 46 | result = super(*keys) 47 | 48 | exit_payload[:memcache][:hits] = result.length 49 | result 50 | rescue => e 51 | exit_payload[:memcache][:error] = e.message rescue nil 52 | ::Instana.tracer.log_error(e) 53 | raise 54 | ensure 55 | ::Instana.tracer.log_exit(:memcache, exit_payload) 56 | end 57 | end 58 | 59 | module DalliRequestHandler 60 | def self.included(klass) 61 | ::Instana::Util.method_alias(klass, :request) 62 | end 63 | 64 | def request(op, *args) 65 | if ::Instana.tracer.tracing? || ::Instana.tracer.tracing_span?(:memcache) 66 | info_payload = { :memcache => {} } 67 | info_payload[:memcache][:server] = "#{hostname}:#{port}" 68 | ::Instana.tracer.log_info(info_payload) 69 | end 70 | super(op, *args) 71 | end 72 | end 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /lib/instana/backend/agent.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | module Instana 5 | module Backend 6 | # Wrapper class around the various transport backends 7 | # @since 1.197.0 8 | class Agent 9 | attr_reader :delegate 10 | 11 | def initialize(fargate_metadata_uri: ENV['ECS_CONTAINER_METADATA_URI'], logger: ::Instana.logger) 12 | @delegate = nil 13 | @logger = logger 14 | @fargate_metadata_uri = fargate_metadata_uri 15 | end 16 | 17 | def setup 18 | @delegate = if ENV.key?('_HANDLER') 19 | ServerlessAgent.new([Snapshot::LambdaFunction.new]) 20 | elsif ENV.key?('K_REVISION') && ENV.key?('INSTANA_ENDPOINT_URL') 21 | ServerlessAgent.new([ 22 | Snapshot::GoogleCloudRunProcess.new, 23 | Snapshot::GoogleCloudRunInstance.new, 24 | Snapshot::RubyProcess.new 25 | ]) 26 | elsif @fargate_metadata_uri && ENV.key?('INSTANA_ENDPOINT_URL') 27 | ServerlessAgent.new(fargate_snapshots) 28 | else 29 | HostAgent.new 30 | end 31 | 32 | @delegate.setup 33 | end 34 | 35 | def method_missing(mth, *args, &block) 36 | if @delegate.respond_to?(mth) 37 | @delegate.public_send(mth, *args, &block) 38 | else 39 | super(mth, *args, &block) 40 | end 41 | end 42 | 43 | def respond_to_missing?(mth, include_all = false) 44 | @delegate.respond_to?(mth, include_all) 45 | end 46 | 47 | private 48 | 49 | def fargate_snapshots 50 | metadata_uri = URI(@fargate_metadata_uri) 51 | client = Backend::RequestClient.new(metadata_uri.host, metadata_uri.port, use_ssl: metadata_uri.scheme == "https") 52 | response = client.send_request('GET', "#{metadata_uri.path}/task") 53 | 54 | if response.ok? 55 | docker = response 56 | .json['Containers'] 57 | .map { |c| [Snapshot::DockerContainer.new(c), Snapshot::FargateContainer.new(c)] } 58 | .flatten 59 | 60 | docker + [Snapshot::FargateProcess.new, Snapshot::RubyProcess.new, Snapshot::FargateTask.new] 61 | else 62 | @logger.warn("Received #{response.code} when requesting containers.") 63 | [] 64 | end 65 | end 66 | end 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /lib/instana/trace/span_kind.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2025 2 | 3 | module Instana 4 | # Type of span. Can be used to specify additional relationships between spans in addition to a 5 | # parent/child relationship. For API ergonomics, use of the symbols rather than the constants 6 | # may be preferred. For example: 7 | # 8 | # span = tracer.on_start('op', kind: :client) 9 | module SpanKind 10 | # Instana specific spans 11 | REGISTERED_SPANS = [:actioncontroller, :actionview, :activerecord, :excon, 12 | :memcache, :'net-http', :rack, :rabbitmq, :render, :'rpc-client', 13 | :'rpc-server', :'sidekiq-client', :'sidekiq-worker', 14 | :redis, :'resque-client', :'resque-worker', :'graphql.server', :dynamodb, :s3, :sns, :sqs, :'aws.lambda.entry', :activejob, :log, :"mail.actionmailer", 15 | :"aws.lambda.invoke", :mongo, :sequel].freeze 16 | ENTRY_SPANS = [:rack, :rabbitmq, :'resque-worker', :'rpc-server', :'sidekiq-worker', :'graphql.server', :sqs, 17 | :'aws.lambda.entry'].freeze 18 | EXIT_SPANS = [:activerecord, :excon, :'net-http', :rabbitmq, :'resque-client', 19 | :'rpc-client', :'sidekiq-client', :redis, :dynamodb, :s3, :sns, :sqs, :log, :"mail.actionmailer", 20 | :"aws.lambda.invoke", :mongo, :sequel].freeze 21 | HTTP_SPANS = [:rack, :excon, :'net-http'].freeze 22 | 23 | # Default value. Indicates that the span is used internally. 24 | INTERNAL = :internal 25 | 26 | # Indicates that the span covers server-side handling of an RPC or other remote request. 27 | SERVER = :server 28 | 29 | # Indicates that the span covers the client-side wrapper around an RPC or other remote request. 30 | CLIENT = :client 31 | 32 | # Indicates that the span describes producer sending a message to a broker. Unlike client and 33 | # server, there is no direct critical path latency relationship between producer and consumer 34 | # spans. 35 | PRODUCER = :producer 36 | 37 | # Indicates that the span describes consumer receiving a message from a broker. Unlike client 38 | # and server, there is no direct critical path latency relationship between producer and 39 | # consumer spans. 40 | CONSUMER = :consumer 41 | 42 | # Indicates an entry span. Equivalant to Server or Consumer 43 | ENTRY = :entry 44 | 45 | # Indicates an exit span. Equivalant to Client or Producer 46 | EXIT = :exit 47 | 48 | # Indicates an intermediate span. This used when sdk is used to produce intermediate traces 49 | INTERMEDIATE = :intermediate 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /test/backend/agent_test.rb: -------------------------------------------------------------------------------- 1 | # (c) Copyright IBM Corp. 2021 2 | # (c) Copyright Instana Inc. 2021 3 | 4 | require 'test_helper' 5 | 6 | class AgentTest < Minitest::Test 7 | def test_host 8 | subject = Instana::Backend::Agent.new 9 | assert_nil subject.delegate 10 | subject.setup 11 | assert subject.delegate.is_a?(Instana::Backend::HostAgent) 12 | end 13 | 14 | def test_fargate 15 | ENV['ECS_CONTAINER_METADATA_URI'] = 'https://10.10.10.10:9292/v3' 16 | ENV['INSTANA_ENDPOINT_URL'] = 'http://example.com' 17 | 18 | stub_request(:get, 'https://10.10.10.10:9292/v3/task') 19 | .to_return(status: 200, body: File.read('test/support/ecs/task.json')) 20 | 21 | subject = Instana::Backend::Agent.new(fargate_metadata_uri: 'https://10.10.10.10:9292/v3') 22 | assert_nil subject.delegate 23 | subject.setup 24 | assert subject.delegate.is_a?(Instana::Backend::ServerlessAgent) 25 | ensure 26 | ENV['INSTANA_ENDPOINT_URL'] = nil 27 | ENV['ECS_CONTAINER_METADATA_URI'] = nil 28 | end 29 | 30 | def test_fargate_error 31 | ENV['ECS_CONTAINER_METADATA_URI'] = 'https://10.10.10.10:9292/v3' 32 | ENV['INSTANA_ENDPOINT_URL'] = 'http://example.com' 33 | 34 | stub_request(:get, 'https://10.10.10.10:9292/v3/task') 35 | .to_return(status: 500) 36 | 37 | subject = Instana::Backend::Agent.new(logger: Logger.new('/dev/null')) 38 | assert_nil subject.delegate 39 | subject.setup 40 | assert subject.delegate.is_a?(Instana::Backend::ServerlessAgent) 41 | ensure 42 | ENV['INSTANA_ENDPOINT_URL'] = nil 43 | ENV['ECS_CONTAINER_METADATA_URI'] = nil 44 | end 45 | 46 | def test_lambda 47 | ENV['_HANDLER'] = 'TEST_FUNCTION' 48 | ENV['INSTANA_ENDPOINT_URL'] = 'http://example.com' 49 | 50 | subject = Instana::Backend::Agent.new 51 | assert_nil subject.delegate 52 | subject.setup 53 | assert subject.delegate.is_a?(Instana::Backend::ServerlessAgent) 54 | ensure 55 | ENV['_HANDLER'] = nil 56 | ENV['INSTANA_ENDPOINT_URL'] = nil 57 | end 58 | 59 | def test_google_cloud 60 | ENV['K_REVISION'] = 'TEST' 61 | ENV['INSTANA_ENDPOINT_URL'] = 'http://example.com' 62 | 63 | subject = Instana::Backend::Agent.new 64 | assert_nil subject.delegate 65 | subject.setup 66 | assert subject.delegate.is_a?(Instana::Backend::ServerlessAgent) 67 | ensure 68 | ENV['K_REVISION'] = nil 69 | ENV['INSTANA_ENDPOINT_URL'] = nil 70 | end 71 | 72 | def test_delegate_super 73 | subject = Instana::Backend::Agent.new 74 | assert_raises NoMethodError do 75 | subject.invalid 76 | end 77 | 78 | refute subject.respond_to?(:invalid) 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /.tekton/.currency/docs/report.md: -------------------------------------------------------------------------------- 1 | ##### This page is auto-generated. Any change will be overwritten after the next sync. Please apply changes directly to the files in the [ruby tracer](https://github.com/instana/ruby-sensor) repo. 2 | ## Ruby supported packages and versions 3 | | Package name | Support Policy | Beta version | Last Supported Version | Latest version | Up-to-date | Cloud Native | 4 | |:---------------|:-----------------|:---------------|:-------------------------|:-----------------|:-------------|:---------------| 5 | | Cuba | On demand | No | 4.0.3 | 4.0.3 | Yes | No | 6 | | Rack | 45-days | No | 3.1.7 | 3.1.7 | Yes | No | 7 | | Rails | 45-days | No | 7.2.1 | 7.2.1 | Yes | No | 8 | | Rails::API | 45-days | No | 0.4.1 | 0.4.1 | Yes | No | 9 | | Rails LTS | On demand | No | 6.1 | 6.1 | Yes | No | 10 | | Roda | 45-days | No | 3.84.0 | 3.84.0 | Yes | No | 11 | | Sinatra | 45-days | No | 4.0.0 | 4.0.0 | Yes | No | 12 | | Excon | 45-days | No | 0.112.0 | 0.112.0 | Yes | Yes | 13 | | gRPC | 45-days | No | 1.66.0 | 1.66.0 | Yes | No | 14 | | Net::HTTP | 0-day | Yes | 0.4.1 | 0.4.1 | Yes | No | 15 | | Rest-Client | 45-days | No | 2.1.0 | 2.1.0 | Yes | No | 16 | | Dalli | 45-days | No | 3.2.8 | 3.2.8 | Yes | No | 17 | | Resque | 45-days | No | 2.6.0 | 2.6.0 | Yes | Yes | 18 | | Sidekiq | 45-days | No | 7.3.2 | 7.3.2 | Yes | Yes | 19 | | GraphQL | 45-days | No | 2.3.17 | 2.3.17 | Yes | Yes | 20 | | Sequel | 45-days | No | 5.60.0 | 5.84.0 | Yes | No | -------------------------------------------------------------------------------- /examples/otel.rb: -------------------------------------------------------------------------------- 1 | # Simple Instana Tracing Examples 2 | # =========================== 3 | 4 | # (c) Copyright IBM Corp. 2025 5 | 6 | ####################################### 7 | ## in_span Method Examples 8 | ####################################### 9 | 10 | # 1. Basic tracing - simplest way to trace code 11 | Instana.tracer.in_span('my_operation') do 12 | # Your code here 13 | end 14 | 15 | # 2. HTTP request tracing with basic attributes 16 | Instana.tracer.in_span('http.request', 17 | attributes: { 18 | 'http.method' => 'GET', 19 | 'http.url' => 'https://example.com' 20 | }, 21 | kind: Instana::Trace::SpanKind::CLIENT) do 22 | # HTTP request code here 23 | end 24 | 25 | # 3. Error handling - errors are automatically captured 26 | begin 27 | Instana.tracer.in_span('risky_operation') do 28 | raise StandardError, 'Something went wrong' 29 | end 30 | rescue 31 | # Handle error 32 | end 33 | 34 | # 4. Nested spans - parent-child relationship 35 | Instana.tracer.in_span('parent') do 36 | # Parent code 37 | Instana.tracer.in_span('child') do 38 | # Child code 39 | end 40 | end 41 | 42 | # 5. Adding tags during execution 43 | Instana.tracer.in_span('process_order') do |span| 44 | span.set_tag('order.id', 12345) 45 | span.set_tag('status', 'completed') 46 | end 47 | 48 | ####################################### 49 | ## start_span Method Examples 50 | ####################################### 51 | 52 | # 1. Basic manual span control 53 | span = Instana.tracer.start_span('manual_operation') 54 | # Your code here 55 | span.finish 56 | 57 | # 2. Spans across methods 58 | def start_work 59 | span = Instana.tracer.start_span('long_process') 60 | # Initial work 61 | finish_work(span) 62 | end 63 | 64 | def finish_work(span) 65 | # More work 66 | span.finish 67 | end 68 | 69 | # 3. Async operations across threads 70 | parent_span = Instana.tracer.start_span('main_task') 71 | 72 | Thread.new do 73 | child_span = Instana.tracer.start_span('async_task', 74 | with_parent: parent_span.context) 75 | # Async work 76 | child_span.finish 77 | end 78 | 79 | parent_span.finish 80 | 81 | # 4. Manual error handling 82 | span = Instana.tracer.start_span('custom_error_handling') 83 | begin 84 | # Your code here 85 | rescue => e 86 | span.record_exception(e) 87 | ensure 88 | span.finish 89 | end 90 | 91 | # 5. Parent-child relationship (explicit) 92 | parent = Instana.tracer.start_span('parent_task') 93 | child = Instana.tracer.start_span('child_task', with_parent: parent.context) 94 | 95 | child.finish 96 | parent.finish 97 | 98 | # Made with Bob 99 | --------------------------------------------------------------------------------