pingops 0.0.1
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 +7 -0
- data/CHANGELOG.md +26 -0
- data/README.md +377 -0
- data/lib/pingops/core/body_capture.rb +102 -0
- data/lib/pingops/core/configuration.rb +181 -0
- data/lib/pingops/core/constants.rb +126 -0
- data/lib/pingops/core/context_keys.rb +96 -0
- data/lib/pingops/core/domain_filter.rb +123 -0
- data/lib/pingops/core/header_filter.rb +123 -0
- data/lib/pingops/core/id_generator.rb +78 -0
- data/lib/pingops/core/span_eligibility.rb +56 -0
- data/lib/pingops/core/types.rb +190 -0
- data/lib/pingops/errors.rb +15 -0
- data/lib/pingops/instrumentation/manager.rb +87 -0
- data/lib/pingops/instrumentation/net_http.rb +146 -0
- data/lib/pingops/otel/config_store.rb +101 -0
- data/lib/pingops/otel/span_processor.rb +293 -0
- data/lib/pingops/otel/tracer_provider.rb +93 -0
- data/lib/pingops/register.rb +48 -0
- data/lib/pingops/sdk.rb +291 -0
- data/lib/pingops/version.rb +5 -0
- data/lib/pingops.rb +160 -0
- data/pingops.gemspec +54 -0
- metadata +295 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pingops
|
|
4
|
+
module Core
|
|
5
|
+
# Domain rule for allow/deny lists
|
|
6
|
+
# @attr domain [String] Domain to match (exact or suffix if starts with ".")
|
|
7
|
+
# @attr paths [Array<String>, nil] If set, URL path must start with one of these
|
|
8
|
+
# @attr headers_allow_list [Array<String>, nil] Override global: only these headers for this domain
|
|
9
|
+
# @attr headers_deny_list [Array<String>, nil] Override global: exclude these headers for this domain
|
|
10
|
+
# @attr capture_request_body [Boolean, nil] Override global/context for this domain
|
|
11
|
+
# @attr capture_response_body [Boolean, nil] Override global/context for this domain
|
|
12
|
+
class DomainRule
|
|
13
|
+
attr_reader :domain, :paths, :headers_allow_list, :headers_deny_list,
|
|
14
|
+
:capture_request_body, :capture_response_body
|
|
15
|
+
|
|
16
|
+
def initialize(
|
|
17
|
+
domain:,
|
|
18
|
+
paths: nil,
|
|
19
|
+
headers_allow_list: nil,
|
|
20
|
+
headers_deny_list: nil,
|
|
21
|
+
capture_request_body: nil,
|
|
22
|
+
capture_response_body: nil
|
|
23
|
+
)
|
|
24
|
+
@domain = domain.to_s
|
|
25
|
+
@paths = paths&.map(&:to_s)
|
|
26
|
+
@headers_allow_list = headers_allow_list&.map { |h| h.to_s.downcase }
|
|
27
|
+
@headers_deny_list = headers_deny_list&.map { |h| h.to_s.downcase }
|
|
28
|
+
@capture_request_body = capture_request_body
|
|
29
|
+
@capture_response_body = capture_response_body
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Check if this rule matches the given domain
|
|
33
|
+
# @param target_domain [String] The domain to check
|
|
34
|
+
# @return [Boolean]
|
|
35
|
+
def matches_domain?(target_domain)
|
|
36
|
+
return false if target_domain.nil? || target_domain.empty?
|
|
37
|
+
|
|
38
|
+
target = target_domain.downcase
|
|
39
|
+
rule_domain = @domain.downcase
|
|
40
|
+
|
|
41
|
+
if rule_domain.start_with?('.')
|
|
42
|
+
# Suffix match: ".example.com" matches "api.example.com" or "example.com"
|
|
43
|
+
target.end_with?(rule_domain) || target == rule_domain[1..]
|
|
44
|
+
else
|
|
45
|
+
# Exact match
|
|
46
|
+
target == rule_domain
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Check if this rule matches the given path
|
|
51
|
+
# @param path [String] The path to check
|
|
52
|
+
# @return [Boolean]
|
|
53
|
+
def matches_path?(path)
|
|
54
|
+
return true if @paths.nil? || @paths.empty?
|
|
55
|
+
|
|
56
|
+
@paths.any? { |p| path.to_s.start_with?(p) }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Create from hash (for config loading)
|
|
60
|
+
# @param hash [Hash] Hash representation
|
|
61
|
+
# @return [DomainRule]
|
|
62
|
+
def self.from_hash(hash)
|
|
63
|
+
return hash if hash.is_a?(DomainRule)
|
|
64
|
+
|
|
65
|
+
new(
|
|
66
|
+
domain: hash[:domain] || hash['domain'],
|
|
67
|
+
paths: hash[:paths] || hash['paths'],
|
|
68
|
+
headers_allow_list: hash[:headers_allow_list] || hash['headersAllowList'],
|
|
69
|
+
headers_deny_list: hash[:headers_deny_list] || hash['headersDenyList'],
|
|
70
|
+
capture_request_body: hash[:capture_request_body] || hash['captureRequestBody'],
|
|
71
|
+
capture_response_body: hash[:capture_response_body] || hash['captureResponseBody']
|
|
72
|
+
)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Header redaction configuration
|
|
77
|
+
# @attr sensitive_patterns [Array<String>] Case-insensitive patterns to match header names
|
|
78
|
+
# @attr strategy [String] How to redact: replace, partial, partial_end, remove
|
|
79
|
+
# @attr redaction_string [String] String used for redaction
|
|
80
|
+
# @attr visible_chars [Integer] For partial strategies, how many chars to show
|
|
81
|
+
# @attr enabled [Boolean] If false, no redaction
|
|
82
|
+
class HeaderRedactionConfig
|
|
83
|
+
attr_reader :sensitive_patterns, :strategy, :redaction_string, :visible_chars, :enabled
|
|
84
|
+
|
|
85
|
+
def initialize(
|
|
86
|
+
sensitive_patterns: nil,
|
|
87
|
+
strategy: nil,
|
|
88
|
+
redaction_string: nil,
|
|
89
|
+
visible_chars: nil,
|
|
90
|
+
enabled: nil
|
|
91
|
+
)
|
|
92
|
+
@sensitive_patterns = (sensitive_patterns || Constants::DEFAULT_SENSITIVE_HEADER_PATTERNS).map(&:downcase)
|
|
93
|
+
@strategy = strategy || Constants::REDACTION_STRATEGY_REPLACE
|
|
94
|
+
@redaction_string = redaction_string || Constants::DEFAULT_REDACTION_STRING
|
|
95
|
+
@visible_chars = visible_chars || Constants::DEFAULT_VISIBLE_CHARS
|
|
96
|
+
@enabled = enabled.nil? || enabled
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Create from hash (for config loading)
|
|
100
|
+
# @param hash [Hash, nil] Hash representation
|
|
101
|
+
# @return [HeaderRedactionConfig]
|
|
102
|
+
def self.from_hash(hash)
|
|
103
|
+
return new if hash.nil?
|
|
104
|
+
return hash if hash.is_a?(HeaderRedactionConfig)
|
|
105
|
+
|
|
106
|
+
new(
|
|
107
|
+
sensitive_patterns: hash[:sensitive_patterns] || hash['sensitivePatterns'],
|
|
108
|
+
strategy: hash[:strategy] || hash['strategy'],
|
|
109
|
+
redaction_string: hash[:redaction_string] || hash['redactionString'],
|
|
110
|
+
visible_chars: hash[:visible_chars] || hash['visibleChars'],
|
|
111
|
+
enabled: hash[:enabled].nil? ? hash['enabled'] : hash[:enabled]
|
|
112
|
+
)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Trace attributes for startTrace
|
|
117
|
+
# @attr trace_id [String, nil] Optional; if set, used as trace ID
|
|
118
|
+
# @attr user_id [String, nil] Propagated to context and span attribute
|
|
119
|
+
# @attr session_id [String, nil] Propagated to context and span attribute
|
|
120
|
+
# @attr tags [Array<String>, nil] Propagated to context and span attribute
|
|
121
|
+
# @attr metadata [Hash<String, String>, nil] Propagated to context and span attributes
|
|
122
|
+
# @attr capture_request_body [Boolean, nil] Override for request body capture
|
|
123
|
+
# @attr capture_response_body [Boolean, nil] Override for response body capture
|
|
124
|
+
class TraceAttributes
|
|
125
|
+
attr_reader :trace_id, :user_id, :session_id, :tags, :metadata,
|
|
126
|
+
:capture_request_body, :capture_response_body
|
|
127
|
+
|
|
128
|
+
def initialize(
|
|
129
|
+
trace_id: nil,
|
|
130
|
+
user_id: nil,
|
|
131
|
+
session_id: nil,
|
|
132
|
+
tags: nil,
|
|
133
|
+
metadata: nil,
|
|
134
|
+
capture_request_body: nil,
|
|
135
|
+
capture_response_body: nil
|
|
136
|
+
)
|
|
137
|
+
@trace_id = trace_id
|
|
138
|
+
@user_id = user_id
|
|
139
|
+
@session_id = session_id
|
|
140
|
+
@tags = tags
|
|
141
|
+
@metadata = metadata
|
|
142
|
+
@capture_request_body = capture_request_body
|
|
143
|
+
@capture_response_body = capture_response_body
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Create from hash
|
|
147
|
+
# @param hash [Hash, nil] Hash representation
|
|
148
|
+
# @return [TraceAttributes, nil]
|
|
149
|
+
def self.from_hash(hash)
|
|
150
|
+
return nil if hash.nil?
|
|
151
|
+
return hash if hash.is_a?(TraceAttributes)
|
|
152
|
+
|
|
153
|
+
new(
|
|
154
|
+
trace_id: hash[:trace_id] || hash['traceId'],
|
|
155
|
+
user_id: hash[:user_id] || hash['userId'],
|
|
156
|
+
session_id: hash[:session_id] || hash['sessionId'],
|
|
157
|
+
tags: hash[:tags] || hash['tags'],
|
|
158
|
+
metadata: hash[:metadata] || hash['metadata'],
|
|
159
|
+
capture_request_body: hash[:capture_request_body] || hash['captureRequestBody'],
|
|
160
|
+
capture_response_body: hash[:capture_response_body] || hash['captureResponseBody']
|
|
161
|
+
)
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Options for startTrace
|
|
166
|
+
# @attr attributes [TraceAttributes, nil] Optional attributes to set in context
|
|
167
|
+
# @attr seed [String, nil] Optional. If provided, trace ID is deterministic
|
|
168
|
+
class StartTraceOptions
|
|
169
|
+
attr_reader :attributes, :seed
|
|
170
|
+
|
|
171
|
+
def initialize(attributes: nil, seed: nil)
|
|
172
|
+
@attributes = attributes.is_a?(Hash) ? TraceAttributes.from_hash(attributes) : attributes
|
|
173
|
+
@seed = seed
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Create from hash
|
|
177
|
+
# @param hash [Hash, nil] Hash representation
|
|
178
|
+
# @return [StartTraceOptions]
|
|
179
|
+
def self.from_hash(hash)
|
|
180
|
+
return new if hash.nil?
|
|
181
|
+
return hash if hash.is_a?(StartTraceOptions)
|
|
182
|
+
|
|
183
|
+
new(
|
|
184
|
+
attributes: hash[:attributes] || hash['attributes'],
|
|
185
|
+
seed: hash[:seed] || hash['seed']
|
|
186
|
+
)
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pingops
|
|
4
|
+
# Base error class for all PingOps errors
|
|
5
|
+
class Error < StandardError; end
|
|
6
|
+
|
|
7
|
+
# Raised when configuration is invalid or missing required fields
|
|
8
|
+
class ConfigurationError < Error; end
|
|
9
|
+
|
|
10
|
+
# Raised when the SDK is not initialized
|
|
11
|
+
class NotInitializedError < Error; end
|
|
12
|
+
|
|
13
|
+
# Raised when auto-initialization fails
|
|
14
|
+
class AutoInitializationError < Error; end
|
|
15
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pingops
|
|
4
|
+
module Instrumentation
|
|
5
|
+
# Manages all PingOps instrumentations
|
|
6
|
+
module Manager
|
|
7
|
+
@installed = false
|
|
8
|
+
@mutex = Mutex.new
|
|
9
|
+
|
|
10
|
+
class << self
|
|
11
|
+
# Install all instrumentations
|
|
12
|
+
# @param config [Core::Configuration] Configuration
|
|
13
|
+
def install(config)
|
|
14
|
+
@mutex.synchronize do
|
|
15
|
+
return if @installed
|
|
16
|
+
|
|
17
|
+
# Store config in global store for instrumentations
|
|
18
|
+
Otel::ConfigStore.set(config)
|
|
19
|
+
|
|
20
|
+
# Install Net::HTTP instrumentation
|
|
21
|
+
NetHttp.install
|
|
22
|
+
|
|
23
|
+
@installed = true
|
|
24
|
+
log_debug(config, 'Instrumentations installed')
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Check if instrumentations are installed
|
|
29
|
+
# @return [Boolean]
|
|
30
|
+
def installed?
|
|
31
|
+
@mutex.synchronize do
|
|
32
|
+
@installed
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Reset state (for testing)
|
|
37
|
+
def reset!
|
|
38
|
+
@mutex.synchronize do
|
|
39
|
+
@installed = false
|
|
40
|
+
Otel::ConfigStore.clear!
|
|
41
|
+
NetHttp.reset!
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Get OpenTelemetry instrumentations to register with the SDK
|
|
46
|
+
# @return [Array] Array of instrumentation instances
|
|
47
|
+
def otel_instrumentations
|
|
48
|
+
instrumentations = []
|
|
49
|
+
|
|
50
|
+
# Add Net::HTTP instrumentation
|
|
51
|
+
begin
|
|
52
|
+
require 'opentelemetry/instrumentation/net_http'
|
|
53
|
+
instrumentations << OpenTelemetry::Instrumentation::Net::HTTP::Instrumentation.instance
|
|
54
|
+
rescue LoadError
|
|
55
|
+
# Net::HTTP instrumentation not available
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Add Faraday instrumentation if available
|
|
59
|
+
begin
|
|
60
|
+
require 'opentelemetry/instrumentation/faraday'
|
|
61
|
+
instrumentations << OpenTelemetry::Instrumentation::Faraday::Instrumentation.instance
|
|
62
|
+
rescue LoadError
|
|
63
|
+
# Faraday instrumentation not available
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Add HTTP client instrumentation if available
|
|
67
|
+
begin
|
|
68
|
+
require 'opentelemetry/instrumentation/http_client'
|
|
69
|
+
instrumentations << OpenTelemetry::Instrumentation::HttpClient::Instrumentation.instance
|
|
70
|
+
rescue LoadError
|
|
71
|
+
# HTTP client instrumentation not available
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
instrumentations
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def log_debug(config, message)
|
|
80
|
+
return unless config.debug
|
|
81
|
+
|
|
82
|
+
puts "[Pingops DEBUG] #{message}"
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'net/http'
|
|
4
|
+
|
|
5
|
+
module Pingops
|
|
6
|
+
module Instrumentation
|
|
7
|
+
# Net::HTTP instrumentation for capturing headers and bodies
|
|
8
|
+
# This enhances the standard OpenTelemetry Net::HTTP instrumentation
|
|
9
|
+
module NetHttp
|
|
10
|
+
class << self
|
|
11
|
+
# Install the instrumentation
|
|
12
|
+
def install
|
|
13
|
+
return if @installed
|
|
14
|
+
|
|
15
|
+
# Prepend our module to Net::HTTP
|
|
16
|
+
::Net::HTTP.prepend(RequestPatch)
|
|
17
|
+
@installed = true
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Check if installed
|
|
21
|
+
# @return [Boolean]
|
|
22
|
+
def installed?
|
|
23
|
+
@installed || false
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Uninstall (for testing)
|
|
27
|
+
def reset!
|
|
28
|
+
@installed = false
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Patch for Net::HTTP#request
|
|
33
|
+
module RequestPatch
|
|
34
|
+
def request(req, body = nil, &block)
|
|
35
|
+
# Get current span from context
|
|
36
|
+
span = OpenTelemetry::Trace.current_span
|
|
37
|
+
return super unless span&.recording?
|
|
38
|
+
|
|
39
|
+
# Capture request headers
|
|
40
|
+
capture_request_headers(span, req)
|
|
41
|
+
|
|
42
|
+
# Capture request body if enabled
|
|
43
|
+
capture_request_body(span, body || req.body)
|
|
44
|
+
|
|
45
|
+
# Execute the request
|
|
46
|
+
response = super
|
|
47
|
+
|
|
48
|
+
# Capture response headers
|
|
49
|
+
capture_response_headers(span, response)
|
|
50
|
+
|
|
51
|
+
# Capture response body if enabled
|
|
52
|
+
capture_response_body(span, response)
|
|
53
|
+
|
|
54
|
+
response
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def capture_request_headers(span, request)
|
|
60
|
+
request.each_header do |name, value|
|
|
61
|
+
attr_name = "#{Core::Constants::ATTR_HTTP_REQUEST_HEADER_PREFIX}#{name.downcase}"
|
|
62
|
+
span.set_attribute(attr_name, value)
|
|
63
|
+
end
|
|
64
|
+
rescue StandardError => e
|
|
65
|
+
log_debug("Error capturing request headers: #{e.message}")
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def capture_response_headers(span, response)
|
|
69
|
+
response.each_header do |name, value|
|
|
70
|
+
attr_name = "#{Core::Constants::ATTR_HTTP_RESPONSE_HEADER_PREFIX}#{name.downcase}"
|
|
71
|
+
span.set_attribute(attr_name, value)
|
|
72
|
+
end
|
|
73
|
+
rescue StandardError => e
|
|
74
|
+
log_debug("Error capturing response headers: #{e.message}")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def capture_request_body(span, body)
|
|
78
|
+
return unless should_capture_request_body?
|
|
79
|
+
return if body.nil? || body.empty?
|
|
80
|
+
|
|
81
|
+
max_size = Otel::ConfigStore.max_request_body_size
|
|
82
|
+
truncated = Core::BodyCapture.truncate_body(body.to_s, max_size)
|
|
83
|
+
span.set_attribute(Core::Constants::ATTR_HTTP_REQUEST_BODY, truncated) if truncated
|
|
84
|
+
rescue StandardError => e
|
|
85
|
+
log_debug("Error capturing request body: #{e.message}")
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def capture_response_body(span, response)
|
|
89
|
+
return unless should_capture_response_body?
|
|
90
|
+
|
|
91
|
+
body = read_response_body(response)
|
|
92
|
+
return if body.nil? || body.empty?
|
|
93
|
+
|
|
94
|
+
max_size = Otel::ConfigStore.max_response_body_size
|
|
95
|
+
content_encoding = response['Content-Encoding']
|
|
96
|
+
|
|
97
|
+
if Core::BodyCapture.compressed?(content_encoding)
|
|
98
|
+
encoded = Core::BodyCapture.encode_compressed_body(body, max_size)
|
|
99
|
+
if encoded
|
|
100
|
+
span.set_attribute(Core::Constants::ATTR_HTTP_RESPONSE_BODY, encoded)
|
|
101
|
+
span.set_attribute(Core::Constants::ATTR_HTTP_RESPONSE_CONTENT_ENCODING, content_encoding)
|
|
102
|
+
end
|
|
103
|
+
else
|
|
104
|
+
truncated = Core::BodyCapture.truncate_body(body, max_size)
|
|
105
|
+
span.set_attribute(Core::Constants::ATTR_HTTP_RESPONSE_BODY, truncated) if truncated
|
|
106
|
+
end
|
|
107
|
+
rescue StandardError => e
|
|
108
|
+
log_debug("Error capturing response body: #{e.message}")
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def read_response_body(response)
|
|
112
|
+
# Try to get the body without consuming it
|
|
113
|
+
return unless response.respond_to?(:body)
|
|
114
|
+
|
|
115
|
+
response.body
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def should_capture_request_body?
|
|
119
|
+
# Check context first
|
|
120
|
+
context = OpenTelemetry::Context.current
|
|
121
|
+
context_value = Core::ContextKeys.capture_request_body?(context)
|
|
122
|
+
return context_value unless context_value.nil?
|
|
123
|
+
|
|
124
|
+
# Fall back to global config
|
|
125
|
+
Otel::ConfigStore.capture_request_body?
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def should_capture_response_body?
|
|
129
|
+
# Check context first
|
|
130
|
+
context = OpenTelemetry::Context.current
|
|
131
|
+
context_value = Core::ContextKeys.capture_response_body?(context)
|
|
132
|
+
return context_value unless context_value.nil?
|
|
133
|
+
|
|
134
|
+
# Fall back to global config
|
|
135
|
+
Otel::ConfigStore.capture_response_body?
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def log_debug(message)
|
|
139
|
+
return unless Otel::ConfigStore.debug?
|
|
140
|
+
|
|
141
|
+
puts "[Pingops DEBUG] NetHttp: #{message}"
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pingops
|
|
4
|
+
module Otel
|
|
5
|
+
# Global configuration store for instrumentations
|
|
6
|
+
# Instrumentations read these settings at span creation/body capture time
|
|
7
|
+
module ConfigStore
|
|
8
|
+
@config = nil
|
|
9
|
+
@mutex = Mutex.new
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
# Set the global configuration
|
|
13
|
+
# @param config [Pingops::Core::Configuration] The configuration
|
|
14
|
+
def set(config)
|
|
15
|
+
@mutex.synchronize do
|
|
16
|
+
@config = config
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Get the global configuration
|
|
21
|
+
# @return [Pingops::Core::Configuration, nil]
|
|
22
|
+
def get
|
|
23
|
+
@mutex.synchronize do
|
|
24
|
+
@config
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Check if configuration is set
|
|
29
|
+
# @return [Boolean]
|
|
30
|
+
def set?
|
|
31
|
+
@mutex.synchronize do
|
|
32
|
+
!@config.nil?
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Clear the configuration (for testing/shutdown)
|
|
37
|
+
def clear!
|
|
38
|
+
@mutex.synchronize do
|
|
39
|
+
@config = nil
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Get capture request body setting
|
|
44
|
+
# @return [Boolean]
|
|
45
|
+
def capture_request_body?
|
|
46
|
+
@mutex.synchronize do
|
|
47
|
+
@config&.capture_request_body || false
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Get capture response body setting
|
|
52
|
+
# @return [Boolean]
|
|
53
|
+
def capture_response_body?
|
|
54
|
+
@mutex.synchronize do
|
|
55
|
+
@config&.capture_response_body || false
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Get max request body size
|
|
60
|
+
# @return [Integer]
|
|
61
|
+
def max_request_body_size
|
|
62
|
+
@mutex.synchronize do
|
|
63
|
+
@config&.max_request_body_size || Core::Constants::DEFAULT_MAX_REQUEST_BODY_SIZE
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Get max response body size
|
|
68
|
+
# @return [Integer]
|
|
69
|
+
def max_response_body_size
|
|
70
|
+
@mutex.synchronize do
|
|
71
|
+
@config&.max_response_body_size || Core::Constants::DEFAULT_MAX_RESPONSE_BODY_SIZE
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Get domain allow list
|
|
76
|
+
# @return [Array<Core::DomainRule>, nil]
|
|
77
|
+
def domain_allow_list
|
|
78
|
+
@mutex.synchronize do
|
|
79
|
+
@config&.domain_allow_list
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Get domain deny list
|
|
84
|
+
# @return [Array<Core::DomainRule>, nil]
|
|
85
|
+
def domain_deny_list
|
|
86
|
+
@mutex.synchronize do
|
|
87
|
+
@config&.domain_deny_list
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Check if debug mode is enabled
|
|
92
|
+
# @return [Boolean]
|
|
93
|
+
def debug?
|
|
94
|
+
@mutex.synchronize do
|
|
95
|
+
@config&.debug || false
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|