yeti_logger 3.0.0

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