semantic_logger 2.8.0 → 2.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,16 +3,17 @@ require 'semantic_logger/version'
3
3
  require 'semantic_logger/semantic_logger'
4
4
 
5
5
  module SemanticLogger
6
- autoload :Base, 'semantic_logger/base'
7
- autoload :Logger, 'semantic_logger/logger'
8
- autoload :Loggable, 'semantic_logger/loggable'
6
+ autoload :Base, 'semantic_logger/base'
7
+ autoload :Logger, 'semantic_logger/logger'
8
+ autoload :Loggable, 'semantic_logger/loggable'
9
9
 
10
10
  module Appender
11
- autoload :Base, 'semantic_logger/appender/base'
12
- autoload :File, 'semantic_logger/appender/file'
13
- autoload :Wrapper, 'semantic_logger/appender/wrapper'
14
- autoload :MongoDB, 'semantic_logger/appender/mongodb'
15
- autoload :Syslog, 'semantic_logger/appender/syslog'
11
+ autoload :Base, 'semantic_logger/appender/base'
12
+ autoload :File, 'semantic_logger/appender/file'
13
+ autoload :Wrapper, 'semantic_logger/appender/wrapper'
14
+ autoload :MongoDB, 'semantic_logger/appender/mongodb'
15
+ autoload :Syslog, 'semantic_logger/appender/syslog'
16
+ autoload :NewRelic, 'semantic_logger/appender/new_relic'
16
17
  end
17
18
  end
18
19
 
@@ -77,6 +77,10 @@ module SemanticLogger
77
77
  end
78
78
  end
79
79
 
80
+ def flush
81
+ # An appender can implement a flush method if it supports it.
82
+ end
83
+
80
84
  ############################################################################
81
85
  protected
82
86
 
@@ -141,7 +141,7 @@ module SemanticLogger
141
141
  document = {
142
142
  :time => log.time,
143
143
  :host_name => host_name,
144
- :pid => $PID,
144
+ :pid => $$,
145
145
  :thread_name => log.thread_name,
146
146
  :name => log.name,
147
147
  :level => log.level,
@@ -0,0 +1,105 @@
1
+ =begin
2
+ New Relic appender for SemanticLogger
3
+
4
+ The :error and :fatal log entries will show up under Applications > "Application Name" > Events > Errors in New Relic
5
+
6
+ Note: Payload information is not filtered, so take care not to push any sensitive information when logging with tags or a payload.
7
+
8
+
9
+ Example 1
10
+
11
+ Adding the New Relic appender will send :error and :fatal log entries to New Relic as error events.
12
+
13
+ For a Rails application already configured to use SemanticLogger and New Relic, create a file called <Rails Root>/config/initializers/newrelic_appender.rb with the following contents and restart the application:
14
+
15
+ # Send :error and :fatal log messages to New Relic
16
+ SemanticLogger.add_appender(SemanticLogger::Appender::NewRelic.new)
17
+ Rails.logger.info 'SemanticLogger New Relic Appender added.'
18
+
19
+
20
+ Example 2
21
+
22
+ For a non-Rails application, send :info and more severe log entries to a file called application.log and also send :error and :fatal log entries to New Relic.
23
+
24
+ # ./newrelic.yml needs to be set up -- see https://docs.newrelic.com/docs/ruby/ruby-agent-installation for more information.
25
+
26
+ require 'semantic_logger'
27
+ require 'newrelic_rpm'
28
+
29
+ # New Relic setup
30
+ NewRelic::Agent.manual_start
31
+
32
+ # SemanticLogger setup
33
+ SemanticLogger.default_level = :info
34
+ SemanticLogger.add_appender('application.log')
35
+ SemanticLogger.add_appender(SemanticLogger::Appender::NewRelic.new)
36
+ logger = SemanticLogger['Example']
37
+
38
+ # Log some messages
39
+ logger.info 'This is only written to application.log'
40
+ logger.error 'This is written to application.log and will also be sent to New Relic as an error event'
41
+
42
+ # The appender will send tags, payloads and benchmark duration to New Relic
43
+ logger.tagged('test') do
44
+ logger.with_payload( {key1: 123, key2: 'abc'} ) do
45
+ logger.benchmark_error(@message) do
46
+ sleep 0.001
47
+ end
48
+ end
49
+ end
50
+
51
+ # New Relic does not seem to receive any errors if the application exits too soon after sending error alerts.
52
+ sleep 10
53
+
54
+ # New Relic shutdown - should send any queued data before exiting
55
+ ::NewRelic::Agent.shutdown
56
+ =end
57
+
58
+
59
+ require 'newrelic_rpm'
60
+
61
+ class SemanticLogger::Appender::NewRelic < SemanticLogger::Appender::Base
62
+
63
+ # Allow the level for this appender to be overwritten
64
+ # Default: :error
65
+ # Note: Not recommended to set the log level to :info, :debug, or :trace as that would flood NewRelic with Error notices
66
+ def initialize(level=:error,&block)
67
+ # Pass on the level and custom formatter if supplied
68
+ super(level, &block)
69
+ end
70
+
71
+ # The application may send a multiline string to the appender... use the first non-blank line as a shorter message.
72
+ def self.first_non_empty_line(string)
73
+ string.strip.split("\n").first.to_s
74
+ end
75
+
76
+ # Returns [Hash] of parameters to send to New Relic.
77
+ def default_formatter
78
+ Proc.new do |log|
79
+ short_message = self.class.first_non_empty_line(log.message)
80
+ metric = log.metric || "#{log.name}/#{short_message}"
81
+
82
+ custom_params = { :thread_name => log.thread_name }
83
+ # Only show the message under custom attributes if the error message uses an exception or shortened message (first non-empty line).
84
+ custom_params[:message] = log.message if log.message && (log.exception || log.message != short_message)
85
+ custom_params[:duration] = "#{log.duration} ms" if log.duration
86
+ custom_params[:payload] = log.payload if log.payload
87
+ custom_params[:tags] = log.tags if log.tags && (log.tags.size > 0)
88
+
89
+ { :metric => metric, :custom_params => custom_params }
90
+ end
91
+ end
92
+
93
+ # Send an error notification to New Relic
94
+ def log(log)
95
+ # Ensure minimum log level is met.
96
+ return false unless level_index <= (log.level_index || 0)
97
+
98
+ # For more documentation on the NewRelic::Agent.notice_error method see:
99
+ # http://rubydoc.info/github/newrelic/rpm/NewRelic/Agent#notice_error-instance_method
100
+ # and https://docs.newrelic.com/docs/ruby/ruby-agent-api
101
+ NewRelic::Agent.notice_error(log.exception || self.class.first_non_empty_line(log.message), formatter.call(log))
102
+ return true
103
+ end
104
+
105
+ end
@@ -18,7 +18,7 @@ module SemanticLogger
18
18
  # SemanticLogger.default_level
19
19
  #
20
20
  # Must be one of the values in SemanticLogger::LEVELS, or
21
- # nil if this logger instance should use the global default log_level
21
+ # nil if this logger instance should use the global default level
22
22
  def level=(level)
23
23
  @level_index = SemanticLogger.level_to_index(level)
24
24
  @level = level
@@ -263,6 +263,9 @@ module SemanticLogger
263
263
  # level_index
264
264
  # Internal index of the log level
265
265
  #
266
+ # exception
267
+ # Ruby Exception object to log
268
+ #
266
269
  # metric [Object]
267
270
  # Object supplied when benchmark_x was called
268
271
  Log = Struct.new(:level, :thread_name, :name, :message, :payload, :time, :duration, :tags, :level_index, :exception, :metric)
@@ -47,9 +47,9 @@ module SemanticLogger
47
47
  # Ruby built-in Logger, or any logger that implements the following methods:
48
48
  # :debug, :info, :warn, :error, :fatal
49
49
  #
50
- # log_level [Symbol]
50
+ # level [Symbol]
51
51
  # Optional
52
- # By setting the log_level higher than the SemanticLogger::default_level
52
+ # By setting the level higher than the SemanticLogger::default_level
53
53
  # this appender can exclude lower level log messages
54
54
  # Any one of SemanticLogger::LEVELS. For example: :trace, :debug, :info, :warn, :error, :fatal
55
55
  #
@@ -82,13 +82,13 @@ module SemanticLogger
82
82
  # logger.info "Hello World"
83
83
  # logger.debug("Login time", :user => 'Joe', :duration => 100, :ip_address=>'127.0.0.1')
84
84
  #
85
- def self.add_appender(appender, log_level=nil, &block)
85
+ def self.add_appender(appender, level=nil, &block)
86
86
  appender_instance = if appender.is_a?(String) || appender.is_a?(IO)
87
87
  # $stderr, STDOUT, other IO, or a filename
88
- SemanticLogger::Appender::File.new(appender, log_level, &block)
88
+ SemanticLogger::Appender::File.new(appender, level, &block)
89
89
  elsif appender.is_a? Appender::Base
90
90
  # Already an instance of an appender
91
- appender.log_level = log_level if log_level
91
+ appender.level = level if level
92
92
  appender.formatter = block if block
93
93
  appender
94
94
  else
@@ -97,7 +97,7 @@ module SemanticLogger
97
97
  raise "Supplied appender does not implement:#{does_not_implement}. It must implement all of #{LEVELS[1..-1].inspect}"
98
98
  end
99
99
 
100
- raise "Change the log level to #{log_level}, update the log level directly against the supplied appender" if log_level
100
+ raise "Change the log level to #{level}, update the log level directly against the supplied appender" if level
101
101
  SemanticLogger::Appender::Wrapper.new(appender, &block)
102
102
  end
103
103
  @@appenders << appender_instance
@@ -192,7 +192,7 @@ module SemanticLogger
192
192
  # Also supports mapping the ::Logger levels to SemanticLogger levels
193
193
  def self.level_to_index(level)
194
194
  return if level.nil?
195
-
195
+
196
196
  index = if level.is_a?(Symbol)
197
197
  LEVELS.index(level)
198
198
  elsif level.is_a?(String)
@@ -216,4 +216,4 @@ module SemanticLogger
216
216
  # Initial default Level for all new instances of SemanticLogger::Logger
217
217
  @@default_level = :info
218
218
  @@default_level_index = level_to_index(@@default_level)
219
- end
219
+ end
@@ -1,3 +1,3 @@
1
1
  module SemanticLogger #:nodoc
2
- VERSION = "2.8.0"
2
+ VERSION = "2.9.0"
3
3
  end
@@ -37,7 +37,7 @@ class AppenderMongoDBTest < Test::Unit::TestCase
37
37
  assert_equal nil, document['thread_name']
38
38
  assert_equal nil, document['time']
39
39
  assert_equal nil, document['payload']
40
- assert_equal $PID, document['pid']
40
+ assert_equal $$, document['pid']
41
41
  assert_equal 'test', document['host_name']
42
42
  assert_equal 'test_application', document['application']
43
43
  end
@@ -53,7 +53,7 @@ class AppenderMongoDBTest < Test::Unit::TestCase
53
53
  assert_equal nil, document['thread_name']
54
54
  assert_equal nil, document['time']
55
55
  assert_equal({ "tracking_number" => 12345, "session_id" => 'HSSKLEU@JDK767'}, document['payload'])
56
- assert_equal $PID, document['pid']
56
+ assert_equal $$, document['pid']
57
57
  assert_equal 'test', document['host_name']
58
58
  assert_equal 'test_application', document['application']
59
59
  end
@@ -72,7 +72,7 @@ class AppenderMongoDBTest < Test::Unit::TestCase
72
72
  assert_equal 'thread', document['thread_name']
73
73
  assert_equal @time.to_i, document['time'].to_i
74
74
  assert_equal({ "tracking_number" => 12345, "session_id" => 'HSSKLEU@JDK767'}, document['payload'])
75
- assert_equal $PID, document['pid']
75
+ assert_equal $$, document['pid']
76
76
  assert_equal 'test', document['host_name']
77
77
  assert_equal 'test_application', document['application']
78
78
  end
@@ -90,7 +90,7 @@ class AppenderMongoDBTest < Test::Unit::TestCase
90
90
  assert_equal 'thread', document['thread_name']
91
91
  assert_equal @time.to_i, document['time'].to_i
92
92
  assert_equal nil, document['payload']
93
- assert_equal $PID, document['pid']
93
+ assert_equal $$, document['pid']
94
94
  assert_equal 'test', document['host_name']
95
95
  assert_equal 'test_application', document['application']
96
96
  end
@@ -107,7 +107,7 @@ class AppenderMongoDBTest < Test::Unit::TestCase
107
107
  assert_equal 'thread', document['thread_name']
108
108
  assert_equal @time.to_i, document['time'].to_i
109
109
  assert_equal({ "tracking_number" => 12345, "session_id" => 'HSSKLEU@JDK767'}, document['payload'])
110
- assert_equal $PID, document['pid']
110
+ assert_equal $$, document['pid']
111
111
  assert_equal 'test', document['host_name']
112
112
  assert_equal 'test_application', document['application']
113
113
  end
@@ -0,0 +1,79 @@
1
+ # Allow test to be run in-place without requiring a gem install
2
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
3
+ # Load mocked newrelic_rpm from test directory
4
+ $LOAD_PATH.unshift File.dirname(__FILE__)
5
+
6
+
7
+ require 'rubygems'
8
+ require 'test/unit'
9
+ require 'shoulda'
10
+ require 'mocha/setup'
11
+ require 'semantic_logger'
12
+
13
+ # Unit Test for SemanticLogger::Appender::NewRelic
14
+ #
15
+ class AppenderNewRelicTest < Test::Unit::TestCase
16
+ context SemanticLogger::Appender::NewRelic do
17
+
18
+ setup do
19
+ @appender = SemanticLogger::Appender::NewRelic.new
20
+ @message = 'AppenderNewRelicTest log message'
21
+ @multi_line_message = <<-EOSTR
22
+
23
+
24
+ first non-blank line
25
+ second non-blank line
26
+
27
+ third non-blank line
28
+
29
+ EOSTR
30
+ end
31
+
32
+ (SemanticLogger::LEVELS - [:error, :fatal]).each do |level|
33
+ should "not send :#{level.to_s} notifications to New Relic" do
34
+ @appender.tagged('test') do
35
+ @appender.send(level, "AppenderNewRelicTest #{level.to_s} message")
36
+ end
37
+ assert_nil ::NewRelic::Agent.message
38
+ assert_nil ::NewRelic::Agent.hash, ::NewRelic::Agent.hash
39
+ end
40
+ end
41
+
42
+ [:error, :fatal].each do |level|
43
+ should "send :#{level.to_s} notifications to New Relic" do
44
+ @appender.tagged('test') do
45
+ @appender.send(level, @message)
46
+ end
47
+ assert_equal @message, ::NewRelic::Agent.message
48
+ assert_equal ['test'], ::NewRelic::Agent.hash[:custom_params][:tags], ::NewRelic::Agent.hash
49
+ assert_equal "SemanticLogger::Appender::NewRelic/#{@message}", ::NewRelic::Agent.hash[:metric]
50
+ assert_nil ::NewRelic::Agent.hash[:custom_params][:duration], ::NewRelic::Agent.hash
51
+ assert_not_nil ::NewRelic::Agent.hash[:custom_params][:thread_name], ::NewRelic::Agent.hash
52
+ end
53
+ end
54
+
55
+ should 'send notification to New Relic with custom attributes' do
56
+ @appender.tagged('test') do
57
+ @appender.with_payload({:key1 => 1, :key2 => 'a'}) do
58
+ @appender.benchmark(:error, @message) do
59
+ sleep 0.001
60
+ end
61
+ end
62
+ end
63
+ assert_equal @message, ::NewRelic::Agent.message
64
+ assert_equal ['test'], ::NewRelic::Agent.hash[:custom_params][:tags], ::NewRelic::Agent.hash
65
+ assert_equal "SemanticLogger::Appender::NewRelic/#{@message}", ::NewRelic::Agent.hash[:metric]
66
+ assert_equal({:key1 => 1, :key2 => 'a'}, ::NewRelic::Agent.hash[:custom_params][:payload], ::NewRelic::Agent.hash)
67
+ assert_not_nil ::NewRelic::Agent.hash[:custom_params][:duration], ::NewRelic::Agent.hash
68
+ assert_not_nil ::NewRelic::Agent.hash[:custom_params][:thread_name], ::NewRelic::Agent.hash
69
+ end
70
+
71
+ should 'use the first non-blank line for a multi-line message' do
72
+ @appender.tagged('test') do
73
+ @appender.error @multi_line_message
74
+ end
75
+ assert_equal 'first non-blank line', ::NewRelic::Agent.message
76
+ end
77
+
78
+ end
79
+ end
@@ -0,0 +1,21 @@
1
+ # Mocked NewRelic::Agent used for testing
2
+
3
+ module NewRelic
4
+ module Agent
5
+ @@message = nil
6
+ @@hash = nil
7
+
8
+ def self.notice_error(message, hash)
9
+ @@message = message
10
+ @@hash = hash
11
+ end
12
+
13
+ def self.message
14
+ @@message
15
+ end
16
+
17
+ def self.hash
18
+ @@hash
19
+ end
20
+ end
21
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: semantic_logger
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.8.0
4
+ version: 2.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Reid Morrison
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-21 00:00:00.000000000 Z
11
+ date: 2014-06-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sync_attr
@@ -38,8 +38,8 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: 0.1.0
41
- description: Semantic Logger takes logging in Ruby to the next level by adding several
42
- new capabilities to the commonly used Logging API
41
+ description: Next generation logging system for Ruby to support highly concurrent,
42
+ high throughput, low latency systems
43
43
  email:
44
44
  - reidmo@gmail.com
45
45
  executables: []
@@ -53,6 +53,7 @@ files:
53
53
  - lib/semantic_logger/appender/base.rb
54
54
  - lib/semantic_logger/appender/file.rb
55
55
  - lib/semantic_logger/appender/mongodb.rb
56
+ - lib/semantic_logger/appender/new_relic.rb
56
57
  - lib/semantic_logger/appender/syslog.rb
57
58
  - lib/semantic_logger/appender/wrapper.rb
58
59
  - lib/semantic_logger/base.rb
@@ -63,11 +64,13 @@ files:
63
64
  - lib/semantic_logger/version.rb
64
65
  - test/appender_file_test.rb
65
66
  - test/appender_mongodb_test.rb
67
+ - test/appender_new_relic_test.rb
66
68
  - test/appender_syslog_test.rb
67
69
  - test/appender_wrapper_test.rb
68
70
  - test/loggable_test.rb
69
71
  - test/logger_test.rb
70
72
  - test/mock_logger.rb
73
+ - test/newrelic_rpm.rb
71
74
  homepage: https://github.com/reidmorrison/semantic_logger
72
75
  licenses:
73
76
  - Apache License V2.0
@@ -95,8 +98,10 @@ summary: Scalable, next generation logging for Ruby
95
98
  test_files:
96
99
  - test/appender_file_test.rb
97
100
  - test/appender_mongodb_test.rb
101
+ - test/appender_new_relic_test.rb
98
102
  - test/appender_syslog_test.rb
99
103
  - test/appender_wrapper_test.rb
100
104
  - test/loggable_test.rb
101
105
  - test/logger_test.rb
102
106
  - test/mock_logger.rb
107
+ - test/newrelic_rpm.rb