sqreen-kit 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|