berkeley_library-logging 0.2.3 → 0.2.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.idea/inspectionProfiles/Project_Default.xml +6 -1
- data/.idea/logging.iml +98 -126
- data/.rubocop.yml +100 -136
- data/.simplecov +0 -12
- data/CHANGES.md +19 -0
- data/Rakefile +3 -1
- data/artifacts/.keep +0 -0
- data/berkeley_library-logging.gemspec +7 -4
- data/lib/berkeley_library/logging/configurator.rb +6 -2
- data/lib/berkeley_library/logging/env.rb +1 -1
- data/lib/berkeley_library/logging/events.rb +80 -24
- data/lib/berkeley_library/logging/exception_serializer.rb +15 -0
- data/lib/berkeley_library/logging/formatters.rb +8 -18
- data/lib/berkeley_library/logging/logger.rb +1 -0
- data/lib/berkeley_library/logging/module_info.rb +1 -1
- data/lib/berkeley_library/logging/safe_serializer.rb +77 -0
- data/spec/.rubocop.yml +88 -7
- data/spec/rails/ucblit/logging/configurator_spec.rb +100 -30
- data/spec/rails/ucblit/logging/env_spec.rb +2 -2
- data/spec/rails/ucblit/logging/formatters_spec.rb +3 -1
- data/spec/rails/ucblit/logging/loggers_spec.rb +6 -5
- data/spec/rails/ucblit/logging/railtie_spec.rb +2 -2
- data/spec/rails/ucblit/logging/safe_serializer_spec.rb +23 -0
- data/spec/rails/ucblit/logging_spec.rb +3 -3
- data/spec/spec_helper.rb +1 -1
- data/spec/standalone/ucblit/logging/configurator_spec.rb +2 -2
- data/spec/standalone/ucblit/logging/formatters_spec.rb +3 -1
- data/spec/standalone/ucblit/logging/loggers_spec.rb +12 -8
- data/spec/standalone/ucblit/logging/safe_serializer_spec.rb +137 -0
- data/spec/standalone/ucblit/logging_spec.rb +3 -3
- data/spec/standalone_helper.rb +1 -1
- metadata +33 -10
data/CHANGES.md
CHANGED
@@ -1,3 +1,22 @@
|
|
1
|
+
# 0.2.6 (2022-03-14)
|
2
|
+
|
3
|
+
- Add support for Rails 7.
|
4
|
+
|
5
|
+
# 0.2.5 (2021-11-04)
|
6
|
+
|
7
|
+
- Rails event logs now include a subset of session attributes (`session_id` and `_csrf_token`).
|
8
|
+
Set `Rails.config.lograge.verbose_session_logging` to log the full session hash.
|
9
|
+
|
10
|
+
# 0.2.4 (2021-11-02)
|
11
|
+
|
12
|
+
- Rails event logs now include the following, in addition to the
|
13
|
+
headers already logged:
|
14
|
+
|
15
|
+
- `request.origin`
|
16
|
+
- `request.base_url`
|
17
|
+
- `request.x_csrf_token`
|
18
|
+
- `params[:authenticity_token]`
|
19
|
+
|
1
20
|
# 0.2.3 (2021-09-02)
|
2
21
|
|
3
22
|
- JSON formatter now strips all ANSI 7-bit C1 escapes from strings
|
data/Rakefile
CHANGED
@@ -19,9 +19,11 @@ end
|
|
19
19
|
# ------------------------------------------------------------
|
20
20
|
# Custom tasks
|
21
21
|
|
22
|
-
desc 'Remove artifacts directory'
|
22
|
+
desc 'Remove artifacts directory, except for .keep file'
|
23
23
|
task :clean do
|
24
24
|
FileUtils.rm_rf('artifacts')
|
25
|
+
FileUtils.mkdir('artifacts')
|
26
|
+
FileUtils.touch(File.join('artifacts', '.keep'))
|
25
27
|
end
|
26
28
|
|
27
29
|
desc 'Check test coverage, check code style, check gems for vulnerabilities'
|
data/artifacts/.keep
ADDED
File without changes
|
@@ -20,12 +20,12 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.homepage = BerkeleyLibrary::Logging::ModuleInfo::HOMEPAGE
|
21
21
|
|
22
22
|
spec.files = `git ls-files -z`.split("\x0")
|
23
|
-
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
23
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features|artifacts)/})
|
24
24
|
spec.require_paths = ['lib']
|
25
25
|
|
26
26
|
spec.required_ruby_version = ">= #{ruby_version}"
|
27
27
|
|
28
|
-
spec.add_dependency 'activesupport', '
|
28
|
+
spec.add_dependency 'activesupport', '>= 6.0'
|
29
29
|
spec.add_dependency 'amazing_print', '~> 1.1'
|
30
30
|
spec.add_dependency 'colorize', '~> 0.8.1'
|
31
31
|
spec.add_dependency 'lograge', '~> 0.11'
|
@@ -37,11 +37,14 @@ Gem::Specification.new do |spec|
|
|
37
37
|
spec.add_development_dependency 'dotenv', '~> 2.7'
|
38
38
|
spec.add_development_dependency 'irb', '~> 1.2' # workaroundfor https://github.com/bundler/bundler/issues/6929
|
39
39
|
spec.add_development_dependency 'listen', '>= 3.0.5', '< 3.2'
|
40
|
-
spec.add_development_dependency 'rails', '
|
40
|
+
spec.add_development_dependency 'rails', '>= 6.0'
|
41
41
|
spec.add_development_dependency 'rake', '~> 13.0'
|
42
42
|
spec.add_development_dependency 'rspec-support', '~> 3.9'
|
43
|
-
spec.add_development_dependency 'rubocop', '~>
|
43
|
+
spec.add_development_dependency 'rubocop', '~> 1.26.0'
|
44
|
+
spec.add_development_dependency 'rubocop-rspec', '~> 2.4.0'
|
44
45
|
spec.add_development_dependency 'simplecov', '~> 0.21.1'
|
45
46
|
spec.add_development_dependency 'simplecov-console', '~> 0.9.1'
|
46
47
|
spec.add_development_dependency 'simplecov-rcov', '~> 0.2'
|
48
|
+
|
49
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
47
50
|
end
|
@@ -22,9 +22,13 @@ module BerkeleyLibrary
|
|
22
22
|
def configure_lograge(config)
|
23
23
|
return unless config.respond_to?(:lograge)
|
24
24
|
|
25
|
-
config.lograge
|
25
|
+
lograge_config = config.lograge
|
26
|
+
|
27
|
+
custom_options = Events.extract_data_for_lograge(lograge_config)
|
28
|
+
|
29
|
+
lograge_config.tap do |lograge|
|
26
30
|
lograge.enabled = true
|
27
|
-
lograge.custom_options =
|
31
|
+
lograge.custom_options = custom_options
|
28
32
|
lograge.formatter = Formatters.lograge_formatter
|
29
33
|
end
|
30
34
|
end
|
@@ -11,7 +11,7 @@ module BerkeleyLibrary
|
|
11
11
|
return Rails.env if defined?(Rails)
|
12
12
|
|
13
13
|
@env ||= begin
|
14
|
-
#
|
14
|
+
# NOTE: can't just self.env= b/c it returns the wrong value -- see
|
15
15
|
# https://stackoverflow.com/q/65226532/27358
|
16
16
|
env = (ENV['RAILS_ENV'] || ENV['RACK_ENV'] || FALLBACK_ENV)
|
17
17
|
ensure_rails_env_like(env)
|
@@ -1,37 +1,93 @@
|
|
1
|
+
require 'berkeley_library/logging/safe_serializer'
|
2
|
+
|
1
3
|
module BerkeleyLibrary
|
2
4
|
module Logging
|
3
5
|
module Events
|
6
|
+
LOGGED_REQUEST_ATTRIBUTES = %i[origin base_url x_csrf_token].freeze
|
7
|
+
LOGGED_SESSION_ATTRIBUTES = %i[session_id _csrf_token].freeze
|
8
|
+
LOGGED_PARAMETERS = [:authenticity_token].freeze
|
9
|
+
LOGGED_HEADERS = {
|
10
|
+
# yes, RFC 2616 uses a variant spelling for 'referrer', it's a known issue
|
11
|
+
# https://tools.ietf.org/html/rfc2616#section-14.36
|
12
|
+
referer: 'HTTP_REFERER',
|
13
|
+
request_id: 'action_dispatch.request_id',
|
14
|
+
remote_ip: 'action_dispatch.remote_ip',
|
15
|
+
remote_addr: 'REMOTE_ADDR',
|
16
|
+
x_forwarded_for: 'HTTP_X_FORWARDED_FOR',
|
17
|
+
forwarded: 'HTTP_FORWARDED' # RFC 7239
|
18
|
+
}.freeze
|
19
|
+
|
4
20
|
class << self
|
5
|
-
|
6
|
-
|
21
|
+
|
22
|
+
def extract_data_for_lograge(lograge_config)
|
23
|
+
verbose_session_logging = lograge_config.verbose_session_logging
|
24
|
+
|
25
|
+
->(event) { extract_event_data(event.payload, verbose_session_logging) }
|
7
26
|
end
|
8
27
|
|
9
28
|
private
|
10
29
|
|
11
|
-
def extract_event_data(
|
12
|
-
|
13
|
-
|
14
|
-
|
30
|
+
def extract_event_data(payload, verbose_session_logging)
|
31
|
+
[
|
32
|
+
extract_headers(payload),
|
33
|
+
extract_request_attributes(payload),
|
34
|
+
extract_param_values(payload),
|
35
|
+
extract_session(payload, verbose_session_logging)
|
36
|
+
].inject({ time: Time.now }) do |data, values|
|
37
|
+
clean_values = SafeSerializer.serialize(values)
|
38
|
+
data.merge(clean_values)
|
39
|
+
end
|
15
40
|
end
|
16
41
|
|
17
|
-
def
|
18
|
-
return {} unless (
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
42
|
+
def extract_param_values(payload)
|
43
|
+
return {} unless (params = payload[:params])
|
44
|
+
|
45
|
+
LOGGED_PARAMETERS.each_with_object({}) do |param, values|
|
46
|
+
next unless (param_val = params[param])
|
47
|
+
|
48
|
+
values[param] = param_val
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def extract_request_attributes(payload)
|
53
|
+
return {} unless (request = payload[:request])
|
54
|
+
|
55
|
+
LOGGED_REQUEST_ATTRIBUTES.each_with_object({}) do |attr, values|
|
56
|
+
next unless request.respond_to?(attr)
|
57
|
+
next if (attr_val = request.send(attr)).nil?
|
58
|
+
|
59
|
+
values[attr] = attr_val
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def extract_headers(payload)
|
64
|
+
return {} unless (headers = payload[:headers])
|
65
|
+
|
66
|
+
LOGGED_HEADERS.each_with_object({}) do |(key, header), values|
|
67
|
+
next unless (header_val = headers[header])
|
68
|
+
|
69
|
+
values[key] = header_val
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def extract_session(payload, verbose_session_logging)
|
74
|
+
return {} unless (request = payload[:request])
|
75
|
+
return {} unless (session = request.session)
|
76
|
+
return {} unless session.respond_to?(:to_hash)
|
77
|
+
|
78
|
+
session_hash = session_hash(session, verbose_session_logging)
|
79
|
+
|
80
|
+
{ session: session_hash }
|
81
|
+
end
|
82
|
+
|
83
|
+
def session_hash(session, verbose)
|
84
|
+
raw_session_hash = session.to_hash
|
85
|
+
return raw_session_hash if verbose
|
86
|
+
|
87
|
+
LOGGED_SESSION_ATTRIBUTES.filter_map do |attr|
|
88
|
+
attr_str = attr.to_s
|
89
|
+
[attr, raw_session_hash[attr_str]] if raw_session_hash.key?(attr_str)
|
90
|
+
end.to_h
|
35
91
|
end
|
36
92
|
end
|
37
93
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module BerkeleyLibrary
|
2
|
+
module Logging
|
3
|
+
module ExceptionSerializer
|
4
|
+
def serialize_exc(ex, serialized = Set.new)
|
5
|
+
raw_result = { name: ex.class.name, message: ex.message, stack: ex.backtrace }
|
6
|
+
raw_result.tap do |result|
|
7
|
+
next unless (cause = ex.cause)
|
8
|
+
next if (serialized << ex).include?(cause) # prevent circular references
|
9
|
+
|
10
|
+
result[:cause] = serialize_exc(cause, serialized)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'ougai'
|
2
|
+
require 'berkeley_library/logging/exception_serializer'
|
2
3
|
|
3
4
|
module BerkeleyLibrary
|
4
5
|
module Logging
|
@@ -41,21 +42,8 @@ module BerkeleyLibrary
|
|
41
42
|
# ------------------------------------------------------------
|
42
43
|
# Private helper classes
|
43
44
|
|
44
|
-
module ErrorCauseSerializer
|
45
|
-
def serialize_exc(ex, serialized = Set.new)
|
46
|
-
super(ex).tap do |result|
|
47
|
-
next unless (cause = ex.cause)
|
48
|
-
next if (serialized << ex).include?(cause) # prevent circular references
|
49
|
-
|
50
|
-
result[:cause] = serialize_exc(cause, serialized)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
private_constant :ErrorCauseSerializer
|
56
|
-
|
57
45
|
class Readable < Ougai::Formatters::Readable
|
58
|
-
include
|
46
|
+
include ExceptionSerializer
|
59
47
|
|
60
48
|
protected
|
61
49
|
|
@@ -71,9 +59,11 @@ module BerkeleyLibrary
|
|
71
59
|
" #{err_hash[:name]} (#{err_hash[:message]}):".tap do |msg|
|
72
60
|
next unless (stack = err_hash[:stack])
|
73
61
|
|
74
|
-
|
75
|
-
|
76
|
-
|
62
|
+
trace_indent = (' ' * @trace_indent)
|
63
|
+
trace_separator = "\n#{trace_indent}"
|
64
|
+
|
65
|
+
msg << trace_separator
|
66
|
+
msg << stack.join(trace_separator)
|
77
67
|
|
78
68
|
next unless (cause_hash = err_hash[:cause])
|
79
69
|
|
@@ -87,7 +77,7 @@ module BerkeleyLibrary
|
|
87
77
|
|
88
78
|
class Bunyan < Ougai::Formatters::Bunyan
|
89
79
|
include Ougai::Logging::Severity
|
90
|
-
include
|
80
|
+
include ExceptionSerializer
|
91
81
|
|
92
82
|
def _call(severity, time, progname, data)
|
93
83
|
original_data = Formatters.ensure_hash(data)
|
@@ -7,7 +7,7 @@ module BerkeleyLibrary
|
|
7
7
|
SUMMARY = 'Opinionated Ruby/Rails logging for UC Berkeley Library'.freeze
|
8
8
|
DESCRIPTION = 'A gem providing shared logging code for UC Berkeley Library gems and Rails applications'.freeze
|
9
9
|
LICENSE = 'MIT'.freeze
|
10
|
-
VERSION = '0.2.
|
10
|
+
VERSION = '0.2.6'.freeze
|
11
11
|
HOMEPAGE = 'https://github.com/BerkeleyLibrary/logging'.freeze
|
12
12
|
|
13
13
|
private_class_method :new
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'berkeley_library/logging/exception_serializer'
|
3
|
+
|
4
|
+
module BerkeleyLibrary
|
5
|
+
module Logging
|
6
|
+
# Some of values include recursive structures
|
7
|
+
# that cause SystemStackErrors in JSON serialization,
|
8
|
+
# so we convert them all to strings
|
9
|
+
class SafeSerializer
|
10
|
+
include ExceptionSerializer
|
11
|
+
|
12
|
+
RAW_TYPES = [NilClass, FalseClass, TrueClass, Numeric, String, Symbol, Date, Time].freeze
|
13
|
+
|
14
|
+
def initialize(value)
|
15
|
+
@value = value
|
16
|
+
end
|
17
|
+
|
18
|
+
class << self
|
19
|
+
def serialize(value)
|
20
|
+
SafeSerializer.new(value).serialized_value
|
21
|
+
end
|
22
|
+
|
23
|
+
def placeholder_for(value)
|
24
|
+
"#<#{value.class}:#{value.object_id}> (recursive reference)"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def serialized_value
|
29
|
+
@serialized_value ||= serialize(@value)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
35
|
+
def serialize(value)
|
36
|
+
return value if safe_raw_value?(value)
|
37
|
+
return SafeSerializer.placeholder_for(value) if serialized_values.include?(value)
|
38
|
+
|
39
|
+
serialized_values << value
|
40
|
+
|
41
|
+
return serialize_hash(value) if value.is_a?(Hash)
|
42
|
+
return serialize_hash(value.to_hash) if value.respond_to?(:to_hash)
|
43
|
+
return serialize_array(value) if value.is_a?(Array)
|
44
|
+
return serialize_array(value.to_ary) if value.respond_to?(:to_ary)
|
45
|
+
return serialize_exc(value, serialized_values) if value.is_a?(Exception)
|
46
|
+
|
47
|
+
value.to_s
|
48
|
+
end
|
49
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
50
|
+
|
51
|
+
def safe_raw_value?(value)
|
52
|
+
return true if rails_time_with_zone?(value)
|
53
|
+
|
54
|
+
RAW_TYPES.any? { |t| value.is_a?(t) }
|
55
|
+
end
|
56
|
+
|
57
|
+
def rails_time_with_zone?(value)
|
58
|
+
defined?(ActiveSupport::TimeWithZone) && value.is_a?(ActiveSupport::TimeWithZone)
|
59
|
+
end
|
60
|
+
|
61
|
+
def serialize_array(value)
|
62
|
+
value.map { |v| serialize(v) }
|
63
|
+
end
|
64
|
+
|
65
|
+
def serialize_hash(value)
|
66
|
+
value.each_with_object({}) do |(k, v), h|
|
67
|
+
k1, v1 = [k, v].map { |x| serialize(x) }
|
68
|
+
h[k1] = v1
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def serialized_values
|
73
|
+
@serialized_values ||= Set.new
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/spec/.rubocop.yml
CHANGED
@@ -1,15 +1,18 @@
|
|
1
1
|
inherit_from: ../.rubocop.yml
|
2
2
|
|
3
|
+
require:
|
4
|
+
- rubocop-rspec
|
5
|
+
|
3
6
|
Style/ClassAndModuleChildren:
|
4
7
|
Enabled: false
|
5
8
|
|
6
|
-
|
9
|
+
Style/OpenStructUse:
|
7
10
|
Enabled: false
|
8
11
|
|
9
|
-
Metrics/
|
12
|
+
Metrics/AbcSize:
|
10
13
|
Enabled: false
|
11
14
|
|
12
|
-
Metrics/
|
15
|
+
Metrics/BlockLength:
|
13
16
|
Enabled: false
|
14
17
|
|
15
18
|
Metrics/ModuleLength:
|
@@ -19,9 +22,87 @@ Metrics/MethodLength:
|
|
19
22
|
Enabled: false
|
20
23
|
|
21
24
|
############################################################
|
22
|
-
#
|
25
|
+
# rubocop-rspec
|
26
|
+
|
27
|
+
# believe me, it wasn't by choice
|
28
|
+
RSpec/AnyInstance:
|
29
|
+
Enabled: false
|
30
|
+
|
31
|
+
# we meant to do that
|
32
|
+
RSpec/BeforeAfterAll:
|
33
|
+
Enabled: false
|
34
|
+
|
35
|
+
# more words != more readable
|
36
|
+
RSpec/ContextWording:
|
37
|
+
Enabled: false
|
38
|
+
|
39
|
+
# explicit >>> implicit
|
40
|
+
RSpec/DescribedClass:
|
41
|
+
Enabled: false
|
42
|
+
|
43
|
+
# more punctuation != more readable
|
44
|
+
RSpec/DescribeSymbol:
|
45
|
+
Enabled: false
|
46
|
+
|
47
|
+
# too late now
|
48
|
+
RSpec/ExampleLength:
|
49
|
+
Enabled: false
|
50
|
+
|
51
|
+
# we meant to do that
|
52
|
+
RSpec/ExpectInHook:
|
53
|
+
Enabled: false
|
54
|
+
|
55
|
+
# if only it were that simple
|
56
|
+
RSpec/ExpectOutput:
|
57
|
+
Enabled: false
|
58
|
+
|
59
|
+
# your naming scheme is not in possession of all the facts
|
60
|
+
RSpec/FilePath:
|
61
|
+
Enabled: false
|
23
62
|
|
24
|
-
#
|
25
|
-
|
63
|
+
# explicit >>> implicit
|
64
|
+
RSpec/InstanceVariable:
|
26
65
|
Enabled: false
|
27
|
-
|
66
|
+
|
67
|
+
# maybe when 'all' has a corresponding 'none' matcher
|
68
|
+
RSpec/IteratedExpectation:
|
69
|
+
Enabled: false
|
70
|
+
|
71
|
+
# TODO: clean these up & de-disable this
|
72
|
+
RSpec/LeakyConstantDeclaration:
|
73
|
+
Enabled: false
|
74
|
+
|
75
|
+
# too late now
|
76
|
+
RSpec/MultipleMemoizedHelpers:
|
77
|
+
Enabled: false
|
78
|
+
|
79
|
+
# setup cost / time >>> failure granularity
|
80
|
+
RSpec/MultipleExpectations:
|
81
|
+
Enabled: false
|
82
|
+
|
83
|
+
# cure is worse than the disease
|
84
|
+
RSpec/NestedGroups:
|
85
|
+
Enabled: false
|
86
|
+
|
87
|
+
# more quotation marks != more readable
|
88
|
+
RSpec/SharedExamples:
|
89
|
+
Enabled: false
|
90
|
+
|
91
|
+
# we meant to do that
|
92
|
+
RSpec/StubbedMock:
|
93
|
+
Enabled: false
|
94
|
+
|
95
|
+
# we meant to do that
|
96
|
+
RSpec/VerifiedDoubles:
|
97
|
+
Enabled: false
|
98
|
+
|
99
|
+
############################################################
|
100
|
+
# rubocop-rspec
|
101
|
+
|
102
|
+
# enable newer rubocop-rspec cops
|
103
|
+
|
104
|
+
RSpec/IdenticalEqualityAssertion: # new in 2.4
|
105
|
+
Enabled: true
|
106
|
+
|
107
|
+
RSpec/Rails/AvoidSetupHook: # new in 2.4
|
108
|
+
Enabled: true
|
@@ -10,7 +10,7 @@ module BerkeleyLibrary
|
|
10
10
|
|
11
11
|
attr_reader :config
|
12
12
|
|
13
|
-
before
|
13
|
+
before do
|
14
14
|
app = Class.new(Rails::Application).new
|
15
15
|
allow(Rails).to receive(:application).and_return(app)
|
16
16
|
@config = app.config
|
@@ -30,36 +30,106 @@ module BerkeleyLibrary
|
|
30
30
|
expect(lograge.enabled).to eq(true)
|
31
31
|
end
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
33
|
+
context 'events' do
|
34
|
+
let(:params) { { authenticity_token: '8675309' } }
|
35
|
+
|
36
|
+
let(:session_hash) do
|
37
|
+
{
|
38
|
+
'session_id' => '17cd06b1b7cb9744e8fa626ef5f37c67',
|
39
|
+
'user' => {
|
40
|
+
'id' => 71,
|
41
|
+
'user_name' => 'Ms. Magoo',
|
42
|
+
'created_at' => '2021-10-14T09:24:42.730-07:00',
|
43
|
+
'updated_at' => '2021-10-14T09:24:42.729-07:00',
|
44
|
+
'user_role' => 'Administrator',
|
45
|
+
'user_active' => true,
|
46
|
+
'uid' => 1_684_944,
|
47
|
+
'updated_by' => 'Dr. Pibb'
|
48
|
+
},
|
49
|
+
'expires_at' => '2021-11-03T12:30:01.281-07:00',
|
50
|
+
'_csrf_token' => 'HiN1xUxFcOvWvoe2nwoBSGlmGSN6x0jprpSqDrzquxA='
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
let(:request_headers) do
|
55
|
+
{
|
56
|
+
'HTTP_REFERER' => 'value from HTTP_REFERER',
|
57
|
+
'action_dispatch.request_id' => 'value from action_dispatch.request_id',
|
58
|
+
'action_dispatch.remote_ip' => 'value from action_dispatch.remote_ip',
|
59
|
+
'REMOTE_ADDR' => 'value from REMOTE_ADDR',
|
60
|
+
'HTTP_X_FORWARDED_FOR' => 'value from HTTP_X_FORWARDED_FOR',
|
61
|
+
'HTTP_FORWARDED' => 'value from HTTP_FORWARDED'
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
let(:expected_header_map) do
|
66
|
+
{
|
67
|
+
referer: 'HTTP_REFERER',
|
68
|
+
request_id: 'action_dispatch.request_id',
|
69
|
+
remote_ip: 'action_dispatch.remote_ip',
|
70
|
+
remote_addr: 'REMOTE_ADDR',
|
71
|
+
x_forwarded_for: 'HTTP_X_FORWARDED_FOR',
|
72
|
+
forwarded: 'HTTP_FORWARDED'
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
attr_reader :session, :request, :payload, :event
|
77
|
+
|
78
|
+
before do
|
79
|
+
@session = instance_double(ActionDispatch::Request::Session)
|
80
|
+
allow(session).to receive(:to_hash).and_return(session_hash)
|
81
|
+
|
82
|
+
@request = instance_double(ActionDispatch::Request)
|
83
|
+
allow(request).to receive(:origin).and_return('http://example.org:3000')
|
84
|
+
allow(request).to receive(:base_url).and_return('https://example.org:3443')
|
85
|
+
allow(request).to receive(:x_csrf_token).and_return('5551212')
|
86
|
+
allow(request).to receive(:session).and_return(session)
|
87
|
+
|
88
|
+
@payload = {
|
89
|
+
params: params,
|
90
|
+
request: request,
|
91
|
+
headers: request_headers
|
92
|
+
}
|
93
|
+
|
94
|
+
@event = instance_double(ActiveSupport::Notifications::Event)
|
95
|
+
allow(event).to receive(:payload).and_return(payload)
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'extracts request info from log events' do
|
99
|
+
Configurator.configure(config)
|
100
|
+
data = config.lograge.custom_options.call(event)
|
36
101
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
102
|
+
expect(data).to be_a(Hash)
|
103
|
+
expect(data[:time]).to be_a(Time)
|
104
|
+
expect(data[:time].to_i).to be_within(60).of(Time.now.to_i)
|
105
|
+
|
106
|
+
expected_header_map.each { |xh, rh| expect(data[xh]).to eq(request_headers[rh]) }
|
107
|
+
|
108
|
+
expect(data[:authenticity_token]).to eq(params[:authenticity_token])
|
109
|
+
|
110
|
+
Events::LOGGED_REQUEST_ATTRIBUTES.each do |attr|
|
111
|
+
expect(data[attr]).to eq(request.send(attr))
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'includes the entire session if `verbose_session_logging` is true' do
|
116
|
+
config.lograge.verbose_session_logging = true
|
117
|
+
Configurator.configure(config)
|
118
|
+
data = config.lograge.custom_options.call(event)
|
119
|
+
|
120
|
+
expect(data[:session]).to eq(session_hash)
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'includes only the session ID by default' do
|
124
|
+
Configurator.configure(config)
|
125
|
+
data = config.lograge.custom_options.call(event)
|
126
|
+
|
127
|
+
expected_hash = Events::LOGGED_SESSION_ATTRIBUTES.each_with_object({}) do |attr, h|
|
128
|
+
h[attr] = session_hash[attr.to_s]
|
129
|
+
end
|
130
|
+
|
131
|
+
expect(data[:session]).to eq(expected_hash)
|
132
|
+
end
|
63
133
|
end
|
64
134
|
|
65
135
|
it 'formats Lograge data as a hash' do
|