smart_logger_wrapper 0.4.3 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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.