├── 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 |
--------------------------------------------------------------------------------