grape_logging 1.1.2 → 1.2.1
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 +4 -4
- data/README.md +50 -4
- data/lib/grape_logging.rb +8 -1
- data/lib/grape_logging/formatters/default.rb +2 -2
- data/lib/grape_logging/formatters/json.rb +33 -0
- data/lib/grape_logging/loggers/base.rb +9 -0
- data/lib/grape_logging/loggers/filter_parameters.rb +24 -0
- data/lib/grape_logging/loggers/response.rb +21 -0
- data/lib/grape_logging/middleware/request_logger.rb +67 -18
- data/lib/grape_logging/reporters/active_support_reporter.rb +11 -0
- data/lib/grape_logging/reporters/logger_reporter.rb +12 -0
- data/lib/grape_logging/timings.rb +21 -0
- data/lib/grape_logging/version.rb +1 -1
- metadata +9 -3
- data/grape_logging-1.1.1.gem +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 59face7f1f95aa107acdf703dbc1f3b7cd7a7089
|
4
|
+
data.tar.gz: ddbc3faa4075116e49201ed0e42a8a484fe6f0bb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 80f4eea80aa5b7cdee7e6e2f45ec1a52c35c41839f67988fa89a70290b6706547cc4a664cce4a0dea136fd5d56b6bc266e8d592176b732e94527c8731c038219
|
7
|
+
data.tar.gz: 6effcdf54c98000c0b8417ea2f02c6a707054ade7e66c035cc55e68ee721d5d25cf9d81c34683545f1a31c88d8bc6a433219600164369078294c3b48fc92b8ed
|
data/README.md
CHANGED
@@ -10,7 +10,7 @@ Add this line to your application's Gemfile:
|
|
10
10
|
|
11
11
|
And then execute:
|
12
12
|
|
13
|
-
$ bundle
|
13
|
+
$ bundle install
|
14
14
|
|
15
15
|
Or install it yourself as:
|
16
16
|
|
@@ -19,10 +19,12 @@ Or install it yourself as:
|
|
19
19
|
## Basic Usage
|
20
20
|
|
21
21
|
In your api file (somewhere on the top)
|
22
|
-
|
22
|
+
|
23
|
+
require 'grape_logging'
|
23
24
|
logger.formatter = GrapeLogging::Formatters::Default.new
|
24
25
|
use GrapeLogging::Middleware::RequestLogger, { logger: logger }
|
25
26
|
|
27
|
+
**ProTip:** If your logger doesn't support setting formatter you can remove this line - it's optional
|
26
28
|
|
27
29
|
## Features
|
28
30
|
|
@@ -34,11 +36,55 @@ With the default configuration you will get nice log message
|
|
34
36
|
|
35
37
|
If you prefer some other format I strongly encourage you to do pull request with new formatter class ;)
|
36
38
|
|
39
|
+
You can change the formatter like so
|
40
|
+
|
41
|
+
class MyAPI < Grape::API
|
42
|
+
use GrapeLogging::Middleware::RequestLogger, logger: logger, format: MyFormatter.new
|
43
|
+
end
|
44
|
+
|
45
|
+
### Customising What Is Logged
|
46
|
+
|
47
|
+
You can include logging of other parts of the request / response cycle by including subclasses of `GrapeLogging::Loggers::Base`
|
48
|
+
|
49
|
+
class MyAPI < Grape::API
|
50
|
+
use GrapeLogging::Middleware::RequestLogger,
|
51
|
+
logger: logger,
|
52
|
+
include: [ GrapeLogging::Loggers::Response.new,
|
53
|
+
GrapeLogging::Loggers::FilterParameters.new ]
|
54
|
+
end
|
55
|
+
|
56
|
+
The `FilterParameters` logger will filter out sensitive parameters from your logs. If mounted inside rails, will use the `Rails.application.config.filter_parameters` by default. Otherwise, you must specify a list of keys to filter out.
|
57
|
+
|
37
58
|
### Logging to file and STDOUT
|
38
59
|
|
39
|
-
You can to file and STDOUT at the same time, you just need to assign new logger
|
60
|
+
You can log to file and STDOUT at the same time, you just need to assign new logger
|
61
|
+
|
62
|
+
log_file = File.open('path/to/your/logfile.log', 'a')
|
63
|
+
log_file.sync = true
|
64
|
+
logger Logger.new GrapeLogging::MultiIO.new(STDOUT, log_file)
|
65
|
+
|
66
|
+
### Logging via Rails instrumentation
|
67
|
+
|
68
|
+
You can choose to not pass the logger to ```grape_logging``` but instead send logs to Rails instrumentation in order to let Rails and its configured Logger do the log job, for example.
|
69
|
+
First, config ```grape_logging```, like that:
|
40
70
|
|
41
|
-
|
71
|
+
class MyAPI < Grape::API
|
72
|
+
use GrapeLogging::Middleware::RequestLogger,
|
73
|
+
instrumentation_key: 'grape_key',
|
74
|
+
include: [ GrapeLogging::Loggers::Response.new,
|
75
|
+
GrapeLogging::Loggers::FilterParameters.new ]
|
76
|
+
end
|
77
|
+
|
78
|
+
and then add an initializer in your Rails project:
|
79
|
+
|
80
|
+
# config/initializers/instrumentation.rb
|
81
|
+
|
82
|
+
# Subscribe to grape request and log with Rails.logger
|
83
|
+
ActiveSupport::Notifications.subscribe('grape_key') do |name, starts, ends, notification_id, payload|
|
84
|
+
Rails.logger.info payload
|
85
|
+
end
|
86
|
+
|
87
|
+
The idea come from here: https://gist.github.com/teamon/e8ae16ffb0cb447e5b49
|
42
88
|
|
43
89
|
### Logging exceptions
|
44
90
|
|
data/lib/grape_logging.rb
CHANGED
@@ -1,4 +1,11 @@
|
|
1
1
|
require 'grape_logging/multi_io'
|
2
2
|
require 'grape_logging/version'
|
3
3
|
require 'grape_logging/formatters/default'
|
4
|
-
require 'grape_logging/
|
4
|
+
require 'grape_logging/formatters/json'
|
5
|
+
require 'grape_logging/loggers/base'
|
6
|
+
require 'grape_logging/loggers/response'
|
7
|
+
require 'grape_logging/loggers/filter_parameters'
|
8
|
+
require 'grape_logging/reporters/active_support_reporter'
|
9
|
+
require 'grape_logging/reporters/logger_reporter'
|
10
|
+
require 'grape_logging/timings'
|
11
|
+
require 'grape_logging/middleware/request_logger'
|
@@ -11,7 +11,7 @@ module GrapeLogging
|
|
11
11
|
elsif data.is_a?(Exception)
|
12
12
|
format_exception(data)
|
13
13
|
elsif data.is_a?(Hash)
|
14
|
-
"#{data.delete(:status)} --
|
14
|
+
"#{data.delete(:status)} -- #{format_hash(data.delete(:time))} -- #{data.delete(:method)} #{data.delete(:path)} #{format_hash(data)}"
|
15
15
|
else
|
16
16
|
data.inspect
|
17
17
|
end
|
@@ -28,4 +28,4 @@ module GrapeLogging
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
31
|
-
end
|
31
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module GrapeLogging
|
2
|
+
module Formatters
|
3
|
+
class Json
|
4
|
+
def call(severity, datetime, _, data)
|
5
|
+
{
|
6
|
+
date: datetime,
|
7
|
+
severity: severity,
|
8
|
+
data: format(data)
|
9
|
+
}.to_json
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def format(data)
|
15
|
+
if data.is_a?(String) || data.is_a?(Hash)
|
16
|
+
data
|
17
|
+
elsif data.is_a?(Exception)
|
18
|
+
format_exception(data)
|
19
|
+
else
|
20
|
+
data.inspect
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def format_exception(exception)
|
25
|
+
{
|
26
|
+
exception: {
|
27
|
+
message: exception.message
|
28
|
+
}
|
29
|
+
}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module GrapeLogging
|
2
|
+
module Loggers
|
3
|
+
class FilterParameters < GrapeLogging::Loggers::Base
|
4
|
+
def initialize(filter_parameters = nil, replacement = '[FILTERED]')
|
5
|
+
@filter_parameters = filter_parameters || (defined?(Rails.application) ? Rails.application.config.filter_parameters : [])
|
6
|
+
@replacement = replacement
|
7
|
+
end
|
8
|
+
|
9
|
+
def parameters(request, _)
|
10
|
+
{ params: replace_parameters(request.params.clone) }
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
def replace_parameters(parameters)
|
15
|
+
@filter_parameters.each do |parameter_name|
|
16
|
+
if parameters.key?(parameter_name.to_s)
|
17
|
+
parameters[parameter_name.to_s] = @replacement
|
18
|
+
end
|
19
|
+
end
|
20
|
+
parameters
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module GrapeLogging
|
2
|
+
module Loggers
|
3
|
+
class Response < GrapeLogging::Loggers::Base
|
4
|
+
def parameters(_, response)
|
5
|
+
response ? { response: serialized_response_body(response) } : {}
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
# In some cases, response.body is not parseable by JSON.
|
10
|
+
# For example, if you POST on a PUT endpoint, response.body is egal to """".
|
11
|
+
# It's strange but it's the Grape behavior...
|
12
|
+
def serialized_response_body(response)
|
13
|
+
begin
|
14
|
+
response.body.map{ |body| JSON.parse(body.to_s) }
|
15
|
+
rescue => e
|
16
|
+
response.body
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -3,45 +3,66 @@ require 'grape/middleware/base'
|
|
3
3
|
module GrapeLogging
|
4
4
|
module Middleware
|
5
5
|
class RequestLogger < Grape::Middleware::Base
|
6
|
+
|
7
|
+
ActiveSupport::Notifications.subscribe('sql.active_record') do |*args|
|
8
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
9
|
+
GrapeLogging::Timings.append_db_runtime(event)
|
10
|
+
end if defined?(ActiveRecord)
|
11
|
+
|
12
|
+
def initialize(app, options = {})
|
13
|
+
super
|
14
|
+
|
15
|
+
@included_loggers = @options[:include] || []
|
16
|
+
@reporter = if options[:instrumentation_key]
|
17
|
+
Reporters::ActiveSupportReporter.new(@options[:instrumentation_key])
|
18
|
+
else
|
19
|
+
Reporters::LoggerReporter.new(@options[:logger], @options[:formatter])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
6
23
|
def before
|
24
|
+
reset_db_runtime
|
7
25
|
start_time
|
8
26
|
|
9
|
-
|
10
|
-
@subscription = ActiveSupport::Notifications.subscribe('sql.active_record') do |*args|
|
11
|
-
event = ActiveSupport::Notifications::Event.new(*args)
|
12
|
-
@db_duration += event.duration
|
13
|
-
end if defined?(ActiveRecord)
|
27
|
+
invoke_included_loggers(:before)
|
14
28
|
end
|
15
29
|
|
16
30
|
def after
|
17
31
|
stop_time
|
18
|
-
|
32
|
+
@reporter.perform(collect_parameters)
|
33
|
+
invoke_included_loggers(:after)
|
19
34
|
nil
|
20
35
|
end
|
21
36
|
|
22
37
|
def call!(env)
|
23
38
|
super
|
24
|
-
ensure
|
25
|
-
ActiveSupport::Notifications.unsubscribe(@subscription) if @subscription
|
26
39
|
end
|
27
40
|
|
28
41
|
protected
|
42
|
+
|
43
|
+
def response
|
44
|
+
begin
|
45
|
+
super
|
46
|
+
rescue
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
29
51
|
def parameters
|
30
52
|
{
|
31
|
-
|
32
|
-
|
33
|
-
method: request.request_method,
|
53
|
+
status: response.nil? ? 'fail' : response.status,
|
54
|
+
time: {
|
34
55
|
total: total_runtime,
|
35
|
-
db:
|
36
|
-
|
56
|
+
db: db_runtime,
|
57
|
+
view: view_runtime
|
58
|
+
},
|
59
|
+
method: request.request_method,
|
60
|
+
path: request.path,
|
61
|
+
params: request.params
|
37
62
|
}
|
38
63
|
end
|
39
64
|
|
40
65
|
private
|
41
|
-
def logger
|
42
|
-
@logger ||= @options[:logger] || Logger.new(STDOUT)
|
43
|
-
end
|
44
|
-
|
45
66
|
def request
|
46
67
|
@request ||= ::Rack::Request.new(env)
|
47
68
|
end
|
@@ -50,6 +71,18 @@ module GrapeLogging
|
|
50
71
|
((stop_time - start_time) * 1000).round(2)
|
51
72
|
end
|
52
73
|
|
74
|
+
def view_runtime
|
75
|
+
total_runtime - db_runtime
|
76
|
+
end
|
77
|
+
|
78
|
+
def db_runtime
|
79
|
+
GrapeLogging::Timings.db_runtime.round(2)
|
80
|
+
end
|
81
|
+
|
82
|
+
def reset_db_runtime
|
83
|
+
GrapeLogging::Timings.reset_db_runtime
|
84
|
+
end
|
85
|
+
|
53
86
|
def start_time
|
54
87
|
@start_time ||= Time.now
|
55
88
|
end
|
@@ -57,6 +90,22 @@ module GrapeLogging
|
|
57
90
|
def stop_time
|
58
91
|
@stop_time ||= Time.now
|
59
92
|
end
|
93
|
+
|
94
|
+
def collect_parameters
|
95
|
+
parameters.tap do |params|
|
96
|
+
@included_loggers.each do |logger|
|
97
|
+
params.merge! logger.parameters(request, response) do |_, oldval, newval|
|
98
|
+
oldval.respond_to?(:merge) ? oldval.merge(newval) : newval
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def invoke_included_loggers(method_name)
|
105
|
+
@included_loggers.each do |logger|
|
106
|
+
logger.send(method_name) if logger.respond_to?(method_name)
|
107
|
+
end
|
108
|
+
end
|
60
109
|
end
|
61
110
|
end
|
62
|
-
end
|
111
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Reporters
|
2
|
+
class LoggerReporter
|
3
|
+
def initialize(logger, formatter)
|
4
|
+
@logger = logger || Logger.new(STDOUT)
|
5
|
+
@logger.formatter = formatter || GrapeLogging::Formatters::Default.new if @logger.respond_to?(:formatter=)
|
6
|
+
end
|
7
|
+
|
8
|
+
def perform(params)
|
9
|
+
@logger.info params
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module GrapeLogging
|
2
|
+
module Timings
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def db_runtime=(value)
|
6
|
+
Thread.current[:grape_db_runtime] = value
|
7
|
+
end
|
8
|
+
|
9
|
+
def db_runtime
|
10
|
+
Thread.current[:grape_db_runtime] ||= 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def reset_db_runtime
|
14
|
+
self.db_runtime = 0
|
15
|
+
end
|
16
|
+
|
17
|
+
def append_db_runtime(event)
|
18
|
+
self.db_runtime += event.duration
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grape_logging
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1
|
4
|
+
version: 1.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- aserafin
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-03-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: grape
|
@@ -68,12 +68,18 @@ files:
|
|
68
68
|
- Rakefile
|
69
69
|
- bin/console
|
70
70
|
- bin/setup
|
71
|
-
- grape_logging-1.1.1.gem
|
72
71
|
- grape_logging.gemspec
|
73
72
|
- lib/grape_logging.rb
|
74
73
|
- lib/grape_logging/formatters/default.rb
|
74
|
+
- lib/grape_logging/formatters/json.rb
|
75
|
+
- lib/grape_logging/loggers/base.rb
|
76
|
+
- lib/grape_logging/loggers/filter_parameters.rb
|
77
|
+
- lib/grape_logging/loggers/response.rb
|
75
78
|
- lib/grape_logging/middleware/request_logger.rb
|
76
79
|
- lib/grape_logging/multi_io.rb
|
80
|
+
- lib/grape_logging/reporters/active_support_reporter.rb
|
81
|
+
- lib/grape_logging/reporters/logger_reporter.rb
|
82
|
+
- lib/grape_logging/timings.rb
|
77
83
|
- lib/grape_logging/version.rb
|
78
84
|
homepage: http://github.com/aserafin/grape_logging
|
79
85
|
licenses:
|
data/grape_logging-1.1.1.gem
DELETED
Binary file
|