sqreen-kit 0.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 +7 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE +3 -0
- data/lib/sqreen-kit.rb +3 -0
- data/lib/sqreen.rb +1 -0
- data/lib/sqreen/kit.rb +76 -0
- data/lib/sqreen/kit/configuration.rb +67 -0
- data/lib/sqreen/kit/http_client.rb +160 -0
- data/lib/sqreen/kit/http_client/authentication_error.rb +13 -0
- data/lib/sqreen/kit/http_client/unexpected_status_error.rb +18 -0
- data/lib/sqreen/kit/loggable.rb +14 -0
- data/lib/sqreen/kit/retry_policy.rb +56 -0
- data/lib/sqreen/kit/signals/actor.rb +26 -0
- data/lib/sqreen/kit/signals/auth_signals_client.rb +45 -0
- data/lib/sqreen/kit/signals/batch_collector.rb +177 -0
- data/lib/sqreen/kit/signals/context/http_context.rb +139 -0
- data/lib/sqreen/kit/signals/dto_helper.rb +147 -0
- data/lib/sqreen/kit/signals/metric.rb +16 -0
- data/lib/sqreen/kit/signals/point.rb +16 -0
- data/lib/sqreen/kit/signals/signal.rb +28 -0
- data/lib/sqreen/kit/signals/signal_attributes.rb +104 -0
- data/lib/sqreen/kit/signals/signals_client.rb +33 -0
- data/lib/sqreen/kit/signals/stack_trace.rb +140 -0
- data/lib/sqreen/kit/signals/trace.rb +34 -0
- data/lib/sqreen/kit/string_sanitizer.rb +46 -0
- metadata +82 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: feacd9986438ad22e08e0e752a59058956dd63ddfe00b90bdfd094e030db4ced
|
4
|
+
data.tar.gz: 318d9d80e07bcc4336740effe1b243e1f279fdbb5bef84be9dab3425884ae7e0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f43f0e22c84f9d15d749ad4ffaf9078ac9b0d1357d24f8f866cd7af5dfaf395489bd5fb913fdaeacc6b74a195491c42292b34b7c075271c3f4e788d95b105e4d
|
7
|
+
data.tar.gz: 91879a34437077558e6efb76196a9dd6b71fbdb0bdcc00c1c5d9e348035d749e8c0fd3b9537e8ec189737c7a0df1c0a5813b4ef0afd20c6fb0858b00aae2620c
|
data/CHANGELOG.md
ADDED
data/LICENSE
ADDED
data/lib/sqreen-kit.rb
ADDED
data/lib/sqreen.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
module Sqreen; end
|
data/lib/sqreen/kit.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'sqreen/kit/configuration'
|
2
|
+
require 'sqreen/kit/retry_policy'
|
3
|
+
require 'sqreen/kit/http_client'
|
4
|
+
require 'sqreen/kit/http_client/authentication_error'
|
5
|
+
require 'sqreen/kit/signals/batch_collector'
|
6
|
+
require 'sqreen/kit/signals/auth_signals_client'
|
7
|
+
require 'sqreen/kit/signals/signals_client'
|
8
|
+
|
9
|
+
module Sqreen; end
|
10
|
+
|
11
|
+
module Sqreen
|
12
|
+
module Kit
|
13
|
+
# Factories using the global configuration
|
14
|
+
class << self
|
15
|
+
def version
|
16
|
+
@version ||= Gem.loaded_specs['sqreen-kit'].version
|
17
|
+
end
|
18
|
+
|
19
|
+
def auth_signals_client
|
20
|
+
@auth_signals_client ||=
|
21
|
+
Signals::AuthSignalsClient.new(
|
22
|
+
signals_client,
|
23
|
+
session_key: Configuration.session_key,
|
24
|
+
api_key: Configuration.api_key,
|
25
|
+
app_name: Configuration.app_name,
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
def signals_client
|
30
|
+
@signals_client ||=
|
31
|
+
Signals::SignalsClient.new(http_client)
|
32
|
+
end
|
33
|
+
|
34
|
+
def batch_collector
|
35
|
+
@batch_collector ||=
|
36
|
+
Signals::BatchCollector.new(
|
37
|
+
auth_signals_client,
|
38
|
+
flush_size: Configuration.batch_flush_size,
|
39
|
+
max_batch_size: Configuration.batch_max_size,
|
40
|
+
max_delay_s: Configuration.batch_max_delay_s,
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
def reset
|
45
|
+
@auth_signals_client = nil
|
46
|
+
@signals_client = nil
|
47
|
+
@batch_collector = nil
|
48
|
+
@http_client = nil
|
49
|
+
@retry_policy = nil
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def http_client
|
55
|
+
@http_client ||=
|
56
|
+
HttpClient.new(Configuration.ingestion_url,
|
57
|
+
retry_policy,
|
58
|
+
proxy_address: Configuration.proxy_address,
|
59
|
+
proxy_port: Configuration.proxy_port,
|
60
|
+
proxy_user: Configuration.proxy_user,
|
61
|
+
proxy_pass: Configuration.proxy_pass,
|
62
|
+
connect_timeout: Configuration.connect_timeout,
|
63
|
+
read_timeout: Configuration.read_timeout)
|
64
|
+
end
|
65
|
+
|
66
|
+
def retry_policy
|
67
|
+
@retry_policy ||=
|
68
|
+
RetryPolicy.new(
|
69
|
+
max_retries: Configuration.retry_max_retries,
|
70
|
+
wait_s: Configuration.retry_max_retries,
|
71
|
+
fatal_exceptions: [Sqreen::Kit::HttpClient::AuthenticationError],
|
72
|
+
)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Sqreen
|
4
|
+
module Kit
|
5
|
+
class Configuration
|
6
|
+
class << self
|
7
|
+
attr_accessor :logger
|
8
|
+
|
9
|
+
# http client related
|
10
|
+
attr_accessor :ingestion_url,
|
11
|
+
:proxy_address,
|
12
|
+
:proxy_port,
|
13
|
+
:proxy_user,
|
14
|
+
:proxy_pass,
|
15
|
+
:connect_timeout,
|
16
|
+
:read_timeout
|
17
|
+
|
18
|
+
# retry policy
|
19
|
+
attr_accessor :retry_max_retries,
|
20
|
+
:retry_wait_s
|
21
|
+
|
22
|
+
# authentication
|
23
|
+
attr_accessor :session_key,
|
24
|
+
:api_key,
|
25
|
+
:app_name
|
26
|
+
|
27
|
+
# batch collector
|
28
|
+
attr_accessor :batch_max_delay_s,
|
29
|
+
:batch_flush_size,
|
30
|
+
:batch_max_size
|
31
|
+
|
32
|
+
# @param [String] url
|
33
|
+
def proxy_url=(url)
|
34
|
+
if url.nil?
|
35
|
+
self.proxy_address =
|
36
|
+
self.proxy_port =
|
37
|
+
self.proxy_user =
|
38
|
+
self.proxy_pass = nil
|
39
|
+
return
|
40
|
+
end
|
41
|
+
|
42
|
+
parsed = URI.parse(url)
|
43
|
+
raise ArgumentError, 'only http proxies are supported' unless parsed.scheme == 'http'
|
44
|
+
|
45
|
+
self.proxy_address = parsed.host
|
46
|
+
self.proxy_port = parsed.port
|
47
|
+
self.proxy_user = parsed.user
|
48
|
+
self.proxy_port = parsed.port
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# default values
|
53
|
+
def self.set_defaults
|
54
|
+
instance_variables.each do |ia|
|
55
|
+
instance_variable_set(ia, nil)
|
56
|
+
end
|
57
|
+
self.logger = ::Logger.new(STDERR)
|
58
|
+
logger.level = ::Logger::WARN
|
59
|
+
logger.progname = 'sqreen-kit'
|
60
|
+
|
61
|
+
self.ingestion_url = 'https://ingestion.sqreen.com/'
|
62
|
+
end
|
63
|
+
|
64
|
+
set_defaults
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
require 'sqreen/kit/loggable'
|
5
|
+
require 'sqreen/kit/retry_policy'
|
6
|
+
require 'sqreen/kit/http_client/authentication_error'
|
7
|
+
require 'sqreen/kit/http_client/unexpected_status_error'
|
8
|
+
|
9
|
+
module Sqreen
|
10
|
+
module Kit
|
11
|
+
# An http client doing JSON requests
|
12
|
+
class HttpClient
|
13
|
+
include Loggable
|
14
|
+
|
15
|
+
DEFAULT_CONNECT_TIMEOUT_S = 5
|
16
|
+
DEFAULT_READ_TIMEOUT_S = 30
|
17
|
+
|
18
|
+
attr_reader :http
|
19
|
+
|
20
|
+
# @return [URI]
|
21
|
+
attr_reader :base_url
|
22
|
+
|
23
|
+
# @param server_url [String]
|
24
|
+
# @param retry_policy [Sqreen::Kit::RetryPolicy]
|
25
|
+
def initialize(server_url, retry_policy, opts = {})
|
26
|
+
@base_url = parse_uri(server_url)
|
27
|
+
|
28
|
+
@retry_policy = retry_policy
|
29
|
+
|
30
|
+
@http = ::Net::HTTP.new(@base_url.host, @base_url.port,
|
31
|
+
opts[:proxy_address] || :ENV,
|
32
|
+
opts[:proxy_port], opts[:proxy_user],
|
33
|
+
opts[:proxy_pass])
|
34
|
+
@http.use_ssl = @base_url.scheme.downcase == 'https'
|
35
|
+
@http.verify_mode = OpenSSL::SSL::VERIFY_NONE if ENV['SQREEN_SSL_NO_VERIFY'] # for testing
|
36
|
+
|
37
|
+
@http.open_timeout = opts[:connect_timeout] || DEFAULT_CONNECT_TIMEOUT_S
|
38
|
+
@http.ssl_timeout = opts[:read_timeout] || DEFAULT_READ_TIMEOUT_S
|
39
|
+
@http.read_timeout = opts[:read_timeout] || DEFAULT_READ_TIMEOUT_S
|
40
|
+
|
41
|
+
@req_nb = 1
|
42
|
+
|
43
|
+
@static_headers = init_static_headers
|
44
|
+
end
|
45
|
+
|
46
|
+
# @param path [String]
|
47
|
+
# @param data [String]
|
48
|
+
# @param headers [Hash{String=>String}]
|
49
|
+
def post(path, data, headers = {})
|
50
|
+
request(:POST, path, data, headers)
|
51
|
+
end
|
52
|
+
|
53
|
+
def get(path, headers = {})
|
54
|
+
request(:GET, path, nil, headers)
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
# @return [URI::HTTP]
|
60
|
+
def parse_uri(uri)
|
61
|
+
# This regexp is the Ruby constant URI::PATTERN::HOSTNAME augmented
|
62
|
+
# with the _ character that is frequent in Docker linked containers.
|
63
|
+
re = '(?:(?:[a-zA-Z\\d](?:[-_a-zA-Z\\d]*[a-zA-Z\\d])?)\\.)*(?:[a-zA-Z](?:[-_a-zA-Z\\d]*[a-zA-Z\\d])?)\\.?'
|
64
|
+
parser = URI::Parser.new HOSTNAME: re
|
65
|
+
uri += '/' unless uri.end_with? '/'
|
66
|
+
parser.parse(uri)
|
67
|
+
end
|
68
|
+
|
69
|
+
def with_retry(&block)
|
70
|
+
@retry_policy.execute(&block)
|
71
|
+
end
|
72
|
+
|
73
|
+
def connect
|
74
|
+
return if connected?
|
75
|
+
logger.warn { "Connecting to #{base_url}..." }
|
76
|
+
|
77
|
+
begin
|
78
|
+
with_retry do
|
79
|
+
http.start
|
80
|
+
end
|
81
|
+
rescue StandardError => e
|
82
|
+
logger.warn "Cannot connect to #{base_url}: #{e.message}"
|
83
|
+
raise
|
84
|
+
else
|
85
|
+
logger.warn 'Connection success'
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def connected?
|
90
|
+
http.started?
|
91
|
+
end
|
92
|
+
|
93
|
+
def disconnect
|
94
|
+
http.finish if connected?
|
95
|
+
end
|
96
|
+
|
97
|
+
def init_static_headers
|
98
|
+
platform = RUBY_PLATFORM
|
99
|
+
platform += " #{ENV_JAVA['java.runtime.version']}" if defined?(ENV_JAVA)
|
100
|
+
ua_string = "sqreen-kit/#{Sqreen::Kit.version} " \
|
101
|
+
"(#{platform}) ruby/#{RUBY_VERSION}"
|
102
|
+
ua_string += " jruby/#{JRUBY_VERSION}" if defined?(JRUBY_VERSION)
|
103
|
+
{ 'User-Agent' => ua_string }
|
104
|
+
end
|
105
|
+
|
106
|
+
def request(method, path, data, headers = {})
|
107
|
+
connect
|
108
|
+
|
109
|
+
now = Time.now
|
110
|
+
|
111
|
+
headers = @static_headers.merge(headers)
|
112
|
+
@req_nb += 1
|
113
|
+
|
114
|
+
path = (base_url + path).path
|
115
|
+
|
116
|
+
with_retry do
|
117
|
+
logger.debug do
|
118
|
+
format('%s %s %s (%s)',
|
119
|
+
method, path, data.inspect, headers.inspect)
|
120
|
+
end
|
121
|
+
|
122
|
+
resp = case method
|
123
|
+
when :GET
|
124
|
+
http.get(path, headers)
|
125
|
+
when :POST
|
126
|
+
http.post(path, data, headers)
|
127
|
+
end
|
128
|
+
|
129
|
+
result = process_response resp
|
130
|
+
|
131
|
+
logger.debug do
|
132
|
+
format('%s %s (DONE: %s in %f ms)',
|
133
|
+
method, path, resp && resp.code, (Time.now - now) * 1000)
|
134
|
+
end
|
135
|
+
|
136
|
+
result
|
137
|
+
end # end with_retry
|
138
|
+
end
|
139
|
+
|
140
|
+
# @param [Net::HTTPResponse] resp
|
141
|
+
def process_response(resp)
|
142
|
+
body = resp.body
|
143
|
+
logger.debug { "Body of response: #{body}" }
|
144
|
+
|
145
|
+
if resp.code == '401' || resp.code == '403'
|
146
|
+
raise AuthenticationError.new(resp.code, body)
|
147
|
+
end
|
148
|
+
|
149
|
+
unless %w[200 201 202].include?(resp.code)
|
150
|
+
raise UnexpectedStatusError.new(resp.code, body)
|
151
|
+
end
|
152
|
+
|
153
|
+
{
|
154
|
+
code: resp.code,
|
155
|
+
body: body,
|
156
|
+
}
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Sqreen
|
2
|
+
module Kit
|
3
|
+
class HttpClient
|
4
|
+
class UnexpectedStatusError < StandardError
|
5
|
+
attr_reader :code, :body
|
6
|
+
|
7
|
+
def initialize(code, body = nil)
|
8
|
+
@code = code
|
9
|
+
@body = body
|
10
|
+
end
|
11
|
+
|
12
|
+
def message
|
13
|
+
"Unexpected status #{code}: #{body}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'sqreen/kit/loggable'
|
2
|
+
|
3
|
+
module Sqreen
|
4
|
+
module Kit
|
5
|
+
class RetryPolicy
|
6
|
+
include Loggable
|
7
|
+
|
8
|
+
DEFAULT_RETRIES = 2
|
9
|
+
DEFAULT_WAITS_S = 3
|
10
|
+
DEFAULT_FATAL_EXCEPTIONS = [].freeze
|
11
|
+
|
12
|
+
attr_reader :max_retries, :wait_s, :fatal_exceptions
|
13
|
+
|
14
|
+
# @param opts the parameters of the retry policy
|
15
|
+
# @option opts [Integer] the maximum number of tries
|
16
|
+
# @option opts [Float] wait_s wait these seconds before a retry
|
17
|
+
# @option opts [Array<Class>] exception classes for which no retry will
|
18
|
+
# be attempted, besides non-StandardError
|
19
|
+
def initialize(opts = {})
|
20
|
+
@max_retries = opts[:max_retries] || DEFAULT_RETRIES
|
21
|
+
@wait_s = opts[:wait_s] || DEFAULT_WAITS_S
|
22
|
+
@fatal_exceptions = opts[:fatal_exceptions] || DEFAULT_FATAL_EXCEPTIONS
|
23
|
+
end
|
24
|
+
|
25
|
+
def execute
|
26
|
+
attempt = 1
|
27
|
+
begin
|
28
|
+
yield
|
29
|
+
rescue ::Exception => e # rubocop:disable Lint/RescueException
|
30
|
+
logger.warn { "Error on attempt ##{attempt}: #{e.message}" }
|
31
|
+
logger.debug { e.backtrace }
|
32
|
+
if fatal?(e)
|
33
|
+
logger.debug { "Not retrying after seeing exception #{e.class}" }
|
34
|
+
raise
|
35
|
+
end
|
36
|
+
if attempt > max_retries
|
37
|
+
logger.debug { "Not retrying anymore after #{attempt} attempts" }
|
38
|
+
raise
|
39
|
+
end
|
40
|
+
|
41
|
+
logger.debug { "Will retry after #{wait_s} seconds" }
|
42
|
+
sleep(wait_s) unless wait_s.zero?
|
43
|
+
attempt += 1
|
44
|
+
retry
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def fatal?(exception)
|
51
|
+
!exception.is_a?(StandardError) ||
|
52
|
+
fatal_exceptions.any? { |ec| exception.is_a?(ec) }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'sqreen/kit/signals/dto_helper'
|
2
|
+
|
3
|
+
module Sqreen
|
4
|
+
module Kit
|
5
|
+
module Signals
|
6
|
+
class Actor
|
7
|
+
include DtoHelper
|
8
|
+
|
9
|
+
add_mandatory_attrs :ip_addresses
|
10
|
+
|
11
|
+
# mandatory
|
12
|
+
# @return [Array<String>]
|
13
|
+
attr_accessor :ip_addresses
|
14
|
+
|
15
|
+
# @return String
|
16
|
+
attr_accessor :user_agent
|
17
|
+
|
18
|
+
# @return [Hash{String=>String}]
|
19
|
+
attr_accessor :identifiers
|
20
|
+
|
21
|
+
# @return [Hash{String=>String}]
|
22
|
+
attr_accessor :traits
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|