metlo 0.0.4-aarch64-linux
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/.standard.yml +2 -0
- data/Gemfile +8 -0
- data/README.md +19 -0
- data/Rakefile +6 -0
- data/lib/libmetlo.so +0 -0
- data/lib/metlo/version.rb +5 -0
- data/lib/metlo.rb +287 -0
- data/metlo.gemspec +35 -0
- data/sig/metlo.rbs +4 -0
- metadata +53 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: ad284f75832168a0dbe1732089e03d0e06a6e56cb85083d2a5bf011f163b1561
|
|
4
|
+
data.tar.gz: 936cca1361503ca54d4998220892a4e824ef7c5e4f415002aa56fa4be779b1fa
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: dc6b15b59502114d68fbe3073becd212ea872d9631eb7dcd28a28f47cf83664956dce9108b949512d7920633294f42abbe673770483aeec038d24e9153e1457a
|
|
7
|
+
data.tar.gz: 4561db2f634ae5d42b79108e4d2dc8d8bea05b87a5b2bbca310fc452e3977aa288908e0303ce12d91c245e158f799aefb55acfcfb3536b31b1f82d6f8bb854aa
|
data/.standard.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Metlo
|
|
2
|
+
|
|
3
|
+
The Metlo agent for ruby frameworks able to utilize rack based middleware
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add this line to your application's Gemfile:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
gem 'metlo'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
And then execute:
|
|
14
|
+
|
|
15
|
+
$ bundle install
|
|
16
|
+
|
|
17
|
+
Or install it yourself as:
|
|
18
|
+
|
|
19
|
+
$ gem install metlo
|
data/Rakefile
ADDED
data/lib/libmetlo.so
ADDED
|
Binary file
|
data/lib/metlo.rb
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
require_relative "metlo/version"
|
|
2
|
+
require "json"
|
|
3
|
+
require_relative "./libmetlo"
|
|
4
|
+
|
|
5
|
+
module Metlo
|
|
6
|
+
|
|
7
|
+
class Metlo
|
|
8
|
+
|
|
9
|
+
@@MAX_RES_ITER_COUNT = 10
|
|
10
|
+
@@MAX_BODY_LENGTH = 10 * 1024
|
|
11
|
+
@@LOG_LEVELS = ["trace","debug","info","warn","error"]
|
|
12
|
+
|
|
13
|
+
def initialize(app, params = {})
|
|
14
|
+
@app = app
|
|
15
|
+
@disable = false
|
|
16
|
+
@block_fn = method(:metlo_default_block)
|
|
17
|
+
@get_user = nil
|
|
18
|
+
|
|
19
|
+
metlo_url = params[:metlo_url]
|
|
20
|
+
api_key = params[:api_key]
|
|
21
|
+
backend_port = params[:backend_port]
|
|
22
|
+
collector_port = params[:collector_port]
|
|
23
|
+
encryption_key = params[:encryption_key]
|
|
24
|
+
log_level = params[:log_level]
|
|
25
|
+
block_response = params[:block_response]
|
|
26
|
+
get_user = params[:get_user]
|
|
27
|
+
|
|
28
|
+
if not (log_level.nil?)
|
|
29
|
+
if not (log_level.is_a? String)
|
|
30
|
+
log_level = "info"
|
|
31
|
+
@logger = Logger.new($stdout,convert_log_level(log_level))
|
|
32
|
+
@logger.warn("log_level is not a string. log_level is expected to be one of trace, debug, info, warn, error.\nDefaulting log level to info.")
|
|
33
|
+
elsif not (@@LOG_LEVELS.include? log_level)
|
|
34
|
+
log_level = "info"
|
|
35
|
+
@logger = Logger.new($stdout,convert_log_level(log_level))
|
|
36
|
+
@logger.warn("log_level is not one of trace, debug, info, warn, error.\nDefaulting log level to info")
|
|
37
|
+
end
|
|
38
|
+
else
|
|
39
|
+
log_level = "info"
|
|
40
|
+
@logger = Logger.new($stdout,convert_log_level(log_level))
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
setup_log(@logger)
|
|
44
|
+
|
|
45
|
+
# Metlo URL
|
|
46
|
+
if not (metlo_url.is_a? String)
|
|
47
|
+
@logger.warn("metlo_url is not a string")
|
|
48
|
+
@disable = true
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Metlo API Key
|
|
52
|
+
if not (api_key.is_a? String)
|
|
53
|
+
@logger.warn("api_key is not a string")
|
|
54
|
+
@disable = true
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Backend Port
|
|
58
|
+
if not (backend_port.nil?)
|
|
59
|
+
# Value is present
|
|
60
|
+
if not (backend_port.is_a? Integer)
|
|
61
|
+
@logger.warn("backend_port is not an integer")
|
|
62
|
+
@disable = true
|
|
63
|
+
end
|
|
64
|
+
else
|
|
65
|
+
# Value missing.
|
|
66
|
+
if metlo_url.include? "app.metlo.com"
|
|
67
|
+
backend_port = 443
|
|
68
|
+
else
|
|
69
|
+
backend_port = 8000
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Collector Port
|
|
74
|
+
if not (collector_port.nil?)
|
|
75
|
+
if not (collector_port.is_a? Integer)
|
|
76
|
+
@logger.warn("collector_port is not an integer")
|
|
77
|
+
@disable = true
|
|
78
|
+
end
|
|
79
|
+
else
|
|
80
|
+
collector_port = 8081
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
if not (encryption_key.nil?) and not (encryption_key.is_a? String)
|
|
84
|
+
@logger.warn("Encryption key is expected to be a string.")
|
|
85
|
+
encryption_key = nil
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
if not (block_response.nil?)
|
|
89
|
+
if not defined? block_response == "expression"
|
|
90
|
+
@logger.warn("block_response param is expected to be a symbol to a method/function")
|
|
91
|
+
@block_fn = method(:metlo_default_block)
|
|
92
|
+
else
|
|
93
|
+
if not(((defined? method(block_response)) == "method") and (method(block_response).arity == 1))
|
|
94
|
+
@logger.warn("block_response param is expected to be a method that takes a single param")
|
|
95
|
+
@block_fn = method(:metlo_default_block)
|
|
96
|
+
else
|
|
97
|
+
@block_fn = method(block_response)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
else
|
|
101
|
+
@block_fn = method(:metlo_default_block)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
if not (get_user.nil?)
|
|
106
|
+
if not (defined? get_user == "expression" and defined? method(get_user) == "method" and method(get_user).arity == 1)
|
|
107
|
+
@logger.warn("get_user param is expected to be a method that takes a single param env")
|
|
108
|
+
@get_user = nil
|
|
109
|
+
else
|
|
110
|
+
@get_user = method(get_user)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
if not (@disable)
|
|
115
|
+
startup(
|
|
116
|
+
metlo_url,
|
|
117
|
+
api_key,
|
|
118
|
+
backend_port,
|
|
119
|
+
collector_port,
|
|
120
|
+
log_level,
|
|
121
|
+
encryption_key
|
|
122
|
+
)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def call(env)
|
|
127
|
+
if not (@disable)
|
|
128
|
+
req = nil
|
|
129
|
+
meta = nil
|
|
130
|
+
res = nil
|
|
131
|
+
begin
|
|
132
|
+
req_raw = create_req(env)
|
|
133
|
+
req = JSON.generate(req_raw)
|
|
134
|
+
meta_raw = create_meta(env)
|
|
135
|
+
meta = JSON.generate(meta_raw)
|
|
136
|
+
rescue StandardError => e
|
|
137
|
+
status, headers, response = @app.call(env)
|
|
138
|
+
[status, headers, response]
|
|
139
|
+
else
|
|
140
|
+
block = block_trace(req,meta)
|
|
141
|
+
if block
|
|
142
|
+
status, headers, response = (@block_fn).call env
|
|
143
|
+
else
|
|
144
|
+
status, headers, response = @app.call(env)
|
|
145
|
+
end
|
|
146
|
+
res_raw = create_res(status,headers,response)
|
|
147
|
+
res = JSON.generate(res_raw)
|
|
148
|
+
|
|
149
|
+
ingest_trace(req,res,meta)
|
|
150
|
+
|
|
151
|
+
[status, headers, response]
|
|
152
|
+
end
|
|
153
|
+
else
|
|
154
|
+
@app.call(env)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
private
|
|
160
|
+
|
|
161
|
+
def create_req(env)
|
|
162
|
+
input_stream = env["rack.input"]
|
|
163
|
+
body = input_stream.read(1024*10)
|
|
164
|
+
input_stream.rewind
|
|
165
|
+
if body.nil?
|
|
166
|
+
body = ""
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
user = nil
|
|
170
|
+
|
|
171
|
+
if not (@get_user.nil?)
|
|
172
|
+
user = get_user(env)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
body = body.to_str.force_encoding("UTF-8")
|
|
176
|
+
req = {
|
|
177
|
+
"url"=>{
|
|
178
|
+
"host"=>env["SERVER_NAME"],
|
|
179
|
+
"path"=>env["PATH_INFO"],
|
|
180
|
+
"parameters"=>env["QUERY_STRING"] != nil ? env["QUERY_STRING"].split("&").map{|s|s.split("=")}.map{|a,b| {"name"=>a,"value"=>b}}:[],
|
|
181
|
+
},
|
|
182
|
+
"headers"=>env.select{|k,v| k.start_with? "HTTP_"}.map{|k,v| {"name":k[5..-1].gsub("_","-"),"value":v}},
|
|
183
|
+
"method"=>env["REQUEST_METHOD"],
|
|
184
|
+
"body"=>body.to_str,
|
|
185
|
+
"user"=>user
|
|
186
|
+
}
|
|
187
|
+
return req
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def create_res(status,headers,response)
|
|
191
|
+
body = ""
|
|
192
|
+
current_body_fill = ""
|
|
193
|
+
if defined? response.each
|
|
194
|
+
body_tmp = response.each
|
|
195
|
+
if body_tmp.class == Enumerator
|
|
196
|
+
count = 0
|
|
197
|
+
curr_body_length = 0
|
|
198
|
+
loop do
|
|
199
|
+
segment = body_tmp.next
|
|
200
|
+
|
|
201
|
+
remaining_length = @@MAX_BODY_LENGTH - curr_body_length
|
|
202
|
+
curr_segment = segment[0..remaining_length]
|
|
203
|
+
curr_body_length = curr_body_length + curr_segment.length
|
|
204
|
+
body += curr_segment
|
|
205
|
+
|
|
206
|
+
# Make sure that we don't keep reading from an input
|
|
207
|
+
if count < @@MAX_RES_ITER_COUNT
|
|
208
|
+
count = count+1
|
|
209
|
+
else
|
|
210
|
+
break
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
end
|
|
214
|
+
body_tmp.rewind
|
|
215
|
+
end
|
|
216
|
+
else
|
|
217
|
+
@logger.debug("Metlo doesn't currently handle streaming responses")
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# Check response type
|
|
221
|
+
|
|
222
|
+
# If enumerable
|
|
223
|
+
# Give preference to enumerable over stream
|
|
224
|
+
# Also need to make sure that enumerable is read only once.
|
|
225
|
+
# So instead, pass on a new response object that implements the same methods
|
|
226
|
+
# and when each method is called on that object, intercept that and read it and pass on the information to metlo
|
|
227
|
+
# defined?(response.each)
|
|
228
|
+
|
|
229
|
+
# If streaming
|
|
230
|
+
# defined?(response.call)
|
|
231
|
+
# response.call takes in a single argument for stream.
|
|
232
|
+
# Stream must implement methods: read, write, <<, flush, close, close_read, close_write, closed?
|
|
233
|
+
|
|
234
|
+
# If array
|
|
235
|
+
# defined?(response.to_ary)
|
|
236
|
+
|
|
237
|
+
# If path
|
|
238
|
+
# defined?(response.to_path)
|
|
239
|
+
return {
|
|
240
|
+
"status"=>status,
|
|
241
|
+
"headers"=>headers.map{|k,v| {"name":k,"value":v}},
|
|
242
|
+
"body"=>body,
|
|
243
|
+
}
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def create_meta(env)
|
|
247
|
+
server = env['rack.hijack'].io.addr
|
|
248
|
+
client = env['rack.hijack'].io.peeraddr
|
|
249
|
+
return {
|
|
250
|
+
"environment": "production",
|
|
251
|
+
"incoming": true,
|
|
252
|
+
"source": client[2],
|
|
253
|
+
"sourcePort": client[1],
|
|
254
|
+
"destination": server[2],
|
|
255
|
+
"destinationPort": server[1]
|
|
256
|
+
}
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def metlo_default_block(env)
|
|
260
|
+
return [403,{},["Forbidden"]]
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def setup_log(logger)
|
|
264
|
+
logger.formatter = proc do |severity, datetime, _progname, msg|
|
|
265
|
+
datefmt = datetime.strftime('%Y-%m-%dT%H:%M:%S.%6N')
|
|
266
|
+
"[timestamp=#{datefmt} level=#{severity.ljust(5)}] Metlo: #{msg}\n"
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def convert_log_level(level)
|
|
271
|
+
if level == "trace"
|
|
272
|
+
return Logger::DEBUG
|
|
273
|
+
elsif level == "debug"
|
|
274
|
+
return Logger::DEBUG
|
|
275
|
+
elsif level == "info"
|
|
276
|
+
return Logger::INFO
|
|
277
|
+
elsif level == "warn"
|
|
278
|
+
return Logger::WARN
|
|
279
|
+
elsif level == "error"
|
|
280
|
+
return Logger::ERROR
|
|
281
|
+
else
|
|
282
|
+
return Logger::INFO
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
end
|
|
287
|
+
end
|
data/metlo.gemspec
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/metlo/version"
|
|
4
|
+
|
|
5
|
+
raise "RUBY_TARGET_PLATFORM env var not defined" if ENV["RUBY_TARGET_PLATFORM"].nil?
|
|
6
|
+
|
|
7
|
+
Gem::Specification.new do |spec|
|
|
8
|
+
spec.name = "metlo"
|
|
9
|
+
spec.version = Metlo::VERSION
|
|
10
|
+
spec.authors = ["Metlo"]
|
|
11
|
+
spec.description = "The Ruby Agent for Metlo"
|
|
12
|
+
spec.summary = "The Ruby Agent for Metlo"
|
|
13
|
+
spec.homepage = "https://www.metlo.com"
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
spec.required_ruby_version = ">= 2.6.0"
|
|
16
|
+
|
|
17
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
18
|
+
spec.metadata["source_code_uri"] = "https://www.github.com/metlo-labs/metlo"
|
|
19
|
+
|
|
20
|
+
spec.platform = ENV["RUBY_TARGET_PLATFORM"]
|
|
21
|
+
|
|
22
|
+
spec.files = [
|
|
23
|
+
".standard.yml",
|
|
24
|
+
"Gemfile",
|
|
25
|
+
"README.md",
|
|
26
|
+
"Rakefile",
|
|
27
|
+
"lib/metlo.rb",
|
|
28
|
+
"lib/libmetlo.so",
|
|
29
|
+
"lib/metlo/version.rb",
|
|
30
|
+
"metlo.gemspec",
|
|
31
|
+
"sig/metlo.rbs",
|
|
32
|
+
]
|
|
33
|
+
spec.require_paths = ["lib"]
|
|
34
|
+
|
|
35
|
+
end
|
data/sig/metlo.rbs
ADDED
metadata
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: metlo
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.4
|
|
5
|
+
platform: aarch64-linux
|
|
6
|
+
authors:
|
|
7
|
+
- Metlo
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2023-07-28 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: The Ruby Agent for Metlo
|
|
14
|
+
email:
|
|
15
|
+
executables: []
|
|
16
|
+
extensions: []
|
|
17
|
+
extra_rdoc_files: []
|
|
18
|
+
files:
|
|
19
|
+
- ".standard.yml"
|
|
20
|
+
- Gemfile
|
|
21
|
+
- README.md
|
|
22
|
+
- Rakefile
|
|
23
|
+
- lib/libmetlo.so
|
|
24
|
+
- lib/metlo.rb
|
|
25
|
+
- lib/metlo/version.rb
|
|
26
|
+
- metlo.gemspec
|
|
27
|
+
- sig/metlo.rbs
|
|
28
|
+
homepage: https://www.metlo.com
|
|
29
|
+
licenses:
|
|
30
|
+
- MIT
|
|
31
|
+
metadata:
|
|
32
|
+
homepage_uri: https://www.metlo.com
|
|
33
|
+
source_code_uri: https://www.github.com/metlo-labs/metlo
|
|
34
|
+
post_install_message:
|
|
35
|
+
rdoc_options: []
|
|
36
|
+
require_paths:
|
|
37
|
+
- lib
|
|
38
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
39
|
+
requirements:
|
|
40
|
+
- - ">="
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: 2.6.0
|
|
43
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
requirements: []
|
|
49
|
+
rubygems_version: 3.0.3.1
|
|
50
|
+
signing_key:
|
|
51
|
+
specification_version: 4
|
|
52
|
+
summary: The Ruby Agent for Metlo
|
|
53
|
+
test_files: []
|