yeti_logger 3.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3e5e9a8554d3123f83bd27c2cefe1663025fa9b9
4
+ data.tar.gz: 8f79e414571cbc0c5815eb5ce888892be8114231
5
+ SHA512:
6
+ metadata.gz: 3fa2b8fedb91ee5f6cb0d0dcf41b4219cc16a5c98092c574ee55576d2c1ad9e0ce702c2d6e05c87af6445fbf80016e8f4e9134afc0d210a7554728fc8e99432a
7
+ data.tar.gz: 61e988b8d18bc89954b49cbd49a6d4d288280bc53312f9507f15e7404ac3e828f0fd433548599005ca49f8136c41caf02c47db2bdafb12f230dc65ae0823ebf4
data/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+ Gemfile.lock
15
+ .idea/
16
+
17
+ # YARD artifacts
18
+ .yardoc
19
+ _yardoc
20
+ doc/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-2.2.2
data/.travis.yml ADDED
@@ -0,0 +1,18 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 1.9.3
5
+ - 2.0
6
+ - 2.1
7
+ - 2.2
8
+ - jruby-19mode
9
+
10
+ script:
11
+ - bundle exec rake ci
12
+
13
+ sudo: false
14
+
15
+ env:
16
+ global:
17
+ # To allow JRUBY to install c-extension gems
18
+ - "JRUBY_OPTS=-Xcext.enabled=true"
data/CHANGELOG.md ADDED
@@ -0,0 +1,4 @@
1
+ # yeti_logger changelog
2
+
3
+ ## v3.0.0
4
+ - First public release
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in yeti_logger.gemspec
4
+ gemspec
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2013-2015 Yesware, Inc
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SaALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,193 @@
1
+ # yeti_logger
2
+
3
+ Provides standardized logging across Yesware apps.
4
+
5
+ [![Build Status](https://travis-ci.org/Yesware/yeti_logger.svg?branch=master)](https://travis-ci.org/Yesware/yeti_logger)
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'yeti_logger'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install yeti_logger
20
+
21
+ ## Usage
22
+
23
+ ### Initialization
24
+
25
+ To use YetiLogger within an app it must be configured with a logger. For example
26
+ in a Rails application, create an initializer such as
27
+ `config/initializers/yeti_logger.rb`:
28
+
29
+ require 'yeti_logger'
30
+
31
+ YetiLogger.configure do |config|
32
+ config.logger = Rails.logger
33
+ end
34
+
35
+ ### Logging for Classes
36
+
37
+ In classes where you want to use YetiLogger you must include the module:
38
+
39
+ include YetiLogger
40
+
41
+ This will define `log_error`, `log_warn`, `log_info`, and `log_debug` methods
42
+ at both the class and instance level.
43
+
44
+ Each method has a similar signature:
45
+
46
+ log_info(obj = nil, exception = nil, &block)
47
+
48
+ Each of these arguments is optional. Passing no args results in a blank log
49
+ message.
50
+
51
+ All log messages are automatically prefixed with the name of the class.
52
+
53
+ Exceptions will always be logged as their message and a backtrace. If you
54
+ would like a message along with the exception, use this form:
55
+
56
+ log_info("My messsage", exception)
57
+
58
+ If you only need the exception, then use the block form above:
59
+
60
+ log_info { exception }
61
+
62
+ In situations where a logger object is required, the `#as_logger` instance
63
+ method can be used to return an object that responds to `error`, `warn`, `info`,
64
+ and `debug`, and forwards to the instance to format log messages using
65
+ `YetiLogger`:
66
+
67
+ logger = instance.as_logger
68
+
69
+ # The following result in equivalent log messages
70
+ logger.info('this message')
71
+ instance.log_info('this message')
72
+
73
+ ### Preferred use
74
+
75
+ The preferred way to use `YetiLogger` is via the block as it defers evaluation
76
+ of the string until we've decided whether or not to log the message. Along with
77
+ blocks, passing data in as a hash is also preferred.
78
+
79
+ log_debug { { system: "dagobah", jedi: expensive_to_compute() } }
80
+
81
+ log_debug({ system: "dagobah", jedi: expensive_to_compute() })
82
+
83
+ Both of these will result in a log message formatted with `key=value` pairs
84
+ separated by whitespace. The latter will call the `expensive_to_compute()`
85
+ method prior to entry into the `log_debug` function meaning it will be computed
86
+ whether or not the log statement is actually written out.
87
+
88
+ The block format does not support separate arguments for exception and
89
+ non-exception data. If you need both, either use the block format and use the
90
+ functions in `YetiLogger::MessageFormatters` to format the exception, or use the
91
+ `(obj, exception)` arguments taking note of any performance implications in
92
+ building the log message.
93
+
94
+ ### Message formatting
95
+
96
+ The value passed in for obj or returned by the block will be formatted
97
+ depending on the content of it. If it is a hash, it will be formatted into
98
+ "key=value" pairs separated by whitespace. Any value that needs to be quoted
99
+ (embedded quotes, or has whitespace), will be quoted and embedded quotes
100
+ escaped.
101
+
102
+ Formatting of exceptions is dependent on the data type of the obj argument. If
103
+ it is a string, then a string form of the exception details is included. If obj
104
+ is a hash, then the exception in injected into the hash and printed as
105
+ additional `key=value` pairs. Classname, message and backtrace are included in
106
+ the message.
107
+
108
+ ### Nested Hashes
109
+
110
+ For hash logging, each key and value are converted to strings which means
111
+ nested hashes might not serialize like you would think. Additionally, no
112
+ quotes are provided around keys or values, meaning for hashes that contain
113
+ data that may include whitespace, it might make sense to pass in a serialized
114
+ form of the hash instead of the hash itself. If you would like to override
115
+ this behavior, pass in the serialized format for the hash, such as:
116
+
117
+ log_info { hash.to_json }
118
+ log_info { hash.to_s }
119
+ log_info { hash.to_my_log_format }
120
+
121
+ ## Test Support
122
+
123
+ There are a couple helpers provided to support testing of YetiLogger calls. All
124
+ helpers are available by requiring the relevant file and importing the module:
125
+
126
+ require 'yeti_logger/test_helper'
127
+
128
+ describe MyClass do
129
+ include YetiLogger::TestHelper
130
+
131
+ # tests here
132
+ end
133
+
134
+ The simpler form sets up an expectation that the log level method in YetiLogger
135
+ will be called. It returns that expectation and you can extend it with your
136
+ preferred matcher:
137
+
138
+ ...
139
+ should_log(:info).with("exact message here")
140
+ ...
141
+ should_log(:warn).with(/a pattern/)
142
+ ...
143
+
144
+ If you have an application that produces many lines of log messages at any one
145
+ level, this can be cumbersome so `YetiLogger::TestHelper` provides methods for
146
+ you to set up expectation to see specific log messages amongst all of the
147
+ messages it may receive:
148
+
149
+ messages = [
150
+ 'YourClass: one',
151
+ /match a regex!/,
152
+ 'YourOtherClass: three'
153
+ ]
154
+
155
+ expect_to_see_log_messages(messages, :info) do
156
+ trigger_code_that_results_in_logging
157
+ end
158
+
159
+ There is also a singular form of this `expect_to_see_log_message` that takes a
160
+ single message to match.
161
+
162
+ If you have code that logs at a level below your threshold set during testing
163
+ you may have hidden bugs. Consider the following:
164
+
165
+ def my_method
166
+ ...
167
+ log_debug do
168
+ compute_log_message(arg1, arg2)
169
+ end
170
+ ...
171
+ end
172
+
173
+ If in test you set your log level to be warn, then this block will never
174
+ execute. Now, if this method were recently renamed, or another argument was
175
+ added, you would have a runtime error just waiting for someone to set the log
176
+ level down to debug. If you want to temporarily raise the log level for a given
177
+ test, you can do so:
178
+
179
+ with_log_level(:debug) do
180
+ should_log(:debug).with("expected message")
181
+ my_method()
182
+ end
183
+
184
+ Once the block is finished, the log level will be returned to whatever level you
185
+ had it previously.
186
+
187
+ ## Contributing
188
+
189
+ 1. Fork it
190
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
191
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
192
+ 4. Push to the branch (`git push origin my-new-feature`)
193
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+
8
+ require 'yard'
9
+ YARD::Rake::YardocTask.new
10
+
11
+ # All-in-one target for CI servers to run.
12
+ task :ci => ['spec']
@@ -0,0 +1,164 @@
1
+ require 'logger'
2
+ require 'yeti_logger/version'
3
+ require 'yeti_logger/constants'
4
+ require 'yeti_logger/configuration'
5
+ require 'yeti_logger/wrapped_logger'
6
+ require 'yeti_logger/message_formatters'
7
+ require 'active_support/core_ext/benchmark'
8
+ require 'active_support/concern'
9
+ require 'active_support/core_ext/object/blank'
10
+ require 'active_support/core_ext/object/try'
11
+
12
+ # Mixin module providing Yesware logging functionality including formatting of
13
+ # log message data (exceptions, hashes, etc). Refer to the Readme for further
14
+ # information and examples.
15
+ #
16
+ # Module you can include in your class to get logging with class name prefixing.
17
+ # This will also log hashes as key=value pairs and exception backtraces. These
18
+ # methods are added via metaprogramming. When it's done, you'll have methods to
19
+ # log at each level:
20
+ # - debug
21
+ # - info
22
+ # - warn
23
+ # - error
24
+ #
25
+ # Each method will have a signature that looks like:
26
+ # log_info(obj = nil, exception = nil, &block)
27
+ #
28
+ # Each of these arguments is optional. Pass no arguments results in a blank line
29
+ # being logged (with a classname prefix).
30
+ #
31
+ # The ideal usage for YetiLogger is to pass in blocks:
32
+ #
33
+ # log_info { "Here is my message with #{some} class #{values}" }
34
+ #
35
+ # This will defer evaluation of the string until the logger has determined if
36
+ # the current log level is high enough to warrant evaluating the string and
37
+ # logging it.
38
+ #
39
+ # Exceptions will be logged as a combination of the message, the class and some
40
+ # number of lines of the backtrace. If you pass in a value for obj that is a
41
+ # Hash, then the exception will be injected into the hash.
42
+ #
43
+ # log_info("My message", exception)
44
+ #
45
+ # If you only need the exception, then use the block form above:
46
+ #
47
+ # log_info { exception }
48
+ #
49
+ # The value passed in for obj or returned by the block will be formatted
50
+ # depending on the content of it. If it is a hash, we will format into
51
+ # "key=value" pairs separated by whitespace. If the value is an exception, the
52
+ # message will be logged along with the backtrace. All other objects are
53
+ # converted to string via the to_s method.
54
+ #
55
+ # For hash logging, each key and value are converted to strings which means
56
+ # nested hashes might not serialize like you would think. Additionally, no
57
+ # quotes are provided around keys or values, meaning for hashes that contain
58
+ # data that may include whitespace, it might make sense to pass in a serialized
59
+ # form of the hash instead of the hash itself. If you would like to override
60
+ # this behavior, pass in the serialized format for the hash, such as:
61
+ #
62
+ # log_info { hash.to_json }
63
+ # log_info { hash.to_s }
64
+ # log_info { hash.to_my_log_format }
65
+ #
66
+ #
67
+ module YetiLogger
68
+ extend ActiveSupport::Concern
69
+
70
+ # This module contains log method definitions that are used at both the
71
+ # class and the instance level.
72
+ # Each log method is defined explicitly, despite the obvious repetition,
73
+ # to avoid the cost of creating a Proc for any, possibly unused, block
74
+ # passed to the log method.
75
+ # Define these methods explicitly allows the use of yield.
76
+ module LogMethods
77
+ def log_debug(obj = nil, ex = nil)
78
+ if YetiLogger.logger.level <= Logger::DEBUG
79
+ msg = if block_given?
80
+ MessageFormatters.build_log_message(log_class_name, yield)
81
+ else
82
+ MessageFormatters.build_log_message(log_class_name, obj, ex)
83
+ end
84
+ YetiLogger.logger.send(:debug, msg)
85
+ end
86
+ end
87
+
88
+ def log_info(obj = nil, ex = nil)
89
+ if YetiLogger.logger.level <= Logger::INFO
90
+ msg = if block_given?
91
+ MessageFormatters.build_log_message(log_class_name, yield)
92
+ else
93
+ MessageFormatters.build_log_message(log_class_name, obj, ex)
94
+ end
95
+ YetiLogger.logger.send(:info, msg)
96
+ end
97
+ end
98
+
99
+ def log_warn(obj = nil, ex = nil)
100
+ if YetiLogger.logger.level <= Logger::WARN
101
+ msg = if block_given?
102
+ MessageFormatters.build_log_message(log_class_name, yield)
103
+ else
104
+ MessageFormatters.build_log_message(log_class_name, obj, ex)
105
+ end
106
+ YetiLogger.logger.send(:warn, msg)
107
+ end
108
+ end
109
+
110
+ def log_error(obj = nil, ex = nil)
111
+ if YetiLogger.logger.level <= Logger::ERROR
112
+ msg = if block_given?
113
+ MessageFormatters.build_log_message(log_class_name, yield)
114
+ else
115
+ MessageFormatters.build_log_message(log_class_name, obj, ex)
116
+ end
117
+ YetiLogger.logger.send(:error, msg)
118
+ end
119
+ end
120
+
121
+ def log_fatal(obj = nil, ex = nil)
122
+ if YetiLogger.logger.level <= Logger::FATAL
123
+ msg = if block_given?
124
+ MessageFormatters.build_log_message(log_class_name, yield)
125
+ else
126
+ MessageFormatters.build_log_message(log_class_name, obj, ex)
127
+ end
128
+ YetiLogger.logger.send(:fatal, msg)
129
+ end
130
+ end
131
+ end
132
+
133
+ # Class-level methods.
134
+ module ClassMethods
135
+ include LogMethods
136
+
137
+ def log_class_name
138
+ self.name
139
+ end
140
+ end
141
+
142
+ # Instance-level log methods
143
+ include LogMethods
144
+
145
+ def log_class_name
146
+ self.class.name
147
+ end
148
+
149
+ def log_time(action, level = :info)
150
+ ms = Benchmark.ms do
151
+ yield
152
+ end
153
+ YetiLogger.logger.send(level,
154
+ MessageFormatters.build_log_message(self.class.name,
155
+ { action: action,
156
+ time_ms: ms.to_i }))
157
+ end
158
+
159
+ # Wrap self in an object that responds to :info, :warn, :error, :debug, etc.
160
+ # @return [YetiLogger::WrapperLogger]
161
+ def as_logger
162
+ YetiLogger::WrappedLogger.new(self)
163
+ end
164
+ end