Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix APM Disablement V1 (ASM Standalone) #4479

Merged
merged 39 commits into from
Mar 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
cbd2721
Fix apm disablement tag when appsec is disabled
vpellan Feb 24, 2025
df380fb
Add method to skip upstream sampling priority
vpellan Mar 5, 2025
e16e109
Add APM disablement integration tests
vpellan Mar 5, 2025
0dcfc6c
Fix propagate_sampling_priority with no upstream_tags
vpellan Mar 6, 2025
247a628
Fix asm standalone integration tests
vpellan Mar 6, 2025
d603133
Remove appsec_standalone_reject method
vpellan Mar 6, 2025
477668a
Skip distributed tracing if APM is disabled on all HTTP/GRPC/Sidekiq …
vpellan Mar 7, 2025
39256eb
Delete unnecessary default sample rate (as we match all traces agains…
vpellan Mar 7, 2025
4dc496a
Add test to check heartbeat traces
vpellan Mar 7, 2025
2971a23
Fix Datadog-Computed-Client-Stats tests
vpellan Mar 7, 2025
d206fb2
Simplify Datadog-Client-Computed-Stats test
vpellan Mar 7, 2025
844279b
Add separate method to generate APM disablement post_sampler
vpellan Mar 10, 2025
dd8e5d2
Cleaned appsec standalone tests for rack
vpellan Mar 10, 2025
f0117e4
Delete ASM Standalone tests on Rails and Sinatra (ASM Standalone has …
vpellan Mar 10, 2025
0ee8566
Remove forced-tests from git conflict resolution
vpellan Mar 10, 2025
edec9f5
Rename build_above_second_post_sampler to build_rate_limit_post_sampler
vpellan Mar 11, 2025
71a940a
Move before block in context
vpellan Mar 11, 2025
554c409
Rubocop
vpellan Mar 11, 2025
e75c89c
Changed system-tests scenarios to Experimental mode
vpellan Mar 11, 2025
0cbcd34
Add should_skip_distributed_tracing? method
vpellan Mar 12, 2025
bb53227
Fix circuit breaker spec
vpellan Mar 12, 2025
f581672
Fix typo in comment
vpellan Mar 13, 2025
149fbbb
Rename client_config and datadog_config to contrib_*_config
vpellan Mar 13, 2025
f5c5f4e
Rename CircuitBreaker.should_skip_distributed_tracing? to SkipPolicy.…
vpellan Mar 19, 2025
872abbc
Change apm_tracing_disabled to apm_tracing_enabled
vpellan Mar 19, 2025
9e4eda6
Add frozen_string_literals in APM Disablement tests
vpellan Mar 19, 2025
f151293
Define subject in place in skip_policy_spec
vpellan Mar 19, 2025
ba1bfbf
Fix GRPC distributed tracing skipping
vpellan Mar 19, 2025
f0b85e8
Change SkipPolicy.skip? parameters name
vpellan Mar 19, 2025
1938f14
Merge branch 'master' into vpellan/fix-sca-standalone
vpellan Mar 19, 2025
fea594d
Revert adding rack in ruby-3.3.gemfile
vpellan Mar 19, 2025
366b434
Add comment to explain build_rate_limit_post_sampler
vpellan Mar 24, 2025
ab38041
Rename SkipPolicy.skip? to PropagationPolicy.enabled? and inverted it…
vpellan Mar 24, 2025
9712b3f
Add guard clause if Tracing is disabled in PropagationPolicy.enabled?
vpellan Mar 24, 2025
2eac1e8
Invert check order for PropagationPolicy.enabled?
vpellan Mar 24, 2025
c433494
Add appsec_enabled method in tracer
vpellan Mar 24, 2025
c727d6a
Apply comment suggestions
vpellan Mar 24, 2025
913522f
Fix steep error for Tracing.enabled?
vpellan Mar 24, 2025
839e483
Add comment to explain apm_tracing_enabled
vpellan Mar 26, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/forced-tests-list.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
{

}
"SCA_STANDALONE_EXPERIMENTAL": [
"tests/appsec/test_asm_standalone.py::Test_SCAStandalone_Telemetry"
]
}
5 changes: 4 additions & 1 deletion .github/workflows/system-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ jobs:
- APPSEC_DISABLED
- APPSEC_BLOCKING_FULL_DENYLIST
- APPSEC_REQUEST_BLOCKING
- APPSEC_STANDALONE
- APPSEC_STANDALONE_EXPERIMENTAL
- APPSEC_BLOCKING
include:
- library: ruby
Expand Down Expand Up @@ -325,6 +325,9 @@ jobs:
- library: ruby
app: rails70
scenario: DEBUGGER_PII_REDACTION
- library: ruby
app: rails70
scenario: SCA_STANDALONE_EXPERIMENTAL
- library: ruby
app: rack
scenario: SAMPLING
Expand Down
2 changes: 0 additions & 2 deletions lib/datadog/appsec/contrib/rack/request_middleware.rb
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,6 @@ def add_appsec_tags(processor, context)
return unless trace && span

span.set_metric(Datadog::AppSec::Ext::TAG_APPSEC_ENABLED, 1)
# We add this tag when ASM standalone is enabled to make sure we don't bill APM
span.set_metric(Datadog::AppSec::Ext::TAG_APM_ENABLED, 0) if Datadog.configuration.appsec.standalone.enabled
span.set_tag('_dd.runtime_family', 'ruby')
span.set_tag('_dd.appsec.waf.version', Datadog::AppSec::WAF::VERSION::BASE_STRING)

Expand Down
1 change: 0 additions & 1 deletion lib/datadog/appsec/ext.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ module Ext
ACTIVE_CONTEXT_KEY = :datadog_appsec_active_context

TAG_APPSEC_ENABLED = '_dd.appsec.enabled'
TAG_APM_ENABLED = '_dd.apm.enabled'
TAG_DISTRIBUTED_APPSEC_EVENT = '_dd.p.appsec'

TELEMETRY_METRICS_NAMESPACE = 'appsec'
Expand Down
2 changes: 0 additions & 2 deletions lib/datadog/appsec/utils.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# frozen_string_literal: true

require_relative 'utils/trace_operation'

module Datadog
module AppSec
# Utilities for AppSec
Expand Down
15 changes: 0 additions & 15 deletions lib/datadog/appsec/utils/trace_operation.rb

This file was deleted.

17 changes: 10 additions & 7 deletions lib/datadog/tracing/component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,7 @@ def build_sampler(settings)
# so we want to send the minimum amount of traces possible (idealy only traces that contains security events),
# but for features such as API Security, we need to send at least one trace per minute,
# to keep the service alive on the backend side.
if settings.appsec.standalone.enabled
post_sampler = Tracing::Sampling::RuleSampler.new(
[Tracing::Sampling::SimpleRule.new(sample_rate: 1.0)],
rate_limiter: Datadog::Core::TokenBucket.new(1.0 / 60, 1.0),
default_sample_rate: 1.0 / 60
)
end
post_sampler = build_rate_limit_post_sampler(seconds: 60) if settings.appsec.standalone.enabled

# Sampling rules are provided
if (rules = settings.tracing.sampling.rules)
Expand Down Expand Up @@ -197,6 +191,15 @@ def build_tracer_tags(settings)
end
end

# Build a post-sampler that limits the rate of traces to one per `seconds`.
# E.g.: `build_rate_limit_post_sampler(seconds: 60)` will limit the rate to one trace per minute.
def build_rate_limit_post_sampler(seconds:)
Tracing::Sampling::RuleSampler.new(
rate_limiter: Datadog::Core::TokenBucket.new(1.0 / seconds, 1.0),
default_sample_rate: 1.0
)
end

def build_test_mode_trace_flush(settings)
# If context flush behavior is provided, use it instead.
settings.tracing.test_mode.trace_flush || build_trace_flush(settings)
Expand Down
9 changes: 4 additions & 5 deletions lib/datadog/tracing/contrib/ethon/easy_patch.rb
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,10 @@ def datadog_before_request(continue_from: nil)

datadog_tag_request

if Datadog::AppSec::Utils::TraceOperation.appsec_standalone_reject?(datadog_trace)
datadog_trace.sampling_priority = Tracing::Sampling::Ext::Priority::AUTO_REJECT
end

if datadog_configuration[:distributed_tracing]
if Tracing::Distributed::PropagationPolicy.enabled?(
global_config: datadog_configuration,
trace: datadog_trace
)
@datadog_original_headers ||= {}
Contrib::HTTP.inject(datadog_trace, @datadog_original_headers)
self.headers = @datadog_original_headers
Expand Down
8 changes: 5 additions & 3 deletions lib/datadog/tracing/contrib/excon/middleware.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@ def request_call(datum)
trace = Tracing.active_trace
datum[:datadog_span] = span
annotate!(span, datum)
if Datadog::AppSec::Utils::TraceOperation.appsec_standalone_reject?(trace)
trace.sampling_priority = Tracing::Sampling::Ext::Priority::AUTO_REJECT
if Tracing::Distributed::PropagationPolicy.enabled?(
global_config: @options,
trace: trace
)
propagate!(trace, span, datum)
end
propagate!(trace, span, datum) if distributed_tracing?

span
end
Expand Down
8 changes: 5 additions & 3 deletions lib/datadog/tracing/contrib/faraday/middleware.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,12 @@ def call(env)

Tracing.trace(Ext::SPAN_REQUEST, on_error: request_options[:on_error]) do |span, trace|
annotate!(span, env, request_options)
if Datadog::AppSec::Utils::TraceOperation.appsec_standalone_reject?(trace)
trace.sampling_priority = Tracing::Sampling::Ext::Priority::AUTO_REJECT
if Tracing::Distributed::PropagationPolicy.enabled?(
global_config: request_options,
trace: trace
)
propagate!(trace, span, env)
end
propagate!(trace, span, env) if request_options[:distributed_tracing] && Tracing.enabled?
app.call(env).on_complete { |resp| handle_response(span, resp, request_options) }
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,13 @@ def annotate!(trace, span, keywords, formatter)
# Set analytics sample rate
Contrib::Analytics.set_sample_rate(span, analytics_sample_rate) if analytics_enabled?

GRPC.inject(trace, metadata) if distributed_tracing?
if Tracing::Distributed::PropagationPolicy.enabled?(
pin_config: Datadog.configuration_for(self),
global_config: datadog_configuration,
trace: trace
)
GRPC.inject(trace, metadata)
end
Contrib::SpanAttributeSchema.set_peer_service!(span, Ext::PEER_SERVICE_SOURCES)
rescue StandardError => e
Datadog.logger.debug("GRPC client trace failed: #{e}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require_relative 'fetcher'
require_relative '../../../distributed/b3_multi'
require_relative '../../../distributed/b3_single'
require_relative '../../../distributed/propagation_policy'
require_relative '../../../distributed/datadog'
require_relative '../../../distributed/none'
require_relative '../../../distributed/propagation'
Expand Down
15 changes: 0 additions & 15 deletions lib/datadog/tracing/contrib/http/circuit_breaker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,6 @@ def internal_request?(request)
!!(request[Datadog::Core::Transport::Ext::HTTP::HEADER_META_TRACER_VERSION] ||
request[Datadog::Core::Transport::Ext::HTTP::HEADER_DD_INTERNAL_UNTRACED_REQUEST])
end

def should_skip_distributed_tracing?(client_config)
if Datadog.configuration.appsec.standalone.enabled
# Skip distributed tracing so that we don't bill distributed traces in case of absence of
# upstream ASM event (_dd.p.appsec:1) and no local security event (which sets _dd.p.appsec:1 locally).
# If there is an ASM event, we still have to check if distributed tracing is enabled or not
return true unless Tracing.active_trace

return true if Tracing.active_trace.get_tag(Datadog::AppSec::Ext::TAG_DISTRIBUTED_APPSEC_EVENT) != '1'
end

return !client_config[:distributed_tracing] if client_config && client_config.key?(:distributed_tracing)

!Datadog.configuration.tracing[:http][:distributed_tracing]
end
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

require_relative 'fetcher'
require_relative '../../../distributed/propagation'
require_relative '../../../distributed/propagation_policy'
require_relative '../../../distributed/b3_multi'
require_relative '../../../distributed/b3_single'
require_relative '../../../distributed/datadog'
Expand Down
10 changes: 5 additions & 5 deletions lib/datadog/tracing/contrib/http/instrumentation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ def request(req, body = nil, &block)
span.type = Tracing::Metadata::Ext::HTTP::TYPE_OUTBOUND
span.resource = req.method

if Datadog::AppSec::Utils::TraceOperation.appsec_standalone_reject?(trace)
trace.sampling_priority = Tracing::Sampling::Ext::Priority::AUTO_REJECT
end

if Tracing.enabled? && !Contrib::HTTP.should_skip_distributed_tracing?(client_config)
if Tracing::Distributed::PropagationPolicy.enabled?(
pin_config: client_config,
global_config: Datadog.configuration.tracing[:http],
trace: trace
)
Contrib::HTTP.inject(trace, req)
end

Expand Down
16 changes: 5 additions & 11 deletions lib/datadog/tracing/contrib/httpclient/instrumentation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ def do_get_block(req, proxy, conn, &block)
span.service = service_name(host, request_options, client_config)
span.type = Tracing::Metadata::Ext::HTTP::TYPE_OUTBOUND

if Datadog::AppSec::Utils::TraceOperation.appsec_standalone_reject?(trace)
trace.sampling_priority = Tracing::Sampling::Ext::Priority::AUTO_REJECT
end

if Tracing.enabled? && !should_skip_distributed_tracing?(client_config)
if Tracing::Distributed::PropagationPolicy.enabled?(
pin_config: client_config,
global_config: Datadog.configuration.tracing[:httpclient],
trace: trace
)
Contrib::HTTP.inject(trace, req.header)
end

Expand Down Expand Up @@ -123,12 +123,6 @@ def analytics_enabled?(request_options)
Contrib::Analytics.enabled?(request_options[:analytics_enabled])
end

def should_skip_distributed_tracing?(client_config)
return !client_config[:distributed_tracing] if client_config && client_config.key?(:distributed_tracing)

!Datadog.configuration.tracing[:httpclient][:distributed_tracing]
end

def set_analytics_sample_rate(span, request_options)
return unless analytics_enabled?(request_options)

Expand Down
16 changes: 6 additions & 10 deletions lib/datadog/tracing/contrib/httprb/instrumentation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@ def perform(req, options)
span.service = service_name(host, request_options, client_config)
span.type = Tracing::Metadata::Ext::HTTP::TYPE_OUTBOUND

if Datadog::AppSec::Utils::TraceOperation.appsec_standalone_reject?(trace)
trace.sampling_priority = Tracing::Sampling::Ext::Priority::AUTO_REJECT
if Tracing::Distributed::PropagationPolicy.enabled?(
pin_config: client_config,
global_config: Datadog.configuration.tracing[:httprb],
trace: trace
)
Contrib::HTTP.inject(trace, req)
end

Contrib::HTTP.inject(trace, req) if Tracing.enabled? && !should_skip_distributed_tracing?(client_config)

# Add additional request specific tags to the span.
annotate_span_with_request!(span, req, request_options)
rescue StandardError => e
Expand Down Expand Up @@ -135,12 +137,6 @@ def logger
Datadog.logger
end

def should_skip_distributed_tracing?(client_config)
return !client_config[:distributed_tracing] if client_config && client_config.key?(:distributed_tracing)

!Datadog.configuration.tracing[:httprb][:distributed_tracing]
end

def set_analytics_sample_rate(span, request_options)
return unless analytics_enabled?(request_options)

Expand Down
8 changes: 5 additions & 3 deletions lib/datadog/tracing/contrib/rest_client/request_patch.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@ def execute(&block)
return super(&block) unless Tracing.enabled?

datadog_trace_request(uri) do |_span, trace|
if Datadog::AppSec::Utils::TraceOperation.appsec_standalone_reject?(trace)
trace.sampling_priority = Tracing::Sampling::Ext::Priority::AUTO_REJECT
if Tracing::Distributed::PropagationPolicy.enabled?(
global_config: datadog_configuration,
trace: trace
)
Contrib::HTTP.inject(trace, processed_headers)
end
Contrib::HTTP.inject(trace, processed_headers) if datadog_configuration[:distributed_tracing]

super(&block)
end
Expand Down
7 changes: 6 additions & 1 deletion lib/datadog/tracing/contrib/sidekiq/client_tracer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ def call(worker_class, job, queue, redis_pool)
resource = job_resource(job)

Datadog::Tracing.trace(Ext::SPAN_PUSH, service: @sidekiq_service) do |span, trace_op|
Sidekiq.inject(trace_op, job) if configuration[:distributed_tracing]
if Tracing::Distributed::PropagationPolicy.enabled?(
global_config: configuration,
trace: trace_op
)
Sidekiq.inject(trace_op, job)
end

span.resource = resource

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

require_relative '../../../distributed/fetcher'
require_relative '../../../distributed/propagation'
require_relative '../../../distributed/propagation_policy'
require_relative '../../../distributed/b3_multi'
require_relative '../../../distributed/b3_single'
require_relative '../../../distributed/datadog'
Expand Down
32 changes: 32 additions & 0 deletions lib/datadog/tracing/distributed/propagation_policy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# frozen_string_literal: true

module Datadog
module Tracing
module Distributed
# Helper method to decide when to skip distributed tracing
module PropagationPolicy
module_function

# Skips distributed tracing if disabled for this instrumentation
# or if APM is disabled unless there is an AppSec event (from upstream distributed trace or local)
#
# Both pin_config and global_config are configuration for integrations.
# pin_config is a Datadog::Core::Pin object, which gives the configuration of a single instance of an integration.
# global_config is the config for all instances of an integration.
def enabled?(pin_config: nil, global_config: nil, trace: nil)
return false unless Tracing.enabled?

if (trace.nil? || trace.get_tag(::Datadog::AppSec::Ext::TAG_DISTRIBUTED_APPSEC_EVENT) != '1') &&
::Datadog.configuration.appsec.standalone.enabled
return false
end

return pin_config[:distributed_tracing] if pin_config && pin_config.key?(:distributed_tracing)
return global_config[:distributed_tracing] if global_config

true
end
end
end
end
end
2 changes: 2 additions & 0 deletions lib/datadog/tracing/metadata/ext.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ module Ext
# See Datadog-internal "RFC: Identifying which spans have profiling enabled " for details
TAG_PROFILING_ENABLED = '_dd.profiling.enabled'

TAG_APM_ENABLED = '_dd.apm.enabled'

# Defines constants for trace analytics
# @public_api
module Analytics
Expand Down
3 changes: 3 additions & 0 deletions lib/datadog/tracing/trace_operation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ def initialize(
sampling_priority: nil,
service: nil,
profiling_enabled: nil,
apm_tracing_enabled: nil,
tags: nil,
metrics: nil,
trace_state: nil,
Expand Down Expand Up @@ -100,6 +101,7 @@ def initialize(
@sampling_priority = sampling_priority
@service = service
@profiling_enabled = profiling_enabled
@apm_tracing_enabled = apm_tracing_enabled
@trace_state = trace_state
@trace_state_unknown_fields = trace_state_unknown_fields
@tracer = tracer
Expand Down Expand Up @@ -514,6 +516,7 @@ def build_trace(spans, partial = false)
metrics: metrics,
root_span_id: !partial ? root_span && root_span.id : nil,
profiling_enabled: @profiling_enabled,
apm_tracing_enabled: @apm_tracing_enabled
)
end

Expand Down
Loading
Loading