roda-enhanced_logger 0.3.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 +7 -0
- data/README.md +86 -0
- data/lib/roda/enhanced_logger/current.rb +58 -0
- data/lib/roda/enhanced_logger/instance.rb +135 -0
- data/lib/roda/plugins/enhanced_logger.rb +85 -0
- data/lib/sequel/extensions/enhanced_logger.rb +29 -0
- metadata +81 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 10033e09d39197a7f03ad6701bdb91cce5ed31888b160a2e46ac5c16917549eb
|
4
|
+
data.tar.gz: dc48be6ce4b9b65eab182baf4faeebc108a25e7037fc25146648b1583a8c9e7c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6e1bd2ea35322e882b794b740393f1377242fe7fd3fe9d2d2aff733ac3f36e910d11930a54639b3f50c73633013b1869c7592a7ca3556b14e88dd7a861b4bdc1
|
7
|
+
data.tar.gz: 2d1cf39e8cf2ce2b7c009edb32ad2f852869585f705045b8fb47c0018d0cbe846c68694e042db0c825e57fb81d15ec534c8881d62384dfc861c68f878f2be8cd
|
data/README.md
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
# Roda Enhanced Logger
|
2
|
+
|
3
|
+
A powerful logger for Roda with a few tricks up it's sleeve.
|
4
|
+
|
5
|
+
- Coloured output per level
|
6
|
+
- Structured output of query params
|
7
|
+
- Tracking of database query time and count
|
8
|
+
- Tracking of blocks which handle path segment
|
9
|
+
- Tracing missed requests
|
10
|
+
- Tracing all requests
|
11
|
+
|
12
|
+

|
13
|
+
|
14
|
+
## Installation
|
15
|
+
|
16
|
+
Add this line to your application's Gemfile:
|
17
|
+
|
18
|
+
gem "roda-enhanced_logger"
|
19
|
+
|
20
|
+
And then execute:
|
21
|
+
|
22
|
+
$ bundle
|
23
|
+
|
24
|
+
Or install it yourself as:
|
25
|
+
|
26
|
+
$ gem install roda-enhanced_logger
|
27
|
+
|
28
|
+
## Usage
|
29
|
+
|
30
|
+
For basic usage, simply enable through the `plugin` mechanism.
|
31
|
+
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
class App < Roda
|
35
|
+
plugin :enhanced_logger
|
36
|
+
end
|
37
|
+
```
|
38
|
+
|
39
|
+
If you serve assets through Roda, your logs might be fairly noisy, so you can
|
40
|
+
filter them.
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
plugin :enhanced_logger, filter: ->(path) { path.start_with?("/assets") }
|
44
|
+
```
|
45
|
+
|
46
|
+
By default, EnhancedLogger will attempt to filter passwords and CSRF tokens,
|
47
|
+
but you can filter other fields too.
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
plugin :enhanced_logger, filtered_params: %w[api_key]
|
51
|
+
```
|
52
|
+
|
53
|
+
If there's a `DB` constant defined for Sequel, EnhancedLogger will automatically
|
54
|
+
use it, but you can pass in a custom value if necessary.
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
plugin :enhanced_logger, db: Container[:db]
|
58
|
+
```
|
59
|
+
|
60
|
+
During development, a 404 might catch you off guard for a path that you feel should
|
61
|
+
exist, so it's handy to trace missed routes to aide in debugging.
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
plugin :enhanced_logger, trace_missed: true
|
65
|
+
```
|
66
|
+
|
67
|
+
Or always trace every request.
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
plugin :enhanced_logger, trace_all: true
|
71
|
+
```
|
72
|
+
|
73
|
+
## Contributing
|
74
|
+
|
75
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/adam12/roda-enhanced_logger.
|
76
|
+
|
77
|
+
I love pull requests! If you fork this project and modify it, please ping me to see
|
78
|
+
if your changes can be incorporated back into this project.
|
79
|
+
|
80
|
+
That said, if your feature idea is nontrivial, you should probably open an issue to
|
81
|
+
[discuss it](http://www.igvita.com/2011/12/19/dont-push-your-pull-requests/)
|
82
|
+
before attempting a pull request.
|
83
|
+
|
84
|
+
## License
|
85
|
+
|
86
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
require "roda"
|
4
|
+
|
5
|
+
class Roda
|
6
|
+
module EnhancedLogger
|
7
|
+
##
|
8
|
+
# Data collection for request in current thread
|
9
|
+
module Current
|
10
|
+
extend self
|
11
|
+
|
12
|
+
# Increment the accrued database time
|
13
|
+
# @param value [Numeric]
|
14
|
+
# the value to increment
|
15
|
+
# @return [Numeric]
|
16
|
+
# the updated value
|
17
|
+
def increment_accrued_database_time(value)
|
18
|
+
Thread.current[:accrued_database_time] ||= 0
|
19
|
+
Thread.current[:accrued_database_time] += value
|
20
|
+
end
|
21
|
+
|
22
|
+
# The accrued database time
|
23
|
+
# @return [Numeric]
|
24
|
+
def accrued_database_time
|
25
|
+
Thread.current[:accrued_database_time]
|
26
|
+
end
|
27
|
+
|
28
|
+
# Set accrued database time
|
29
|
+
# @param value [Numeric]
|
30
|
+
# the value to set
|
31
|
+
# @return [Numeric]
|
32
|
+
# the new value
|
33
|
+
def accrued_database_time=(value)
|
34
|
+
Thread.current[:accrued_database_time] = value
|
35
|
+
end
|
36
|
+
|
37
|
+
def increment_database_query_count(value = 1)
|
38
|
+
Thread.current[:database_query_count] ||= 0
|
39
|
+
Thread.current[:database_query_count] += value
|
40
|
+
end
|
41
|
+
|
42
|
+
def database_query_count
|
43
|
+
Thread.current[:database_query_count]
|
44
|
+
end
|
45
|
+
|
46
|
+
def database_query_count=(value)
|
47
|
+
Thread.current[:database_query_count] = value
|
48
|
+
end
|
49
|
+
|
50
|
+
# Reset the counters
|
51
|
+
def reset
|
52
|
+
self.accrued_database_time = nil
|
53
|
+
self.database_query_count = nil
|
54
|
+
true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
require "roda"
|
4
|
+
|
5
|
+
class Roda
|
6
|
+
module EnhancedLogger
|
7
|
+
##
|
8
|
+
# Logger instance for this application
|
9
|
+
class Instance
|
10
|
+
# Application root
|
11
|
+
attr_reader :root
|
12
|
+
|
13
|
+
# Log entries generated during request
|
14
|
+
attr_reader :log_entries
|
15
|
+
|
16
|
+
# Logger instance
|
17
|
+
attr_reader :logger
|
18
|
+
|
19
|
+
# Route matches during request
|
20
|
+
attr_reader :matches
|
21
|
+
|
22
|
+
attr_reader :timer
|
23
|
+
private :timer
|
24
|
+
|
25
|
+
# Callable object to filter log entries
|
26
|
+
attr_reader :filter
|
27
|
+
|
28
|
+
def initialize(logger, env, instance_id, root, filter)
|
29
|
+
@logger = logger
|
30
|
+
@root = root
|
31
|
+
@log_entries = []
|
32
|
+
@matches = []
|
33
|
+
@timer = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
34
|
+
@filter = filter || proc { false }
|
35
|
+
if env["enhanced_logger_id"].nil?
|
36
|
+
@primary = true
|
37
|
+
env["enhanced_logger_id"] = instance_id
|
38
|
+
else
|
39
|
+
@primary = false
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Add a matched route handler
|
44
|
+
def add_match(caller)
|
45
|
+
@matches << caller
|
46
|
+
end
|
47
|
+
|
48
|
+
# Add log entry for request
|
49
|
+
# @param status [Integer]
|
50
|
+
# status code for the response
|
51
|
+
# @param request [Roda::RodaRequest]
|
52
|
+
# request object
|
53
|
+
# @param trace [Boolean]
|
54
|
+
# tracing was enabled
|
55
|
+
def add(status, request, trace = false)
|
56
|
+
if (last_matched_caller = matches.last)
|
57
|
+
handler = format("%s:%d",
|
58
|
+
Pathname(last_matched_caller.path).relative_path_from(root),
|
59
|
+
last_matched_caller.lineno)
|
60
|
+
end
|
61
|
+
|
62
|
+
meth =
|
63
|
+
case status
|
64
|
+
when 400..499
|
65
|
+
:warn
|
66
|
+
when 500..599
|
67
|
+
:error
|
68
|
+
else
|
69
|
+
:info
|
70
|
+
end
|
71
|
+
|
72
|
+
data = {
|
73
|
+
duration: (Process.clock_gettime(Process::CLOCK_MONOTONIC) - timer).round(4),
|
74
|
+
status: status,
|
75
|
+
verb: request.request_method,
|
76
|
+
path: request.path,
|
77
|
+
remaining_path: request.remaining_path,
|
78
|
+
handler: handler,
|
79
|
+
params: request.params
|
80
|
+
}
|
81
|
+
|
82
|
+
if (db = Roda::EnhancedLogger::Current.accrued_database_time)
|
83
|
+
data[:db] = db.round(6)
|
84
|
+
end
|
85
|
+
|
86
|
+
if (query_count = Roda::EnhancedLogger::Current.database_query_count)
|
87
|
+
data[:db_queries] = query_count
|
88
|
+
end
|
89
|
+
|
90
|
+
if trace
|
91
|
+
matches.each do |match|
|
92
|
+
add_log_entry([meth, format(" %s (%s:%s)",
|
93
|
+
File.readlines(match.path)[match.lineno - 1].strip.sub(" do", ""),
|
94
|
+
Pathname(match.path).relative_path_from(root),
|
95
|
+
match.lineno)])
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
return if filter.call(request.path)
|
100
|
+
|
101
|
+
add_log_entry([meth, "#{request.request_method} #{request.path}", data])
|
102
|
+
end
|
103
|
+
|
104
|
+
# This instance is the primary logger
|
105
|
+
# @return [Boolean]
|
106
|
+
def primary?
|
107
|
+
@primary
|
108
|
+
end
|
109
|
+
|
110
|
+
# Drain the log entry queue, writing each to the logger at their respective level
|
111
|
+
# @return [Boolean]
|
112
|
+
def drain
|
113
|
+
return unless primary?
|
114
|
+
|
115
|
+
log_entries.each do |args|
|
116
|
+
logger.public_send(*args)
|
117
|
+
end
|
118
|
+
|
119
|
+
true
|
120
|
+
end
|
121
|
+
|
122
|
+
# Reset the counters for this thread
|
123
|
+
# @return [Boolean]
|
124
|
+
def reset
|
125
|
+
Roda::EnhancedLogger::Current.reset
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
def add_log_entry(record)
|
131
|
+
@log_entries << record
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
require "tty-logger"
|
4
|
+
require "roda/enhanced_logger/current"
|
5
|
+
require "roda/enhanced_logger/instance"
|
6
|
+
|
7
|
+
class Roda
|
8
|
+
module RodaPlugins
|
9
|
+
# The +enhanced_logger+ plugin provides a coloured, single line log
|
10
|
+
# entry for requests in a Roda application.
|
11
|
+
#
|
12
|
+
# Some interesting pieces of the log entry include which line matched the request,
|
13
|
+
# any time incurred by Sequel DB queries, and the remaining path that might have
|
14
|
+
# not been matched.
|
15
|
+
#
|
16
|
+
# It's mostly suitable in development but would likely be fine in production.
|
17
|
+
#
|
18
|
+
# @example Basic configuration
|
19
|
+
# plugin :enhanced_logger
|
20
|
+
#
|
21
|
+
# @example Filter requests to assets
|
22
|
+
# plugin :enahanced_logger, filter: ->(path) { path.start_with?("/assets") }
|
23
|
+
#
|
24
|
+
# @example Filter parameters
|
25
|
+
# plugin :enhanced_logger, filtered_params: %i[api_key]
|
26
|
+
#
|
27
|
+
module EnhancedLogger
|
28
|
+
DEFAULTS = {
|
29
|
+
db: nil,
|
30
|
+
log_time: false,
|
31
|
+
trace_missed: true,
|
32
|
+
trace_all: false,
|
33
|
+
filtered_params: %w[password password_confirmation _csrf],
|
34
|
+
handlers: [:console]
|
35
|
+
}.freeze
|
36
|
+
|
37
|
+
def self.load_dependencies(app, _opts = {})
|
38
|
+
app.plugin :hooks
|
39
|
+
app.plugin :match_hook
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.configure(app, opts = {})
|
43
|
+
options = DEFAULTS.merge(opts)
|
44
|
+
|
45
|
+
logger = TTY::Logger.new { |config|
|
46
|
+
config.handlers = options[:handlers]
|
47
|
+
config.output = options.fetch(:output) { $stdout }
|
48
|
+
config.metadata = [:data, :time] if options[:log_time]
|
49
|
+
config.filters.data = options[:filtered_params].map(&:to_s)
|
50
|
+
config.filters.mask = "<FILTERED>"
|
51
|
+
}
|
52
|
+
|
53
|
+
root = Pathname(app.opts[:root] || Dir.pwd)
|
54
|
+
|
55
|
+
db = options[:db] || (defined?(DB) && DB)
|
56
|
+
db&.extension :enhanced_logger
|
57
|
+
|
58
|
+
app.match_hook do
|
59
|
+
callee = caller_locations.find { |location|
|
60
|
+
location.path.start_with?(root.to_s)
|
61
|
+
}
|
62
|
+
|
63
|
+
@_enhanced_logger_instance.add_match(callee)
|
64
|
+
end
|
65
|
+
|
66
|
+
app.before do
|
67
|
+
@_enhanced_logger_instance = Roda::EnhancedLogger::Instance.new(logger, env, object_id, root, options[:filter])
|
68
|
+
end
|
69
|
+
|
70
|
+
app.after do |status, _|
|
71
|
+
@_enhanced_logger_instance.add(
|
72
|
+
status,
|
73
|
+
request,
|
74
|
+
(options[:trace_missed] && status == 404) || options[:trace_all]
|
75
|
+
)
|
76
|
+
|
77
|
+
@_enhanced_logger_instance.drain
|
78
|
+
@_enhanced_logger_instance.reset
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
register_plugin :enhanced_logger, EnhancedLogger
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
require "sequel"
|
4
|
+
|
5
|
+
module EnhancedLogger
|
6
|
+
module Sequel
|
7
|
+
if ::Sequel::VERSION_NUMBER >= 50240
|
8
|
+
def skip_logging?
|
9
|
+
false
|
10
|
+
end
|
11
|
+
else
|
12
|
+
def self.extended(base)
|
13
|
+
return if base.loggers.any?
|
14
|
+
|
15
|
+
require "logger"
|
16
|
+
base.loggers = [Logger.new("/dev/null")]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def log_duration(duration, _message)
|
21
|
+
Roda::EnhancedLogger::Current.increment_accrued_database_time(duration)
|
22
|
+
Roda::EnhancedLogger::Current.increment_database_query_count
|
23
|
+
|
24
|
+
super
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
::Sequel::Database.register_extension :enhanced_logger, EnhancedLogger::Sequel
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: roda-enhanced_logger
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Adam Daniels
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-11-30 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: roda
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 3.19.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.19.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: tty-logger
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.3'
|
34
|
+
- - "<"
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '1.0'
|
37
|
+
type: :runtime
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0.3'
|
44
|
+
- - "<"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '1.0'
|
47
|
+
description:
|
48
|
+
email: adam@mediadrive.ca
|
49
|
+
executables: []
|
50
|
+
extensions: []
|
51
|
+
extra_rdoc_files: []
|
52
|
+
files:
|
53
|
+
- README.md
|
54
|
+
- lib/roda/enhanced_logger/current.rb
|
55
|
+
- lib/roda/enhanced_logger/instance.rb
|
56
|
+
- lib/roda/plugins/enhanced_logger.rb
|
57
|
+
- lib/sequel/extensions/enhanced_logger.rb
|
58
|
+
homepage: https://github.com/adam12/roda-enhanced_logger
|
59
|
+
licenses:
|
60
|
+
- MIT
|
61
|
+
metadata: {}
|
62
|
+
post_install_message:
|
63
|
+
rdoc_options: []
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '2.1'
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
requirements: []
|
77
|
+
rubygems_version: 3.1.4
|
78
|
+
signing_key:
|
79
|
+
specification_version: 4
|
80
|
+
summary: An enhanced logger for Roda applications
|
81
|
+
test_files: []
|