hoss-agent 1.0.11
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/.github/ISSUE_TEMPLATE/Bug_report.md +40 -0
- data/.github/ISSUE_TEMPLATE/Feature_request.md +17 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +60 -0
- data/.gitignore +27 -0
- data/.rspec +2 -0
- data/Dockerfile +43 -0
- data/Gemfile +105 -0
- data/LICENSE +201 -0
- data/hoss-agent.gemspec +42 -0
- data/lib/hoss-agent.rb +210 -0
- data/lib/hoss.rb +21 -0
- data/lib/hoss/agent.rb +235 -0
- data/lib/hoss/central_config.rb +184 -0
- data/lib/hoss/central_config/cache_control.rb +51 -0
- data/lib/hoss/child_durations.rb +64 -0
- data/lib/hoss/config.rb +315 -0
- data/lib/hoss/config/bytes.rb +42 -0
- data/lib/hoss/config/duration.rb +40 -0
- data/lib/hoss/config/options.rb +154 -0
- data/lib/hoss/config/regexp_list.rb +30 -0
- data/lib/hoss/config/wildcard_pattern_list.rb +54 -0
- data/lib/hoss/context.rb +64 -0
- data/lib/hoss/context/request.rb +28 -0
- data/lib/hoss/context/request/socket.rb +36 -0
- data/lib/hoss/context/request/url.rb +59 -0
- data/lib/hoss/context/response.rb +47 -0
- data/lib/hoss/context/user.rb +59 -0
- data/lib/hoss/context_builder.rb +112 -0
- data/lib/hoss/deprecations.rb +39 -0
- data/lib/hoss/error.rb +49 -0
- data/lib/hoss/error/exception.rb +70 -0
- data/lib/hoss/error/log.rb +41 -0
- data/lib/hoss/error_builder.rb +90 -0
- data/lib/hoss/event.rb +131 -0
- data/lib/hoss/instrumenter.rb +107 -0
- data/lib/hoss/internal_error.rb +23 -0
- data/lib/hoss/logging.rb +70 -0
- data/lib/hoss/metadata.rb +36 -0
- data/lib/hoss/metadata/process_info.rb +35 -0
- data/lib/hoss/metadata/service_info.rb +76 -0
- data/lib/hoss/metadata/system_info.rb +47 -0
- data/lib/hoss/metadata/system_info/container_info.rb +136 -0
- data/lib/hoss/naively_hashable.rb +38 -0
- data/lib/hoss/rails.rb +68 -0
- data/lib/hoss/railtie.rb +42 -0
- data/lib/hoss/report.rb +9 -0
- data/lib/hoss/sinatra.rb +53 -0
- data/lib/hoss/spies.rb +104 -0
- data/lib/hoss/spies/faraday.rb +117 -0
- data/lib/hoss/spies/http.rb +93 -0
- data/lib/hoss/spies/net_http.rb +113 -0
- data/lib/hoss/stacktrace.rb +33 -0
- data/lib/hoss/stacktrace/frame.rb +66 -0
- data/lib/hoss/stacktrace_builder.rb +124 -0
- data/lib/hoss/transport/base.rb +191 -0
- data/lib/hoss/transport/connection.rb +55 -0
- data/lib/hoss/transport/connection/http.rb +139 -0
- data/lib/hoss/transport/connection/proxy_pipe.rb +94 -0
- data/lib/hoss/transport/filters.rb +60 -0
- data/lib/hoss/transport/filters/hash_sanitizer.rb +77 -0
- data/lib/hoss/transport/filters/secrets_filter.rb +48 -0
- data/lib/hoss/transport/headers.rb +74 -0
- data/lib/hoss/transport/serializers.rb +113 -0
- data/lib/hoss/transport/serializers/context_serializer.rb +112 -0
- data/lib/hoss/transport/serializers/error_serializer.rb +92 -0
- data/lib/hoss/transport/serializers/event_serializer.rb +73 -0
- data/lib/hoss/transport/serializers/metadata_serializer.rb +92 -0
- data/lib/hoss/transport/serializers/report_serializer.rb +33 -0
- data/lib/hoss/transport/user_agent.rb +48 -0
- data/lib/hoss/transport/worker.rb +330 -0
- data/lib/hoss/util.rb +54 -0
- data/lib/hoss/util/inflector.rb +110 -0
- data/lib/hoss/util/lru_cache.rb +65 -0
- data/lib/hoss/util/throttle.rb +52 -0
- data/lib/hoss/version.rb +22 -0
- metadata +147 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
# Licensed to Elasticsearch B.V. under one or more contributor
|
2
|
+
# license agreements. See the NOTICE file distributed with
|
3
|
+
# this work for additional information regarding copyright
|
4
|
+
# ownership. Elasticsearch B.V. licenses this file to you under
|
5
|
+
# the Apache License, Version 2.0 (the "License"); you may
|
6
|
+
# not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
12
|
+
# software distributed under the License is distributed on an
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
14
|
+
# KIND, either express or implied. See the License for the
|
15
|
+
# specific language governing permissions and limitations
|
16
|
+
# under the License.
|
17
|
+
|
18
|
+
# frozen_string_literal: true
|
19
|
+
|
20
|
+
module Hoss
|
21
|
+
# @api private
|
22
|
+
class Stacktrace
|
23
|
+
attr_accessor :frames
|
24
|
+
|
25
|
+
def length
|
26
|
+
frames.length
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_a
|
30
|
+
frames.map(&:to_h)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# Licensed to Elasticsearch B.V. under one or more contributor
|
2
|
+
# license agreements. See the NOTICE file distributed with
|
3
|
+
# this work for additional information regarding copyright
|
4
|
+
# ownership. Elasticsearch B.V. licenses this file to you under
|
5
|
+
# the Apache License, Version 2.0 (the "License"); you may
|
6
|
+
# not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
12
|
+
# software distributed under the License is distributed on an
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
14
|
+
# KIND, either express or implied. See the License for the
|
15
|
+
# specific language governing permissions and limitations
|
16
|
+
# under the License.
|
17
|
+
|
18
|
+
# frozen_string_literal: true
|
19
|
+
|
20
|
+
require 'hoss/naively_hashable'
|
21
|
+
|
22
|
+
module Hoss
|
23
|
+
class Stacktrace
|
24
|
+
# @api private
|
25
|
+
class Frame
|
26
|
+
include NaivelyHashable
|
27
|
+
|
28
|
+
attr_accessor(
|
29
|
+
:abs_path,
|
30
|
+
:filename,
|
31
|
+
:function,
|
32
|
+
:vars,
|
33
|
+
:pre_context,
|
34
|
+
:context_line,
|
35
|
+
:post_context,
|
36
|
+
:library_frame,
|
37
|
+
:lineno,
|
38
|
+
:module,
|
39
|
+
:colno
|
40
|
+
)
|
41
|
+
def build_context(context_line_count)
|
42
|
+
return unless abs_path && context_line_count > 0
|
43
|
+
|
44
|
+
padding = (context_line_count - 1) / 2
|
45
|
+
from = lineno - padding - 1
|
46
|
+
from = 0 if from < 0
|
47
|
+
to = lineno + padding - 1
|
48
|
+
file_lines = read_lines(abs_path, from..to)
|
49
|
+
|
50
|
+
return unless file_lines
|
51
|
+
|
52
|
+
self.context_line = file_lines[padding]
|
53
|
+
self.pre_context = file_lines.first(padding)
|
54
|
+
self.post_context = file_lines.last(padding)
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def read_lines(path, range)
|
60
|
+
File.readlines(path)[range]
|
61
|
+
rescue Errno::ENOENT
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# Licensed to Elasticsearch B.V. under one or more contributor
|
2
|
+
# license agreements. See the NOTICE file distributed with
|
3
|
+
# this work for additional information regarding copyright
|
4
|
+
# ownership. Elasticsearch B.V. licenses this file to you under
|
5
|
+
# the Apache License, Version 2.0 (the "License"); you may
|
6
|
+
# not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
12
|
+
# software distributed under the License is distributed on an
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
14
|
+
# KIND, either express or implied. See the License for the
|
15
|
+
# specific language governing permissions and limitations
|
16
|
+
# under the License.
|
17
|
+
|
18
|
+
# frozen_string_literal: true
|
19
|
+
|
20
|
+
require 'hoss/stacktrace/frame'
|
21
|
+
require 'hoss/util/lru_cache'
|
22
|
+
|
23
|
+
module Hoss
|
24
|
+
# @api private
|
25
|
+
class StacktraceBuilder
|
26
|
+
JAVA_FORMAT = /^(.+)\.([^\.]+)\(([^\:]+)\:(\d+)\)$/.freeze
|
27
|
+
RUBY_FORMAT = /^(.+?):(\d+)(?::in `(.+?)')?$/.freeze
|
28
|
+
|
29
|
+
RUBY_VERS_REGEX = %r{ruby(/gems)?[-/](\d+\.)+\d}.freeze
|
30
|
+
JRUBY_ORG_REGEX = %r{org/jruby}.freeze
|
31
|
+
|
32
|
+
GEMS_PATH =
|
33
|
+
if defined?(Bundler) && Bundler.default_bundle_dir
|
34
|
+
Bundler.bundle_path.to_s
|
35
|
+
else
|
36
|
+
Gem.dir
|
37
|
+
end
|
38
|
+
|
39
|
+
def initialize(config)
|
40
|
+
@config = config
|
41
|
+
@cache = Util::LruCache.new(2048, &method(:build_frame))
|
42
|
+
end
|
43
|
+
|
44
|
+
attr_reader :config
|
45
|
+
|
46
|
+
def build(backtrace, type:)
|
47
|
+
Stacktrace.new.tap do |s|
|
48
|
+
s.frames = backtrace[0...config.stack_trace_limit].map do |line|
|
49
|
+
@cache[[line, type]]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def build_frame(cache, keys)
|
57
|
+
line, type = keys
|
58
|
+
abs_path, lineno, function, _module_name = parse_line(line)
|
59
|
+
|
60
|
+
frame = Stacktrace::Frame.new
|
61
|
+
frame.abs_path = abs_path
|
62
|
+
frame.filename = strip_load_path(abs_path)
|
63
|
+
frame.function = function
|
64
|
+
frame.lineno = lineno.to_i
|
65
|
+
frame.library_frame = library_frame?(config, abs_path)
|
66
|
+
|
67
|
+
line_count =
|
68
|
+
context_lines_for(config, type, library_frame: frame.library_frame)
|
69
|
+
frame.build_context line_count
|
70
|
+
|
71
|
+
cache[[line, type]] = frame
|
72
|
+
end
|
73
|
+
|
74
|
+
def parse_line(line)
|
75
|
+
ruby_match = line.match(RUBY_FORMAT)
|
76
|
+
|
77
|
+
if ruby_match
|
78
|
+
_, file, number, method = ruby_match.to_a
|
79
|
+
file.sub!(/\.class$/, '.rb')
|
80
|
+
module_name = nil
|
81
|
+
else
|
82
|
+
java_match = line.match(JAVA_FORMAT)
|
83
|
+
_, module_name, method, file, number = java_match.to_a
|
84
|
+
end
|
85
|
+
|
86
|
+
[file, number, method, module_name]
|
87
|
+
end
|
88
|
+
|
89
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
90
|
+
def library_frame?(config, abs_path)
|
91
|
+
return false unless abs_path
|
92
|
+
|
93
|
+
return true if abs_path.start_with?(GEMS_PATH)
|
94
|
+
|
95
|
+
if abs_path.start_with?(config.__root_path)
|
96
|
+
return true if abs_path.start_with?(config.__root_path + '/vendor')
|
97
|
+
return false
|
98
|
+
end
|
99
|
+
|
100
|
+
return true if abs_path.match(RUBY_VERS_REGEX)
|
101
|
+
return true if abs_path.match(JRUBY_ORG_REGEX)
|
102
|
+
|
103
|
+
false
|
104
|
+
end
|
105
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
106
|
+
|
107
|
+
def strip_load_path(path)
|
108
|
+
return nil if path.nil?
|
109
|
+
|
110
|
+
prefix =
|
111
|
+
$LOAD_PATH
|
112
|
+
.map(&:to_s)
|
113
|
+
.select { |s| path.start_with?(s) }
|
114
|
+
.max_by(&:length)
|
115
|
+
|
116
|
+
prefix ? path[prefix.chomp(File::SEPARATOR).length + 1..-1] : path
|
117
|
+
end
|
118
|
+
|
119
|
+
def context_lines_for(config, type, library_frame:)
|
120
|
+
key = "source_lines_#{type}_#{library_frame ? 'library' : 'app'}_frames"
|
121
|
+
config.send(key.to_sym)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
# Licensed to Elasticsearch B.V. under one or more contributor
|
2
|
+
# license agreements. See the NOTICE file distributed with
|
3
|
+
# this work for additional information regarding copyright
|
4
|
+
# ownership. Elasticsearch B.V. licenses this file to you under
|
5
|
+
# the Apache License, Version 2.0 (the "License"); you may
|
6
|
+
# not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
12
|
+
# software distributed under the License is distributed on an
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
14
|
+
# KIND, either express or implied. See the License for the
|
15
|
+
# specific language governing permissions and limitations
|
16
|
+
# under the License.
|
17
|
+
|
18
|
+
# frozen_string_literal: true
|
19
|
+
|
20
|
+
require 'hoss/metadata'
|
21
|
+
require 'hoss/transport/user_agent'
|
22
|
+
require 'hoss/transport/headers'
|
23
|
+
require 'hoss/transport/connection'
|
24
|
+
require 'hoss/transport/worker'
|
25
|
+
require 'hoss/transport/serializers'
|
26
|
+
require 'hoss/transport/filters'
|
27
|
+
require 'hoss/transport/connection/http'
|
28
|
+
|
29
|
+
require 'hoss/util/throttle'
|
30
|
+
|
31
|
+
module Hoss
|
32
|
+
module Transport
|
33
|
+
# @api private
|
34
|
+
class Base
|
35
|
+
include Logging
|
36
|
+
|
37
|
+
WATCHER_EXECUTION_INTERVAL = 5
|
38
|
+
WATCHER_TIMEOUT_INTERVAL = 4
|
39
|
+
WORKER_JOIN_TIMEOUT = 5
|
40
|
+
|
41
|
+
def initialize(config)
|
42
|
+
@config = config
|
43
|
+
@queue = SizedQueue.new(config.max_queue_size)
|
44
|
+
|
45
|
+
@serializers = Serializers.new(config)
|
46
|
+
@filters = Filters.new(config)
|
47
|
+
|
48
|
+
@stopped = Concurrent::AtomicBoolean.new
|
49
|
+
@workers = Array.new(config.pool_size)
|
50
|
+
|
51
|
+
@worker_mutex = Mutex.new
|
52
|
+
end
|
53
|
+
|
54
|
+
attr_reader :config, :queue, :filters, :workers, :watcher, :stopped
|
55
|
+
|
56
|
+
def start
|
57
|
+
debug '%s: Starting Transport', pid_str
|
58
|
+
# Set @stopped to false first, in case transport is restarted;
|
59
|
+
# ensure_worker_count requires @stopped to be false
|
60
|
+
# ~estolfo
|
61
|
+
@stopped.make_false unless @stopped.false?
|
62
|
+
|
63
|
+
ensure_worker_count
|
64
|
+
create_watcher
|
65
|
+
end
|
66
|
+
|
67
|
+
def stop
|
68
|
+
debug '%s: Stopping Transport', pid_str
|
69
|
+
|
70
|
+
@stopped.make_true
|
71
|
+
|
72
|
+
stop_watcher
|
73
|
+
stop_workers
|
74
|
+
end
|
75
|
+
|
76
|
+
def submit(resource)
|
77
|
+
if @stopped.true?
|
78
|
+
warn '%s: Transport stopping, no new events accepted', pid_str
|
79
|
+
debug 'Dropping: %s', resource.inspect
|
80
|
+
return false
|
81
|
+
end
|
82
|
+
|
83
|
+
queue.push(resource, true)
|
84
|
+
|
85
|
+
true
|
86
|
+
rescue ThreadError
|
87
|
+
throttled_queue_full_warning
|
88
|
+
nil
|
89
|
+
rescue Exception => e
|
90
|
+
error '%s: Failed adding to the transport queue: %p', pid_str, e.inspect
|
91
|
+
nil
|
92
|
+
end
|
93
|
+
|
94
|
+
def add_filter(key, callback)
|
95
|
+
@filters.add(key, callback)
|
96
|
+
end
|
97
|
+
|
98
|
+
def handle_forking!
|
99
|
+
# We can't just stop and start again because the StopMessage
|
100
|
+
# will then be the first message processed when the transport is
|
101
|
+
# restarted.
|
102
|
+
stop_watcher
|
103
|
+
ensure_worker_count
|
104
|
+
create_watcher
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
def pid_str
|
110
|
+
format('[PID:%s]', Process.pid)
|
111
|
+
end
|
112
|
+
|
113
|
+
def create_watcher
|
114
|
+
@watcher = Concurrent::TimerTask.execute(
|
115
|
+
execution_interval: WATCHER_EXECUTION_INTERVAL,
|
116
|
+
timeout_interval: WATCHER_TIMEOUT_INTERVAL
|
117
|
+
) { ensure_worker_count }
|
118
|
+
end
|
119
|
+
|
120
|
+
def ensure_worker_count
|
121
|
+
@worker_mutex.synchronize do
|
122
|
+
return if all_workers_alive?
|
123
|
+
return if stopped.true?
|
124
|
+
|
125
|
+
@workers.map! do |thread|
|
126
|
+
next thread if thread&.alive?
|
127
|
+
|
128
|
+
boot_worker
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def all_workers_alive?
|
134
|
+
!!workers.all? { |t| t&.alive? }
|
135
|
+
end
|
136
|
+
|
137
|
+
def boot_worker
|
138
|
+
debug '%s: Booting worker...', pid_str
|
139
|
+
|
140
|
+
Thread.new do
|
141
|
+
Worker.new(
|
142
|
+
config, queue,
|
143
|
+
serializers: @serializers,
|
144
|
+
filters: @filters
|
145
|
+
).work_forever
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def stop_workers
|
150
|
+
debug '%s: Stopping workers', pid_str
|
151
|
+
|
152
|
+
send_stop_messages
|
153
|
+
|
154
|
+
@worker_mutex.synchronize do
|
155
|
+
workers.each do |thread|
|
156
|
+
next if thread.nil?
|
157
|
+
next if thread.join(WORKER_JOIN_TIMEOUT)
|
158
|
+
|
159
|
+
debug(
|
160
|
+
'%s: Worker did not stop in %ds, killing...',
|
161
|
+
pid_str, WORKER_JOIN_TIMEOUT
|
162
|
+
)
|
163
|
+
thread.kill
|
164
|
+
end
|
165
|
+
|
166
|
+
# Maintain the @worker array size for when transport is restarted
|
167
|
+
@workers.fill(nil)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def send_stop_messages
|
172
|
+
config.pool_size.times { queue.push(Worker::StopMessage.new, true) }
|
173
|
+
rescue ThreadError
|
174
|
+
warn 'Cannot push stop messages to worker queue as it is full'
|
175
|
+
end
|
176
|
+
|
177
|
+
def stop_watcher
|
178
|
+
watcher&.shutdown
|
179
|
+
end
|
180
|
+
|
181
|
+
def throttled_queue_full_warning
|
182
|
+
(@queue_full_log ||= Util::Throttle.new(5) do
|
183
|
+
warn(
|
184
|
+
'%s: Queue is full (%i items), skipping…',
|
185
|
+
pid_str, config.max_queue_size
|
186
|
+
)
|
187
|
+
end).call
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# Licensed to Elasticsearch B.V. under one or more contributor
|
2
|
+
# license agreements. See the NOTICE file distributed with
|
3
|
+
# this work for additional information regarding copyright
|
4
|
+
# ownership. Elasticsearch B.V. licenses this file to you under
|
5
|
+
# the Apache License, Version 2.0 (the "License"); you may
|
6
|
+
# not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
12
|
+
# software distributed under the License is distributed on an
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
14
|
+
# KIND, either express or implied. See the License for the
|
15
|
+
# specific language governing permissions and limitations
|
16
|
+
# under the License.
|
17
|
+
|
18
|
+
# frozen_string_literal: true
|
19
|
+
|
20
|
+
module Hoss
|
21
|
+
module Transport
|
22
|
+
# @api private
|
23
|
+
class Connection
|
24
|
+
include Logging
|
25
|
+
|
26
|
+
def initialize(config)
|
27
|
+
@config = config
|
28
|
+
@metadata = JSON.fast_generate(
|
29
|
+
Serializers::MetadataSerializer.new(config).build(
|
30
|
+
Metadata.new(config)
|
31
|
+
)
|
32
|
+
)
|
33
|
+
@url = @config.ingress_host
|
34
|
+
@mutex = Mutex.new
|
35
|
+
end
|
36
|
+
|
37
|
+
attr_reader :http
|
38
|
+
def write(str)
|
39
|
+
debug "Sending report"
|
40
|
+
uri = URI(@url)
|
41
|
+
req = Net::HTTP::Post.new(
|
42
|
+
uri,
|
43
|
+
'Authorization' => "Bearer #{@config.api_key}", #"Basic " + Base64.encode64("#{@config.api_key}:"),
|
44
|
+
'Content-Type' => 'application/json',
|
45
|
+
'HOSS-SKIP-INSTRUMENTATION' => 'true',
|
46
|
+
)
|
47
|
+
req.body = str
|
48
|
+
res = Net::HTTP.start(uri.hostname, uri.port, :use_ssl => uri.scheme == 'https') do |http|
|
49
|
+
http.request(req)
|
50
|
+
end
|
51
|
+
raise unless res.code == "200"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|