instana 1.217.1 → 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/README.md +2 -1
- data/lib/instana/base.rb +4 -2
- data/lib/instana/instrumentation/action_cable.rb +8 -4
- data/lib/instana/instrumentation/action_controller.rb +2 -4
- data/lib/instana/instrumentation/action_mailer.rb +1 -1
- data/lib/instana/instrumentation/action_view.rb +4 -4
- data/lib/instana/instrumentation/active_job.rb +20 -5
- data/lib/instana/instrumentation/active_record.rb +1 -1
- data/lib/instana/instrumentation/aws_sdk_dynamodb.rb +1 -1
- data/lib/instana/instrumentation/aws_sdk_lambda.rb +1 -1
- data/lib/instana/instrumentation/aws_sdk_s3.rb +1 -1
- data/lib/instana/instrumentation/aws_sdk_sns.rb +1 -1
- data/lib/instana/instrumentation/aws_sdk_sqs.rb +1 -1
- data/lib/instana/instrumentation/dalli.rb +1 -1
- data/lib/instana/instrumentation/excon.rb +2 -2
- data/lib/instana/instrumentation/graphql.rb +3 -3
- data/lib/instana/instrumentation/grpc.rb +14 -13
- data/lib/instana/instrumentation/mongo.rb +3 -3
- data/lib/instana/instrumentation/net-http.rb +5 -4
- data/lib/instana/instrumentation/rack.rb +36 -4
- data/lib/instana/instrumentation/redis.rb +1 -1
- data/lib/instana/instrumentation/resque.rb +10 -8
- data/lib/instana/instrumentation/rest-client.rb +4 -4
- data/lib/instana/instrumentation/sequel.rb +3 -3
- data/lib/instana/instrumentation/shoryuken.rb +4 -1
- data/lib/instana/instrumentation/sidekiq-client.rb +21 -19
- data/lib/instana/instrumentation/sidekiq-worker.rb +22 -21
- data/lib/instana/instrumented_logger.rb +1 -1
- data/lib/instana/samplers/result.rb +32 -0
- data/lib/instana/samplers/samplers.rb +76 -0
- data/lib/instana/serverless.rb +4 -2
- data/lib/instana/setup.rb +5 -5
- 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/export.rb +36 -0
- data/lib/instana/{tracing → trace}/processor.rb +19 -15
- data/lib/instana/trace/span.rb +534 -0
- data/lib/instana/{tracing → trace}/span_context.rb +17 -8
- data/lib/instana/trace/span_kind.rb +51 -0
- data/lib/instana/trace/span_limits.rb +63 -0
- data/lib/instana/{tracer.rb → trace/tracer.rb} +106 -54
- data/lib/instana/trace/tracer_provider.rb +198 -0
- data/lib/instana/trace.rb +74 -0
- data/lib/instana/util.rb +11 -0
- data/lib/instana/version.rb +1 -1
- metadata +89 -267
- data/.circleci/config.yml +0 -485
- 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 -136
- 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 -571
- data/.tekton/pipelinerun.yaml +0 -21
- data/.tekton/ruby-tracer-prepuller.yaml +0 -87
- data/.tekton/run_unittests.sh +0 -87
- data/.tekton/scheduled-eventlistener.yaml +0 -108
- data/.tekton/task.yaml +0 -449
- data/.tekton/tekton-triggers-eventlistener-serviceaccount.yaml +0 -29
- data/Appraisals +0 -124
- data/CONTRIBUTING.md +0 -86
- data/Gemfile +0 -17
- 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/opentracing.rb +0 -35
- data/examples/tracing.rb +0 -84
- 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 -20
- data/gemfiles/rails_70.gemfile +0 -17
- 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 -48
- data/lib/instana/open_tracing/carrier.rb +0 -7
- data/lib/instana/open_tracing/instana_tracer.rb +0 -99
- data/lib/instana/tracing/span.rb +0 -431
- data/lib/opentracing.rb +0 -32
- 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 -45
- data/test/instrumentation/rails_active_record_test.rb +0 -115
- data/test/instrumentation/redis_test.rb +0 -152
- data/test/instrumentation/resque_test.rb +0 -188
- data/test/instrumentation/rest_client_test.rb +0 -107
- data/test/instrumentation/sequel_test.rb +0 -105
- 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/tracing/custom_test.rb +0 -226
- data/test/tracing/id_management_test.rb +0 -80
- data/test/tracing/instrumented_logger_test.rb +0 -39
- data/test/tracing/opentracing_test.rb +0 -382
- data/test/tracing/processor_test.rb +0 -58
- data/test/tracing/span_context_test.rb +0 -22
- data/test/tracing/span_test.rb +0 -179
- data/test/tracing/tracer_async_test.rb +0 -230
- data/test/tracing/tracer_test.rb +0 -352
- data/test/util_test.rb +0 -10
@@ -5,11 +5,11 @@ module Instana
|
|
5
5
|
module Instrumentation
|
6
6
|
class SidekiqWorker
|
7
7
|
def call(_worker, msg, _queue)
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
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
13
|
|
14
14
|
# Temporary until we move connection collection to redis
|
15
15
|
# instrumentation
|
@@ -24,29 +24,30 @@ module Instana
|
|
24
24
|
else # Unexpected version, continue without recording any redis-url
|
25
25
|
break
|
26
26
|
end
|
27
|
-
|
27
|
+
kvs[:'sidekiq-worker'][:'redis-url'] = "#{host}:#{port}"
|
28
28
|
end
|
29
29
|
|
30
|
-
|
30
|
+
trace_context = nil
|
31
31
|
if msg.key?('X-Instana-T')
|
32
32
|
trace_id = msg.delete('X-Instana-T')
|
33
33
|
span_id = msg.delete('X-Instana-S')
|
34
|
-
|
35
|
-
|
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
|
+
)
|
36
38
|
end
|
37
39
|
|
38
|
-
::
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
::Instana.tracer.log_end(:'sidekiq-worker', {}) if ::Instana.tracer.tracing?
|
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
|
50
51
|
end
|
51
52
|
end
|
52
53
|
end
|
@@ -0,0 +1,32 @@
|
|
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
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# (c) Copyright IBM Corp. 2025
|
2
|
+
require 'instana/samplers/result'
|
3
|
+
module Instana
|
4
|
+
module Trace
|
5
|
+
# The Samplers module contains the sampling logic for OpenTelemetry. The
|
6
|
+
# reference implementation provides a {TraceIdRatioBased}, {ALWAYS_ON},
|
7
|
+
# {ALWAYS_OFF}, and {ParentBased}.
|
8
|
+
#
|
9
|
+
# Custom samplers can be provided by SDK users. The required interface is:
|
10
|
+
#
|
11
|
+
# should_sample?(trace_id:, parent_context:, links:, name:, kind:, attributes:) -> Result
|
12
|
+
# description -> String
|
13
|
+
#
|
14
|
+
# Where:
|
15
|
+
#
|
16
|
+
# @param [String] trace_id The trace_id of the {Span} to be created.
|
17
|
+
# @param [OpenTelemetry::Context] parent_context The
|
18
|
+
# {OpenTelemetry::Context} with a parent {Span}. The {Span}'s
|
19
|
+
# {OpenTelemetry::Trace::SpanContext} may be invalid to indicate a
|
20
|
+
# root span.
|
21
|
+
# @param [Enumerable<Link>] links A collection of links to be associated
|
22
|
+
# with the {Span} to be created. Can be nil.
|
23
|
+
# @param [String] name Name of the {Span} to be created.
|
24
|
+
# @param [Symbol] kind The {OpenTelemetry::Trace::SpanKind} of the {Span}
|
25
|
+
# to be created. Can be nil.
|
26
|
+
# @param [Hash<String, Object>] attributes Attributes to be attached
|
27
|
+
# to the {Span} to be created. Can be nil.
|
28
|
+
# @return [Result] The sampling result.
|
29
|
+
module Samplers
|
30
|
+
# Returns a {Result} with {Decision::RECORD_AND_SAMPLE}.
|
31
|
+
ALWAYS_ON = false
|
32
|
+
# # Returns a {Result} with {Decision::DROP}.
|
33
|
+
ALWAYS_OFF = true
|
34
|
+
|
35
|
+
# Returns a new sampler. It delegates to samplers according to the following rules:
|
36
|
+
#
|
37
|
+
# | Parent | parent.remote? | parent.trace_flags.sampled? | Invoke sampler |
|
38
|
+
# |--|--|--|--|
|
39
|
+
# | absent | n/a | n/a | root |
|
40
|
+
# | present | true | true | remote_parent_sampled |
|
41
|
+
# | present | true | false | remote_parent_not_sampled |
|
42
|
+
# | present | false | true | local_parent_sampled |
|
43
|
+
# | present | false | false | local_parent_not_sampled |
|
44
|
+
#
|
45
|
+
# @param [Sampler] root The sampler to which the sampling
|
46
|
+
# decision is delegated for spans with no parent (root spans).
|
47
|
+
# @param [optional Sampler] remote_parent_sampled The sampler to which the sampling
|
48
|
+
# decision is delegated for remote parent sampled spans. Defaults to ALWAYS_ON.
|
49
|
+
# @param [optional Sampler] remote_parent_not_sampled The sampler to which the sampling
|
50
|
+
# decision is delegated for remote parent not sampled spans. Defaults to ALWAYS_OFF.
|
51
|
+
# @param [optional Sampler] local_parent_sampled The sampler to which the sampling
|
52
|
+
# decision is delegated for local parent sampled spans. Defaults to ALWAYS_ON.
|
53
|
+
# @param [optional Sampler] local_parent_not_sampled The sampler to which the sampling
|
54
|
+
# decision is delegated for local parent not sampld spans. Defaults to ALWAYS_OFF.
|
55
|
+
def self.parent_based(_)
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns a new sampler. The ratio describes the proportion of the trace ID
|
60
|
+
# space that is sampled.
|
61
|
+
#
|
62
|
+
# @param [Numeric] ratio The desired sampling ratio.
|
63
|
+
# Must be within [0.0, 1.0].
|
64
|
+
# @raise [ArgumentError] if ratio is out of range
|
65
|
+
def self.trace_id_ratio_based(_)
|
66
|
+
self
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.should_sample?(trace_id:, parent_context:, links:, name:, kind:, attributes:) # rubocop:disable Metrics/ParameterLists, Lint/UnusedMethodArgument:
|
70
|
+
parent_span_context = OpenTelemetry::Trace.current_span(parent_context).context
|
71
|
+
tracestate = parent_span_context&.tracestate
|
72
|
+
Result.new(decision: :__record_only__, tracestate: tracestate)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/instana/serverless.rb
CHANGED
@@ -42,8 +42,10 @@ module Instana
|
|
42
42
|
else
|
43
43
|
tags[:lambda] = tags[:lambda].merge(event_tags)
|
44
44
|
end
|
45
|
-
|
46
|
-
|
45
|
+
Trace.with_span(OpenTelemetry::Trace.non_recording_span(::Instana::SpanContext.new(trace_id: span_context[:trace_id], span_id: span_context[:span_id],
|
46
|
+
level: span_context[:level]))) do
|
47
|
+
@tracer.in_span(:'aws.lambda.entry', attributes: tags, &block)
|
48
|
+
end
|
47
49
|
ensure
|
48
50
|
begin
|
49
51
|
@agent.send_bundle
|
data/lib/instana/setup.rb
CHANGED
@@ -11,8 +11,8 @@ require 'instana/instrumented_logger'
|
|
11
11
|
require "instana/base"
|
12
12
|
require "instana/config"
|
13
13
|
require "instana/secrets"
|
14
|
-
require "instana/tracer"
|
15
|
-
require "instana/
|
14
|
+
require "instana/trace/tracer"
|
15
|
+
require "instana/trace/processor"
|
16
16
|
|
17
17
|
require 'instana/serverless'
|
18
18
|
|
@@ -39,13 +39,13 @@ require 'instana/backend/host_agent_reporting_observer'
|
|
39
39
|
require 'instana/backend/host_agent'
|
40
40
|
require 'instana/backend/serverless_agent'
|
41
41
|
require 'instana/backend/agent'
|
42
|
+
require 'instana/trace'
|
43
|
+
require 'instana/trace/tracer_provider'
|
44
|
+
require 'instana/span_filtering'
|
42
45
|
|
43
46
|
::Instana.setup
|
44
47
|
::Instana.agent.setup
|
45
48
|
|
46
|
-
# Require supported OpenTracing interfaces
|
47
|
-
require "opentracing"
|
48
|
-
|
49
49
|
# The Instana agent is now setup. The only remaining
|
50
50
|
# task for a complete boot is to call
|
51
51
|
# `Instana.agent.start` in the thread of your choice.
|
@@ -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
|