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 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
@@ -0,0 +1,2 @@
1
+ # For available configuration options, see:
2
+ # https://github.com/testdouble/standard
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in metlo.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 12.0"
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
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "standard/rake"
5
+
6
+ task default: :standard
data/lib/libmetlo.so ADDED
Binary file
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metlo
4
+ VERSION = "0.0.4"
5
+ end
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
@@ -0,0 +1,4 @@
1
+ module Metlo
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
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: []