berkeley_library-logging 0.2.1 → 0.2.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/build.yml +1 -1
- data/.idea/inspectionProfiles/Project_Default.xml +2 -1
- data/.idea/logging.iml +30 -30
- data/.simplecov +0 -1
- data/CHANGES.md +25 -0
- data/README.md +1 -1
- data/Rakefile +3 -1
- data/artifacts/.keep +0 -0
- data/berkeley_library-logging.gemspec +2 -2
- data/lib/berkeley_library/logging/configurator.rb +6 -2
- 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 +22 -18
- data/lib/berkeley_library/logging/module_info.rb +1 -1
- data/lib/berkeley_library/logging/safe_serializer.rb +77 -0
- data/spec/rails/ucblit/logging/configurator_spec.rb +99 -29
- data/spec/rails/ucblit/logging/formatters_spec.rb +59 -5
- data/spec/rails/ucblit/logging/loggers_spec.rb +14 -0
- data/spec/rails/ucblit/logging/safe_serializer_spec.rb +23 -0
- data/spec/standalone/ucblit/logging/formatters_spec.rb +57 -5
- data/spec/standalone/ucblit/logging/loggers_spec.rb +21 -1
- data/spec/standalone/ucblit/logging/safe_serializer_spec.rb +137 -0
- metadata +24 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cafd11d3f9d11029ae88c9ed475e08d9c5227d56f2924085145e6363fe76bfc1
|
4
|
+
data.tar.gz: 42eb37cd205ee71cbe710d86ddafd0a342ca31b3f54b40d988b16d5786ef6335
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bc1a877b7dd123c92aa2cbfdcb8f55676bb6ef2257857f4af395e19c2d27fb950e3fcbf69813a0a639dd372f50a711e3b5cc84dfd593aa67dd5035534447a339
|
7
|
+
data.tar.gz: 2fc6bbf11a1d51aa90a15d0841df1c49a38aad8add0a824e1dfcab0bfe4d3489bb6a40d8afe56e3da255d6caac62d68535acfd726f4673604fb1548f5c71d0d0
|
data/.github/workflows/build.yml
CHANGED
@@ -7,6 +7,7 @@
|
|
7
7
|
<inspection_tool class="Rubocop" enabled="false" level="WARNING" enabled_by_default="false" />
|
8
8
|
<inspection_tool class="RubyCaseWithoutElseBlockInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
9
9
|
<inspection_tool class="RubyNilAnalysis" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
10
|
+
<inspection_tool class="RubyParameterNamingConvention" enabled="false" level="WARNING" enabled_by_default="false" />
|
10
11
|
<inspection_tool class="RubyStringKeysInHashInspection" enabled="true" level="INFORMATION" enabled_by_default="true" />
|
11
12
|
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
12
13
|
<option name="processCode" value="true" />
|
@@ -14,4 +15,4 @@
|
|
14
15
|
<option name="processComments" value="true" />
|
15
16
|
</inspection_tool>
|
16
17
|
</profile>
|
17
|
-
</component>
|
18
|
+
</component>
|
data/.idea/logging.iml
CHANGED
@@ -4,32 +4,28 @@
|
|
4
4
|
<shared />
|
5
5
|
</component>
|
6
6
|
<component name="NewModuleRootManager">
|
7
|
-
<content url="file://$MODULE_DIR$"
|
8
|
-
<sourceFolder url="file://$MODULE_DIR$/features" isTestSource="true" />
|
9
|
-
<sourceFolder url="file://$MODULE_DIR$/spec" isTestSource="true" />
|
10
|
-
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
11
|
-
</content>
|
7
|
+
<content url="file://$MODULE_DIR$" />
|
12
8
|
<orderEntry type="jdk" jdkName="RVM: ruby-2.7.4" jdkType="RUBY_SDK" />
|
13
9
|
<orderEntry type="sourceFolder" forTests="false" />
|
14
|
-
<orderEntry type="library" scope="PROVIDED" name="actioncable (v6.1.4, RVM: ruby-2.7.4) [gem]" level="application" />
|
15
|
-
<orderEntry type="library" scope="PROVIDED" name="actionmailbox (v6.1.4, RVM: ruby-2.7.4) [gem]" level="application" />
|
16
|
-
<orderEntry type="library" scope="PROVIDED" name="actionmailer (v6.1.4, RVM: ruby-2.7.4) [gem]" level="application" />
|
17
|
-
<orderEntry type="library" scope="PROVIDED" name="actionpack (v6.1.4, RVM: ruby-2.7.4) [gem]" level="application" />
|
18
|
-
<orderEntry type="library" scope="PROVIDED" name="actiontext (v6.1.4, RVM: ruby-2.7.4) [gem]" level="application" />
|
19
|
-
<orderEntry type="library" scope="PROVIDED" name="actionview (v6.1.4, RVM: ruby-2.7.4) [gem]" level="application" />
|
20
|
-
<orderEntry type="library" scope="PROVIDED" name="activejob (v6.1.4, RVM: ruby-2.7.4) [gem]" level="application" />
|
21
|
-
<orderEntry type="library" scope="PROVIDED" name="activemodel (v6.1.4, RVM: ruby-2.7.4) [gem]" level="application" />
|
22
|
-
<orderEntry type="library" scope="PROVIDED" name="activerecord (v6.1.4, RVM: ruby-2.7.4) [gem]" level="application" />
|
23
|
-
<orderEntry type="library" scope="PROVIDED" name="activestorage (v6.1.4, RVM: ruby-2.7.4) [gem]" level="application" />
|
24
|
-
<orderEntry type="library" scope="PROVIDED" name="activesupport (v6.1.4, RVM: ruby-2.7.4) [gem]" level="application" />
|
25
|
-
<orderEntry type="library" scope="PROVIDED" name="amazing_print (v1.
|
10
|
+
<orderEntry type="library" scope="PROVIDED" name="actioncable (v6.1.4.1, RVM: ruby-2.7.4) [gem]" level="application" />
|
11
|
+
<orderEntry type="library" scope="PROVIDED" name="actionmailbox (v6.1.4.1, RVM: ruby-2.7.4) [gem]" level="application" />
|
12
|
+
<orderEntry type="library" scope="PROVIDED" name="actionmailer (v6.1.4.1, RVM: ruby-2.7.4) [gem]" level="application" />
|
13
|
+
<orderEntry type="library" scope="PROVIDED" name="actionpack (v6.1.4.1, RVM: ruby-2.7.4) [gem]" level="application" />
|
14
|
+
<orderEntry type="library" scope="PROVIDED" name="actiontext (v6.1.4.1, RVM: ruby-2.7.4) [gem]" level="application" />
|
15
|
+
<orderEntry type="library" scope="PROVIDED" name="actionview (v6.1.4.1, RVM: ruby-2.7.4) [gem]" level="application" />
|
16
|
+
<orderEntry type="library" scope="PROVIDED" name="activejob (v6.1.4.1, RVM: ruby-2.7.4) [gem]" level="application" />
|
17
|
+
<orderEntry type="library" scope="PROVIDED" name="activemodel (v6.1.4.1, RVM: ruby-2.7.4) [gem]" level="application" />
|
18
|
+
<orderEntry type="library" scope="PROVIDED" name="activerecord (v6.1.4.1, RVM: ruby-2.7.4) [gem]" level="application" />
|
19
|
+
<orderEntry type="library" scope="PROVIDED" name="activestorage (v6.1.4.1, RVM: ruby-2.7.4) [gem]" level="application" />
|
20
|
+
<orderEntry type="library" scope="PROVIDED" name="activesupport (v6.1.4.1, RVM: ruby-2.7.4) [gem]" level="application" />
|
21
|
+
<orderEntry type="library" scope="PROVIDED" name="amazing_print (v1.4.0, RVM: ruby-2.7.4) [gem]" level="application" />
|
26
22
|
<orderEntry type="library" scope="PROVIDED" name="ansi (v1.5.0, RVM: ruby-2.7.4) [gem]" level="application" />
|
27
23
|
<orderEntry type="library" scope="PROVIDED" name="ast (v2.4.2, RVM: ruby-2.7.4) [gem]" level="application" />
|
28
24
|
<orderEntry type="library" scope="PROVIDED" name="brakeman (v4.10.1, RVM: ruby-2.7.4) [gem]" level="application" />
|
29
25
|
<orderEntry type="library" scope="PROVIDED" name="builder (v3.2.4, RVM: ruby-2.7.4) [gem]" level="application" />
|
30
26
|
<orderEntry type="library" scope="PROVIDED" name="bundle-audit (v0.1.0, RVM: ruby-2.7.4) [gem]" level="application" />
|
31
27
|
<orderEntry type="library" scope="PROVIDED" name="bundler (v2.2.14, RVM: ruby-2.7.4) [gem]" level="application" />
|
32
|
-
<orderEntry type="library" scope="PROVIDED" name="bundler-audit (v0.
|
28
|
+
<orderEntry type="library" scope="PROVIDED" name="bundler-audit (v0.9.0.1, RVM: ruby-2.7.4) [gem]" level="application" />
|
33
29
|
<orderEntry type="library" scope="PROVIDED" name="ci_reporter (v2.0.0, RVM: ruby-2.7.4) [gem]" level="application" />
|
34
30
|
<orderEntry type="library" scope="PROVIDED" name="ci_reporter_rspec (v1.0.0, RVM: ruby-2.7.4) [gem]" level="application" />
|
35
31
|
<orderEntry type="library" scope="PROVIDED" name="colorize (v0.8.1, RVM: ruby-2.7.4) [gem]" level="application" />
|
@@ -39,7 +35,7 @@
|
|
39
35
|
<orderEntry type="library" scope="PROVIDED" name="docile (v1.4.0, RVM: ruby-2.7.4) [gem]" level="application" />
|
40
36
|
<orderEntry type="library" scope="PROVIDED" name="dotenv (v2.7.6, RVM: ruby-2.7.4) [gem]" level="application" />
|
41
37
|
<orderEntry type="library" scope="PROVIDED" name="erubi (v1.10.0, RVM: ruby-2.7.4) [gem]" level="application" />
|
42
|
-
<orderEntry type="library" scope="PROVIDED" name="ffi (v1.15.
|
38
|
+
<orderEntry type="library" scope="PROVIDED" name="ffi (v1.15.4, RVM: ruby-2.7.4) [gem]" level="application" />
|
43
39
|
<orderEntry type="library" scope="PROVIDED" name="globalid (v0.5.2, RVM: ruby-2.7.4) [gem]" level="application" />
|
44
40
|
<orderEntry type="library" scope="PROVIDED" name="i18n (v1.8.10, RVM: ruby-2.7.4) [gem]" level="application" />
|
45
41
|
<orderEntry type="library" scope="PROVIDED" name="io-console (v0.5.9, RVM: ruby-2.7.4) [gem]" level="application" />
|
@@ -48,23 +44,23 @@
|
|
48
44
|
<orderEntry type="library" scope="PROVIDED" name="lograge (v0.11.2, RVM: ruby-2.7.4) [gem]" level="application" />
|
49
45
|
<orderEntry type="library" scope="PROVIDED" name="loofah (v2.12.0, RVM: ruby-2.7.4) [gem]" level="application" />
|
50
46
|
<orderEntry type="library" scope="PROVIDED" name="mail (v2.7.1, RVM: ruby-2.7.4) [gem]" level="application" />
|
51
|
-
<orderEntry type="library" scope="PROVIDED" name="marcel (v1.0.
|
47
|
+
<orderEntry type="library" scope="PROVIDED" name="marcel (v1.0.2, RVM: ruby-2.7.4) [gem]" level="application" />
|
52
48
|
<orderEntry type="library" scope="PROVIDED" name="method_source (v1.0.0, RVM: ruby-2.7.4) [gem]" level="application" />
|
53
|
-
<orderEntry type="library" scope="PROVIDED" name="mini_mime (v1.1.
|
49
|
+
<orderEntry type="library" scope="PROVIDED" name="mini_mime (v1.1.1, RVM: ruby-2.7.4) [gem]" level="application" />
|
54
50
|
<orderEntry type="library" scope="PROVIDED" name="minitest (v5.14.4, RVM: ruby-2.7.4) [gem]" level="application" />
|
55
51
|
<orderEntry type="library" scope="PROVIDED" name="nio4r (v2.5.8, RVM: ruby-2.7.4) [gem]" level="application" />
|
56
|
-
<orderEntry type="library" scope="PROVIDED" name="nokogiri (v1.12.
|
57
|
-
<orderEntry type="library" scope="PROVIDED" name="oj (v3.13.
|
52
|
+
<orderEntry type="library" scope="PROVIDED" name="nokogiri (v1.12.4, RVM: ruby-2.7.4) [gem]" level="application" />
|
53
|
+
<orderEntry type="library" scope="PROVIDED" name="oj (v3.13.9, RVM: ruby-2.7.4) [gem]" level="application" />
|
58
54
|
<orderEntry type="library" scope="PROVIDED" name="ougai (v1.9.1, RVM: ruby-2.7.4) [gem]" level="application" />
|
59
|
-
<orderEntry type="library" scope="PROVIDED" name="parallel (v1.
|
55
|
+
<orderEntry type="library" scope="PROVIDED" name="parallel (v1.21.0, RVM: ruby-2.7.4) [gem]" level="application" />
|
60
56
|
<orderEntry type="library" scope="PROVIDED" name="parser (v3.0.2.0, RVM: ruby-2.7.4) [gem]" level="application" />
|
61
57
|
<orderEntry type="library" scope="PROVIDED" name="racc (v1.5.2, RVM: ruby-2.7.4) [gem]" level="application" />
|
62
58
|
<orderEntry type="library" scope="PROVIDED" name="rack (v2.2.3, RVM: ruby-2.7.4) [gem]" level="application" />
|
63
59
|
<orderEntry type="library" scope="PROVIDED" name="rack-test (v1.1.0, RVM: ruby-2.7.4) [gem]" level="application" />
|
64
|
-
<orderEntry type="library" scope="PROVIDED" name="rails (v6.1.4, RVM: ruby-2.7.4) [gem]" level="application" />
|
60
|
+
<orderEntry type="library" scope="PROVIDED" name="rails (v6.1.4.1, RVM: ruby-2.7.4) [gem]" level="application" />
|
65
61
|
<orderEntry type="library" scope="PROVIDED" name="rails-dom-testing (v2.0.3, RVM: ruby-2.7.4) [gem]" level="application" />
|
66
|
-
<orderEntry type="library" scope="PROVIDED" name="rails-html-sanitizer (v1.4.
|
67
|
-
<orderEntry type="library" scope="PROVIDED" name="railties (v6.1.4, RVM: ruby-2.7.4) [gem]" level="application" />
|
62
|
+
<orderEntry type="library" scope="PROVIDED" name="rails-html-sanitizer (v1.4.2, RVM: ruby-2.7.4) [gem]" level="application" />
|
63
|
+
<orderEntry type="library" scope="PROVIDED" name="railties (v6.1.4.1, RVM: ruby-2.7.4) [gem]" level="application" />
|
68
64
|
<orderEntry type="library" scope="PROVIDED" name="rainbow (v3.0.0, RVM: ruby-2.7.4) [gem]" level="application" />
|
69
65
|
<orderEntry type="library" scope="PROVIDED" name="rake (v13.0.6, RVM: ruby-2.7.4) [gem]" level="application" />
|
70
66
|
<orderEntry type="library" scope="PROVIDED" name="rb-fsevent (v0.11.0, RVM: ruby-2.7.4) [gem]" level="application" />
|
@@ -89,14 +85,18 @@
|
|
89
85
|
<orderEntry type="library" scope="PROVIDED" name="simplecov_json_formatter (v0.1.3, RVM: ruby-2.7.4) [gem]" level="application" />
|
90
86
|
<orderEntry type="library" scope="PROVIDED" name="sprockets (v4.0.2, RVM: ruby-2.7.4) [gem]" level="application" />
|
91
87
|
<orderEntry type="library" scope="PROVIDED" name="sprockets-rails (v3.2.2, RVM: ruby-2.7.4) [gem]" level="application" />
|
92
|
-
<orderEntry type="library" scope="PROVIDED" name="terminal-table (v3.0.
|
88
|
+
<orderEntry type="library" scope="PROVIDED" name="terminal-table (v3.0.2, RVM: ruby-2.7.4) [gem]" level="application" />
|
93
89
|
<orderEntry type="library" scope="PROVIDED" name="thor (v1.1.0, RVM: ruby-2.7.4) [gem]" level="application" />
|
94
90
|
<orderEntry type="library" scope="PROVIDED" name="tzinfo (v2.0.4, RVM: ruby-2.7.4) [gem]" level="application" />
|
95
|
-
<orderEntry type="library" scope="PROVIDED" name="unicode-display_width (v1.
|
91
|
+
<orderEntry type="library" scope="PROVIDED" name="unicode-display_width (v1.8.0, RVM: ruby-2.7.4) [gem]" level="application" />
|
96
92
|
<orderEntry type="library" scope="PROVIDED" name="websocket-driver (v0.7.5, RVM: ruby-2.7.4) [gem]" level="application" />
|
97
93
|
<orderEntry type="library" scope="PROVIDED" name="websocket-extensions (v0.1.5, RVM: ruby-2.7.4) [gem]" level="application" />
|
98
94
|
<orderEntry type="library" scope="PROVIDED" name="zeitwerk (v2.4.2, RVM: ruby-2.7.4) [gem]" level="application" />
|
99
95
|
</component>
|
96
|
+
<component name="RModuleSettingsStorage">
|
97
|
+
<LOAD_PATH number="2" string0="$MODULE_DIR$/lib" string1="$MODULE_DIR$/spec" />
|
98
|
+
<I18N_FOLDERS number="0" />
|
99
|
+
</component>
|
100
100
|
<component name="RakeTasksCache">
|
101
101
|
<option name="myRootTask">
|
102
102
|
<RakeTaskImpl id="rake">
|
@@ -110,7 +110,7 @@
|
|
110
110
|
<RakeTaskImpl description="Remove artifacts directory" fullCommand="clean" id="clean" />
|
111
111
|
<RakeTaskImpl description="Run all specs in spec directory, with coverage" fullCommand="coverage" id="coverage" />
|
112
112
|
<RakeTaskImpl description="Clean, check, build gem" fullCommand="default" id="default" />
|
113
|
-
<RakeTaskImpl description="Build berkeley_library-logging.gemspec as berkeley_library-logging-0.2.
|
113
|
+
<RakeTaskImpl description="Build berkeley_library-logging.gemspec as berkeley_library-logging-0.2.4.gem" fullCommand="gem" id="gem" />
|
114
114
|
<RakeTaskImpl description="Run RuboCop" fullCommand="rubocop" id="rubocop" />
|
115
115
|
<RakeTaskImpl id="rubocop">
|
116
116
|
<subtasks>
|
data/.simplecov
CHANGED
data/CHANGES.md
CHANGED
@@ -1,3 +1,28 @@
|
|
1
|
+
# 0.2.5 (2021-11-04)
|
2
|
+
|
3
|
+
- Rails event logs now include a subset of session attributes (`session_id` and `_csrf_token`).
|
4
|
+
Set `Rails.config.lograge.verbose_session_logging` to log the full session hash.
|
5
|
+
|
6
|
+
# 0.2.4 (2021-11-02)
|
7
|
+
|
8
|
+
- Rails event logs now include the following, in addition to the
|
9
|
+
headers already logged:
|
10
|
+
|
11
|
+
- `request.origin`
|
12
|
+
- `request.base_url`
|
13
|
+
- `request.x_csrf_token`
|
14
|
+
- `params[:authenticity_token]`
|
15
|
+
|
16
|
+
# 0.2.3 (2021-09-02)
|
17
|
+
|
18
|
+
- JSON formatter now strips all ANSI 7-bit C1 escapes from strings
|
19
|
+
(fixes [#1](https://github.com/BerkeleyLibrary/logging/issues/1) properly)
|
20
|
+
|
21
|
+
# 0.2.2 (2021-09-02)
|
22
|
+
|
23
|
+
- JSON formatter now strips ANSI color escapes from strings
|
24
|
+
(fixes [#1](https://github.com/BerkeleyLibrary/logging/issues/1))
|
25
|
+
|
1
26
|
# 0.2.1 (2021-08-19)
|
2
27
|
|
3
28
|
- Recursively log error cause
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# BerkeleyLibrary::Logging
|
2
2
|
|
3
3
|
[![Build Status](https://github.com/BerkeleyLibrary/logging/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/BerkeleyLibrary/logging/actions/workflows/build.yml)
|
4
|
-
[![Gem Version](https://img.shields.io/gem/v/berkeley_library-logging.svg)](https://
|
4
|
+
[![Gem Version](https://img.shields.io/gem/v/berkeley_library-logging.svg)](https://rubygems.org/gems/berkeley_library-logging/)
|
5
5
|
|
6
6
|
Opinionated logging for UCB Library IT Rails applications.
|
7
7
|
|
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,20 +20,20 @@ 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
28
|
spec.add_dependency 'activesupport', '~> 6.0'
|
29
29
|
spec.add_dependency 'amazing_print', '~> 1.1'
|
30
|
+
spec.add_dependency 'colorize', '~> 0.8.1'
|
30
31
|
spec.add_dependency 'lograge', '~> 0.11'
|
31
32
|
spec.add_dependency 'ougai', '~> 1.8'
|
32
33
|
|
33
34
|
spec.add_development_dependency 'brakeman', '~> 4.9'
|
34
35
|
spec.add_development_dependency 'bundle-audit', '~> 0.1'
|
35
36
|
spec.add_development_dependency 'ci_reporter_rspec', '~> 1.0'
|
36
|
-
spec.add_development_dependency 'colorize', '~> 0.8'
|
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'
|
@@ -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
|
@@ -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,10 +1,15 @@
|
|
1
1
|
require 'ougai'
|
2
|
+
require 'berkeley_library/logging/exception_serializer'
|
2
3
|
|
3
4
|
module BerkeleyLibrary
|
4
5
|
module Logging
|
5
6
|
module Formatters
|
6
7
|
|
7
8
|
class << self
|
9
|
+
|
10
|
+
# See https://stackoverflow.com/a/14693789/27358
|
11
|
+
ANSI_7C1_RE = %r{\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])}.freeze
|
12
|
+
|
8
13
|
def new_json_formatter
|
9
14
|
Bunyan.new
|
10
15
|
end
|
@@ -23,26 +28,22 @@ module BerkeleyLibrary
|
|
23
28
|
|
24
29
|
{ msg: message }
|
25
30
|
end
|
26
|
-
end
|
27
|
-
|
28
|
-
# ------------------------------------------------------------
|
29
|
-
# Private helper classes
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
32
|
+
def strip_ansi_escapes(message)
|
33
|
+
return unless message
|
34
|
+
return message.gsub(ANSI_7C1_RE, '') if message.is_a?(String)
|
35
|
+
return message.map { |v| strip_ansi_escapes(v) } if message.is_a?(Array)
|
36
|
+
return message.transform_values { |v| strip_ansi_escapes(v) } if message.is_a?(Hash)
|
36
37
|
|
37
|
-
|
38
|
-
end
|
38
|
+
message
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
|
42
|
+
# ------------------------------------------------------------
|
43
|
+
# Private helper classes
|
43
44
|
|
44
45
|
class Readable < Ougai::Formatters::Readable
|
45
|
-
include
|
46
|
+
include ExceptionSerializer
|
46
47
|
|
47
48
|
protected
|
48
49
|
|
@@ -58,9 +59,11 @@ module BerkeleyLibrary
|
|
58
59
|
" #{err_hash[:name]} (#{err_hash[:message]}):".tap do |msg|
|
59
60
|
next unless (stack = err_hash[:stack])
|
60
61
|
|
61
|
-
|
62
|
-
|
63
|
-
|
62
|
+
trace_indent = (' ' * @trace_indent)
|
63
|
+
trace_separator = "\n#{trace_indent}"
|
64
|
+
|
65
|
+
msg << trace_separator
|
66
|
+
msg << stack.join(trace_separator)
|
64
67
|
|
65
68
|
next unless (cause_hash = err_hash[:cause])
|
66
69
|
|
@@ -74,15 +77,16 @@ module BerkeleyLibrary
|
|
74
77
|
|
75
78
|
class Bunyan < Ougai::Formatters::Bunyan
|
76
79
|
include Ougai::Logging::Severity
|
77
|
-
include
|
80
|
+
include ExceptionSerializer
|
78
81
|
|
79
82
|
def _call(severity, time, progname, data)
|
80
83
|
original_data = Formatters.ensure_hash(data)
|
84
|
+
decolorized_data = Formatters.strip_ansi_escapes(original_data)
|
81
85
|
|
82
86
|
# Ougai::Formatters::Bunyan replaces the human-readable severity string
|
83
87
|
# with a numeric level, so we add it here as a separate attribute
|
84
88
|
severity = ensure_human_readable(severity)
|
85
|
-
merged_data = { severity: severity }.merge(
|
89
|
+
merged_data = { severity: severity }.merge(decolorized_data)
|
86
90
|
super(severity, time, progname, merged_data)
|
87
91
|
end
|
88
92
|
|
@@ -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.5'.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
|
@@ -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(:each) 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
|
@@ -1,27 +1,81 @@
|
|
1
1
|
require 'rails_helper'
|
2
2
|
require 'json'
|
3
|
+
require 'colorize'
|
3
4
|
require 'berkeley_library/logging'
|
4
5
|
|
5
6
|
module BerkeleyLibrary
|
6
7
|
module Logging
|
7
8
|
describe Formatters do
|
8
9
|
describe :new_json_formatter do
|
9
|
-
|
10
|
-
|
11
|
-
|
10
|
+
attr_reader :out, :logger
|
11
|
+
|
12
|
+
before(:each) do
|
13
|
+
@out = StringIO.new
|
14
|
+
@logger = Logger.new(out)
|
12
15
|
logger.formatter = Formatters.new_json_formatter
|
16
|
+
end
|
13
17
|
|
14
|
-
|
18
|
+
it 'supports tagged logging' do
|
19
|
+
tagged_logger = ActiveSupport::TaggedLogging.new(logger)
|
15
20
|
|
16
21
|
expected_tag = 'hello'
|
17
22
|
expected_msg = 'this is a test'
|
18
23
|
|
19
|
-
|
24
|
+
tagged_logger.tagged(expected_tag) { tagged_logger.info(expected_msg) }
|
20
25
|
|
21
26
|
logged_json = JSON.parse(out.string)
|
22
27
|
expect(logged_json['msg']).to eq(expected_msg)
|
23
28
|
expect(logged_json['tags']).to eq([expected_tag])
|
24
29
|
end
|
30
|
+
|
31
|
+
it 'decolorizes ANSI-colored strings' do
|
32
|
+
colors = %i[red green yellow blue magenta cyan]
|
33
|
+
colorized_string = colors.map { |c| c.to_s.colorize(c) }.join(' ')
|
34
|
+
expect(colorized_string).to include("\u001b") # just to be sure
|
35
|
+
|
36
|
+
expected_string = colors.map(&:to_s).join(' ')
|
37
|
+
|
38
|
+
logger.info(colorized_string)
|
39
|
+
logged_json = JSON.parse(out.string)
|
40
|
+
msg = logged_json['msg']
|
41
|
+
expect(msg).not_to include("\u001b")
|
42
|
+
expect(msg).to eq(expected_string)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'decolorizes ANSI-colored strings in attached data' do
|
46
|
+
colors = %i[red green yellow blue magenta cyan]
|
47
|
+
colorized_string = colors.map { |c| c.to_s.colorize(c) }.join(' ')
|
48
|
+
expect(colorized_string).to include("\u001b") # just to be sure
|
49
|
+
|
50
|
+
expected_string = colors.map(&:to_s).join(' ')
|
51
|
+
|
52
|
+
data = {
|
53
|
+
the_string: colorized_string,
|
54
|
+
additional_data: {
|
55
|
+
not_a_string: 12,
|
56
|
+
another_string: colorized_string,
|
57
|
+
more_strings: [colorized_string, colorized_string]
|
58
|
+
}
|
59
|
+
}
|
60
|
+
logger.info('a colorized string', data)
|
61
|
+
|
62
|
+
logged_json = JSON.parse(out.string)
|
63
|
+
data = logged_json
|
64
|
+
expect(data['the_string']).to eq(expected_string)
|
65
|
+
additional_data = data['additional_data']
|
66
|
+
expect(additional_data['not_a_string']).to eq(12)
|
67
|
+
expect(additional_data['another_string']).to eq(expected_string)
|
68
|
+
expect(additional_data['more_strings']).to eq([expected_string, expected_string])
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'removes ANSI formatting from ActiveRecord logs' do
|
72
|
+
original = " \e[1m\e[36mLendingItem Load (2.0ms)\e[0m \e[1m\e[34mSELECT \"lending_items\".* FROM \"lending_items\" WHERE \"lending_items\".\"directory\" = $1 LIMIT $2\e[0m [[\"directory\", \"b135297126_C068087930\"], [\"LIMIT\", 1]]"
|
73
|
+
expected = ' LendingItem Load (2.0ms) SELECT "lending_items".* FROM "lending_items" WHERE "lending_items"."directory" = $1 LIMIT $2 [["directory", "b135297126_C068087930"], ["LIMIT", 1]]'
|
74
|
+
logger.info(original)
|
75
|
+
logged_json = JSON.parse(out.string)
|
76
|
+
msg = logged_json['msg']
|
77
|
+
expect(msg).to eq(expected)
|
78
|
+
end
|
25
79
|
end
|
26
80
|
|
27
81
|
describe :ensure_hash do
|
@@ -14,6 +14,20 @@ module BerkeleyLibrary
|
|
14
14
|
Rails.env = orig_rails_env
|
15
15
|
end
|
16
16
|
|
17
|
+
describe :new_readable_logger do
|
18
|
+
it 'logs ANSI colors' do
|
19
|
+
out = StringIO.new
|
20
|
+
|
21
|
+
logger = Loggers.new_readable_logger(out)
|
22
|
+
colors = %i[red green yellow blue magenta cyan]
|
23
|
+
colorized_string = colors.map { |c| c.to_s.colorize(c) }.join(' ')
|
24
|
+
expect(colorized_string).to include("\u001b") # just to be sure
|
25
|
+
|
26
|
+
logger.info(colorized_string)
|
27
|
+
expect(out.string).to include(colorized_string)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
17
31
|
describe :new_json_logger do
|
18
32
|
it 'supports tagged logging' do
|
19
33
|
out = StringIO.new
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
require 'active_support/time'
|
4
|
+
|
5
|
+
module BerkeleyLibrary
|
6
|
+
module Logging
|
7
|
+
describe SafeSerializer do
|
8
|
+
describe :serialize do
|
9
|
+
it 'handles ActiveSupport::TimeWithZone' do
|
10
|
+
Time.zone = 'America/Los_Angeles'
|
11
|
+
|
12
|
+
t = Time.current
|
13
|
+
expect(t).to be_a(ActiveSupport::TimeWithZone) # just to be sure
|
14
|
+
|
15
|
+
expect(SafeSerializer.serialize(t)).to be(t)
|
16
|
+
|
17
|
+
h = { time: t }
|
18
|
+
expect(SafeSerializer.serialize(h)).to eq(h)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -1,27 +1,79 @@
|
|
1
1
|
require 'standalone_helper'
|
2
2
|
require 'json'
|
3
|
+
require 'colorize'
|
3
4
|
require 'berkeley_library/logging'
|
4
5
|
|
5
6
|
module BerkeleyLibrary
|
6
7
|
module Logging
|
7
8
|
describe Formatters do
|
8
9
|
describe :new_json_formatter do
|
9
|
-
|
10
|
-
|
11
|
-
|
10
|
+
attr_reader :out, :logger
|
11
|
+
|
12
|
+
before(:each) do
|
13
|
+
@out = StringIO.new
|
14
|
+
@logger = Logger.new(out)
|
12
15
|
logger.formatter = Formatters.new_json_formatter
|
16
|
+
end
|
13
17
|
|
14
|
-
|
18
|
+
it 'supports tagged logging' do
|
19
|
+
tagged_logger = ActiveSupport::TaggedLogging.new(logger)
|
15
20
|
|
16
21
|
expected_tag = 'hello'
|
17
22
|
expected_msg = 'this is a test'
|
18
23
|
|
19
|
-
|
24
|
+
tagged_logger.tagged(expected_tag) { tagged_logger.info(expected_msg) }
|
20
25
|
|
21
26
|
logged_json = JSON.parse(out.string)
|
22
27
|
expect(logged_json['msg']).to eq(expected_msg)
|
23
28
|
expect(logged_json['tags']).to eq([expected_tag])
|
24
29
|
end
|
30
|
+
|
31
|
+
it 'decolorizes ANSI-colored strings' do
|
32
|
+
colors = %i[red green yellow blue magenta cyan]
|
33
|
+
colorized_string = colors.map { |c| c.to_s.colorize(c) }.join(' ')
|
34
|
+
expect(colorized_string).to include("\u001b") # just to be sure
|
35
|
+
|
36
|
+
expected_string = colors.map(&:to_s).join(' ')
|
37
|
+
|
38
|
+
logger.info(colorized_string)
|
39
|
+
logged_json = JSON.parse(out.string)
|
40
|
+
msg = logged_json['msg']
|
41
|
+
expect(msg).not_to include("\u001b")
|
42
|
+
expect(msg).to eq(expected_string)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'decolorizes ANSI-colored strings in attached data' do
|
46
|
+
colors = %i[red green yellow blue magenta cyan]
|
47
|
+
colorized_string = colors.map { |c| c.to_s.colorize(c) }.join(' ')
|
48
|
+
expect(colorized_string).to include("\u001b") # just to be sure
|
49
|
+
|
50
|
+
expected_string = colors.map(&:to_s).join(' ')
|
51
|
+
|
52
|
+
data = {
|
53
|
+
the_string: colorized_string,
|
54
|
+
additional_data: {
|
55
|
+
another_string: colorized_string,
|
56
|
+
more_strings: [colorized_string, colorized_string]
|
57
|
+
}
|
58
|
+
}
|
59
|
+
logger.info('a colorized string', data)
|
60
|
+
|
61
|
+
logged_json = JSON.parse(out.string)
|
62
|
+
data = logged_json
|
63
|
+
expect(data['the_string']).to eq(expected_string)
|
64
|
+
additional_data = data['additional_data']
|
65
|
+
expect(additional_data['another_string']).to eq(expected_string)
|
66
|
+
expect(additional_data['more_strings']).to eq([expected_string, expected_string])
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'removes ANSI formatting from ActiveRecord logs' do
|
70
|
+
original = " \e[1m\e[36mLendingItem Load (2.0ms)\e[0m \e[1m\e[34mSELECT \"lending_items\".* FROM \"lending_items\" WHERE \"lending_items\".\"directory\" = $1 LIMIT $2\e[0m [[\"directory\", \"b135297126_C068087930\"], [\"LIMIT\", 1]]"
|
71
|
+
expected = ' LendingItem Load (2.0ms) SELECT "lending_items".* FROM "lending_items" WHERE "lending_items"."directory" = $1 LIMIT $2 [["directory", "b135297126_C068087930"], ["LIMIT", 1]]'
|
72
|
+
logger.info(original)
|
73
|
+
logged_json = JSON.parse(out.string)
|
74
|
+
msg = logged_json['msg']
|
75
|
+
expect(msg).to eq(expected)
|
76
|
+
end
|
25
77
|
end
|
26
78
|
|
27
79
|
describe :ensure_hash do
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'standalone_helper'
|
2
2
|
require 'json'
|
3
|
+
require 'colorize'
|
3
4
|
|
4
5
|
module BerkeleyLibrary
|
5
6
|
module Logging
|
@@ -23,6 +24,18 @@ module BerkeleyLibrary
|
|
23
24
|
Object.send(:remove_const, :TestError)
|
24
25
|
end
|
25
26
|
|
27
|
+
describe :new_readable_logger do
|
28
|
+
it 'logs ANSI colors' do
|
29
|
+
logger = Loggers.new_readable_logger(out)
|
30
|
+
colors = %i[red green yellow blue magenta cyan]
|
31
|
+
colorized_string = colors.map { |c| c.to_s.colorize(c) }.join(' ')
|
32
|
+
expect(colorized_string).to include("\u001b") # just to be sure
|
33
|
+
|
34
|
+
logger.info(colorized_string)
|
35
|
+
expect(out.string).to include(colorized_string)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
26
39
|
describe :new_json_logger do
|
27
40
|
|
28
41
|
# TODO: rewrite this as a matcher
|
@@ -114,7 +127,14 @@ module BerkeleyLibrary
|
|
114
127
|
end
|
115
128
|
end
|
116
129
|
|
117
|
-
|
130
|
+
begin
|
131
|
+
Loggers.new_json_logger(out).error(ex_outer)
|
132
|
+
rescue SystemStackError => e
|
133
|
+
RSpec::Expectations.fail_with("Expected no SystemStackError, but got #{e}: #{e.backtrace[0]}")
|
134
|
+
end
|
135
|
+
|
136
|
+
expect(out.string).to include(msg_outer)
|
137
|
+
expect(out.string).to include(msg_inner)
|
118
138
|
end
|
119
139
|
# rubocop:enable Naming/RescuedExceptionsVariableName
|
120
140
|
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'standalone_helper'
|
2
|
+
|
3
|
+
module BerkeleyLibrary
|
4
|
+
module Logging
|
5
|
+
describe SafeSerializer do
|
6
|
+
describe :serialize do
|
7
|
+
it 'returns primitives unchanged' do
|
8
|
+
values = [
|
9
|
+
nil,
|
10
|
+
false,
|
11
|
+
true,
|
12
|
+
1,
|
13
|
+
1.0,
|
14
|
+
'1/3'.to_r,
|
15
|
+
'a string',
|
16
|
+
:a_symbol,
|
17
|
+
Date.today,
|
18
|
+
Time.now
|
19
|
+
]
|
20
|
+
|
21
|
+
aggregate_failures do
|
22
|
+
values.each do |original|
|
23
|
+
actual = SafeSerializer.serialize(original)
|
24
|
+
expect(actual).to be(original)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'returns an object as a string' do
|
30
|
+
value = Object.new
|
31
|
+
expect(SafeSerializer.serialize(value)).to eq(value.to_s)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'returns a hash as a hash' do
|
35
|
+
hash = { a: 1, b: 2 }
|
36
|
+
expect(SafeSerializer.serialize(hash)).to eq(hash)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'returns an array as an array' do
|
40
|
+
arr = [0, 1, 2, 'elvis', false]
|
41
|
+
expect(SafeSerializer.serialize(arr)).to eq(arr)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'cleans nested values' do
|
45
|
+
b_value = Object.new
|
46
|
+
c_key = Object.new
|
47
|
+
d_value = Object.new
|
48
|
+
|
49
|
+
d_array = ['d', :d, { d: "\ud7ff", 'd' => d_value }]
|
50
|
+
|
51
|
+
h = {
|
52
|
+
a: 1,
|
53
|
+
b: b_value,
|
54
|
+
c_key => 'c value',
|
55
|
+
0xd => d_array,
|
56
|
+
"\uEEEE" => 0xe
|
57
|
+
}
|
58
|
+
|
59
|
+
expected = {
|
60
|
+
a: 1,
|
61
|
+
b: b_value.to_s,
|
62
|
+
c_key.to_s => 'c value',
|
63
|
+
0xd => ['d', :d, { d: "\ud7ff", 'd' => d_value.to_s }],
|
64
|
+
"\uEEEE" => 0xe
|
65
|
+
}
|
66
|
+
|
67
|
+
actual = SafeSerializer.serialize(h)
|
68
|
+
expect(actual).to eq(expected)
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'handles recursive structures' do
|
72
|
+
a = []
|
73
|
+
h = { a: a }
|
74
|
+
a << h
|
75
|
+
|
76
|
+
h_expected = { a: [SafeSerializer.placeholder_for(h)] }
|
77
|
+
a_expected = [{ a: SafeSerializer.placeholder_for(a) }]
|
78
|
+
|
79
|
+
expect(SafeSerializer.serialize(h)).to eq(h_expected)
|
80
|
+
expect(SafeSerializer.serialize(a)).to eq(a_expected)
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'exceptions' do
|
84
|
+
# rubocop:disable Lint/ConstantDefinitionInBlock
|
85
|
+
before(:each) do
|
86
|
+
class ::TestError < StandardError
|
87
|
+
attr_writer :cause
|
88
|
+
|
89
|
+
def cause
|
90
|
+
@cause || super
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
# rubocop:enable Lint/ConstantDefinitionInBlock
|
95
|
+
|
96
|
+
after(:each) do
|
97
|
+
Object.send(:remove_const, :TestError)
|
98
|
+
end
|
99
|
+
|
100
|
+
# rubocop:disable Naming/RescuedExceptionsVariableName
|
101
|
+
it 'handles exceptions' do
|
102
|
+
msg_outer = 'Help I am trapped in the outer part of a unit test'
|
103
|
+
msg_inner = 'Help I am trapped in the inner part of a unit test'
|
104
|
+
|
105
|
+
begin
|
106
|
+
raise TestError, msg_inner
|
107
|
+
rescue TestError => ex_inner
|
108
|
+
begin
|
109
|
+
raise TestError, msg_outer
|
110
|
+
rescue TestError => ex_outer
|
111
|
+
ex_inner.cause = ex_outer
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
expect(ex_outer.cause).to eq(ex_inner) # just to be sure
|
116
|
+
expect(ex_inner.cause).to eq(ex_outer) # just to be sure
|
117
|
+
|
118
|
+
expected = {
|
119
|
+
name: 'TestError',
|
120
|
+
message: msg_outer,
|
121
|
+
stack: ex_outer.backtrace,
|
122
|
+
cause: {
|
123
|
+
name: 'TestError',
|
124
|
+
message: msg_inner,
|
125
|
+
stack: ex_inner.backtrace
|
126
|
+
}
|
127
|
+
}
|
128
|
+
|
129
|
+
expect(SafeSerializer.serialize(ex_outer)).to eq(expected)
|
130
|
+
end
|
131
|
+
# rubocop:enable Naming/RescuedExceptionsVariableName
|
132
|
+
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: berkeley_library-logging
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Moles
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-11-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '1.1'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: colorize
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.8.1
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.8.1
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: lograge
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -108,20 +122,6 @@ dependencies:
|
|
108
122
|
- - "~>"
|
109
123
|
- !ruby/object:Gem::Version
|
110
124
|
version: '1.0'
|
111
|
-
- !ruby/object:Gem::Dependency
|
112
|
-
name: colorize
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
114
|
-
requirements:
|
115
|
-
- - "~>"
|
116
|
-
- !ruby/object:Gem::Version
|
117
|
-
version: '0.8'
|
118
|
-
type: :development
|
119
|
-
prerelease: false
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
requirements:
|
122
|
-
- - "~>"
|
123
|
-
- !ruby/object:Gem::Version
|
124
|
-
version: '0.8'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: dotenv
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -290,17 +290,20 @@ files:
|
|
290
290
|
- LICENSE.md
|
291
291
|
- README.md
|
292
292
|
- Rakefile
|
293
|
+
- artifacts/.keep
|
293
294
|
- berkeley_library-logging.gemspec
|
294
295
|
- docker-compose.yml
|
295
296
|
- lib/berkeley_library/logging.rb
|
296
297
|
- lib/berkeley_library/logging/configurator.rb
|
297
298
|
- lib/berkeley_library/logging/env.rb
|
298
299
|
- lib/berkeley_library/logging/events.rb
|
300
|
+
- lib/berkeley_library/logging/exception_serializer.rb
|
299
301
|
- lib/berkeley_library/logging/formatters.rb
|
300
302
|
- lib/berkeley_library/logging/logger.rb
|
301
303
|
- lib/berkeley_library/logging/loggers.rb
|
302
304
|
- lib/berkeley_library/logging/module_info.rb
|
303
305
|
- lib/berkeley_library/logging/railtie.rb
|
306
|
+
- lib/berkeley_library/logging/safe_serializer.rb
|
304
307
|
- lib/berkeley_library/logging/tagged_logging_extensions.rb
|
305
308
|
- rakelib/.rubocop.yml
|
306
309
|
- rakelib/bundle.rake
|
@@ -314,12 +317,14 @@ files:
|
|
314
317
|
- spec/rails/ucblit/logging/formatters_spec.rb
|
315
318
|
- spec/rails/ucblit/logging/loggers_spec.rb
|
316
319
|
- spec/rails/ucblit/logging/railtie_spec.rb
|
320
|
+
- spec/rails/ucblit/logging/safe_serializer_spec.rb
|
317
321
|
- spec/rails/ucblit/logging_spec.rb
|
318
322
|
- spec/rails_helper.rb
|
319
323
|
- spec/spec_helper.rb
|
320
324
|
- spec/standalone/ucblit/logging/configurator_spec.rb
|
321
325
|
- spec/standalone/ucblit/logging/formatters_spec.rb
|
322
326
|
- spec/standalone/ucblit/logging/loggers_spec.rb
|
327
|
+
- spec/standalone/ucblit/logging/safe_serializer_spec.rb
|
323
328
|
- spec/standalone/ucblit/logging_spec.rb
|
324
329
|
- spec/standalone_helper.rb
|
325
330
|
homepage: https://github.com/BerkeleyLibrary/logging
|
@@ -346,17 +351,20 @@ signing_key:
|
|
346
351
|
specification_version: 4
|
347
352
|
summary: Opinionated Ruby/Rails logging for UC Berkeley Library
|
348
353
|
test_files:
|
354
|
+
- artifacts/.keep
|
349
355
|
- spec/.rubocop.yml
|
350
356
|
- spec/rails/ucblit/logging/configurator_spec.rb
|
351
357
|
- spec/rails/ucblit/logging/env_spec.rb
|
352
358
|
- spec/rails/ucblit/logging/formatters_spec.rb
|
353
359
|
- spec/rails/ucblit/logging/loggers_spec.rb
|
354
360
|
- spec/rails/ucblit/logging/railtie_spec.rb
|
361
|
+
- spec/rails/ucblit/logging/safe_serializer_spec.rb
|
355
362
|
- spec/rails/ucblit/logging_spec.rb
|
356
363
|
- spec/rails_helper.rb
|
357
364
|
- spec/spec_helper.rb
|
358
365
|
- spec/standalone/ucblit/logging/configurator_spec.rb
|
359
366
|
- spec/standalone/ucblit/logging/formatters_spec.rb
|
360
367
|
- spec/standalone/ucblit/logging/loggers_spec.rb
|
368
|
+
- spec/standalone/ucblit/logging/safe_serializer_spec.rb
|
361
369
|
- spec/standalone/ucblit/logging_spec.rb
|
362
370
|
- spec/standalone_helper.rb
|