bugsnag_performance 0.1.0 → 0.2.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/bugsnag_performance/configuration.rb +0 -2
- data/lib/bugsnag_performance/internal/configuration_validator.rb +0 -11
- data/lib/bugsnag_performance/internal/delivery.rb +2 -0
- data/lib/bugsnag_performance/internal/payload_encoder.rb +0 -5
- data/lib/bugsnag_performance/internal/probability_attribute_span_processor.rb +32 -0
- data/lib/bugsnag_performance/internal/span_exporter.rb +48 -5
- data/lib/bugsnag_performance/version.rb +1 -1
- data/lib/bugsnag_performance.rb +19 -3
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 84d3f11ac05bf81b8de7503e8992327b5152196ed024ee348aaac879fa7b845e
|
4
|
+
data.tar.gz: 517ae6ce8cfa46220e1a2d5b3394514cc2096a8139cd7c82492e5d62dbcff615
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 74294e03b1b6bfada4a0043b12fa5d82ea0953dd592d274c36792b49a96445fbf21026b275ae0055d2a5505aad93099bc063332102e25fdfae48a8cd2242e1d9
|
7
|
+
data.tar.gz: 8f4c8de90d51d4e613869b7f74b5ed8a88f142819d4e576442df0e457dd3534b6d5a3c959feb7d2557286784211658a17ae387503df2078b819b77c9f4e078d3
|
@@ -9,7 +9,6 @@ module BugsnagPerformance
|
|
9
9
|
attr_accessor :app_version
|
10
10
|
attr_accessor :release_stage
|
11
11
|
attr_accessor :enabled_release_stages
|
12
|
-
attr_accessor :use_managed_quota
|
13
12
|
|
14
13
|
attr_writer :endpoint
|
15
14
|
|
@@ -20,7 +19,6 @@ module BugsnagPerformance
|
|
20
19
|
@api_key = fetch(errors_configuration, :api_key, env: "BUGSNAG_PERFORMANCE_API_KEY")
|
21
20
|
@app_version = fetch(errors_configuration, :app_version)
|
22
21
|
@release_stage = fetch(errors_configuration, :release_stage, env: "BUGSNAG_PERFORMANCE_RELEASE_STAGE", default: "production")
|
23
|
-
@use_managed_quota = true
|
24
22
|
|
25
23
|
@enabled_release_stages = fetch(errors_configuration, :enabled_release_stages, env: "BUGSNAG_PERFORMANCE_ENABLED_RELEASE_STAGES")
|
26
24
|
|
@@ -16,7 +16,6 @@ module BugsnagPerformance
|
|
16
16
|
validate_string(:app_version, optional: true)
|
17
17
|
validate_string(:release_stage, optional: true)
|
18
18
|
validate_array(:enabled_release_stages, "non-empty strings", optional: true, &method(:valid_string?))
|
19
|
-
validate_boolean(:use_managed_quota, optional: false)
|
20
19
|
valid_endpoint = validate_endpoint
|
21
20
|
|
22
21
|
# if the endpoint is invalid then we shouldn't attempt to send traces
|
@@ -72,16 +71,6 @@ module BugsnagPerformance
|
|
72
71
|
end
|
73
72
|
end
|
74
73
|
|
75
|
-
def validate_boolean(name, optional:)
|
76
|
-
value = @configuration.send(name)
|
77
|
-
|
78
|
-
if (value.nil? && optional) || value == true || value == false
|
79
|
-
@valid_configuration.send("#{name}=", value)
|
80
|
-
else
|
81
|
-
@messages << "#{name} should be a boolean, got #{value.inspect}"
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
74
|
def validate_array(name, description, optional:, &block)
|
86
75
|
value = @configuration.send(name)
|
87
76
|
|
@@ -26,14 +26,9 @@ module BugsnagPerformance
|
|
26
26
|
:SPAN_STATUS_UNSET,
|
27
27
|
:SPAN_STATUS_ERROR
|
28
28
|
|
29
|
-
def initialize(sampler)
|
30
|
-
@sampler = sampler
|
31
|
-
end
|
32
|
-
|
33
29
|
def encode(span_data)
|
34
30
|
{
|
35
31
|
resourceSpans: span_data
|
36
|
-
.filter { |span| @sampler.resample_span?(span) }
|
37
32
|
.group_by(&:resource)
|
38
33
|
.map do |resource, scope_spans|
|
39
34
|
{
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BugsnagPerformance
|
4
|
+
module Internal
|
5
|
+
class ProbabilityAttributeSpanProcessor
|
6
|
+
def initialize(probability_manager)
|
7
|
+
@probability_manager = probability_manager
|
8
|
+
end
|
9
|
+
|
10
|
+
def on_start(span, parent_context)
|
11
|
+
# avoid overwriting the attribute if the sampler has already set it
|
12
|
+
if span.attributes.nil? || span.attributes["bugsnag.sampling.p"].nil?
|
13
|
+
span.set_attribute("bugsnag.sampling.p", @probability_manager.probability)
|
14
|
+
end
|
15
|
+
|
16
|
+
OpenTelemetry::SDK::Trace::Export::SUCCESS
|
17
|
+
end
|
18
|
+
|
19
|
+
def on_finish(span)
|
20
|
+
OpenTelemetry::SDK::Trace::Export::SUCCESS
|
21
|
+
end
|
22
|
+
|
23
|
+
def force_flush(timeout: nil)
|
24
|
+
OpenTelemetry::SDK::Trace::Export::SUCCESS
|
25
|
+
end
|
26
|
+
|
27
|
+
def shutdown(timeout: nil)
|
28
|
+
OpenTelemetry::SDK::Trace::Export::SUCCESS
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -7,36 +7,67 @@ module BugsnagPerformance
|
|
7
7
|
logger,
|
8
8
|
probability_manager,
|
9
9
|
delivery,
|
10
|
+
sampler,
|
10
11
|
payload_encoder,
|
11
12
|
sampling_header_encoder
|
12
13
|
)
|
13
14
|
@logger = logger
|
14
15
|
@probability_manager = probability_manager
|
15
16
|
@delivery = delivery
|
17
|
+
@sampler = sampler
|
16
18
|
@payload_encoder = payload_encoder
|
17
19
|
@sampling_header_encoder = sampling_header_encoder
|
18
20
|
@disabled = false
|
21
|
+
@unmanaged_mode = false
|
22
|
+
@logged_first_batch_destination = false
|
19
23
|
end
|
20
24
|
|
21
25
|
def disable!
|
22
26
|
@disabled = true
|
23
27
|
end
|
24
28
|
|
29
|
+
def unmanaged_mode!
|
30
|
+
@unmanaged_mode = true
|
31
|
+
end
|
32
|
+
|
33
|
+
def unmanaged_mode?
|
34
|
+
@unmanaged_mode
|
35
|
+
end
|
36
|
+
|
25
37
|
def export(span_data, timeout: nil)
|
26
38
|
return OpenTelemetry::SDK::Trace::Export::SUCCESS if @disabled
|
27
39
|
|
28
40
|
with_timeout(timeout) do
|
41
|
+
# ensure we're in the correct managed or unmanaged mode
|
42
|
+
maybe_enter_unmanaged_mode
|
43
|
+
managed_status = unmanaged_mode? ? "unmanaged" : "managed"
|
44
|
+
|
29
45
|
headers = {}
|
30
|
-
sampling_header = @sampling_header_encoder.encode(span_data)
|
31
46
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
47
|
+
# resample the spans and attach the Bugsnag-Span-Sampling header only
|
48
|
+
# if we're in managed mode
|
49
|
+
unless unmanaged_mode?
|
50
|
+
span_data = span_data.filter { |span| @sampler.resample_span?(span) }
|
51
|
+
|
52
|
+
sampling_header = @sampling_header_encoder.encode(span_data)
|
53
|
+
|
54
|
+
if sampling_header.nil?
|
55
|
+
@logger.warn("One or more spans are missing the 'bugsnag.sampling.p' attribute. This trace will be sent as unmanaged")
|
56
|
+
managed_status = "unmanaged"
|
57
|
+
else
|
58
|
+
headers["Bugsnag-Span-Sampling"] = sampling_header
|
59
|
+
end
|
36
60
|
end
|
37
61
|
|
38
62
|
body = JSON.generate(@payload_encoder.encode(span_data))
|
39
63
|
|
64
|
+
# log whether we're sending managed or unmanaged spans on the first
|
65
|
+
# batch only
|
66
|
+
unless @logged_first_batch_destination
|
67
|
+
@logger.info("Sending #{managed_status} spans to #{@delivery.uri}")
|
68
|
+
@logged_first_batch_destination = true
|
69
|
+
end
|
70
|
+
|
40
71
|
response = @delivery.deliver(headers, body)
|
41
72
|
|
42
73
|
if response.sampling_probability
|
@@ -64,6 +95,18 @@ module BugsnagPerformance
|
|
64
95
|
|
65
96
|
private
|
66
97
|
|
98
|
+
def maybe_enter_unmanaged_mode
|
99
|
+
# we're in unmanaged mode already so don't need to do anything
|
100
|
+
return if unmanaged_mode?
|
101
|
+
|
102
|
+
# our sampler is in use so we're in managed mode
|
103
|
+
return if OpenTelemetry.tracer_provider.sampler.is_a?(Sampler)
|
104
|
+
|
105
|
+
# the user has changed the sampler from ours to a custom one; enter
|
106
|
+
# unmanaged mode
|
107
|
+
unmanaged_mode!
|
108
|
+
end
|
109
|
+
|
67
110
|
def with_timeout(timeout, &block)
|
68
111
|
if timeout.nil?
|
69
112
|
block.call
|
data/lib/bugsnag_performance.rb
CHANGED
@@ -23,6 +23,7 @@ require_relative "bugsnag_performance/internal/probability_manager"
|
|
23
23
|
require_relative "bugsnag_performance/internal/configuration_validator"
|
24
24
|
require_relative "bugsnag_performance/internal/sampling_header_encoder"
|
25
25
|
require_relative "bugsnag_performance/internal/nil_errors_configuration"
|
26
|
+
require_relative "bugsnag_performance/internal/probability_attribute_span_processor"
|
26
27
|
|
27
28
|
module BugsnagPerformance
|
28
29
|
def self.configure(&block)
|
@@ -45,10 +46,17 @@ module BugsnagPerformance
|
|
45
46
|
configuration.logger,
|
46
47
|
probability_manager,
|
47
48
|
delivery,
|
48
|
-
|
49
|
+
sampler,
|
50
|
+
Internal::PayloadEncoder.new,
|
49
51
|
Internal::SamplingHeaderEncoder.new,
|
50
52
|
)
|
51
53
|
|
54
|
+
# enter unmanaged mode if the OTel sampler environment variable has been set
|
55
|
+
# note: we assume any value means a non-default sampler will be used because
|
56
|
+
# we don't control what the valid values are
|
57
|
+
user_has_custom_sampler = ENV.key?("OTEL_TRACES_SAMPLER")
|
58
|
+
exporter.unmanaged_mode! if user_has_custom_sampler
|
59
|
+
|
52
60
|
if configuration.enabled_release_stages && !configuration.enabled_release_stages.include?(configuration.release_stage)
|
53
61
|
configuration.logger.info("Not exporting spans as the current release stage is not in the enabled release stages.")
|
54
62
|
exporter.disable!
|
@@ -76,10 +84,18 @@ module BugsnagPerformance
|
|
76
84
|
otel_configurator.add_span_processor(
|
77
85
|
OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(exporter)
|
78
86
|
)
|
87
|
+
|
88
|
+
# ensure the "bugsnag.sampling.p" attribute is set on all spans even when
|
89
|
+
# our sampler is not in use
|
90
|
+
otel_configurator.add_span_processor(
|
91
|
+
Internal::ProbabilityAttributeSpanProcessor.new(probability_manager)
|
92
|
+
)
|
79
93
|
end
|
80
94
|
|
81
|
-
# use our sampler
|
82
|
-
|
95
|
+
# don't use our sampler if the user has configured a sampler via the OTel
|
96
|
+
# environment variable
|
97
|
+
# note: the user can still replace our sampler with their own after this
|
98
|
+
OpenTelemetry.tracer_provider.sampler = sampler unless user_has_custom_sampler
|
83
99
|
|
84
100
|
return_value
|
85
101
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bugsnag_performance
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- BugSnag
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-08-
|
11
|
+
date: 2024-08-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -84,6 +84,7 @@ files:
|
|
84
84
|
- lib/bugsnag_performance/internal/logger_wrapper.rb
|
85
85
|
- lib/bugsnag_performance/internal/nil_errors_configuration.rb
|
86
86
|
- lib/bugsnag_performance/internal/payload_encoder.rb
|
87
|
+
- lib/bugsnag_performance/internal/probability_attribute_span_processor.rb
|
87
88
|
- lib/bugsnag_performance/internal/probability_fetcher.rb
|
88
89
|
- lib/bugsnag_performance/internal/probability_manager.rb
|
89
90
|
- lib/bugsnag_performance/internal/sampler.rb
|
@@ -99,7 +100,7 @@ metadata:
|
|
99
100
|
homepage_uri: https://www.bugsnag.com
|
100
101
|
source_code_uri: https://github.com/bugsnag/bugsnag-ruby-performance
|
101
102
|
bug_tracker_uri: https://github.com/bugsnag/bugsnag-ruby-performance/issues
|
102
|
-
changelog_uri: https://github.com/bugsnag/bugsnag-ruby-performance/blob/v0.
|
103
|
+
changelog_uri: https://github.com/bugsnag/bugsnag-ruby-performance/blob/v0.2.0/CHANGELOG.md
|
103
104
|
documentation_uri: https://docs.bugsnag.com/performance/integration-guides/ruby/
|
104
105
|
rubygems_mfa_required: 'true'
|
105
106
|
post_install_message:
|