smart_logger_wrapper 0.4.3 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: dc96e2cf2599e135aa4413b1c04214c573f1603e
4
- data.tar.gz: c7809de2f8e1fb1e1a3a00e44484f02ad5e7c08e
2
+ SHA256:
3
+ metadata.gz: d466f7e45ea8cef683330d3ac32cbe04e13b23e1887b36e4383999018778dbb7
4
+ data.tar.gz: 86b7a9d0f135905ce16d46e6e69131eec966f2486791c90d646bbb7cdf439aef
5
5
  SHA512:
6
- metadata.gz: aff557feb85a24581aeb9867cde9d38f6083e0de68d6b6b66c290c03f508bb817009209720e22898bf5e53982525ff740870897ef6151506cd975ebf4653b175
7
- data.tar.gz: 9cfe545b190eaf060d1ba5e0b97dbcef9abe98ab906952e113be45d2e7cd43d4857d61609e80406501d4814d56288758b8dac7f64505805cf4c464d27fd77d12
6
+ metadata.gz: 5b299638a87014710a8a04ca9f54a43b6d4bf36dc53324e77fe1830a30634ffb5871efaca002f54ebd5a5edb320f5d8c8a7530873d1b4406c9e1f6cc628eaddf
7
+ data.tar.gz: 494b1d44d563486e4ee737da7bf0208be22c343893f2fb2bfe8b160d48e008d5816ad81ea6a39b453cea203f410dec021ff9deffd1c356764758f3636b342054
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.5.1
data/README.md CHANGED
@@ -26,7 +26,7 @@ Wrap your logger with `SmartLoggerWrapper`, for example, in `config/environments
26
26
 
27
27
  ```diff
28
28
  - config.logger = Logger.new('log/production.log', 'daily')
29
- + config.logger = SmartLoggerWrapper(Logger.new('log/production.log', 'daily')).with_position
29
+ + config.logger = SmartLoggerWrapper.new(Logger.new('log/production.log', 'daily')).with_position
30
30
  ```
31
31
 
32
32
  Note that it is strongly recommended to use the wrapper for all kind of environments so that you can avoid exceptions such as `NoMethodError` due to the unique features of this library.
@@ -105,7 +105,7 @@ logger.to(STDERR).info 'A message'
105
105
 
106
106
  #### #with\_position
107
107
 
108
- `with_position` option makes the logger tag the position where the logger is called.
108
+ `with_position` option makes the logger tag the position where the logger is called. On Rails, this information will be filtered by `Rails.backtrace_cleaner` not to bother you with annoying long file paths on logs generated by Rails.
109
109
 
110
110
  ```ruby
111
111
  logger.with_position.info 'A message'
@@ -140,11 +140,11 @@ For instance, in the case you want to integrate a messenger, such as Slack, in a
140
140
 
141
141
  ```ruby
142
142
  SmartLoggerWrapper::Options.define_redirector :to_messenger, Class.new(SmartLoggerWrapper::Options::Base) {
143
- def apply!(messages, argument, severity, wrapper)
144
- channel = argument || 'general'
143
+ def apply!(messages, arguments, severity, wrapper)
144
+ channel = arguments.first || 'general'
145
145
  time = Time.now
146
146
  severity_label = wrapper.format_severity(severity)
147
- formatted_messages = messages.map { |message| wrapper.formatted_message(severity_label, time, nil, message) }
147
+ formatted_messages = messages.map { |message| wrapper.format_message(severity_label, time, nil, message) }
148
148
  Thread.new do
149
149
  SomeMessenger.new(channel: channel).post(['```', *formatted_messages, '```'].join("\n"))
150
150
  end
@@ -158,17 +158,23 @@ Then, you can post log messages as follows:
158
158
  Rails.logger.to_messenger('channel').error('foo')
159
159
  ```
160
160
 
161
+ #### Implementation
162
+
163
+ Eash option is expected to be defined with a subclass of `SmartLoggerWrapper::Options::Base`. The class is required to respond to `#apply!` with the following arguments: `messages`, `argument`, `severity` and `wrapper`. Firstly, `messages` is an array of messages to be logged. In the case that you want to update the messages, you need to destructively update the array (because of its performance). Second, `argument` is the one which is passed as the option method argument. `severity` is an integer in response to `Logger::Severity`. Lastly, `wrapper` is the caller `SmartLoggerWrapper`.
164
+
165
+ #### Option priority
166
+
161
167
  There are three categories for `SmartLoggerWrapper::Options`. Each option will be applied in the following order according to its category:
162
168
 
163
- #### 1. Tagger
169
+ ##### 1. Tagger
164
170
 
165
171
  A tagger is expected to be used to tag each message. To define a tagger, you will call `SmartLoggerWrapper::Options.define_tagger`.
166
172
 
167
- #### 2. Appender
173
+ ##### 2. Appender
168
174
 
169
175
  An appender is expected to append some additinal information to the message list. To define an appender, you will call `SmartLoggerWrapper::Options.define_appender`.
170
176
 
171
- #### 3. Redirector
177
+ ##### 3. Redirector
172
178
 
173
179
  A redirector should put messages to another location from the one where the wrapped logger specifies. To define a redirector, you will call `SmartLoggerWrapper::Options.define_redirector`.
174
180
 
@@ -7,8 +7,8 @@ class SmartLoggerWrapper < Logger
7
7
  class AppendBacktrace < Base
8
8
  include ::SmartLoggerWrapper::Utils::Backtrace
9
9
 
10
- def apply!(messages, argument, severity, wrapper)
11
- length = argument.is_a?(Numeric) ? argument : nil
10
+ def apply!(messages, arguments, severity, wrapper)
11
+ length = arguments.first.is_a?(Numeric) ? arguments.first : nil
12
12
  messages << [
13
13
  'BACKTRACE:',
14
14
  *get_backtrace(wrapper.offset + APPLY_CALLER_STACK_DEPTH + 1, length)
@@ -3,7 +3,7 @@ require 'logger'
3
3
  class SmartLoggerWrapper < Logger
4
4
  module Options
5
5
  class Base
6
- def apply!(messages, argument, severity, wrapper)
6
+ def apply!(messages, arguments, severity, wrapper)
7
7
  raise NotImplementedError, __callee__
8
8
  end
9
9
  end
@@ -4,11 +4,12 @@ require 'smart_logger_wrapper/options/base'
4
4
  class SmartLoggerWrapper < Logger
5
5
  module Options
6
6
  class To < Base
7
- def apply!(messages, argument, severity, wrapper)
8
- raise ApplicationError, 'No handler given' if argument == nil
7
+ def apply!(messages, arguments, severity, wrapper)
8
+ raise ApplicationError, 'No handler given' if arguments.empty?
9
+ out = arguments.first
9
10
  time = Time.now
10
11
  severity_label = wrapper.format_severity(severity)
11
- argument.puts messages.map { |message| wrapper.format_message(severity_label, time, nil, message) }.join("\n")
12
+ out.puts messages.map { |message| wrapper.format_message(severity_label, time, nil, message) }.join("\n")
12
13
  rescue NoMethodError => e
13
14
  raise ApplicationError, e.message
14
15
  end
@@ -1,18 +1,21 @@
1
1
  require 'logger'
2
2
  require 'smart_logger_wrapper/options/base'
3
3
  require 'smart_logger_wrapper/utils/path'
4
+ require 'smart_logger_wrapper/utils/backtrace'
4
5
 
5
6
  class SmartLoggerWrapper < Logger
6
7
  module Options
7
8
  class WithPosition < Base
8
9
  include ::SmartLoggerWrapper::Utils::Path
10
+ include ::SmartLoggerWrapper::Utils::Backtrace
9
11
 
10
- def apply!(messages, argument, severity, wrapper)
11
- return if argument == false
12
+ def apply!(messages, arguments, severity, wrapper)
13
+ enabled = arguments.first || true
14
+ return unless enabled
12
15
  # add 1 to `start` because this method dug the backtrace by 1
13
16
  location = caller_locations(wrapper.offset + APPLY_CALLER_STACK_DEPTH + 1, 1)
14
17
  prefix =
15
- if location && location.length > 0
18
+ if location && location.length > 0 && location_important?(location)
16
19
  method_name = location[0].label
17
20
  path = trim_dirname(location[0].absolute_path)
18
21
  lineno = location[0].lineno
@@ -22,6 +25,12 @@ class SmartLoggerWrapper < Logger
22
25
  end
23
26
  messages.map! { |message| [prefix, message].compact.join(' ') }
24
27
  end
28
+
29
+ private
30
+
31
+ def location_important?(location)
32
+ ! clean_backtrace(location.map(&:to_s)).empty?
33
+ end
25
34
  end
26
35
 
27
36
  define_tagger :with_position, WithPosition
@@ -9,10 +9,10 @@ class SmartLoggerWrapper < Logger
9
9
 
10
10
  module_function
11
11
 
12
- def apply_all!(messages, severity, logger)
12
+ def apply_all!(messages, severity, wrapper)
13
13
  [defined_appenders, defined_taggers, defined_redirectors].flatten.each do |option_key|
14
- if logger.options.include?(option_key)
15
- defined_options[option_key].apply!(messages, logger.options[option_key], severity, logger)
14
+ if wrapper.options.include?(option_key)
15
+ defined_options[option_key].apply!(messages, wrapper.options[option_key], severity, wrapper)
16
16
  end
17
17
  end
18
18
  end
@@ -1,5 +1,5 @@
1
1
  require 'logger'
2
2
 
3
3
  class SmartLoggerWrapper < Logger
4
- VERSION = "0.4.3"
4
+ VERSION = "0.5.0"
5
5
  end
@@ -5,7 +5,8 @@ require 'smart_logger_wrapper/options'
5
5
  class SmartLoggerWrapper < Logger
6
6
  include Logger::Severity
7
7
 
8
- LOGGER_SHORTCUT_OFFSET = 3
8
+ BASE_OFFSET = 3
9
+ NESTED_WRAPPER_OFFSET = 6
9
10
 
10
11
  SEVERITY_MAPPING = {
11
12
  debug: DEBUG,
@@ -15,16 +16,16 @@ class SmartLoggerWrapper < Logger
15
16
  fatal: FATAL,
16
17
  unknown: UNKNOWN
17
18
  }.freeze
18
- DELEGETING_METHODS = %i(<< reopen close log add level debug? level= progname datetime_format= datetime_format formatter sev_threshold sev_threshold= info? warn? error? fatal? progname= formatter=)
19
+ DELEGETING_METHODS = %i(<< reopen close log add level debug? level= progname datetime_format= datetime_format formatter sev_threshold sev_threshold= info? warn? error? fatal? progname= formatter=).freeze
19
20
 
20
- attr_reader :loggers, :options, :offset
21
+ attr_reader :loggers, :options, :base_offset, :parent
21
22
 
22
- def initialize(logger = Logger.new(STDOUT), *loggers, **options)
23
- @loggers = [logger, *loggers].freeze
23
+ def initialize(logger = Logger.new(STDOUT), *loggers, base_offset: nil, parent: nil, **options)
24
+ @base_offset = base_offset || BASE_OFFSET
25
+ @parent = parent
26
+ @loggers = be_parent_of!(logger, *loggers).freeze
24
27
  @options = options.freeze
25
- @offset = LOGGER_SHORTCUT_OFFSET
26
28
  @_loggers_cache = {}
27
- @_loggers_with_offset_cache = {}
28
29
  end
29
30
 
30
31
  # For all methods with severity label, logger accepts multiple messages.
@@ -49,14 +50,13 @@ class SmartLoggerWrapper < Logger
49
50
  end
50
51
  end
51
52
 
52
- def with_offset(_offset)
53
- @_loggers_with_offset_cache[_offset] ||= clone.tap do |logger_with_offset|
54
- logger_with_offset.instance_variable_set(:@offset, _offset)
55
- end
53
+ def offset
54
+ @base_offset + depth * NESTED_WRAPPER_OFFSET
56
55
  end
57
56
 
58
- def overwrite_options(_options)
59
- @options = options.merge(_options).freeze
57
+ def depth
58
+ return 0 if root?
59
+ parent.depth + 1
60
60
  end
61
61
 
62
62
  def format_severity(severity)
@@ -67,6 +67,11 @@ class SmartLoggerWrapper < Logger
67
67
  loggers.first.send(:format_message, severity, datetime, progname, msg)
68
68
  end
69
69
 
70
+ def with_option(option_name, *args)
71
+ new_options = options.merge(option_name => args)
72
+ self.class.new(*loggers, base_offset: base_offset, **new_options)
73
+ end
74
+
70
75
  private
71
76
 
72
77
  def build_messages(severity, *args, &block)
@@ -98,18 +103,36 @@ class SmartLoggerWrapper < Logger
98
103
  end
99
104
  end
100
105
 
106
+ def set_parent!(new_parent)
107
+ @_loggers_cache.clear if new_parent != nil
108
+ @parent = new_parent == self ? nil : new_parent # to avoid stack overflow at #depth
109
+ end
110
+
111
+ def be_parent_of!(*loggers)
112
+ loggers.each do |logger|
113
+ # XXX: Calling a private method because it is an internal procedure
114
+ logger.is_a?(SmartLoggerWrapper) ? logger.send(:set_parent!, self) : logger
115
+ end
116
+ end
117
+
118
+ def root?
119
+ parent == nil
120
+ end
121
+
101
122
  def method_missing(method_name, *args, &block)
102
- if Options.defined_option?(method_name)
103
- # If there is an defined option with the same name as the method name, return a new logger with the option.
104
- arg = args.first
123
+ if root? && Options.defined_option?(method_name)
124
+ # When the root wrapper receive an defined option with the same name as the method name,
125
+ # return a new logger wrapper with the option.
105
126
  @_loggers_cache[method_name] = {} unless @_loggers_cache.include?(method_name)
106
- new_logger = @_loggers_cache[method_name][arg] ||= clone.tap do |cloned|
107
- cloned.overwrite_options(method_name => arg)
108
- end
109
- return block.(new_logger) if block_given?
110
- new_logger
127
+ logger_with_option = @_loggers_cache[method_name][args] ||= with_option(method_name, *args)
128
+ return block.(logger_with_option) if block_given?
129
+ logger_with_option
111
130
  else
112
131
  super
113
132
  end
114
133
  end
134
+
135
+ def respond_to_missing?(method_name, includes_private)
136
+ root? && Options.defined_option?(method_name) || super
137
+ end
115
138
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smart_logger_wrapper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Akihiro Katsura
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-04-03 00:00:00.000000000 Z
11
+ date: 2018-05-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -61,6 +61,7 @@ extra_rdoc_files: []
61
61
  files:
62
62
  - ".gitignore"
63
63
  - ".rspec"
64
+ - ".ruby-version"
64
65
  - ".travis.yml"
65
66
  - Gemfile
66
67
  - LICENSE.txt
@@ -98,7 +99,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
98
99
  version: '0'
99
100
  requirements: []
100
101
  rubyforge_project:
101
- rubygems_version: 2.6.8
102
+ rubygems_version: 2.7.6
102
103
  signing_key:
103
104
  specification_version: 4
104
105
  summary: SmartLoggerWrapper adds some useful features to the Ruby Logger or a subclass.