rack_middleware_metrics 0.0.1 → 0.0.7

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 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