semantic_logger 2.8.0 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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