batlog 0.9

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.
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require "./lib/log/version"
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = 'batlog'
6
+ s.version = Log::VERSION
7
+ s.homepage = "https://github.com/TheGiftsProject/batlog"
8
+ s.summary = "A structured logging system"
9
+ s.authors = ["Asaf Gartner", "Yonatan Bergman"]
10
+ s.email = 'agartner@ebay.com'
11
+ s.platform = Gem::Platform::RUBY
12
+
13
+ s.files = `git ls-files`.split("\n")
14
+ s.require_path = "lib"
15
+
16
+ s.add_dependency "rails"
17
+ s.add_development_dependency 'rake'
18
+ s.add_development_dependency 'rspec'
19
+
20
+ s.test_files = Dir.glob('spec/lib/*_spec.rb')
21
+
22
+ s.post_install_message = "Run 'rails generate db_log' and then migrate to start using the log with database backend"
23
+ end
@@ -0,0 +1 @@
1
+ .idea
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour --format documentation
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use 1.8.7@log --create
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 1.8.7
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
@@ -0,0 +1,102 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ batlog (0.9)
5
+ rails
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ actionmailer (3.2.8)
11
+ actionpack (= 3.2.8)
12
+ mail (~> 2.4.4)
13
+ actionpack (3.2.8)
14
+ activemodel (= 3.2.8)
15
+ activesupport (= 3.2.8)
16
+ builder (~> 3.0.0)
17
+ erubis (~> 2.7.0)
18
+ journey (~> 1.0.4)
19
+ rack (~> 1.4.0)
20
+ rack-cache (~> 1.2)
21
+ rack-test (~> 0.6.1)
22
+ sprockets (~> 2.1.3)
23
+ activemodel (3.2.8)
24
+ activesupport (= 3.2.8)
25
+ builder (~> 3.0.0)
26
+ activerecord (3.2.8)
27
+ activemodel (= 3.2.8)
28
+ activesupport (= 3.2.8)
29
+ arel (~> 3.0.2)
30
+ tzinfo (~> 0.3.29)
31
+ activeresource (3.2.8)
32
+ activemodel (= 3.2.8)
33
+ activesupport (= 3.2.8)
34
+ activesupport (3.2.8)
35
+ i18n (~> 0.6)
36
+ multi_json (~> 1.0)
37
+ arel (3.0.2)
38
+ builder (3.0.4)
39
+ diff-lcs (1.1.3)
40
+ erubis (2.7.0)
41
+ hike (1.2.1)
42
+ i18n (0.6.1)
43
+ journey (1.0.4)
44
+ json (1.7.5)
45
+ mail (2.4.4)
46
+ i18n (>= 0.4.0)
47
+ mime-types (~> 1.16)
48
+ treetop (~> 1.4.8)
49
+ mime-types (1.19)
50
+ multi_json (1.3.6)
51
+ polyglot (0.3.3)
52
+ rack (1.4.1)
53
+ rack-cache (1.2)
54
+ rack (>= 0.4)
55
+ rack-ssl (1.3.2)
56
+ rack
57
+ rack-test (0.6.2)
58
+ rack (>= 1.0)
59
+ rails (3.2.8)
60
+ actionmailer (= 3.2.8)
61
+ actionpack (= 3.2.8)
62
+ activerecord (= 3.2.8)
63
+ activeresource (= 3.2.8)
64
+ activesupport (= 3.2.8)
65
+ bundler (~> 1.0)
66
+ railties (= 3.2.8)
67
+ railties (3.2.8)
68
+ actionpack (= 3.2.8)
69
+ activesupport (= 3.2.8)
70
+ rack-ssl (~> 1.3.2)
71
+ rake (>= 0.8.7)
72
+ rdoc (~> 3.4)
73
+ thor (>= 0.14.6, < 2.0)
74
+ rake (0.9.2.2)
75
+ rdoc (3.12)
76
+ json (~> 1.4)
77
+ rspec (2.11.0)
78
+ rspec-core (~> 2.11.0)
79
+ rspec-expectations (~> 2.11.0)
80
+ rspec-mocks (~> 2.11.0)
81
+ rspec-core (2.11.1)
82
+ rspec-expectations (2.11.3)
83
+ diff-lcs (~> 1.1.3)
84
+ rspec-mocks (2.11.3)
85
+ sprockets (2.1.3)
86
+ hike (~> 1.2)
87
+ rack (~> 1.0)
88
+ tilt (~> 1.1, != 1.3.0)
89
+ thor (0.16.0)
90
+ tilt (1.3.3)
91
+ treetop (1.4.11)
92
+ polyglot
93
+ polyglot (>= 0.3.1)
94
+ tzinfo (0.3.33)
95
+
96
+ PLATFORMS
97
+ ruby
98
+
99
+ DEPENDENCIES
100
+ batlog!
101
+ rake
102
+ rspec
@@ -0,0 +1,112 @@
1
+ # BatLog v0.9
2
+ ![batlog-logo](https://dl.dropbox.com/u/7525692/batlog-withtext.png "BatLog")
3
+
4
+ ## Setup
5
+ To install BatLog into your Rails app just run `rails generate db_log` to create the db_log's table migration.
6
+ By default BatLog writes to three places - the database, the rails logger and exceptions to exceptional
7
+ You can overide this behaivour by providing your own loggers or changing the default logger in the configuration.
8
+
9
+ The cool thing about BatLog is that it adds the concept of `context` - your logs aren't one time message left by a deity but
10
+ are part of a flow of a user in specific system and circumstance. BatLog lets you collect a context for the current Thread
11
+ and dump it as part of the BatLog whenever needed be it in Exception and be it in debug prints.
12
+
13
+ ## Usage
14
+ ### Logging
15
+ ```ruby
16
+ Log.debug(message, context)
17
+ Log.info(message, context)
18
+ Log.warn(message, context)
19
+ Log.error(message, context)
20
+ Log.fatal(message, context)
21
+ ```
22
+ Use these to record logs. The method indicates the severity.
23
+ * `message` - The message to record to log. Can be of the following types:
24
+ * `String` - Will be used as-is.
25
+ * `Exception` - Message will be extracted from exception.
26
+ * `Log::LoggableError` - Message will be extracted from exception and data will be merged into context
27
+ * `context` - `Hash`. Extra data that's related to the message.
28
+
29
+ ### Events
30
+ Events are kept in memory (until clear_events is called or the thread exits) and
31
+ are only written when one of the above log methods is called.
32
+
33
+ ```ruby
34
+ Log.event(name, data)
35
+ ```
36
+ Adds an event to `Log`'s internal event array.
37
+ * `name` - `String`. The name of the event.
38
+ * `data` - `Hash`. Extra data that's related to the event.
39
+
40
+ ```ruby
41
+ Log.clear_events
42
+ ```
43
+ Empties the list of events.
44
+
45
+ ### Asserts
46
+ ```ruby
47
+ Log.assert(condition, message, context, options)
48
+ ```
49
+ * `condition` - `Boolean`. Indicates whether the condition you were testing succeeded. When `false` - triggers a log.
50
+ * `message` - Same as in the logging methods.
51
+ * `context` - Same as in the logging methods.
52
+ * `options` - `Hash`. Includes the following:
53
+ * `severity` - `Symbol`. The severity to use for logging when the condition fails. (Default: `:error`)
54
+ * `raise_error` - `Boolean`. Indicates whether the assert should raise an error if condition fails. (Default: `false`)
55
+
56
+ ### Configuring `Log`
57
+ To configure Log just create an initializer `config/initializers/log.rb`
58
+ There you can set the configuration of the log system.
59
+ ```ruby
60
+ Log.config.loggers << MyCustomLogger
61
+ # or
62
+ Log.config.loggers = [MyCustomLogger]
63
+ ```
64
+ `Log.config.loggers` is an array of the loggers that will be called by the log dispatch each time you write a log.
65
+ They will be called in the order they appear in the array.
66
+ Default: `[DBlogger, RailsLogger, ExceptionalLogger]`
67
+
68
+ ```ruby
69
+ Log.config,raise_on_failed_asserts = false #boolean
70
+ ```
71
+ Set to `true` if you want all asserts to raise an exception when they fail. Useful
72
+ when developing and testing.
73
+ Default: `false`
74
+
75
+ ```ruby
76
+ Log.config,raise_on_log_failure = false #boolean
77
+ ```
78
+ Set to true if you want to raise an exception when one of the loggers raises an
79
+ exception.
80
+ If this is `false`, `Log` will only try to log the failure using the loggers that
81
+ didn't fail.
82
+ Default: `false`
83
+
84
+ ### Creating a custom logger
85
+ A logger object needs to implement the following interface:
86
+ ```ruby
87
+ class YourLogger
88
+ def self.log(severity, message, context, events, metadata)
89
+ # record stuff here
90
+ end
91
+ end
92
+ ```
93
+
94
+ * `severity` - `Symbol`. The log level. One of the following: `debug`, `info`, `warn`, `error`, `fatal`
95
+ * `message` - `String`.
96
+ * `context` - `Hash`. Extra data that's related to the message.
97
+ * `events` - `Array`. A collection of all recorded events prior to the log call. (contents of the array covered in later section)
98
+ * `metadata` - `Hash`. Extra data related to the logging process, added by other loggers that ran prior to this one.
99
+
100
+ If your log method returns a `Hash`, it will be merged into metadata and sent to
101
+ all subsequent loggers.
102
+
103
+
104
+ ### Controller Support
105
+ Also included with BatLog is the log controller support to use it we recommend you add it to your ApplicationController
106
+ by adding these lines to it.
107
+ ```ruby
108
+ require 'log/controller_support'
109
+
110
+ include Log::ControllerSupport
111
+ ```
112
+ This does two things, first it adds a before_filter that captures as much data as it can into the log.context and also hooks for when a CSRF exception occurs
@@ -0,0 +1,3 @@
1
+ require 'rspec/core/rake_task'
2
+ RSpec::Core::RakeTask.new('spec')
3
+ task :default => :spec
@@ -0,0 +1,9 @@
1
+ module Log
2
+ class DbLog < ActiveRecord::Base
3
+ self.table_name = :logs
4
+ attr_accessible :severity, :message, :context
5
+
6
+ store :context, :accessors => [:environment]
7
+
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ require 'log/log'
2
+ require "log/engine"
3
+
4
+ module Log
5
+ end
@@ -0,0 +1,35 @@
1
+ require 'loggers/database_logger'
2
+ require 'loggers/rails_logger'
3
+
4
+ module Log
5
+ class Config
6
+
7
+ attr_accessor :raise_on_log_failure, :raise_on_failed_asserts, :loggers
8
+
9
+ def self.default
10
+ new.instance_eval {
11
+ @raise_on_log_failure = false
12
+ @raise_on_failed_asserts = false
13
+ @loggers = default_loggers
14
+
15
+ self
16
+ }
17
+ end
18
+
19
+ def default_loggers
20
+ loggers = []
21
+
22
+ loggers << DatabaseLogger
23
+ loggers << RailsLogger
24
+
25
+ if defined?(Exceptional)
26
+ require 'loggers/exceptional_logger'
27
+ loggers << ExceptionalLogger
28
+ end
29
+
30
+ loggers
31
+
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,43 @@
1
+ require 'active_support/concern'
2
+
3
+ module Log
4
+ module ControllerSupport
5
+
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ before_filter :set_log_context if respond_to? :before_filter
10
+ after_filter :clear_log_context if respond_to? :after_filter
11
+ alias_method_chain :handle_unverified_request, :log
12
+ end
13
+
14
+ def set_log_context
15
+ context = {}
16
+ context.merge!(
17
+ :url => request.url,
18
+ :user_agent => request.user_agent,
19
+ :ip => request.ip,
20
+ :remote_ip => request.remote_ip,
21
+ :referer => request.referer,
22
+ :environment => Rails.env.to_s,
23
+ :session_id => request.session_options[:id],
24
+ :session => session,
25
+ :params => params
26
+ )
27
+ context.merge!(:current_user => current_user.try(:id)) if respond_to? :current_user
28
+ context.merge!(more_context) if respond_to? :more_context
29
+ Log.clear_context
30
+ Log.context(context)
31
+ end
32
+
33
+ def clear_log_context
34
+ Log.clear_context
35
+ end
36
+
37
+ def handle_unverified_request_with_log
38
+ Log.warn("Can't verify CSRF token authenticity")
39
+ handle_unverified_request_without_log
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,57 @@
1
+ require 'log/loggable_error'
2
+
3
+ module Log
4
+ class Dispatcher
5
+ attr_accessor :raise_on_log_failure
6
+
7
+ def self.dispatch(severity, message, context = {}, events = [])
8
+ Dispatcher.new(Log.config.loggers).dispatch(severity, message, context, events)
9
+ end
10
+
11
+ def initialize(loggers)
12
+ Dispatcher.verify_loggers(config.loggers)
13
+ @loggers = loggers
14
+ end
15
+
16
+ def dispatch(severity, message, context={}, events=[])
17
+ raise ArgumentError.new("context must be Hash") unless context.kind_of?(Hash)
18
+
19
+ failed_loggers = {}
20
+ metadata = {}
21
+ @loggers.each do |logger|
22
+ begin
23
+ result = logger.log(severity, message, context, events, metadata)
24
+ metadata.merge!(result) if (result.kind_of?(Hash))
25
+ rescue => e
26
+ failed_loggers[logger.name] = e
27
+ end
28
+ end
29
+
30
+ handle_failed_loggers(failed_loggers)
31
+ end
32
+
33
+ private
34
+
35
+ def config
36
+ Log.config
37
+ end
38
+
39
+ def self.verify_loggers(loggers)
40
+ raise ArgumentError.new("Dispatcher requires an array of loggers") if (!loggers.kind_of?(Array))
41
+
42
+ loggers.each do |logger|
43
+ raise ArgumentError.new("Logger #{logger.name} doesn't implement self.log") if (!logger.respond_to?(:log))
44
+ end
45
+ end
46
+
47
+ def handle_failed_loggers(failed_loggers)
48
+ unless failed_loggers.empty?
49
+ logger_errors = failed_loggers.map{ |logger_name, logger_error| { logger_name => logger_error.message } }
50
+ working_loggers = @loggers.reject{ |logger| failed_loggers.keys.include?(logger.name) }
51
+ Dispatcher.new(working_loggers).dispatch(:error, "Loggers failed", :logger_errors => logger_errors)
52
+ raise LoggableError.new("Loggers failed", { :logger_errors => logger_errors }) if config.raise_on_log_failure
53
+ end
54
+ end
55
+
56
+ end
57
+ end
@@ -0,0 +1,8 @@
1
+ require "batlog"
2
+ require 'rails'
3
+
4
+ module Log
5
+ class Engine < ::Rails::Engine
6
+ #isolate_namespace Log
7
+ end
8
+ end
@@ -0,0 +1,19 @@
1
+ module Log
2
+ module Events
3
+ EVENTS_KEY = :log_events
4
+
5
+ def self.add(name, data)
6
+ Thread.current[EVENTS_KEY] = all.push({ :name => name, :data => data })
7
+ return self
8
+ end
9
+
10
+ def self.reset
11
+ Thread.current[EVENTS_KEY] = []
12
+ return self
13
+ end
14
+
15
+ def self.all
16
+ (Thread.current[EVENTS_KEY] || []).dup
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,80 @@
1
+ require 'log/config'
2
+ require 'log/dispatcher'
3
+ require 'log/events'
4
+ require 'log/loggable_error'
5
+ require 'log/utils'
6
+
7
+ module Log
8
+
9
+ SEVERITIES = {
10
+ :debug => 0,
11
+ :info => 1,
12
+ :warn => 2,
13
+ :error => 3,
14
+ :fatal => 4
15
+ }
16
+
17
+ def self.config
18
+ @config ||= Log::Config.default
19
+ end
20
+
21
+ # This generates the following interface for each severity:
22
+ # log.{severity}(message, context=nil)
23
+ # i.e. log.info("hello", {:subsystem => :user, :action => "user creation"})
24
+ SEVERITIES.each_key do |severity|
25
+ (class << self; self; end).send(:define_method, severity.to_s) do |*args|
26
+ message, ctx = *args
27
+ ctx = context.merge(ctx)
28
+ self.write(severity, message, ctx)
29
+ end
30
+ end
31
+
32
+ def self.assert(condition, message, context = {}, severity=:error, raise_error=nil)
33
+ if condition
34
+ return true
35
+ else
36
+ assert_failed(severity, message, context, raise_error)
37
+ return false
38
+ end
39
+ end
40
+
41
+ def self.event(name, data={})
42
+ Events.add(name, data)
43
+ end
44
+
45
+ def self.clear_events
46
+ Events.reset
47
+ end
48
+
49
+ def self.context(hash = {})
50
+ Thread.current[:log_context] ||= {}
51
+ Thread.current[:log_context].merge!(hash)
52
+ end
53
+
54
+ def self.clear_context
55
+ Thread.current[:log_context] = nil
56
+ end
57
+
58
+ private
59
+
60
+ def self.write(severity, message, context)
61
+ context = {} if context.nil? # This is different from setting a default. If a user passes nil, it'll be converted to {}.
62
+ context = handle_loggable_error(message, context)
63
+ Dispatcher.dispatch(severity, message, context, Events.all)
64
+ end
65
+
66
+ def self.handle_loggable_error(message, context)
67
+ if message.kind_of?(LoggableError)
68
+ context.merge(:error_data => message.data)
69
+ else
70
+ context
71
+ end
72
+ end
73
+
74
+ def self.assert_failed(severity, message, context, raise_error)
75
+ write(severity, message, context)
76
+ return if raise_error == false #specific if raise_error is false but config is on
77
+ raise LoggableError.new(message, context) if (raise_error || config.raise_on_failed_asserts)
78
+ end
79
+
80
+ end
@@ -0,0 +1,10 @@
1
+ module Log
2
+ class LoggableError < StandardError
3
+ attr_accessor :data
4
+
5
+ def initialize(message=nil, data=nil)
6
+ super(message)
7
+ @data = data
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,28 @@
1
+ module Log
2
+ class Utils
3
+ def self.pretty_print_exception(exception)
4
+ raise ArgumentError.new("must pass exception") if (!exception.kind_of? Exception)
5
+
6
+ # Code taken from C:\Ruby192\lib\ruby\gems\1.9.1\gems\actionpack-3.0.3\lib\action_dispatch\middleware\show_exceptions.rb
7
+ message = "\n#{exception.class} (#{exception.message}):\n"
8
+ message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
9
+ message << " " << (defined?(Rails) && Rails.respond_to?(:backtrace_cleaner) && exception.backtrace ?
10
+ Rails.backtrace_cleaner.clean(exception.backtrace) :
11
+ exception.backtrace).try(:join, "\n ").to_s
12
+
13
+ return message
14
+ end
15
+
16
+ def self.prepare_message(message, simple=false)
17
+ if message.kind_of? Exception
18
+ if simple
19
+ return message.message
20
+ else
21
+ return pretty_print_exception(message)
22
+ end
23
+ end
24
+
25
+ return message.to_s
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,3 @@
1
+ module Log
2
+ VERSION = 0.9
3
+ end
@@ -0,0 +1,28 @@
1
+ require 'log/utils'
2
+
3
+ class DatabaseLogger
4
+ def self.log(severity, message, context, events, metadata)
5
+ log = nil
6
+ context = context.merge({ :metadata => metadata }) unless metadata.empty?
7
+ message = Log::Utils.prepare_message(message, true)
8
+ # In order to prevent logs from being deleted by a rollback, we need to create a new database connection (a new transaction doesn't work).
9
+ # The only way to create a new database connection is to open a new thread.
10
+ # Connection must be manually closed at the end of the thread. This only closes the thread's connection, not the main one.
11
+ Thread.new do
12
+ begin
13
+ log = create_log(severity, message, context, events)
14
+ ensure
15
+ ActiveRecord::Base.connection.close # must be done manually on new threads
16
+ end
17
+ end.join # we need the result of this operation before moving on.
18
+ return { :db_log_id => log.id }
19
+ end
20
+
21
+ private
22
+
23
+ def self.create_log(severity, message, context, events)
24
+ Log::DbLog.create!(:severity => severity.to_s,
25
+ :message => message,
26
+ :context => context)
27
+ end
28
+ end
@@ -0,0 +1,19 @@
1
+ require 'log/log'
2
+ require 'exceptional'
3
+
4
+ class ExceptionalLogger
5
+ class HandleMessageError < StandardError; end
6
+
7
+ def self.log(severity, message, context, events, metadata)
8
+ context = context.merge(:severity => severity.to_s, :events => events, :metadata => metadata)
9
+ if message.kind_of?(Exception)
10
+ Exceptional.context(context)
11
+ Exceptional.handle(message)
12
+ Exceptional.clear!
13
+ elsif Log::SEVERITIES[severity] >= Log::SEVERITIES[:error]
14
+ Exceptional.rescue(message, context) do
15
+ raise HandleMessageError.new(message)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,49 @@
1
+ require 'log/utils'
2
+
3
+ class RailsLogger
4
+
5
+ NEW_LINE = "\r\n"
6
+
7
+ def self.log(severity, message, context, events, metadata)
8
+ text = build_log_text(severity, message, context, events, metadata)
9
+
10
+ logger = ::Rails.logger
11
+ begin
12
+ logger.send(severity.to_s, text)
13
+ rescue
14
+ logger.info(text) # chosen severity might not exist for file logger
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def self.build_log_text(severity, message, context, events, metadata)
21
+ lines = []
22
+ lines.push("======= #{severity.to_s.capitalize} =======")
23
+ lines.push("## Message: #{Log::Utils.prepare_message(message)}")
24
+ lines.push("##")
25
+
26
+ lines.push("## Events:")
27
+ events.each do |event|
28
+ lines.push("## #{event[:name]}: #{event[:data]}")
29
+ end
30
+ lines.push("##")
31
+
32
+ lines.push("## Context:")
33
+ context.each do |item|
34
+ lines.push("## #{item[0]}: #{item[1].inspect}")
35
+ end
36
+ lines.push("##")
37
+
38
+ lines.push("## Metadata:")
39
+ metadata.each do |item|
40
+ lines.push("## #{item[0]}: #{item[1].inspect}")
41
+ end
42
+
43
+ lines.push("========#{'=' * severity.to_s.length}========")
44
+
45
+ text = "#{NEW_LINE*2}"
46
+ text += "#{lines.join(NEW_LINE)}"
47
+ text += "#{NEW_LINE*2}"
48
+ end
49
+ end
@@ -0,0 +1,105 @@
1
+ require 'spec_helper'
2
+
3
+ module Log
4
+ describe Dispatcher do
5
+ class Logger1
6
+ def self.meta_data
7
+ { :some => "Hash" }
8
+ end
9
+
10
+ def self.log(severity, message, context_data, events, metadata)
11
+ meta_data
12
+ end
13
+ end
14
+
15
+ class Logger2
16
+ def self.log(severity, message, context_data, events, metadata)
17
+ end
18
+ end
19
+
20
+ class BadLogger
21
+ def self.error_message
22
+ "Bad logger is bad"
23
+ end
24
+
25
+ def self.log(severity, message, context_data, events, metadata)
26
+ raise error_message
27
+ end
28
+ end
29
+
30
+ describe "dispatch" do
31
+
32
+ before do
33
+ Log.config.loggers = [Logger1, Logger2]
34
+ end
35
+
36
+ let (:severity) { :error }
37
+ let (:message) { "test message" }
38
+ let (:context_data) { { :a => 2 } }
39
+ let (:events) { [{ :name => "bla", :data => { :b => 3 } }] }
40
+
41
+ def dispatch
42
+ Dispatcher.dispatch(severity, message, context_data, events)
43
+ end
44
+
45
+ it "calls each logger's log method in order" do
46
+ Logger1.should_receive(:log).ordered
47
+ Logger2.should_receive(:log).ordered
48
+ dispatch
49
+ end
50
+
51
+ it "passes the severity to the logger" do
52
+ Logger1.should_receive(:log).with(severity, anything, anything, anything, anything)
53
+ dispatch
54
+ end
55
+
56
+ it "passes the message to the logger" do
57
+ Logger1.should_receive(:log).with(anything, message, anything, anything, anything)
58
+ dispatch
59
+ end
60
+
61
+ it "passes the context to the logger" do
62
+ Logger1.should_receive(:log).with(anything, anything, context_data, anything, anything)
63
+ dispatch
64
+ end
65
+
66
+ it "passes the events to the logger" do
67
+ Logger1.should_receive(:log).with(anything, anything, anything, events, anything)
68
+ dispatch
69
+ end
70
+
71
+ it "passes the metadata returned from previous loggers" do
72
+ Logger2.should_receive(:log).with(anything, anything, anything, anything, Logger1.meta_data)
73
+ dispatch
74
+ end
75
+
76
+ context "when logger failed" do
77
+
78
+ before do
79
+ Log.config.loggers << BadLogger
80
+ end
81
+
82
+ it "logs an error using the loggers that worked" do
83
+ Logger1.should_receive(:log).twice
84
+ Logger2.should_receive(:log).twice
85
+ BadLogger.should_receive(:log).once.and_raise("log error")
86
+ dispatch
87
+ end
88
+
89
+ context "when set to raise error on log failure" do
90
+ before do
91
+ Log.config.raise_on_log_failure = true
92
+ end
93
+
94
+ it "raises an error" do
95
+ BadLogger.should_receive(:log).and_raise("log error")
96
+ expect {
97
+ dispatch
98
+ }.to raise_error
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+
105
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe Log::Events do
4
+ let(:test_name) { "Test name" }
5
+ let(:test_data) { { :a => 5 } }
6
+
7
+ it "adds new event to the events array" do
8
+ subject.add(test_name, test_data)
9
+ subject.all.last.should == { :name => test_name, :data => test_data }
10
+ end
11
+
12
+ it "clears the events array" do
13
+ subject.add(test_name, test_data)
14
+ subject.reset
15
+ subject.all.should == []
16
+ end
17
+
18
+ it "returns the whole events array" do
19
+ subject.add(test_name, test_data)
20
+ subject.add(test_name, test_data)
21
+ subject.add(test_name, test_data)
22
+ subject.all.count.should == 3
23
+ end
24
+ end
@@ -0,0 +1,155 @@
1
+ require 'spec_helper'
2
+
3
+ describe Log do
4
+ subject { Log }
5
+
6
+ let(:severity) { :error }
7
+ let(:test_message) { "Test message" }
8
+ let(:test_context) { { :foo => 2, :bar => "hello" } }
9
+
10
+ let(:event_name) { "Event name" }
11
+ let(:event_data) { { :ev_data => 5 } }
12
+
13
+ let(:error_context) { { :error => 10 } }
14
+ let(:error_message) { Log::LoggableError.new("Loggable Error Message", error_context) }
15
+
16
+ before(:each) do
17
+ subject.clear_events
18
+ end
19
+
20
+ describe "log.writes" do
21
+ shared_examples_for "log severity" do |severity|
22
+ context "when message is a LoggableError" do
23
+ it "adds the log data to the context" do
24
+ new_context = subject.send(:handle_loggable_error, error_message, test_context)
25
+ new_context.should == test_context.merge(:error_data => error_context)
26
+ end
27
+
28
+ it "adds the error's data to the context" do
29
+ Log::Dispatcher.should_receive(:dispatch).with(severity, error_message, test_context.merge(:error_data => error_context), Log::Events.all)
30
+ subject.send(severity, error_message, test_context)
31
+ end
32
+ end
33
+
34
+ it "dispatches the log data to the loggers" do
35
+ Log::Dispatcher.should_receive(:dispatch).with(severity, test_message, test_context, Log::Events.all)
36
+ subject.send(severity, test_message, test_context)
37
+ end
38
+ end
39
+
40
+ Log::SEVERITIES.each_key do |severity|
41
+ describe severity do
42
+ it_behaves_like "log severity", severity
43
+ end
44
+ end
45
+ end
46
+
47
+ describe "log.asserts" do
48
+
49
+ describe "assert" do
50
+
51
+ context "when condition is true" do
52
+ it "returns true" do
53
+ subject.assert(true, test_message, test_context).should == true
54
+ end
55
+
56
+ it "doesn't fail" do
57
+ subject.should_not_receive(:assert_failed)
58
+ subject.assert(true, test_message, test_context)
59
+ end
60
+ end
61
+
62
+ context "when condition is false" do
63
+ it "fails" do
64
+ subject.should_receive(:assert_failed)
65
+ subject.assert(false, test_message, test_context)
66
+ end
67
+
68
+ it "returns false" do
69
+ subject.stub(:assert_failed)
70
+ subject.assert(false, test_message, test_context).should == false
71
+ end
72
+ end
73
+ end
74
+
75
+ describe "assert_failed" do
76
+
77
+ before do
78
+ Log.config.raise_on_log_failure = false
79
+ end
80
+
81
+ it "writes to log" do
82
+ subject.should_receive(:write).with(severity, test_message, test_context)
83
+ subject.send(:assert_failed, severity, test_message, test_context, false)
84
+ end
85
+
86
+ def should_raise_an_error(raise_error_flag)
87
+ expect {
88
+ subject.send(:assert_failed, severity, test_message, test_context, raise_error_flag)
89
+ }.to raise_error(Log::LoggableError)
90
+ end
91
+
92
+ def should_not_raise_an_error(raise_error_flag)
93
+ expect {
94
+ subject.send(:assert_failed, severity, test_message, test_context, raise_error_flag)
95
+ }.not_to raise_error
96
+ end
97
+
98
+ context "config is false" do
99
+
100
+ before do
101
+ subject.config.raise_on_failed_asserts = false
102
+ end
103
+
104
+ it "should raise error when flag is true" do
105
+ should_raise_an_error(true)
106
+ end
107
+
108
+ it "should not raise error when flag is nil" do
109
+ should_not_raise_an_error(nil)
110
+ end
111
+
112
+ it "should not raise error when flag is false" do
113
+ should_not_raise_an_error(false)
114
+ end
115
+ end
116
+
117
+ context "config is true" do
118
+
119
+ before do
120
+ subject.config.raise_on_failed_asserts = true
121
+ end
122
+
123
+ it "should raise error when flag is true" do
124
+ should_raise_an_error(true)
125
+ end
126
+
127
+ it "should raise error when flag is nil" do
128
+ should_raise_an_error(nil)
129
+ end
130
+
131
+ it "should not raise error when flag is false" do
132
+ should_not_raise_an_error(false)
133
+ end
134
+
135
+ end
136
+ end
137
+ end
138
+
139
+ describe "write events" do
140
+ describe "event" do
141
+ it "adds an event to the events array" do
142
+ Log::Events.should_receive(:add).with(event_name, event_data)
143
+ subject.event(event_name, event_data)
144
+ end
145
+ end
146
+
147
+ describe "clear_events" do
148
+ it "resets the events array" do
149
+ Log::Events.should_receive(:reset)
150
+ subject.clear_events
151
+ end
152
+ end
153
+ end
154
+
155
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+ #require 'lib/log/loggable_error'
3
+
4
+ describe Log::LoggableError do
5
+ subject { Log::LoggableError }
6
+
7
+ let (:test_message) { "test message" }
8
+ let (:test_data) { { :a => 2 } }
9
+
10
+ it "contains the error data" do
11
+ error = subject.new(test_message, test_data)
12
+ error.data.should == test_data
13
+ end
14
+ end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'batlog'
4
+
5
+
6
+ RSpec.configure do |config|
7
+ config.treat_symbols_as_metadata_keys_with_true_values = true
8
+ config.run_all_when_everything_filtered = true
9
+ config.filter_run :focus
10
+ end
metadata ADDED
@@ -0,0 +1,136 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: batlog
3
+ version: !ruby/object:Gem::Version
4
+ hash: 25
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 9
9
+ version: "0.9"
10
+ platform: ruby
11
+ authors:
12
+ - Asaf Gartner
13
+ - Yonatan Bergman
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-10-22 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rails
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: rake
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 3
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ type: :development
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: rspec
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ hash: 3
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ type: :development
61
+ version_requirements: *id003
62
+ description:
63
+ email: agartner@ebay.com
64
+ executables: []
65
+
66
+ extensions: []
67
+
68
+ extra_rdoc_files: []
69
+
70
+ files:
71
+ - .gemspec
72
+ - .gitignore
73
+ - .rspec
74
+ - .rvmrc
75
+ - .travis.yml
76
+ - Gemfile
77
+ - Gemfile.lock
78
+ - README.md
79
+ - Rakefile
80
+ - app/models/log/db_log.rb
81
+ - lib/batlog.rb
82
+ - lib/log/config.rb
83
+ - lib/log/controller_support.rb
84
+ - lib/log/dispatcher.rb
85
+ - lib/log/engine.rb
86
+ - lib/log/events.rb
87
+ - lib/log/log.rb
88
+ - lib/log/loggable_error.rb
89
+ - lib/log/utils.rb
90
+ - lib/log/version.rb
91
+ - lib/loggers/database_logger.rb
92
+ - lib/loggers/exceptional_logger.rb
93
+ - lib/loggers/rails_logger.rb
94
+ - spec/lib/dispatcher_spec.rb
95
+ - spec/lib/events_spec.rb
96
+ - spec/lib/log_spec.rb
97
+ - spec/lib/loggable_error_spec.rb
98
+ - spec/spec_helper.rb
99
+ homepage: https://github.com/TheGiftsProject/batlog
100
+ licenses: []
101
+
102
+ post_install_message: Run 'rails generate db_log' and then migrate to start using the log with database backend
103
+ rdoc_options: []
104
+
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ none: false
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ hash: 3
113
+ segments:
114
+ - 0
115
+ version: "0"
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ none: false
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ hash: 3
122
+ segments:
123
+ - 0
124
+ version: "0"
125
+ requirements: []
126
+
127
+ rubyforge_project:
128
+ rubygems_version: 1.8.10
129
+ signing_key:
130
+ specification_version: 3
131
+ summary: A structured logging system
132
+ test_files:
133
+ - spec/lib/dispatcher_spec.rb
134
+ - spec/lib/events_spec.rb
135
+ - spec/lib/log_spec.rb
136
+ - spec/lib/loggable_error_spec.rb