rack-server-timing 0.2.0

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: c161a8abce8a3ecdee2ba07e7eeeecb3663d4d90c4ac0d399755df9b1b4862bb
4
+ data.tar.gz: 13ff6d6d9b44386a64a4dba1bfb953e8b23651deaf2312d6f983ec4b01f1f837
5
+ SHA512:
6
+ metadata.gz: b16d0a90faa4add1a6cca311866e3a3069a92e641ff788db076cc36f46e9be63cad22969b0224361bc7b29d7af361d3fc624a8ea71c94318510b00889e07f27e
7
+ data.tar.gz: ac7e7003ebd3e2da9131ed846a52ece52a08789ff7261eb0aadf5d53cbdb876d6000ec4fbd71e5b479f2ec2910805618f5c0a5102b9c9310bf61fe6362d59102
data/README.md ADDED
@@ -0,0 +1,80 @@
1
+ # Server Timing for Rack
2
+
3
+ Easily record and emit Server-Timing headers in your Rack applications.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem "rack-server-timing"
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install rack-server-timing
20
+
21
+ ## Usage in a Rack application
22
+
23
+ Simply require and add middleware:
24
+
25
+ ```ruby
26
+ # config.ru
27
+ require "rack-server-timing/middleware"
28
+
29
+ use RackServerTiming::Middleware
30
+
31
+ run ->(env) {
32
+ [200, {}, ["It Works!"]]
33
+ }
34
+ ```
35
+
36
+ And then record your metrics:
37
+
38
+ ```ruby
39
+ env["rack.server_timing"].record("DB", 200)
40
+ ```
41
+
42
+ ## Usage in a Roda application
43
+
44
+ Enable the plugin `server_timing` after `render` (if you wish to have the rendering profiled automatically).
45
+
46
+ ```ruby
47
+ class App < Roda
48
+ plugin :render # Optional
49
+ plugin :server_timing
50
+ end
51
+ ```
52
+
53
+ A convenient `server_timing` helper is available to quickly `record` or `benchmark`
54
+ timing values.
55
+
56
+ ## Sequel timing
57
+
58
+ Enable the `server_timing` extension in your database instance. This extension will automatically create a
59
+ null logging instance if there is no logging configured so there _may_ be a small performance hit.
60
+
61
+ ```ruby
62
+ DB = Sequel.connect
63
+
64
+ DB.extension :server_timing
65
+ ```
66
+
67
+ ## Contributing
68
+
69
+ Bug reports and pull requests are welcome on GitHub at https://github.com/adam12/rack-server-timing.
70
+
71
+ I love pull requests! If you fork this project and modify it, please ping me to see
72
+ if your changes can be incorporated back into this project.
73
+
74
+ That said, if your feature idea is nontrivial, you should probably open an issue to
75
+ [discuss it](http://www.igvita.com/2011/12/19/dont-push-your-pull-requests/)
76
+ before attempting a pull request.
77
+
78
+ ## License
79
+
80
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << "test"
6
+ t.test_files = FileList["test/test*.rb"]
7
+ t.verbose = true
8
+ end
9
+
10
+ task default: :test
@@ -0,0 +1,16 @@
1
+ # frozen-string-literal: true
2
+ module RackServerTiming
3
+ module Current
4
+ def self.clear!
5
+ Thread.current[:server_timing] = nil
6
+ end
7
+
8
+ def self.recorder=(recorder)
9
+ Thread.current[:server_timing] = recorder
10
+ end
11
+
12
+ def self.recorder
13
+ Thread.current[:server_timing]
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,26 @@
1
+ # frozen-string-literal: true
2
+ module RackServerTiming
3
+ class Metric
4
+ attr_reader :name
5
+ attr_reader :duration
6
+ attr_reader :description
7
+
8
+ def initialize(name:, duration: nil, description: nil)
9
+ @name = name
10
+ @duration = duration
11
+ @description = description
12
+ end
13
+
14
+ def increment(delta)
15
+ @duration += delta
16
+ self
17
+ end
18
+
19
+ def formatted
20
+ String.new(name).tap do |output|
21
+ output << %Q|;dur=#{duration}| if duration
22
+ output << %Q|;desc="#{description}"| if description
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ # frozen-string-literal: true
2
+ require_relative "recorder"
3
+ require_relative "current"
4
+
5
+ module RackServerTiming
6
+ class Middleware
7
+ def initialize(app)
8
+ @app = app
9
+ end
10
+
11
+ def call(env)
12
+ recorder = Recorder.new
13
+
14
+ env["rack.server_timing"] = Current.recorder = recorder
15
+ status, headers, response = @app.call(env)
16
+
17
+ if (value = recorder.header_value) and not value.empty?
18
+ headers[recorder.header_name] ||= value
19
+ end
20
+
21
+ [status, headers, response]
22
+ ensure
23
+ Current.clear!
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,75 @@
1
+ # frozen-string-literal: true
2
+ require "benchmark"
3
+ require_relative "metric"
4
+
5
+ module RackServerTiming
6
+ class Recorder
7
+ attr_reader :metrics
8
+
9
+ def initialize
10
+ @metrics = {}
11
+ end
12
+
13
+ ##
14
+ # Increment the metric of :name: by :duration:, creating the metric if
15
+ # it did not already exist.
16
+ #
17
+ # Accepts arguments as positional arguments or keyword arguments.
18
+ def increment(name = nil, duration = nil, description = nil, **kwargs)
19
+ name = kwargs[:name] if name.nil?
20
+ duration = kwargs[:duration] if duration.nil?
21
+ description = kwargs[:description] if description.nil?
22
+
23
+ if (metric = metrics[name])
24
+ metric.increment(duration)
25
+ else
26
+ metric = Metric.new(name: name, duration: duration, description: description)
27
+ metrics[name] = metric
28
+ end
29
+ end
30
+
31
+ ##
32
+ # Record the metric of :name: with :duration: and optionally :description:.
33
+ #
34
+ # Accepts arguments as positional arguments or keyword arguments.
35
+ def record(name = nil, duration = nil, description = nil, **kwargs)
36
+ name ||= kwargs[:name]
37
+ duration ||= kwargs[:duration]
38
+ description ||= kwargs[:description]
39
+
40
+ metrics[name] = Metric.new(
41
+ name: name,
42
+ duration: duration,
43
+ description: description
44
+ )
45
+ end
46
+
47
+ ##
48
+ # Record the metric of :name: with an optional :description:, evaluating the
49
+ # provided block for :duration:.
50
+ #
51
+ # Accepts arguments as positional arguments or keyword arguments.
52
+ def benchmark(name = nil, description = nil, **kwargs, &block)
53
+ result = nil
54
+ name ||= kwargs[:name]
55
+ description ||= kwargs[:description]
56
+ duration = Benchmark.realtime { result = block.call }
57
+
58
+ metrics[name] = Metric.new(
59
+ name: name,
60
+ duration: duration,
61
+ description: description,
62
+ )
63
+
64
+ result
65
+ end
66
+
67
+ def header_name
68
+ "Server-Timing"
69
+ end
70
+
71
+ def header_value
72
+ metrics.map {|_name, metric| metric.formatted }.join(", ")
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,47 @@
1
+ # frozen-string-literal: true
2
+
3
+ require "rack-server-timing/middleware"
4
+
5
+ class Roda
6
+ module RodaPlugins
7
+ module ServerTiming
8
+ module InstanceMethods
9
+ # Shorthand to the +RackServerTiming::Recorder+ instance
10
+ def server_timing
11
+ env["rack.server_timing"]
12
+ end
13
+
14
+ # Benchmark the provided block and return it's result
15
+ def benchmark(name:, description: nil)
16
+ result = nil
17
+
18
+ server_timing.benchmark(name: name, description: description) do
19
+ result = yield
20
+ end
21
+
22
+ result
23
+ end
24
+ end
25
+
26
+ module RenderMethods # :nodoc:
27
+ def render(*)
28
+ benchmark(name: "Render") { super }
29
+ end
30
+
31
+ def view(*)
32
+ benchmark(name: "Render") { super }
33
+ end
34
+ end
35
+
36
+ def self.configure(app) # :nodoc:
37
+ app.use RackServerTiming::Middleware
38
+
39
+ if app.opts.has_key?(:render)
40
+ app.send(:include, RenderMethods)
41
+ end
42
+ end
43
+ end
44
+
45
+ register_plugin :server_timing, ServerTiming
46
+ end
47
+ end
@@ -0,0 +1,21 @@
1
+ # frozen-string-literal: true
2
+ require "sequel"
3
+
4
+ module RackServerTiming
5
+ module Sequel
6
+ def log_duration(duration, message)
7
+ RackServerTiming::Current.recorder.increment("DB", duration, "Database")
8
+
9
+ super
10
+ end
11
+
12
+ def self.extended(base)
13
+ return unless base.loggers.empty?
14
+
15
+ require "logger"
16
+ base.loggers = [Logger.new("/dev/null")]
17
+ end
18
+ end
19
+
20
+ ::Sequel::Database.register_extension(:server_timing, RackServerTiming::Sequel)
21
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-server-timing
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Adam Daniels
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: benchmark
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0.1'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0.1'
26
+ - !ruby/object:Gem::Dependency
27
+ name: logger
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '1.2'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '1.2'
40
+ - !ruby/object:Gem::Dependency
41
+ name: cgi
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0.1'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0.1'
54
+ - !ruby/object:Gem::Dependency
55
+ name: rack
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '2.0'
61
+ - - "<"
62
+ - !ruby/object:Gem::Version
63
+ version: '4.0'
64
+ type: :runtime
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '2.0'
71
+ - - "<"
72
+ - !ruby/object:Gem::Version
73
+ version: '4.0'
74
+ executables: []
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - README.md
79
+ - Rakefile
80
+ - lib/rack-server-timing/current.rb
81
+ - lib/rack-server-timing/metric.rb
82
+ - lib/rack-server-timing/middleware.rb
83
+ - lib/rack-server-timing/recorder.rb
84
+ - lib/roda/plugins/server_timing.rb
85
+ - lib/sequel/extensions/server_timing.rb
86
+ homepage: https://github.com/adam12/rack-server-timing
87
+ licenses:
88
+ - MIT
89
+ metadata: {}
90
+ rdoc_options: []
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ requirements: []
104
+ rubygems_version: 4.0.3
105
+ specification_version: 4
106
+ summary: Server-Timing headers for Rack applications
107
+ test_files: []