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.
- checksums.yaml +4 -4
- data/lib/instana/setup.rb +1 -0
- data/lib/instana/span_filtering/condition.rb +134 -0
- data/lib/instana/span_filtering/configuration.rb +262 -0
- data/lib/instana/span_filtering/filter_rule.rb +31 -0
- data/lib/instana/span_filtering.rb +62 -0
- data/lib/instana/trace/span.rb +5 -3
- data/lib/instana/version.rb +1 -1
- metadata +7 -258
- data/.circleci/config.yml +0 -361
- data/.codeclimate.yml +0 -23
- data/.editorconfig +0 -10
- data/.fasterer.yml +0 -23
- data/.github/ISSUE_TEMPLATE/bug.yml +0 -39
- data/.github/ISSUE_TEMPLATE/config.yml +0 -8
- data/.github/workflows/pr_commits_signed_off.yml +0 -16
- data/.github/workflows/release-notification-on-slack.yml +0 -34
- data/.gitignore +0 -19
- data/.rubocop.yml +0 -34
- data/.rubocop_todo.yml +0 -1140
- data/.tekton/.currency/docs/report.md +0 -20
- data/.tekton/.currency/resources/requirements.txt +0 -4
- data/.tekton/.currency/resources/table.json +0 -100
- data/.tekton/.currency/scripts/generate_report.py +0 -308
- data/.tekton/README.md +0 -278
- data/.tekton/github-interceptor-secret.yaml +0 -8
- data/.tekton/github-pr-eventlistener.yaml +0 -104
- data/.tekton/github-pr-pipeline.yaml.part +0 -38
- data/.tekton/github-set-status-task.yaml +0 -43
- data/.tekton/github-webhook-ingress.yaml +0 -20
- data/.tekton/pipeline.yaml +0 -484
- data/.tekton/pipelinerun.yaml +0 -21
- data/.tekton/prepuller-restart-service-account.yaml +0 -31
- data/.tekton/ruby-tracer-prepuller-cronjob.yaml +0 -20
- data/.tekton/ruby-tracer-prepuller.yaml +0 -88
- data/.tekton/run_unittests.sh +0 -87
- data/.tekton/scheduled-eventlistener.yaml +0 -108
- data/.tekton/task.yaml +0 -453
- data/.tekton/tekton-triggers-eventlistener-serviceaccount.yaml +0 -29
- data/Appraisals +0 -124
- data/CONTRIBUTING.md +0 -86
- data/Gemfile +0 -22
- data/LICENSE +0 -22
- data/MAINTAINERS.md +0 -3
- data/Rakefile +0 -49
- data/bin/announce_release_on_slack.py +0 -103
- data/docker-compose.yml +0 -20
- data/download.sh +0 -85
- data/examples/otel.rb +0 -98
- data/examples/tracing.rb +0 -85
- data/extras/license_header.rb +0 -44
- data/gemfiles/.bundle/config +0 -2
- data/gemfiles/aws_30.gemfile +0 -21
- data/gemfiles/aws_60.gemfile +0 -16
- data/gemfiles/cuba_30.gemfile +0 -16
- data/gemfiles/cuba_40.gemfile +0 -13
- data/gemfiles/dalli_20.gemfile +0 -15
- data/gemfiles/dalli_30.gemfile +0 -12
- data/gemfiles/dalli_32.gemfile +0 -12
- data/gemfiles/excon_0100.gemfile +0 -14
- data/gemfiles/excon_021.gemfile +0 -17
- data/gemfiles/excon_079.gemfile +0 -17
- data/gemfiles/excon_100.gemfile +0 -14
- data/gemfiles/graphql_10.gemfile +0 -16
- data/gemfiles/graphql_20.gemfile +0 -15
- data/gemfiles/grpc_10.gemfile +0 -15
- data/gemfiles/mongo_216.gemfile +0 -15
- data/gemfiles/mongo_219.gemfile +0 -12
- data/gemfiles/net_http_01.gemfile +0 -17
- data/gemfiles/rack_16.gemfile +0 -15
- data/gemfiles/rack_20.gemfile +0 -15
- data/gemfiles/rack_30.gemfile +0 -13
- data/gemfiles/rails_42.gemfile +0 -18
- data/gemfiles/rails_50.gemfile +0 -19
- data/gemfiles/rails_52.gemfile +0 -19
- data/gemfiles/rails_60.gemfile +0 -19
- data/gemfiles/rails_61.gemfile +0 -21
- data/gemfiles/rails_70.gemfile +0 -18
- data/gemfiles/rails_71.gemfile +0 -17
- data/gemfiles/rails_80.gemfile +0 -17
- data/gemfiles/redis_40.gemfile +0 -15
- data/gemfiles/redis_50.gemfile +0 -13
- data/gemfiles/redis_51.gemfile +0 -13
- data/gemfiles/resque_122.gemfile +0 -16
- data/gemfiles/resque_1274_3scale.gemfile +0 -17
- data/gemfiles/resque_20.gemfile +0 -16
- data/gemfiles/rest_client_16.gemfile +0 -17
- data/gemfiles/rest_client_20.gemfile +0 -17
- data/gemfiles/roda_20.gemfile +0 -16
- data/gemfiles/roda_30.gemfile +0 -16
- data/gemfiles/rubocop_162.gemfile +0 -6
- data/gemfiles/sequel_56.gemfile +0 -16
- data/gemfiles/sequel_57.gemfile +0 -16
- data/gemfiles/sequel_58.gemfile +0 -16
- data/gemfiles/shoryuken_50.gemfile +0 -16
- data/gemfiles/shoryuken_60.gemfile +0 -13
- data/gemfiles/sidekiq_42.gemfile +0 -15
- data/gemfiles/sidekiq_50.gemfile +0 -15
- data/gemfiles/sidekiq_60.gemfile +0 -12
- data/gemfiles/sidekiq_65.gemfile +0 -12
- data/gemfiles/sidekiq_70.gemfile +0 -12
- data/gemfiles/sinatra_14.gemfile +0 -15
- data/gemfiles/sinatra_22.gemfile +0 -12
- data/gemfiles/sinatra_30.gemfile +0 -12
- data/gemfiles/sinatra_40.gemfile +0 -12
- data/instana.gemspec +0 -53
- data/log/.keep +0 -0
- data/sonar-project.properties +0 -9
- data/test/activator_test.rb +0 -50
- data/test/backend/agent_test.rb +0 -80
- data/test/backend/gc_snapshot_test.rb +0 -11
- data/test/backend/host_agent_activation_observer_test.rb +0 -73
- data/test/backend/host_agent_lookup_test.rb +0 -78
- data/test/backend/host_agent_reporting_observer_test.rb +0 -276
- data/test/backend/host_agent_test.rb +0 -89
- data/test/backend/process_info_test.rb +0 -83
- data/test/backend/request_client_test.rb +0 -39
- data/test/backend/serverless_agent_test.rb +0 -83
- data/test/benchmarks/bench_id_generation.rb +0 -15
- data/test/benchmarks/bench_opentracing.rb +0 -16
- data/test/config_test.rb +0 -34
- data/test/frameworks/cuba_test.rb +0 -61
- data/test/frameworks/roda_test.rb +0 -60
- data/test/frameworks/sinatra_test.rb +0 -71
- data/test/instana_test.rb +0 -37
- data/test/instrumentation/aws_test.rb +0 -241
- data/test/instrumentation/dalli_test.rb +0 -325
- data/test/instrumentation/excon_test.rb +0 -204
- data/test/instrumentation/graphql_test.rb +0 -289
- data/test/instrumentation/grpc_test.rb +0 -420
- data/test/instrumentation/mongo_test.rb +0 -68
- data/test/instrumentation/net_http_test.rb +0 -220
- data/test/instrumentation/rack_instrumented_request_test.rb +0 -211
- data/test/instrumentation/rack_test.rb +0 -415
- data/test/instrumentation/rails_action_cable_test.rb +0 -135
- data/test/instrumentation/rails_action_controller_test.rb +0 -218
- data/test/instrumentation/rails_action_mailer_test.rb +0 -66
- data/test/instrumentation/rails_action_view_test.rb +0 -154
- data/test/instrumentation/rails_active_job_test.rb +0 -65
- data/test/instrumentation/rails_active_record_database_missing_test.rb +0 -44
- data/test/instrumentation/rails_active_record_test.rb +0 -116
- data/test/instrumentation/redis_test.rb +0 -152
- data/test/instrumentation/resque_test.rb +0 -188
- data/test/instrumentation/rest_client_test.rb +0 -106
- data/test/instrumentation/sequel_test.rb +0 -111
- data/test/instrumentation/shoryuken_test.rb +0 -47
- data/test/instrumentation/sidekiq-client_test.rb +0 -169
- data/test/instrumentation/sidekiq-worker_test.rb +0 -180
- data/test/secrets_test.rb +0 -112
- data/test/serverless_test.rb +0 -369
- data/test/snapshot/deltable_test.rb +0 -17
- data/test/snapshot/docker_container_test.rb +0 -82
- data/test/snapshot/fargate_container_test.rb +0 -82
- data/test/snapshot/fargate_process_test.rb +0 -35
- data/test/snapshot/fargate_task_test.rb +0 -49
- data/test/snapshot/google_cloud_run_instance_test.rb +0 -74
- data/test/snapshot/google_cloud_run_process_test.rb +0 -33
- data/test/snapshot/lambda_function_test.rb +0 -37
- data/test/snapshot/ruby_process_test.rb +0 -32
- data/test/support/apps/active_record/active_record.rb +0 -24
- data/test/support/apps/grpc/boot.rb +0 -23
- data/test/support/apps/grpc/grpc_server.rb +0 -84
- data/test/support/apps/http_endpoint/boot.rb +0 -28
- data/test/support/apps/rails/boot.rb +0 -219
- data/test/support/apps/rails/models/block.rb +0 -21
- data/test/support/apps/rails/models/block6.rb +0 -21
- data/test/support/apps/resque/boot.rb +0 -5
- data/test/support/apps/resque/jobs/resque_error_job.rb +0 -22
- data/test/support/apps/resque/jobs/resque_fast_job.rb +0 -23
- data/test/support/apps/sidekiq/boot.rb +0 -25
- data/test/support/apps/sidekiq/jobs/sidekiq_job_1.rb +0 -9
- data/test/support/apps/sidekiq/jobs/sidekiq_job_2.rb +0 -10
- data/test/support/apps/sidekiq/worker.rb +0 -37
- data/test/support/helpers.rb +0 -85
- data/test/support/mock_timer.rb +0 -20
- data/test/test_helper.rb +0 -69
- data/test/trace/custom_test.rb +0 -233
- data/test/trace/id_management_test.rb +0 -78
- data/test/trace/instrumented_logger_test.rb +0 -39
- data/test/trace/processor_test.rb +0 -58
- data/test/trace/span_context_test.rb +0 -22
- data/test/trace/span_test.rb +0 -179
- data/test/trace/tracer_async_test.rb +0 -243
- data/test/trace/tracer_provider_test.rb +0 -148
- data/test/trace/tracer_test.rb +0 -363
- data/test/util_test.rb +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ca8072a81b44cce54a3a2db8ef236038fa4814226c714e88f12285ea952eede4
|
4
|
+
data.tar.gz: 6f3b666908eb9442c1a8e6d3d1b9a332576bb818810a2c588df4de2232c1b65a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 84861f4a775983202c19731ce78302bf5e7cfd677591bf9b42014be4dee8b93efd1e6606910f756bbba495584a932ac860eb76cad5cf6539202d0588c3c01592
|
7
|
+
data.tar.gz: ab26f124f9e087501046f56645649aed9b077ee656120d99ac1c375f44552f92345e338c1d5495c28e4a1959a7c459bf652511e13031912812a1acef003ca3f3
|
data/lib/instana/setup.rb
CHANGED
@@ -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
|
data/lib/instana/trace/span.rb
CHANGED
@@ -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
|
|