dry-monitor 0.0.1 → 0.0.2

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
  SHA1:
3
- metadata.gz: e21a185d78c3e50cd1c99ddf4d211ed3650b298f
4
- data.tar.gz: e103e5a459c69901f96423978e55fdf325f85f54
3
+ metadata.gz: 6454d8e5127c145f85a2f9e35dc21e7a84bd6037
4
+ data.tar.gz: c3f01289d91fa96c7aa3316b866b74fbd2524ccf
5
5
  SHA512:
6
- metadata.gz: 7539abd470e9413b7882ff41f0674f2fc3bfcf860c394e4e53991611316a636f2ba76cea514d2f6178ede7677ae6b782b3844e44f806d95f6719cc906beb38b8
7
- data.tar.gz: 8d97ebaba61a89e895669d1ecfa88266fe596f44c92590bffa68a5acf4dc5dcdc1fc8ff03f24f65d1966d06b38c9cb1ba28b6aa04e5ad58926304b868ca7d565
6
+ metadata.gz: 2ac158bd1e1b591de60c7e8afa817ea79a2ea437e5776cf8290fbdb12d5ac2c1309ee48e72078f579c702b0f288fdcac19de152fac8ccec215f369930381bfff
7
+ data.tar.gz: 4e52db2286bdb3cb5e94b412b767c7ea2d88f0eb9f3d77f9e17fe359e89653f2d917dd9bd1290f591fe0db45accd367dbf8f6d5d8190f7d1fe0964e373c5e896
data/CHANGELOG.md CHANGED
@@ -1,3 +1,16 @@
1
+ # v0.0.2 2017-02-02
2
+
3
+ ### Added
4
+
5
+ * `Dry::Monitor::Rack::Middleware#on` shortcut (solnic)
6
+ * `Dry::Monitor::Rack::Middleware#instrument` shortcut (solnic)
7
+ * `Dry::Monitor::SQL::Logger` can be configured with a custom message template (solnic)
8
+
9
+ ### Changed
10
+
11
+ * `Dry::Monitor::Rack::Logger#{subscribe=>attach}` and now it accepts a rack middleware instance (solnic)
12
+ * `Dry::Monitor::Rack::Logger` appends new lines when logging `:rack.request.stop` events (solnic)
13
+
1
14
  # v0.0.1 2017-02-01
2
15
 
3
16
  First public release
data/lib/dry/monitor.rb CHANGED
@@ -1,85 +1,5 @@
1
- require 'dry-equalizer'
2
-
3
- require 'dry/monitor/rack/middleware'
4
1
  require 'dry/monitor/logger'
2
+ require 'dry/monitor/notifications'
3
+ require 'dry/monitor/rack/middleware'
4
+ require 'dry/monitor/rack/logger'
5
5
  require 'dry/monitor/sql/logger'
6
-
7
- module Dry
8
- module Monitor
9
- class Clock
10
- def measure(&block)
11
- start = current
12
- result = block.()
13
- stop = current
14
- [result, ((stop - start) * 1000).round(2)]
15
- end
16
-
17
- def current
18
- Time.now
19
- end
20
- end
21
-
22
- CLOCK = Clock.new
23
-
24
- class Event
25
- attr_reader :id
26
-
27
- attr_reader :info
28
-
29
- def initialize(id, info = {})
30
- @id = id
31
- @info = info
32
- end
33
- end
34
-
35
- class Notifications
36
- attr_reader :id
37
- attr_reader :events
38
- attr_reader :listeners
39
- attr_reader :clock
40
-
41
- def initialize(id)
42
- @id = id
43
- @listeners = Hash.new { |h, k| h[k] = [] }
44
- @events = {}
45
- @clock = CLOCK
46
- end
47
-
48
- def event(id, info = {})
49
- events[id] = Event.new(id, info)
50
- self
51
- end
52
-
53
- def subscribe(event_id, listener = nil, &block)
54
- listeners[event_id] << (listener || block)
55
- self
56
- end
57
-
58
- def start(event_id, payload)
59
- instrument(event_id, payload)
60
- end
61
-
62
- def stop(event_id, payload)
63
- instrument(event_id, payload)
64
- end
65
-
66
- def instrument(event_id, payload = nil, &block)
67
- event = events[event_id]
68
-
69
- if block
70
- result, time = clock.measure(&block)
71
- end
72
-
73
- listeners[event_id].each do |listener|
74
- if time
75
- listener.(time, event.id, payload.merge(event.info))
76
- else
77
- listener.(event.id, payload.merge(event.info))
78
- end
79
- end
80
-
81
- result
82
- end
83
- end
84
- end
85
- end
@@ -0,0 +1,79 @@
1
+ module Dry
2
+ module Monitor
3
+ class Clock
4
+ def measure(&block)
5
+ start = current
6
+ result = block.()
7
+ stop = current
8
+ [result, ((stop - start) * 1000).round(2)]
9
+ end
10
+
11
+ def current
12
+ Time.now
13
+ end
14
+ end
15
+
16
+ CLOCK = Clock.new
17
+
18
+ class Event
19
+ attr_reader :id
20
+
21
+ attr_reader :info
22
+
23
+ def initialize(id, info = {})
24
+ @id = id
25
+ @info = info
26
+ end
27
+ end
28
+
29
+ class Notifications
30
+ attr_reader :id
31
+ attr_reader :events
32
+ attr_reader :listeners
33
+ attr_reader :clock
34
+
35
+ def initialize(id)
36
+ @id = id
37
+ @listeners = Hash.new { |h, k| h[k] = [] }
38
+ @events = {}
39
+ @clock = CLOCK
40
+ end
41
+
42
+ def event(id, info = {})
43
+ events[id] = Event.new(id, info) unless events.key?(id)
44
+ self
45
+ end
46
+
47
+ def subscribe(event_id, listener = nil, &block)
48
+ listeners[event_id] << (listener || block)
49
+ self
50
+ end
51
+
52
+ def start(event_id, payload)
53
+ instrument(event_id, payload)
54
+ end
55
+
56
+ def stop(event_id, payload)
57
+ instrument(event_id, payload)
58
+ end
59
+
60
+ def instrument(event_id, payload = nil, &block)
61
+ event = events[event_id]
62
+
63
+ if block
64
+ result, time = clock.measure(&block)
65
+ end
66
+
67
+ listeners[event_id].each do |listener|
68
+ if time
69
+ listener.(time, event.id, payload.merge(event.info))
70
+ else
71
+ listener.(event.id, payload.merge(event.info))
72
+ end
73
+ end
74
+
75
+ result
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,109 @@
1
+ require 'dry/configurable'
2
+
3
+ module Dry
4
+ module Monitor
5
+ module Rack
6
+ class Logger
7
+ extend Dry::Configurable
8
+
9
+ setting :filtered_params, %w[_csrf password]
10
+
11
+ REQUEST_METHOD = 'REQUEST_METHOD'.freeze
12
+ PATH_INFO = 'PATH_INFO'.freeze
13
+ REMOTE_ADDR = 'REMOTE_ADDR'.freeze
14
+ RACK_INPUT = 'rack.input'.freeze
15
+ QUERY_PARAMS = 'QUERY_PARAMS'.freeze
16
+
17
+ START_MSG = %(Started %s "%s" for %s at %s).freeze
18
+ STOP_MSG = %(Finished %s "%s" for %s in %sms [Status: %s]\n).freeze
19
+ PARAMS_MSG = %( Parameters %s).freeze
20
+ QUERY_MSG = %( Query parameters %s).freeze
21
+ FILTERED = '[FILTERED]'.freeze
22
+
23
+ attr_reader :logger
24
+
25
+ attr_reader :config
26
+
27
+ def initialize(logger, config = self.class.config)
28
+ @logger = logger
29
+ @config = config
30
+ end
31
+
32
+ def attach(rack_monitor)
33
+ rack_monitor.on(:start) do |id, payload|
34
+ log_start_request(payload[:env])
35
+ end
36
+
37
+ rack_monitor.on(:stop) do |id, payload|
38
+ log_stop_request(payload[:env], payload[:status], payload[:time])
39
+ end
40
+
41
+ rack_monitor.on(:error) do |id, payload|
42
+ log_exception(payload[:exception], payload[:name])
43
+ end
44
+ end
45
+
46
+ def log_exception(e, app_name)
47
+ logger.error e.message
48
+ logger.error filter_backtrace(e.backtrace, app_name).join("\n")
49
+ end
50
+
51
+ def log_start_request(request)
52
+ info START_MSG % [
53
+ request[REQUEST_METHOD],
54
+ request[PATH_INFO],
55
+ request[REMOTE_ADDR],
56
+ Time.now
57
+ ]
58
+ log_request_params(request)
59
+ end
60
+
61
+ def log_stop_request(request, status, time)
62
+ info STOP_MSG % [
63
+ request[REQUEST_METHOD],
64
+ request[PATH_INFO],
65
+ request[REMOTE_ADDR],
66
+ time,
67
+ status
68
+ ]
69
+ end
70
+
71
+ def log_request_params(request)
72
+ with_http_params(request[QUERY_PARAMS]) do |params|
73
+ info QUERY_MSG % [params.inspect]
74
+ end
75
+ end
76
+
77
+ def info(*args)
78
+ logger.info(*args)
79
+ end
80
+
81
+ def with_http_params(params)
82
+ params = ::Rack::Utils.parse_nested_query(params)
83
+ if params.size > 0
84
+ yield(filter_params(params))
85
+ end
86
+ end
87
+
88
+ def filter_backtrace(backtrace, app_name)
89
+ # TODO: what do we want to do with this?
90
+ backtrace.reject { |l| l.include?('gems') }
91
+ end
92
+
93
+ def filter_params(params)
94
+ params.each_with_object({}) do |(k, v), h|
95
+ if v.is_a?(Hash)
96
+ h.update(k => filter_params(v))
97
+ elsif v.is_a?(Array)
98
+ h.update(k => v.map { |m| filter_params(m) })
99
+ elsif config.filtered_params.include?(k)
100
+ h.update(k => FILTERED)
101
+ else
102
+ h[k] = v
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -1,115 +1,12 @@
1
- require 'dry/configurable'
2
1
  require 'rack/utils'
3
2
 
4
3
  module Dry
5
4
  module Monitor
6
5
  module Rack
7
- class Logger
8
- extend Dry::Configurable
9
-
10
- setting :filtered_params, %w[_csrf password]
11
-
12
- REQUEST_METHOD = 'REQUEST_METHOD'.freeze
13
- PATH_INFO = 'PATH_INFO'.freeze
14
- REMOTE_ADDR = 'REMOTE_ADDR'.freeze
15
- RACK_INPUT = 'rack.input'.freeze
16
- QUERY_PARAMS = 'QUERY_PARAMS'.freeze
17
-
18
- START_MSG = %(Started %s "%s" for %s at %s).freeze
19
- STOP_MSG = %(Finished %s "%s" for %s in %sms [Status: %s]).freeze
20
- PARAMS_MSG = %( Parameters %s).freeze
21
- QUERY_MSG = %( Query parameters %s).freeze
22
- FILTERED = '[FILTERED]'.freeze
23
-
24
- attr_reader :logger
25
-
26
- attr_reader :config
27
-
28
- def initialize(logger, config = self.class.config)
29
- @logger = logger
30
- @config = config
31
- end
32
-
33
- def subscribe(notifications)
34
- notifications.subscribe(Middleware::REQUEST_START) do |id, payload|
35
- log_start_request(payload[:env])
36
- end
37
-
38
- notifications.subscribe(Middleware::REQUEST_STOP) do |id, payload|
39
- log_stop_request(payload[:env], payload[:status], payload[:time])
40
- end
41
-
42
- notifications.subscribe(Middleware::APP_ERROR) do |id, payload|
43
- log_exception(payload[:exception], payload[:name])
44
- end
45
- end
46
-
47
- def log_exception(e, app_name)
48
- logger.error e.message
49
- logger.error filter_backtrace(e.backtrace, app_name).join("\n")
50
- end
51
-
52
- def log_start_request(request)
53
- info START_MSG % [
54
- request[REQUEST_METHOD],
55
- request[PATH_INFO],
56
- request[REMOTE_ADDR],
57
- Time.now
58
- ]
59
- log_request_params(request)
60
- end
61
-
62
- def log_stop_request(request, status, time)
63
- info STOP_MSG % [
64
- request[REQUEST_METHOD],
65
- request[PATH_INFO],
66
- request[REMOTE_ADDR],
67
- time,
68
- status
69
- ]
70
- end
71
-
72
- def log_request_params(request)
73
- with_http_params(request[QUERY_PARAMS]) do |params|
74
- info QUERY_MSG % [params.inspect]
75
- end
76
- end
77
-
78
- def info(*args)
79
- logger.info(*args)
80
- end
81
-
82
- def with_http_params(params)
83
- params = ::Rack::Utils.parse_nested_query(params)
84
- if params.size > 0
85
- yield(filter_params(params))
86
- end
87
- end
88
-
89
- def filter_backtrace(backtrace, app_name)
90
- # TODO: what do we want to do with this?
91
- backtrace.reject { |l| l.include?('gems') }
92
- end
93
-
94
- def filter_params(params)
95
- params.each_with_object({}) do |(k, v), h|
96
- if v.is_a?(Hash)
97
- h.update(k => filter_params(v))
98
- elsif v.is_a?(Array)
99
- h.update(k => v.map { |m| filter_params(m) })
100
- elsif config.filtered_params.include?(k)
101
- h.update(k => FILTERED)
102
- else
103
- h[k] = v
104
- end
105
- end
106
- end
107
- end
108
-
109
6
  class Middleware
110
- REQUEST_START = :'request.start'
111
- REQUEST_STOP = :'request.stop'
112
- APP_ERROR = :'app.error'
7
+ REQUEST_START = :'rack.request.start'
8
+ REQUEST_STOP = :'rack.request.stop'
9
+ REQUEST_ERROR = :'rack.request.error'
113
10
 
114
11
  attr_reader :app
115
12
  attr_reader :notifications
@@ -119,13 +16,21 @@ module Dry
119
16
 
120
17
  notifications.event(REQUEST_START)
121
18
  notifications.event(REQUEST_STOP)
122
- notifications.event(APP_ERROR)
19
+ notifications.event(REQUEST_ERROR)
123
20
  end
124
21
 
125
22
  def new(app)
126
23
  self.class.new(notifications, app)
127
24
  end
128
25
 
26
+ def on(event_id, &block)
27
+ notifications.subscribe(:"rack.request.#{event_id}", &block)
28
+ end
29
+
30
+ def instrument(event_id, *args, &block)
31
+ notifications.instrument(:"rack.request.#{event_id}", *args, &block)
32
+ end
33
+
129
34
  def call(env)
130
35
  notifications.start(REQUEST_START, env: env)
131
36
  response, time = CLOCK.measure { app.call(env) }
@@ -9,17 +9,20 @@ module Dry
9
9
 
10
10
  setting :theme, Rouge::Themes::Gruvbox
11
11
  setting :colorize, true
12
+ setting :message_template, %( Loaded %s in %sms %s).freeze
12
13
 
13
14
  attr_reader :config
14
15
  attr_reader :logger
15
16
  attr_reader :formatter
16
17
  attr_reader :lexer
18
+ attr_reader :template
17
19
 
18
20
  def initialize(logger, config = self.class.config)
19
21
  @logger = logger
20
22
  @config = config
21
23
  @formatter = Rouge::Formatters::Terminal256.new(config.theme)
22
24
  @lexer = Rouge::Lexers::SQL.new
25
+ @template = config.message_template
23
26
  end
24
27
 
25
28
  def subscribe(notifications)
@@ -29,7 +32,7 @@ module Dry
29
32
  end
30
33
 
31
34
  def log_query(time, name, query)
32
- logger.info " Loaded #{name.inspect} in #{time}ms #{colorize(query)}"
35
+ logger.info template % [name.inspect, time, colorize(query)]
33
36
  end
34
37
 
35
38
  private
@@ -1,5 +1,5 @@
1
1
  module Dry
2
2
  module Monitor
3
- VERSION = '0.0.1'.freeze
3
+ VERSION = '0.0.2'.freeze
4
4
  end
5
5
  end
@@ -30,7 +30,7 @@ RSpec.describe Dry::Monitor::Rack::Middleware do
30
30
 
31
31
  before do
32
32
  File.open(log_file_path, 'w').close
33
- rack_logger.subscribe(notifications)
33
+ rack_logger.attach(middleware)
34
34
  end
35
35
 
36
36
  it 'triggers start/stop events for with a rack request' do
@@ -48,4 +48,18 @@ RSpec.describe Dry::Monitor::Rack::Middleware do
48
48
  expect(log_file_content).to include('Query parameters {"_csrf"=>"[FILTERED]", "password"=>"[FILTERED]", "user"=>{"password"=>"[FILTERED]"}, "other"=>[{"password"=>"[FILTERED]"}, {"password"=>"[FILTERED]"}], "foo"=>"bar", "one"=>"1"}')
49
49
  end
50
50
  end
51
+
52
+ describe '#on' do
53
+ it 'subscribe a listener to a specific request event' do
54
+ captured = []
55
+
56
+ middleware.on(:error) do |id, payload|
57
+ captured << payload
58
+ end
59
+
60
+ middleware.instrument(:error, exception: 'oops')
61
+
62
+ expect(captured).to eql([exception: 'oops'])
63
+ end
64
+ end
51
65
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dry-monitor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Solnica
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-02-01 00:00:00.000000000 Z
11
+ date: 2017-02-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rouge
@@ -115,6 +115,8 @@ files:
115
115
  - lib/dry-monitor.rb
116
116
  - lib/dry/monitor.rb
117
117
  - lib/dry/monitor/logger.rb
118
+ - lib/dry/monitor/notifications.rb
119
+ - lib/dry/monitor/rack/logger.rb
118
120
  - lib/dry/monitor/rack/middleware.rb
119
121
  - lib/dry/monitor/sql/logger.rb
120
122
  - lib/dry/monitor/version.rb