instana 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. checksums.yaml +4 -4
  2. data/lib/instana/setup.rb +1 -0
  3. data/lib/instana/span_filtering/condition.rb +134 -0
  4. data/lib/instana/span_filtering/configuration.rb +262 -0
  5. data/lib/instana/span_filtering/filter_rule.rb +31 -0
  6. data/lib/instana/span_filtering.rb +62 -0
  7. data/lib/instana/trace/span.rb +5 -3
  8. data/lib/instana/version.rb +1 -1
  9. metadata +7 -258
  10. data/.circleci/config.yml +0 -361
  11. data/.codeclimate.yml +0 -23
  12. data/.editorconfig +0 -10
  13. data/.fasterer.yml +0 -23
  14. data/.github/ISSUE_TEMPLATE/bug.yml +0 -39
  15. data/.github/ISSUE_TEMPLATE/config.yml +0 -8
  16. data/.github/workflows/pr_commits_signed_off.yml +0 -16
  17. data/.github/workflows/release-notification-on-slack.yml +0 -34
  18. data/.gitignore +0 -19
  19. data/.rubocop.yml +0 -34
  20. data/.rubocop_todo.yml +0 -1140
  21. data/.tekton/.currency/docs/report.md +0 -20
  22. data/.tekton/.currency/resources/requirements.txt +0 -4
  23. data/.tekton/.currency/resources/table.json +0 -100
  24. data/.tekton/.currency/scripts/generate_report.py +0 -308
  25. data/.tekton/README.md +0 -278
  26. data/.tekton/github-interceptor-secret.yaml +0 -8
  27. data/.tekton/github-pr-eventlistener.yaml +0 -104
  28. data/.tekton/github-pr-pipeline.yaml.part +0 -38
  29. data/.tekton/github-set-status-task.yaml +0 -43
  30. data/.tekton/github-webhook-ingress.yaml +0 -20
  31. data/.tekton/pipeline.yaml +0 -484
  32. data/.tekton/pipelinerun.yaml +0 -21
  33. data/.tekton/prepuller-restart-service-account.yaml +0 -31
  34. data/.tekton/ruby-tracer-prepuller-cronjob.yaml +0 -20
  35. data/.tekton/ruby-tracer-prepuller.yaml +0 -88
  36. data/.tekton/run_unittests.sh +0 -87
  37. data/.tekton/scheduled-eventlistener.yaml +0 -108
  38. data/.tekton/task.yaml +0 -453
  39. data/.tekton/tekton-triggers-eventlistener-serviceaccount.yaml +0 -29
  40. data/Appraisals +0 -124
  41. data/CONTRIBUTING.md +0 -86
  42. data/Gemfile +0 -22
  43. data/LICENSE +0 -22
  44. data/MAINTAINERS.md +0 -3
  45. data/Rakefile +0 -49
  46. data/bin/announce_release_on_slack.py +0 -103
  47. data/docker-compose.yml +0 -20
  48. data/download.sh +0 -85
  49. data/examples/otel.rb +0 -98
  50. data/examples/tracing.rb +0 -85
  51. data/extras/license_header.rb +0 -44
  52. data/gemfiles/.bundle/config +0 -2
  53. data/gemfiles/aws_30.gemfile +0 -21
  54. data/gemfiles/aws_60.gemfile +0 -16
  55. data/gemfiles/cuba_30.gemfile +0 -16
  56. data/gemfiles/cuba_40.gemfile +0 -13
  57. data/gemfiles/dalli_20.gemfile +0 -15
  58. data/gemfiles/dalli_30.gemfile +0 -12
  59. data/gemfiles/dalli_32.gemfile +0 -12
  60. data/gemfiles/excon_0100.gemfile +0 -14
  61. data/gemfiles/excon_021.gemfile +0 -17
  62. data/gemfiles/excon_079.gemfile +0 -17
  63. data/gemfiles/excon_100.gemfile +0 -14
  64. data/gemfiles/graphql_10.gemfile +0 -16
  65. data/gemfiles/graphql_20.gemfile +0 -15
  66. data/gemfiles/grpc_10.gemfile +0 -15
  67. data/gemfiles/mongo_216.gemfile +0 -15
  68. data/gemfiles/mongo_219.gemfile +0 -12
  69. data/gemfiles/net_http_01.gemfile +0 -17
  70. data/gemfiles/rack_16.gemfile +0 -15
  71. data/gemfiles/rack_20.gemfile +0 -15
  72. data/gemfiles/rack_30.gemfile +0 -13
  73. data/gemfiles/rails_42.gemfile +0 -18
  74. data/gemfiles/rails_50.gemfile +0 -19
  75. data/gemfiles/rails_52.gemfile +0 -19
  76. data/gemfiles/rails_60.gemfile +0 -19
  77. data/gemfiles/rails_61.gemfile +0 -21
  78. data/gemfiles/rails_70.gemfile +0 -18
  79. data/gemfiles/rails_71.gemfile +0 -17
  80. data/gemfiles/rails_80.gemfile +0 -17
  81. data/gemfiles/redis_40.gemfile +0 -15
  82. data/gemfiles/redis_50.gemfile +0 -13
  83. data/gemfiles/redis_51.gemfile +0 -13
  84. data/gemfiles/resque_122.gemfile +0 -16
  85. data/gemfiles/resque_1274_3scale.gemfile +0 -17
  86. data/gemfiles/resque_20.gemfile +0 -16
  87. data/gemfiles/rest_client_16.gemfile +0 -17
  88. data/gemfiles/rest_client_20.gemfile +0 -17
  89. data/gemfiles/roda_20.gemfile +0 -16
  90. data/gemfiles/roda_30.gemfile +0 -16
  91. data/gemfiles/rubocop_162.gemfile +0 -6
  92. data/gemfiles/sequel_56.gemfile +0 -16
  93. data/gemfiles/sequel_57.gemfile +0 -16
  94. data/gemfiles/sequel_58.gemfile +0 -16
  95. data/gemfiles/shoryuken_50.gemfile +0 -16
  96. data/gemfiles/shoryuken_60.gemfile +0 -13
  97. data/gemfiles/sidekiq_42.gemfile +0 -15
  98. data/gemfiles/sidekiq_50.gemfile +0 -15
  99. data/gemfiles/sidekiq_60.gemfile +0 -12
  100. data/gemfiles/sidekiq_65.gemfile +0 -12
  101. data/gemfiles/sidekiq_70.gemfile +0 -12
  102. data/gemfiles/sinatra_14.gemfile +0 -15
  103. data/gemfiles/sinatra_22.gemfile +0 -12
  104. data/gemfiles/sinatra_30.gemfile +0 -12
  105. data/gemfiles/sinatra_40.gemfile +0 -12
  106. data/instana.gemspec +0 -53
  107. data/log/.keep +0 -0
  108. data/sonar-project.properties +0 -9
  109. data/test/activator_test.rb +0 -50
  110. data/test/backend/agent_test.rb +0 -80
  111. data/test/backend/gc_snapshot_test.rb +0 -11
  112. data/test/backend/host_agent_activation_observer_test.rb +0 -73
  113. data/test/backend/host_agent_lookup_test.rb +0 -78
  114. data/test/backend/host_agent_reporting_observer_test.rb +0 -276
  115. data/test/backend/host_agent_test.rb +0 -89
  116. data/test/backend/process_info_test.rb +0 -83
  117. data/test/backend/request_client_test.rb +0 -39
  118. data/test/backend/serverless_agent_test.rb +0 -83
  119. data/test/benchmarks/bench_id_generation.rb +0 -15
  120. data/test/benchmarks/bench_opentracing.rb +0 -16
  121. data/test/config_test.rb +0 -34
  122. data/test/frameworks/cuba_test.rb +0 -61
  123. data/test/frameworks/roda_test.rb +0 -60
  124. data/test/frameworks/sinatra_test.rb +0 -71
  125. data/test/instana_test.rb +0 -37
  126. data/test/instrumentation/aws_test.rb +0 -241
  127. data/test/instrumentation/dalli_test.rb +0 -325
  128. data/test/instrumentation/excon_test.rb +0 -204
  129. data/test/instrumentation/graphql_test.rb +0 -289
  130. data/test/instrumentation/grpc_test.rb +0 -420
  131. data/test/instrumentation/mongo_test.rb +0 -68
  132. data/test/instrumentation/net_http_test.rb +0 -220
  133. data/test/instrumentation/rack_instrumented_request_test.rb +0 -211
  134. data/test/instrumentation/rack_test.rb +0 -415
  135. data/test/instrumentation/rails_action_cable_test.rb +0 -135
  136. data/test/instrumentation/rails_action_controller_test.rb +0 -218
  137. data/test/instrumentation/rails_action_mailer_test.rb +0 -66
  138. data/test/instrumentation/rails_action_view_test.rb +0 -154
  139. data/test/instrumentation/rails_active_job_test.rb +0 -65
  140. data/test/instrumentation/rails_active_record_database_missing_test.rb +0 -44
  141. data/test/instrumentation/rails_active_record_test.rb +0 -116
  142. data/test/instrumentation/redis_test.rb +0 -152
  143. data/test/instrumentation/resque_test.rb +0 -188
  144. data/test/instrumentation/rest_client_test.rb +0 -106
  145. data/test/instrumentation/sequel_test.rb +0 -111
  146. data/test/instrumentation/shoryuken_test.rb +0 -47
  147. data/test/instrumentation/sidekiq-client_test.rb +0 -169
  148. data/test/instrumentation/sidekiq-worker_test.rb +0 -180
  149. data/test/secrets_test.rb +0 -112
  150. data/test/serverless_test.rb +0 -369
  151. data/test/snapshot/deltable_test.rb +0 -17
  152. data/test/snapshot/docker_container_test.rb +0 -82
  153. data/test/snapshot/fargate_container_test.rb +0 -82
  154. data/test/snapshot/fargate_process_test.rb +0 -35
  155. data/test/snapshot/fargate_task_test.rb +0 -49
  156. data/test/snapshot/google_cloud_run_instance_test.rb +0 -74
  157. data/test/snapshot/google_cloud_run_process_test.rb +0 -33
  158. data/test/snapshot/lambda_function_test.rb +0 -37
  159. data/test/snapshot/ruby_process_test.rb +0 -32
  160. data/test/support/apps/active_record/active_record.rb +0 -24
  161. data/test/support/apps/grpc/boot.rb +0 -23
  162. data/test/support/apps/grpc/grpc_server.rb +0 -84
  163. data/test/support/apps/http_endpoint/boot.rb +0 -28
  164. data/test/support/apps/rails/boot.rb +0 -219
  165. data/test/support/apps/rails/models/block.rb +0 -21
  166. data/test/support/apps/rails/models/block6.rb +0 -21
  167. data/test/support/apps/resque/boot.rb +0 -5
  168. data/test/support/apps/resque/jobs/resque_error_job.rb +0 -22
  169. data/test/support/apps/resque/jobs/resque_fast_job.rb +0 -23
  170. data/test/support/apps/sidekiq/boot.rb +0 -25
  171. data/test/support/apps/sidekiq/jobs/sidekiq_job_1.rb +0 -9
  172. data/test/support/apps/sidekiq/jobs/sidekiq_job_2.rb +0 -10
  173. data/test/support/apps/sidekiq/worker.rb +0 -37
  174. data/test/support/helpers.rb +0 -85
  175. data/test/support/mock_timer.rb +0 -20
  176. data/test/test_helper.rb +0 -69
  177. data/test/trace/custom_test.rb +0 -233
  178. data/test/trace/id_management_test.rb +0 -78
  179. data/test/trace/instrumented_logger_test.rb +0 -39
  180. data/test/trace/processor_test.rb +0 -58
  181. data/test/trace/span_context_test.rb +0 -22
  182. data/test/trace/span_test.rb +0 -179
  183. data/test/trace/tracer_async_test.rb +0 -243
  184. data/test/trace/tracer_provider_test.rb +0 -148
  185. data/test/trace/tracer_test.rb +0 -363
  186. data/test/util_test.rb +0 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 35cfa89077e641cdd3e6fb0a97008f1e10ad7b703dc5b2b79e90a383dfc30099
4
- data.tar.gz: dd9b39744a6432f619e496c759da84bffdfe114824bef0bbbcea155dbef3a284
3
+ metadata.gz: ca8072a81b44cce54a3a2db8ef236038fa4814226c714e88f12285ea952eede4
4
+ data.tar.gz: 6f3b666908eb9442c1a8e6d3d1b9a332576bb818810a2c588df4de2232c1b65a
5
5
  SHA512:
6
- metadata.gz: 47d8317281da1f8ae6adb6f740055d4845f125da6ccac65ce03ff68e4efc9a1eb22530f62571ef12bad952866832d99076b1ba4d62a6c876d0aaf4ab3b9c6ae5
7
- data.tar.gz: bc463f59a0662b0fe8ff5e34996631b1ed01215ef22e8f16781e7a249936e602959c1fecba74a3fe42e0c718f0ed37a4c62aa732c3b0f37f92cb49d9472e1e6f
6
+ metadata.gz: 84861f4a775983202c19731ce78302bf5e7cfd677591bf9b42014be4dee8b93efd1e6606910f756bbba495584a932ac860eb76cad5cf6539202d0588c3c01592
7
+ data.tar.gz: ab26f124f9e087501046f56645649aed9b077ee656120d99ac1c375f44552f92345e338c1d5495c28e4a1959a7c459bf652511e13031912812a1acef003ca3f3
data/lib/instana/setup.rb CHANGED
@@ -41,6 +41,7 @@ require 'instana/backend/serverless_agent'
41
41
  require 'instana/backend/agent'
42
42
  require 'instana/trace'
43
43
  require 'instana/trace/tracer_provider'
44
+ require 'instana/span_filtering'
44
45
 
45
46
  ::Instana.setup
46
47
  ::Instana.agent.setup
@@ -0,0 +1,134 @@
1
+ # (c) Copyright IBM Corp. 2025
2
+
3
+ module Instana
4
+ module SpanFiltering
5
+ # Represents a condition for filtering spans
6
+ #
7
+ # A condition consists of:
8
+ # - key: The attribute to match against (category, kind, type, or span attribute)
9
+ # - values: List of values to match against (OR logic between values)
10
+ # - match_type: String matching strategy (strict, startswith, endswith, contains)
11
+ class Condition
12
+ attr_reader :key, :values, :match_type
13
+
14
+ def initialize(key, values, match_type = 'strict')
15
+ @key = key
16
+ @values = Array(values)
17
+ @match_type = match_type
18
+ end
19
+
20
+ # Check if a span matches this condition
21
+ # @param span [Hash] The span to check
22
+ # @return [Boolean] True if the span matches any of the values
23
+ def matches?(span)
24
+ attribute_value = extract_attribute(span, @key)
25
+ return false if attribute_value.nil?
26
+
27
+ @values.any? { |value| matches_value?(attribute_value, value) }
28
+ end
29
+
30
+ private
31
+
32
+ # Extract an attribute from a span
33
+ # @param span [Hash] The span to extract from
34
+ # @param key [String] The key to extract
35
+ # @return [Object, nil] The attribute value or nil if not found
36
+ def extract_attribute(span, key)
37
+ case key
38
+ when 'category'
39
+ # Map to appropriate span attribute for category
40
+ determine_category(span)
41
+ when 'kind'
42
+ # Map to appropriate span attribute for kind
43
+ span[:k] || span['k']
44
+ when 'type'
45
+ # Map to appropriate span attribute for type
46
+ span[:n] || span['n']
47
+ else
48
+ # Handle nested attributes with dot notation
49
+ extract_nested_attribute(span[:data] || span['data'], key)
50
+ end
51
+ end
52
+
53
+ # Determine the category of a span based on its properties
54
+ # @param span [Hash] The span to categorize
55
+ # @return [String, nil] The category or nil if not determinable
56
+ def determine_category(span)
57
+ data = span[:data] || span['data']
58
+ return nil unless data
59
+
60
+ if data[:http] || data['http']
61
+ 'protocols'
62
+ elsif data[:redis] || data[:mysql] || data[:pg] || data[:db]
63
+ 'databases'
64
+ elsif data[:sqs] || data[:sns] || data[:mq]
65
+ 'messaging'
66
+ elsif (span[:n] || span['n'])&.start_with?('log.')
67
+ 'logging'
68
+ end
69
+ end
70
+
71
+ # Extract a nested attribute using dot notation
72
+ # @param data [Hash] The data hash to extract from
73
+ # @param key [String] The key in dot notation
74
+ # @return [Object, nil] The attribute value or nil if not found
75
+ def extract_nested_attribute(data, key)
76
+ return nil unless data
77
+
78
+ parts = key.split('.')
79
+ current = data
80
+
81
+ parts.each do |part|
82
+ # Try symbol key first, then string key
83
+ if current.key?(part.to_sym)
84
+ current = current[part.to_sym]
85
+ elsif current.key?(part)
86
+ current = current[part]
87
+ else
88
+ return nil # Key not found
89
+ end
90
+
91
+ # Only return nil if the value is actually nil, not for false values
92
+ end
93
+
94
+ current
95
+ end
96
+
97
+ # Check if an attribute value matches a condition value
98
+ # @param attribute_value [Object] The attribute value
99
+ # @param condition_value [String] The condition value
100
+ # @return [Boolean] True if the attribute value matches the condition value
101
+ def matches_value?(attribute_value, condition_value)
102
+ # Handle wildcard
103
+ return true if condition_value == '*'
104
+
105
+ # Direct comparison first - this should handle boolean values correctly
106
+ return true if attribute_value == condition_value
107
+
108
+ # For strict matching with type conversion
109
+ if @match_type == 'strict'
110
+ # Convert to strings and compare
111
+ attribute_str = attribute_value.to_s
112
+ condition_str = condition_value.to_s
113
+ return attribute_str == condition_str
114
+ end
115
+
116
+ # For other match types, convert both to strings
117
+ attribute_str = attribute_value.to_s
118
+ condition_str = condition_value.to_s
119
+
120
+ case @match_type
121
+ when 'startswith'
122
+ attribute_str.start_with?(condition_str)
123
+ when 'endswith'
124
+ attribute_str.end_with?(condition_str)
125
+ when 'contains'
126
+ attribute_str.include?(condition_str)
127
+ else
128
+ # Default to strict matching
129
+ attribute_str == condition_str
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,262 @@
1
+ # (c) Copyright IBM Corp. 2025
2
+
3
+ require 'yaml'
4
+
5
+ module Instana
6
+ module SpanFiltering
7
+ # Configuration class for span filtering
8
+ #
9
+ # This class handles loading and managing span filtering rules from various sources:
10
+ # - YAML configuration file (via INSTANA_CONFIG_PATH)
11
+ # - Environment variables
12
+ # - Agent discovery response
13
+ # It supports both include and exclude rules with various matching strategies
14
+ class Configuration
15
+ attr_reader :include_rules, :exclude_rules, :deactivated
16
+
17
+ TRACING_CONFIG_WARNING = 'Please use "tracing" instead of "com.instana.tracing" for local configuration file.'.freeze
18
+
19
+ def initialize
20
+ @include_rules = []
21
+ @exclude_rules = []
22
+ @deactivated = false
23
+ load_configuration
24
+ end
25
+
26
+ # Load configuration from all available sources
27
+ def load_configuration
28
+ load_from_yaml
29
+ load_from_env_vars unless rules_loaded?
30
+ load_from_agent unless rules_loaded?
31
+ end
32
+
33
+ private
34
+
35
+ # Load configuration from agent discovery response
36
+ def load_from_agent
37
+ # Try to get discovery value immediately first
38
+ discovery = ::Instana.agent&.delegate&.send(:discovery_value)
39
+ if discovery && discovery.is_a?(Hash) && !discovery.empty?
40
+ process_discovery_config(discovery)
41
+ return
42
+ end
43
+
44
+ # If not available, set up a timer task to periodically check for discovery
45
+ setup_discovery_timer
46
+ rescue => e
47
+ Instana.logger.warn("Failed to load span filtering configuration from agent: #{e.message}")
48
+ end
49
+
50
+ # Set up a timer task to periodically check for discovery
51
+ def setup_discovery_timer
52
+ # Don't create a timer task if we're in a test environment
53
+ return if ENV.key?('INSTANA_TEST')
54
+
55
+ # Create a timer task that checks for discovery every second
56
+ @discovery_timer = Concurrent::TimerTask.new(execution_interval: 1) do
57
+ check_discovery
58
+ end
59
+
60
+ # Start the timer task
61
+ @discovery_timer.execute
62
+ end
63
+
64
+ # Check if discovery is available and process it
65
+ def check_discovery
66
+ discovery = ::Instana.agent&.delegate.send(:discovery_value)
67
+ if discovery && discovery.is_a?(Hash) && !discovery.empty?
68
+ process_discovery_config(discovery)
69
+
70
+ # Shutdown the timer task after successful processing
71
+ @discovery_timer.shutdown if @discovery_timer
72
+
73
+ return true
74
+ end
75
+
76
+ false
77
+ rescue => e
78
+ Instana.logger.warn("Error checking discovery in timer task: #{e.message}")
79
+ false
80
+ end
81
+
82
+ # Process the discovery configuration
83
+ def process_discovery_config(discovery)
84
+ # Check if tracing configuration exists in the discovery response
85
+ tracing_config = discovery['tracing']
86
+ return unless tracing_config
87
+
88
+ # Process filter configuration
89
+ if tracing_config['filter']
90
+ filter_config = tracing_config['filter']
91
+ @deactivated = filter_config['deactivate'] == true
92
+
93
+ # Process include rules
94
+ process_rules(filter_config['include'], true) if filter_config['include']
95
+
96
+ # Process exclude rules
97
+ process_rules(filter_config['exclude'], false) if filter_config['exclude']
98
+ end
99
+
100
+ # Process disable configuration
101
+ if tracing_config['disable']
102
+ process_disable_config(tracing_config['disable'])
103
+ end
104
+
105
+ # Return true to indicate successful processing
106
+ true
107
+ rescue => e
108
+ Instana.logger.warn("Failed to process discovery configuration: #{e.message}")
109
+ false
110
+ end
111
+
112
+ # Check if the rules are already loaded
113
+ def rules_loaded?
114
+ @include_rules.any? || @exclude_rules.any?
115
+ end
116
+
117
+ # Load configuration from YAML file specified by INSTANA_CONFIG_PATH
118
+ def load_from_yaml
119
+ config_path = ENV['INSTANA_CONFIG_PATH']
120
+ return unless config_path && File.exist?(config_path)
121
+
122
+ begin
123
+ yaml_content = YAML.safe_load(File.read(config_path))
124
+
125
+ # Support both "tracing" and "com.instana.tracing" as top-level keys
126
+ tracing_config = yaml_content['tracing'] || yaml_content['com.instana.tracing']
127
+ ::Instana.logger.warn(TRACING_CONFIG_WARNING) if yaml_content.key?('com.instana.tracing')
128
+ return unless tracing_config
129
+
130
+ # Process filter configuration
131
+ if tracing_config['filter']
132
+ filter_config = tracing_config['filter']
133
+ @deactivated = filter_config['deactivate'] == true
134
+
135
+ # Process include rules
136
+ process_rules(filter_config['include'], true) if filter_config['include']
137
+
138
+ # Process exclude rules
139
+ process_rules(filter_config['exclude'], false) if filter_config['exclude']
140
+ end
141
+
142
+ # Process disable configuration
143
+ if tracing_config['disable']
144
+ process_disable_config(tracing_config['disable'])
145
+ end
146
+ rescue => e
147
+ Instana.logger.warn("Failed to load span filtering configuration from YAML: #{e.message}")
148
+ end
149
+ end
150
+
151
+ # Load configuration from environment variables
152
+ def load_from_env_vars
153
+ ENV.each do |key, value|
154
+ next unless key.start_with?('INSTANA_TRACING_FILTER_')
155
+
156
+ parts = key.split('_')
157
+ next unless parts.size >= 5
158
+
159
+ policy = parts[3].downcase
160
+ next unless ['include', 'exclude'].include?(policy)
161
+
162
+ if parts[4] == 'ATTRIBUTES'
163
+ process_env_attributes(policy, parts[4..].join('_'), value)
164
+ elsif policy == 'exclude' && parts[4] == 'SUPPRESSION'
165
+ process_env_suppression(parts[3..].join('_'), value)
166
+ end
167
+ end
168
+
169
+ return unless !ENV["INSTANA_TRACING_DISABLE"].nil? && !%w[True true 1].include?(ENV["INSTANA_TRACING_DISABLE"])
170
+
171
+ process_disable_config(ENV["INSTANA_TRACING_DISABLE"].split(','))
172
+ end
173
+
174
+ # Process rules from YAML configuration
175
+ def process_rules(rules_config, is_include)
176
+ rules_config.each do |rule_config|
177
+ name = rule_config['name']
178
+ suppression = is_include ? false : (rule_config['suppression'] != false) # Default true for exclude
179
+
180
+ conditions = []
181
+ rule_config['attributes'].each do |attr_config|
182
+ key = attr_config['key']
183
+ values = attr_config['values']
184
+ match_type = attr_config['match_type'] || 'strict'
185
+
186
+ conditions << Condition.new(key, values, match_type)
187
+ end
188
+
189
+ rule = FilterRule.new(name, suppression, conditions)
190
+ is_include ? @include_rules << rule : @exclude_rules << rule
191
+ end
192
+ end
193
+
194
+ # Process attributes from environment variables
195
+ def process_env_attributes(policy, name, value)
196
+ # Parse rules from environment variable format
197
+ # Format: key;values;match_type|key;values;match_type
198
+ rules = value.split('|')
199
+ conditions = []
200
+
201
+ rules.each do |rule|
202
+ parts = rule.split(';')
203
+ next unless parts.size >= 2
204
+
205
+ key = parts[0]
206
+ values = parts[1].split(',')
207
+ match_type = parts[2] || 'strict'
208
+
209
+ conditions << Condition.new(key, values, match_type)
210
+ end
211
+
212
+ rule_name = "EnvRule_#{name}"
213
+ suppression = policy == 'exclude' # Default true for exclude
214
+
215
+ rule = FilterRule.new(rule_name, suppression, conditions)
216
+ policy == 'include' ? @include_rules << rule : @exclude_rules << rule
217
+ end
218
+
219
+ # Process suppression setting from environment variables
220
+ def process_env_suppression(policy_name, value)
221
+ # Find the corresponding rule and update its suppression value
222
+ rule_index = policy_name.split('_')[1].to_i
223
+ return if rule_index >= @exclude_rules.size
224
+
225
+ suppression = %w[1 true True].include?(value)
226
+ @exclude_rules[rule_index].suppression = suppression
227
+ end
228
+
229
+ # Process disable configuration from YAML or agent discovery
230
+ # @param disable_config [Array] The disable configuration array
231
+ def process_disable_config(disable_config)
232
+ return unless disable_config.is_a?(Array)
233
+
234
+ disable_config.each do |item|
235
+ if item.is_a?(Hash)
236
+ item.each do |key, value|
237
+ if value == true
238
+ update_instana_config_for_disabled_technology(key)
239
+ end
240
+ end
241
+ elsif item.is_a?(String)
242
+ update_instana_config_for_disabled_technology(item)
243
+ end
244
+ end
245
+ end
246
+
247
+ # Update Instana::Config for a disabled technology
248
+ # @param technology [String] The technology to disable
249
+ def update_instana_config_for_disabled_technology(technology)
250
+ tech_sym = technology.to_sym
251
+
252
+ case tech_sym
253
+ when :redis
254
+ ::Instana.config[:redis][:enabled] = false
255
+ when :databases
256
+ # If databases category is disabled, also disable redis
257
+ ::Instana.config[:redis][:enabled] = false
258
+ end
259
+ end
260
+ end
261
+ end
262
+ end
@@ -0,0 +1,31 @@
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
@@ -0,0 +1,62 @@
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
@@ -159,15 +159,17 @@ module Instana
159
159
  # @return [Span]
160
160
  #
161
161
  def close(end_time = ::Instana::Util.now_in_ms)
162
+ result = ::Instana::SpanFiltering.filter_span(self)
162
163
  if end_time.is_a?(Time)
163
164
  end_time = ::Instana::Util.time_to_ms(end_time)
164
165
  end
165
-
166
166
  @attributes[:d] = end_time - @attributes[:ts]
167
167
  @ended = true
168
- # Add this span to the queue for reporting
169
- ::Instana.processor.on_finish(self)
170
168
 
169
+ if result.nil?
170
+ # Add this span to the queue for reporting
171
+ ::Instana.processor.on_finish(self)
172
+ end
171
173
  self
172
174
  end
173
175
 
@@ -2,6 +2,6 @@
2
2
  # (c) Copyright Instana Inc. 2016
3
3
 
4
4
  module Instana
5
- VERSION = "2.0.0"
5
+ VERSION = "2.1.0"
6
6
  VERSION_FULL = "instana-#{VERSION}"
7
7
  end