batlog 0.9

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