rack_middleware_metrics 0.0.1 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 98990077c946511fb1f4b12324b71802b2b7d0a5541c598e1f4396698f545fa3
4
- data.tar.gz: 9f209425500a239563c966cf4ceaaf29d5d831d6c5df2e6f4dfcc559ef189b56
3
+ metadata.gz: d25a76b74595b845fd92fffd63b0cc1b383f78642ee834ceb85b7add4a4945d9
4
+ data.tar.gz: 9691ccdcb57e694c0702ff9037ea830ecf057060dca2400e7fd76078b55e5be9
5
5
  SHA512:
6
- metadata.gz: e00125579f4516e61a798ff8f69b04446ea0e2803a3953ea5bdf83fb90638c87f6edc7095e416a39e7ccc79272007d2b019422cecef1653865ac2e14b1916632
7
- data.tar.gz: a7c14ed40b52f708e33ea7538c1a01c40e86b096d4c3dbd2dbb1968ce5182fc561c6bfb5cc0295984e254c0f3e1148dfbf5e2fd637ffc973ab8d28dd94289588
6
+ metadata.gz: 072aa309b1d14e2bd6ec960fe04777d4a29be2733536777779d25671ad5d827ccc7dad95f3db115a55f9d4b6f7907f6437c4f957f603125746227998891cf639
7
+ data.tar.gz: 82c42fada205c45dc77a7ce6a525294fc2986c248ec865d32eb7abf00e6e0368c994bfc38f7726c9e3aa48e419d317ca0c2d42223cf1cf5b914dceb98daac4df
data/README.md CHANGED
@@ -1,54 +1,95 @@
1
+ [![Build Status](https://travis-ci.com/reedjosh/RackMiddlewareMetrics.svg?branch=main)](https://travis-ci.com/reedjosh/RackMiddlewareMetrics)
1
2
  # Metrics Reporter
2
3
  A minimalist Application Performance Monitoring (APM) library for Ruby on Rails.
3
4
 
4
- [![Build Status](https://travis-ci.com/reedjosh/RackMiddlewareMetrics.svg?branch=main)](https://travis-ci.com/reedjosh/RackMiddlewareMetrics)
5
-
6
- Reports:
7
- - The time that the request enters the middleware.
8
- - The time that it leaves the middleware.
9
- - Request path
10
- - The request's parameter list.
11
- - MD5 hash value of the rendered output.
12
- - Current thread and process ids.
13
5
 
14
- Appends to a CSV file.
6
+ The middleware generates a CSV file with the followinfg fields (no header):
7
+ - Request Time: the timestamp when the request enters the middleware
8
+ - Response Time: the timestamp when the response leaves the middleware
9
+ - Elapsed Time: the time betwen request and response
10
+ - Path: the URI of the request
11
+ - Params: a semi-colon delimited list of GET parameters
12
+ - Thread ID: the thread ID of the current request
13
+ - Process ID: the process ID of the current request
14
+ - MD5: the hash value of the response body
15
15
 
16
16
  The location and name of the file are configurable by the user.
17
17
 
18
- Adds itself to the hosting Ruby on Rails application using Railties AND the agent
18
+ Optionally: Does MD5 calculation in Rust if native extention is built.
19
19
 
20
- Does MD5 calculation in Rust.
20
+ ## Getting Started
21
21
 
22
- Getting Started
22
+ [Yard Docs](https://rubydoc.info/github/reedjosh/RackMiddlewareMetrics)
23
23
 
24
+ ### Installation
25
+ Install from [ruby gems](https://rubygems.org/gems/rack_middleware_metrics)
24
26
 
25
- ## Etc... Notes
26
- TODO: Add the Gem to an open source Ruby on Rails project (RedMine, Discourse, etc) and generate
27
- a performance metrics CSV file.
27
+ `bundle add rack_middleware_metrics`
28
28
 
29
- TODO: The Rack middleware should be implemented as a Railtie
29
+ ### Non-rails Setup
30
+ See [example](https://github.com/reedjosh/RackMiddlewareMetrics/blob/main/example/thin_rack_app.rb) for setup with rack directly.
31
+ ```ruby
32
+ class RackApp
33
+ def self.call _env
34
+ [200, { some_header: 'a value' }, ['Hi!']]
35
+ end
36
+ end
30
37
 
31
- The middleware should generate a CSV file with the following fields:
32
- - Request Time: the timestamp when the request enters the middleware
33
- - Response Time: the timestamp when the request leaves the middleware
34
- - Elapsed Time: the time betwen request and response
35
- - Path: the URI of the request
36
- - Params: a semi-colon delimited list of GET parameters
37
- - MD5: the hashed value of the response body
38
- - Process ID: the process ID of the current request
39
- - Thread ID: the thread ID of the current request
38
+ app =
39
+ Rack::Builder.app do
40
+ use(RackMiddlewareMetrics::Reporter, logpath: 'some_metrics_filepath.csv')
41
+ run(RackApp)
42
+ end
43
+
44
+ handler = Rack::Handler::Thin
45
+ handler.run(app, Port: 8082)
46
+ ```
47
+
48
+ ### Rails setup.
49
+ The middleware is automatically registered with Rails. But to configure the logpath add:
50
+ ``` ruby
51
+ config.rack_middleware_metrics.logpath = Rails.root / 'some_metrics_filepath.csv'
52
+ ```
53
+ to `config/environments/*.rb`
54
+
55
+ ## Example Output from Redmine Rails Project
56
+
57
+ | Start | End | Duration(S) | URI | Params | Thread | PID | MD5 |
58
+ |-------------------------|-------------------------|-------------|------------------------------|----------------------|--------------|-----|---------------------------------|
59
+ |2020-10-27 13:32:46 -0700|2020-10-27 13:32:47 -0700| 0.698014072 |http://localhost:3000/ | |55890640 |18581|1724d0f493f9ed2e191d9aeb49df0f4c |
60
+ |2020-10-27 13:33:07 -0700|2020-10-27 13:33:07 -0700| 0.02697148 |http://localhost:3000/ | |70368509891620|18581|80eabd1e0373408b0a40b08b0eec6c3f |
61
+ |2020-10-28 09:40:16 -0700|2020-10-28 09:40:16 -0700| 0.717337538 |http://localhost:3000/ | |55777940 |16173|89ab1d3fcc2d9dcdef4a50d79e9dcaff |
62
+ |2020-10-28 09:40:41 -0700|2020-10-28 09:40:41 -0700| 0.103701273 |http://localhost:3000/projects| |55777940 |16173|d2ddd9efc455f83cb24060e6593d6c6c |
63
+ |2020-10-28 09:41:44 -0700|2020-10-28 09:41:44 -0700| 0.051811471 |http://localhost:3000/projects|blah=funk |70368510966560|16173|eb2dacf2043e866a9e8925e53d471e6f |
64
+ |2020-10-28 09:44:02 -0700|2020-10-28 09:44:03 -0700| 0.831327261 |http://localhost:3000/projects|blah=funk;blah2=2funky|55777780 |17355|40895fa7af099cae3f48b27f149beb30 |
65
+
66
+
67
+ ## Development
68
+
69
+ CI/CD is managed via [Travis CI](https://travis-ci.com/github/reedjosh/RackMiddlewareMetrics).
70
+
71
+ ### Build and Test Manually
72
+ Build via:
73
+
74
+ `bundler install --path=vendor`
75
+
76
+ Run spec via:
77
+
78
+ `./bin/rspec`
79
+
80
+ ### Build the Rust Extension
81
+ Install Rust if not present.
82
+
83
+ `curl https://sh.rustup.rs -sSf | sh -s -- -y`
84
+
85
+ `source $HOME/.cargo/env`
40
86
 
41
- Done: The filename and path of the generated CSV file should be configurable by the user of the Gem
42
- Almost Done: generate the MD5 hash in Rust using Helix
43
- Flesh out further: Write unit tests in RSpec.
44
- Done: Add Travis CI.
45
- Partially: Write a README that explains what you built and how to use it.
87
+ Build extension.
46
88
 
47
- Goals:
48
- - Write idiomatic and well-tested ruby code in a gem.
49
- - Make sharable among many Ruby on Rails projects even with MD5 generation in rust.
50
- - Use rvm/Travis to test many ruby versions.
89
+ `./bin/rake build`
51
90
 
52
- ### Build Gem via:
53
- `gem build metrics_reporter.gemspec`
91
+ ### PRs and Testing
92
+ A pull request will trigger builds for each of the Ruby versions supported against Ubuntu Xenial.
54
93
 
94
+ ### Release
95
+ Releases from Travis are triggered whenever a commit to the main branch is tagged. Spec tests must pass for release to occur.
@@ -1,25 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # lib/md5_gen.rb
4
- #
5
- # Provide md5 compute function that either uses native extention or Ruby digest lib.
6
4
  require 'helix_runtime'
7
5
  require('digest/md5')
8
6
 
7
+ begin
8
+ require('md5_ruby_ext/native')
9
+ EXT_LOADED = true
10
+ rescue LoadError
11
+ warn('Unable to load md5_ruby_ext/native. Falling back to Ruby digest/md5. ' \
12
+ 'Please run `rake build` to build native extension.')
13
+ EXT_LOADED = false
14
+ end
15
+
16
+ # Provides md5 compute function that either uses native extention or Ruby digest lib.
9
17
  module MD5Gen
10
- begin
11
- require('md5_ruby_ext/native')
12
- def MD5Gen.compute body
13
- MRubyExt.compute(body)
14
- # Can only work on UTF-8 strings atm. Helix limitation.
15
- rescue TypeError
16
- Digest::MD5.hexdigest(body)
17
- end
18
- rescue LoadError
19
- warn('Unable to load md5_ruby_ext/native. Falling back to Ruby digest/md5. ' \
20
- 'Please run `rake build` to build native extension.')
21
- def MD5Gen.compute body # rubocop:disable Lint/DuplicateMethods
22
- Digest::MD5.hexdigest(body)
18
+ # Define the underlying_compute_method based upon whether the rust extention is loaded.
19
+ class << self
20
+ if EXT_LOADED
21
+ define_method(:underlying_compute_method) do |body|
22
+ MRubyExt.compute(body)
23
+ # Can only work on UTF-8 strings atm. Helix limitation.
24
+ rescue TypeError
25
+ Digest::MD5.hexdigest(body)
26
+ end
27
+ else
28
+ define_method(:underlying_compute_method) do |body|
29
+ Digest::MD5.hexdigest(body)
30
+ end
23
31
  end
24
32
  end
33
+
34
+ # Convert body string to MD5 hash.
35
+ #
36
+ # @param body [String] the body to be hashed.
37
+ # @return [String] the hash of the body.
38
+ def self.compute body
39
+ underlying_compute_method(body)
40
+ end
25
41
  end
@@ -6,6 +6,6 @@ require 'rack_middleware_metrics/reporter'
6
6
 
7
7
  require 'rack_middleware_metrics/railtie' if defined?(Rails)
8
8
 
9
+ # A minimalist Application Performance Monitoring (APM) library for Ruby on Rails.
9
10
  module RackMiddlewareMetrics
10
- class Error < StandardError; end
11
11
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  # lib/rack_middleware_metrics/railtie.rb
4
4
  module RackMiddlewareMetrics
5
- # Rails init...
5
+ # Rails railtie plugin. Adds configuration, startup, and md5 ext build task.
6
6
  class Railtie < Rails::Railtie
7
7
  config.rack_middleware_metrics = ActiveSupport::OrderedOptions.new
8
8
  initializer 'rack_middleware_metrics.configure_rails_initialization' do |app|
@@ -10,5 +10,8 @@ module RackMiddlewareMetrics
10
10
  config.rack_middleware_metrics.fetch(:logpath, Rails.root / 'rack_metrics.csv')
11
11
  app.middleware.use(Reporter, config.rack_middleware_metrics)
12
12
  end
13
+ rake_tasks do
14
+ load(Pathname(__FILE__).parent.parent / 'tasks/helix_runtime.rake')
15
+ end
13
16
  end
14
17
  end
@@ -6,11 +6,19 @@
6
6
  require 'time'
7
7
  require 'rack'
8
8
  require 'md5_gen'
9
+ require 'uri'
10
+ require 'csv'
9
11
 
10
- # The main Gem...
11
12
  module RackMiddlewareMetrics
12
13
  # The metrics reporter middleware.
13
14
  class Reporter
15
+ # Initialize the reporter middleware.
16
+ #
17
+ # @param app [rack::RackApp] the app to add metrics reporting to.
18
+ # @param *args [Array<any>, nil] arguments to pass through to the app.
19
+ # A hash passed as the final argument will be used as config options--of which :logpath
20
+ # can be used to specify the metrics reporter logpath.
21
+ # @returns [Reporter] a new instance of Reporter.
14
22
  def initialize app, *args
15
23
  @app = app
16
24
 
@@ -19,28 +27,59 @@ module RackMiddlewareMetrics
19
27
  @logpath = Pathname.new(options.fetch(:logpath, 'rack_metrics.csv'))
20
28
  end
21
29
 
30
+ # Converts data from rack env object to uri.
31
+ #
32
+ # @param env [rack::ENV] the request env.
33
+ # @return [String] the uri.
22
34
  def uri_from env
23
35
  "#{ env['rack.url_scheme'] }://#{ env['SERVER_NAME'] }"\
24
36
  ":#{ env['SERVER_PORT'] }#{ env['PATH_INFO'] }"
25
37
  end
26
38
 
39
+ # Converts data from rack env object to semicolon delimited parameters.
40
+ # Original semicolons escaped with %3B the ASCII equivalent.
41
+ # See https://www.december.com/html/spec/esccodes.html for more details.
42
+ #
43
+ # @param env [rack::ENV] the request env.
44
+ # @return [String] semicolon delimited parameters..
45
+ def params_from env
46
+ params = Hash[URI.decode_www_form(env['QUERY_STRING'])]
47
+ params = params.map { |key, val| "#{ key }=#{ val }" }
48
+ params.map { |param| param.gsub(';', '%3B') }.join(';')
49
+ end
50
+
51
+ # Run the request--call next step in middleware/server chain with added timing.
52
+ #
53
+ # @param env [rack::ENV] the request env.
54
+ # @return [Array<Array<Objects>>] the response objects: [status, headers, body] plus
55
+ # added timing such that the final form is:
56
+ # [[status, headers, body], [start_time, end_time, duration]]
27
57
  def timed_call env
28
58
  start_time = Time.now
29
59
  response = @app.call(env)
30
60
  end_time = Time.now
31
- duration = start_time - end_time
61
+ duration = end_time - start_time
32
62
  [*response, [start_time, end_time, duration]]
33
63
  end
34
64
 
65
+ # Run the request--call next step in middleware/server chain with added timing.
66
+ #
67
+ # @param data [Array<objects>] metrics data to append to logfile.
68
+ # @return [void]
35
69
  def append_data data
36
- logline = "#{ data.join(',') }\n"
37
- @logpath.open(mode: 'a') { |logfile| logfile.write(logline) }
70
+ CSV.open(@logpath, 'a') do |csv|
71
+ csv << data
72
+ end
38
73
  end
39
74
 
75
+ # Main middleware entry and exit point. Logs metrics as specified.
76
+ #
77
+ # @param env [rack::ENV] the request env.
78
+ # @return [Array<objects>] of the form: [status, headers, body]
40
79
  def call env
41
80
  status, headers, body, timing = timed_call(env)
42
81
 
43
- query_params = env['QUERY_STRING']
82
+ query_params = params_from(env)
44
83
  uri = uri_from(env)
45
84
  thread_id = Thread.current.object_id
46
85
  process_id = Process.pid
@@ -1,3 +1,3 @@
1
1
  module RackMiddlewareMetrics
2
- VERSION ||= "0.0.1"
2
+ VERSION ||= "0.0.7"
3
3
  end
@@ -1,6 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # lib/tasks/helix_runtime.rake
2
4
  require 'helix_runtime/build_task'
3
5
 
4
- HelixRuntime::BuildTask.new()
6
+ Dir.chdir(Pathname(__FILE__).parent) do
7
+ HelixRuntime::BuildTask.new
5
8
 
6
- task default: :build
9
+ task default: :build
10
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack_middleware_metrics
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - reedjosh
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-10-27 00:00:00.000000000 Z
11
+ date: 2020-11-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: helix_runtime
@@ -66,7 +66,6 @@ files:
66
66
  - ".rubocop.yml"
67
67
  - ".solargraph.yml"
68
68
  - ".travis.yml"
69
- - ".~lock.GIST_API.odt#"
70
69
  - Cargo.lock
71
70
  - Cargo.toml
72
71
  - Gemfile
@@ -99,7 +98,6 @@ files:
99
98
  - example/.keep
100
99
  - example/thin_rack_app.rb
101
100
  - lib/md5_gen.rb
102
- - lib/md5_ruby_ext.rb
103
101
  - lib/rack_middleware_metrics.rb
104
102
  - lib/rack_middleware_metrics/railtie.rb
105
103
  - lib/rack_middleware_metrics/reporter.rb
@@ -129,7 +127,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
129
127
  - !ruby/object:Gem::Version
130
128
  version: '0'
131
129
  requirements: []
132
- rubygems_version: 3.0.1
130
+ rubygems_version: 3.0.8
133
131
  signing_key:
134
132
  specification_version: 4
135
133
  summary: A rack based middleware that does fun/silly metrics reporting.
@@ -1 +0,0 @@
1
- ,jreed,jayr.localdomain,14.07.2017 13:50,file:///home/jreed/.config/libreoffice/4;
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # lib/md5_ruby_ext.rb
4
- require 'helix_runtime'
5
-
6
- begin
7
- require('md5_ruby_ext/native')
8
- rescue LoadError
9
- warn('Unable to load md5_ruby_ext/native. Please run `rake build`... Requires Rust/cargo.')
10
- end