semantic_logger 2.21.0 → 3.0.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.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/lib/semantic_logger.rb +13 -9
- data/lib/semantic_logger/appender/base.rb +20 -24
- data/lib/semantic_logger/appender/bugsnag.rb +37 -53
- data/lib/semantic_logger/appender/elasticsearch.rb +53 -0
- data/lib/semantic_logger/appender/file.rb +17 -3
- data/lib/semantic_logger/appender/graylog.rb +123 -0
- data/lib/semantic_logger/appender/http.rb +169 -0
- data/lib/semantic_logger/appender/mongodb.rb +90 -111
- data/lib/semantic_logger/appender/new_relic.rb +38 -72
- data/lib/semantic_logger/appender/splunk.rb +10 -5
- data/lib/semantic_logger/appender/splunk_http.rb +99 -0
- data/lib/semantic_logger/appender/syslog.rb +116 -103
- data/lib/semantic_logger/appender/wrapper.rb +26 -11
- data/lib/semantic_logger/base.rb +13 -16
- data/lib/semantic_logger/log.rb +85 -5
- data/lib/semantic_logger/logger.rb +1 -1
- data/lib/semantic_logger/semantic_logger.rb +24 -0
- data/lib/semantic_logger/version.rb +1 -1
- data/test/appender/bugsnag_test.rb +39 -41
- data/test/appender/graylog_test.rb +63 -0
- data/test/appender/http_test.rb +61 -0
- data/test/appender/mongodb_test.rb +20 -20
- data/test/appender/new_relic_test.rb +4 -3
- data/test/appender/splunk_http_test.rb +78 -0
- data/test/logger_test.rb +1 -1
- metadata +27 -3
@@ -0,0 +1,169 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'uri'
|
3
|
+
require 'socket'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
# Log to any HTTP(S) server that accepts log messages in JSON form
|
7
|
+
#
|
8
|
+
# Features:
|
9
|
+
# * JSON Formatted messages.
|
10
|
+
# * Uses a persistent http connection, if the server supports it.
|
11
|
+
# * SSL encryption (https).
|
12
|
+
#
|
13
|
+
# Example:
|
14
|
+
# appender = SemanticLogger::Appender::Http.new(
|
15
|
+
# url: 'http://localhost:8088/path'
|
16
|
+
# )
|
17
|
+
#
|
18
|
+
# # Optional: Exclude health_check log entries, etc.
|
19
|
+
# appender.filter = Proc.new { |log| log.message !~ /(health_check|Not logged in)/}
|
20
|
+
#
|
21
|
+
# SemanticLogger.add_appender(appender)
|
22
|
+
class SemanticLogger::Appender::Http < SemanticLogger::Appender::Base
|
23
|
+
attr_accessor :username, :application, :host, :compress, :header
|
24
|
+
attr_reader :http, :url, :server, :port, :request_uri, :ssl_options
|
25
|
+
|
26
|
+
# Create HTTP(S) log appender
|
27
|
+
#
|
28
|
+
# Parameters:
|
29
|
+
# url: [String]
|
30
|
+
# Valid URL to post to.
|
31
|
+
# Example: http://example.com/some_path
|
32
|
+
# To enable SSL include https in the URL.
|
33
|
+
# Example: https://example.com/some_path
|
34
|
+
# verify_mode will default: OpenSSL::SSL::VERIFY_PEER
|
35
|
+
#
|
36
|
+
# application: [String]
|
37
|
+
# Name of this application to appear in log messages.
|
38
|
+
# Default: SemanticLogger.application
|
39
|
+
#
|
40
|
+
# host: [String]
|
41
|
+
# Name of this host to appear in log messages.
|
42
|
+
# Default: SemanticLogger.host
|
43
|
+
#
|
44
|
+
# username: [String]
|
45
|
+
# User name for basic Authentication.
|
46
|
+
# Default: nil ( do not use basic auth )
|
47
|
+
#
|
48
|
+
# password: [String]
|
49
|
+
# Password for basic Authentication.
|
50
|
+
#
|
51
|
+
# compress: [true|false]
|
52
|
+
# Whether to compress the JSON string with GZip.
|
53
|
+
# Default: false
|
54
|
+
#
|
55
|
+
# ssl: [Hash]
|
56
|
+
# Specific SSL options: For more details see NET::HTTP.start
|
57
|
+
# ca_file, ca_path, cert, cert_store, ciphers, key, open_timeout, read_timeout, ssl_timeout,
|
58
|
+
# ssl_version, use_ssl, verify_callback, verify_depth and verify_mode.
|
59
|
+
#
|
60
|
+
# level: [:trace | :debug | :info | :warn | :error | :fatal]
|
61
|
+
# Override the log level for this appender.
|
62
|
+
# Default: SemanticLogger.default_level
|
63
|
+
#
|
64
|
+
# filter: [Regexp|Proc]
|
65
|
+
# RegExp: Only include log messages where the class name matches the supplied.
|
66
|
+
# regular expression. All other messages will be ignored.
|
67
|
+
# Proc: Only include log messages where the supplied Proc returns true
|
68
|
+
# The Proc must return true or false.
|
69
|
+
def initialize(options, &block)
|
70
|
+
@options = options.dup
|
71
|
+
level = @options.delete(:level)
|
72
|
+
filter = @options.delete(:filter)
|
73
|
+
@url = options.delete(:url)
|
74
|
+
@ssl_options = options.delete(:ssl)
|
75
|
+
@username = options.delete(:username)
|
76
|
+
@password = options.delete(:password)
|
77
|
+
@application = options.delete(:application) || 'Semantic Logger'
|
78
|
+
@host = options.delete(:host) || SemanticLogger.host
|
79
|
+
@compress = options.delete(:compress) || false
|
80
|
+
raise(ArgumentError, "Unknown options: #{options.inspect}") if options.size > 0
|
81
|
+
|
82
|
+
raise(ArgumentError, 'Missing mandatory parameter :url') unless @url
|
83
|
+
|
84
|
+
@header = {
|
85
|
+
'Accept' => 'application/json',
|
86
|
+
'Content-Type' => 'application/json'
|
87
|
+
}
|
88
|
+
@header['Content-Encoding'] = 'gzip' if @compress
|
89
|
+
|
90
|
+
uri = URI.parse(@url)
|
91
|
+
(@ssl_options ||= {})[:use_ssl] = true if uri.scheme == 'https'
|
92
|
+
|
93
|
+
@server = uri.host
|
94
|
+
@port = uri.port
|
95
|
+
@username = uri.user if !@username && uri.user
|
96
|
+
@password = uri.password if !@password && uri.password
|
97
|
+
@request_uri = uri.request_uri
|
98
|
+
|
99
|
+
reopen
|
100
|
+
|
101
|
+
# Pass on the level and custom formatter if supplied
|
102
|
+
super(level, filter, &block)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Re-open after process fork
|
106
|
+
def reopen
|
107
|
+
# On Ruby v2.0 and greater, Net::HTTP.new uses a persistent connection if the server allows it
|
108
|
+
@http = @ssl_options ? Net::HTTP.new(server, port, @ssl_options) : Net::HTTP.new(server, port)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Forward log messages to HTTP Server
|
112
|
+
def log(log)
|
113
|
+
return false if (level_index > (log.level_index || 0)) ||
|
114
|
+
!include_message?(log) # Filtered out?
|
115
|
+
|
116
|
+
post(formatter.call(log, self))
|
117
|
+
end
|
118
|
+
|
119
|
+
# Use the JSON formatter
|
120
|
+
def default_formatter
|
121
|
+
self.class.json_formatter
|
122
|
+
end
|
123
|
+
|
124
|
+
private
|
125
|
+
|
126
|
+
def compress_data(data)
|
127
|
+
str = StringIO.new
|
128
|
+
gz = Zlib::GzipWriter.new(str)
|
129
|
+
gz << data
|
130
|
+
gz.close
|
131
|
+
str.string
|
132
|
+
end
|
133
|
+
|
134
|
+
# HTTP Post
|
135
|
+
def post(body, request_uri = nil)
|
136
|
+
request = Net::HTTP::Post.new(request_uri || @request_uri, @header)
|
137
|
+
process_request(request, body)
|
138
|
+
end
|
139
|
+
|
140
|
+
# HTTP Put
|
141
|
+
def put(body, request_uri = nil)
|
142
|
+
request = Net::HTTP::Put.new(request_uri || @request_uri, @header)
|
143
|
+
process_request(request, body)
|
144
|
+
end
|
145
|
+
|
146
|
+
# HTTP Delete
|
147
|
+
def delete(request_uri = nil)
|
148
|
+
request = Net::HTTP::Delete.new(request_uri || @request_uri, @header)
|
149
|
+
process_request(request)
|
150
|
+
end
|
151
|
+
|
152
|
+
# Process HTTP Request
|
153
|
+
def process_request(request, body = nil)
|
154
|
+
if body
|
155
|
+
body = compress_data(body) if compress
|
156
|
+
request.body = body
|
157
|
+
end
|
158
|
+
request.basic_auth(@username, @password) if @username
|
159
|
+
response = @http.request(request)
|
160
|
+
if response.code == '200' || response.code == '201'
|
161
|
+
true
|
162
|
+
else
|
163
|
+
# Failures are logged to the global semantic logger failsafe logger (Usually stderr or file)
|
164
|
+
SemanticLogger::Logger.logger.error("Bad HTTP response code: #{response.code}, #{response.body}")
|
165
|
+
false
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
@@ -1,98 +1,115 @@
|
|
1
1
|
require 'socket'
|
2
|
-
|
2
|
+
begin
|
3
|
+
require 'mongo'
|
4
|
+
rescue LoadError
|
5
|
+
raise 'Gem mongo is required for logging to MongoDB. Please add the gem "mongo" to your Gemfile.'
|
6
|
+
end
|
7
|
+
|
3
8
|
module SemanticLogger
|
4
9
|
module Appender
|
5
10
|
# The Mongo Appender for the SemanticLogger
|
6
11
|
#
|
7
12
|
# Mongo Document Schema:
|
8
|
-
# _id
|
9
|
-
# time
|
10
|
-
#
|
11
|
-
# application
|
12
|
-
# pid
|
13
|
-
#
|
14
|
-
# name
|
15
|
-
# level
|
13
|
+
# _id: ObjectId("4d9cbcbf7abb3abdaf9679cd"),
|
14
|
+
# time: ISODate("2011-04-06T19:19:27.006Z"),
|
15
|
+
# host: 'Name of the host on which this log entry originated',
|
16
|
+
# application 'Name of application or service logging the data - clarity_base, nginx, tomcat',
|
17
|
+
# pid: process id
|
18
|
+
# thread: "name or id of thread",
|
19
|
+
# name: "com.clarity.MyClass",
|
20
|
+
# level: 'trace|debug|info|warn|error|fatal'
|
16
21
|
# level_index: 0|1|2|3|4|5
|
17
|
-
# message
|
18
|
-
# duration
|
19
|
-
#
|
20
|
-
#
|
21
|
-
# Optional. Any user supplied data, including any thread specific context variables
|
22
|
-
# values supplied on a per log entry will override any thread context values
|
23
|
-
# }
|
24
|
-
# # When an exception is supplied as the first or second parameter
|
25
|
-
# # If supplied as the first parameter, message='exception name'
|
22
|
+
# message: "Message supplied to the logging call",
|
23
|
+
# duration: 'human readable duration',
|
24
|
+
# duration_ms: ms,
|
25
|
+
# tags: ["id1", "id2"]
|
26
26
|
# exception: {
|
27
27
|
# name: 'MyException',
|
28
28
|
# message: 'Invalid value',
|
29
29
|
# stack_trace: []
|
30
30
|
# }
|
31
31
|
# # When a backtrace is captured
|
32
|
-
# file_name:
|
32
|
+
# file_name: 'my_class.rb'
|
33
33
|
# line_number: 42
|
34
34
|
#
|
35
|
+
# Example:
|
36
|
+
# require 'semantic_logger'
|
37
|
+
#
|
38
|
+
# client = Mongo::MongoClient.new
|
39
|
+
# database = client['test']
|
40
|
+
#
|
41
|
+
# appender = SemanticLogger::Appender::MongoDB.new(
|
42
|
+
# db: database,
|
43
|
+
# collection_size: 1024**3 # 1.gigabyte
|
44
|
+
# )
|
45
|
+
# SemanticLogger.add_appender(appender)
|
46
|
+
#
|
47
|
+
# logger = SemanticLogger['Example']
|
48
|
+
#
|
49
|
+
# # Log some messages
|
50
|
+
# logger.info 'This message is written to mongo as a document'
|
35
51
|
class MongoDB < SemanticLogger::Appender::Base
|
36
52
|
attr_reader :db, :collection_name, :collection
|
37
|
-
attr_accessor :
|
53
|
+
attr_accessor :host, :write_concern, :application
|
38
54
|
|
39
55
|
# Create a MongoDB Appender instance
|
40
56
|
#
|
41
|
-
# SemanticLogger::Appender::MongoDB.new(db: Mongo::Connection.new['database'])
|
42
|
-
#
|
43
57
|
# Parameters:
|
44
|
-
#
|
45
|
-
#
|
58
|
+
# db: [Mongo::Database]
|
59
|
+
# The MongoDB database connection to use, not the database name
|
46
60
|
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
61
|
+
# collection_name: [String]
|
62
|
+
# Name of the collection to store log data in
|
63
|
+
# Default: semantic_logger
|
50
64
|
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
65
|
+
# write_concern: [Integer]
|
66
|
+
# Write concern to use
|
67
|
+
# see: http://docs.mongodb.org/manual/reference/write-concern/
|
68
|
+
# Default: 0
|
54
69
|
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
# Default: 1
|
70
|
+
# host: [String]
|
71
|
+
# host name to include in the document logged to Mongo
|
72
|
+
# Default: SemanticLogger.host_name
|
59
73
|
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
74
|
+
# application: [String]
|
75
|
+
# Name of the application to include in the document written to mongo
|
76
|
+
# Default: nil (None)
|
63
77
|
#
|
64
|
-
#
|
65
|
-
#
|
66
|
-
#
|
78
|
+
# collection_size: [Integer]
|
79
|
+
# The size of the MongoDB capped collection to create in bytes
|
80
|
+
# Default: 1 GB
|
81
|
+
# Examples:
|
82
|
+
# Prod: 25GB (.5GB per day across 4 servers over 10 days)
|
83
|
+
# Dev: .5GB
|
84
|
+
# Test: File
|
85
|
+
# Release: 4GB
|
67
86
|
#
|
68
|
-
#
|
69
|
-
#
|
70
|
-
# Dev: .5GB
|
71
|
-
# Test: File
|
72
|
-
# Release: 4GB
|
87
|
+
# collection_max: [Integer]
|
88
|
+
# Maximum number of log entries that the capped collection will hold.
|
73
89
|
#
|
74
|
-
#
|
75
|
-
#
|
90
|
+
# level: [:trace | :debug | :info | :warn | :error | :fatal]
|
91
|
+
# Override the log level for this appender.
|
92
|
+
# Default: SemanticLogger.default_level
|
76
93
|
#
|
77
|
-
#
|
78
|
-
#
|
79
|
-
#
|
80
|
-
#
|
81
|
-
#
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
@db =
|
87
|
-
@collection_name =
|
88
|
-
@
|
89
|
-
@write_concern =
|
90
|
-
@application =
|
91
|
-
filter = params[:filter]
|
94
|
+
# filter: [Regexp|Proc]
|
95
|
+
# RegExp: Only include log messages where the class name matches the supplied.
|
96
|
+
# regular expression. All other messages will be ignored.
|
97
|
+
# Proc: Only include log messages where the supplied Proc returns true
|
98
|
+
# The Proc must return true or false.
|
99
|
+
def initialize(options = {}, &block)
|
100
|
+
options = options.dup
|
101
|
+
level = options.delete(:level)
|
102
|
+
filter = options.delete(:filter)
|
103
|
+
@db = options.delete(:db) || raise('Missing mandatory parameter :db')
|
104
|
+
@collection_name = options.delete(:collection_name) || 'semantic_logger'
|
105
|
+
@host = options.delete(:host) || options.delete(:host_name) || SemanticLogger.host
|
106
|
+
@write_concern = options.delete(:write_concern) || 0
|
107
|
+
@application = options.delete(:application) || SemanticLogger.application
|
92
108
|
|
93
109
|
# Create a collection that will hold the lesser of 1GB space or 10K documents
|
94
|
-
@collection_size =
|
95
|
-
@collection_max =
|
110
|
+
@collection_size = options.delete(:collection_size) || 1024**3
|
111
|
+
@collection_max = options.delete(:collection_max)
|
112
|
+
raise(ArgumentError, "Unknown options: #{options.inspect}") if options.size > 0
|
96
113
|
|
97
114
|
reopen
|
98
115
|
|
@@ -100,7 +117,7 @@ module SemanticLogger
|
|
100
117
|
create_indexes
|
101
118
|
|
102
119
|
# Set the log level and formatter
|
103
|
-
super(
|
120
|
+
super(level, filter, &block)
|
104
121
|
end
|
105
122
|
|
106
123
|
# After forking an active process call #reopen to re-open
|
@@ -109,7 +126,8 @@ module SemanticLogger
|
|
109
126
|
@collection = db[@collection_name]
|
110
127
|
end
|
111
128
|
|
112
|
-
# Create the required capped collection
|
129
|
+
# Create the required capped collection.
|
130
|
+
#
|
113
131
|
# Features of capped collection:
|
114
132
|
# * No indexes by default (not even on _id)
|
115
133
|
# * Documents cannot be deleted,
|
@@ -117,7 +135,7 @@ module SemanticLogger
|
|
117
135
|
# * Documents are always stored in insertion order
|
118
136
|
# * A find will always return the documents in their insertion order
|
119
137
|
#
|
120
|
-
# Creates an index based on tags to support faster
|
138
|
+
# Creates an index based on tags to support faster searches.
|
121
139
|
def create_indexes
|
122
140
|
options = {capped: true, size: @collection_size}
|
123
141
|
options[:max] = @collection_max if @collection_max
|
@@ -144,59 +162,20 @@ module SemanticLogger
|
|
144
162
|
# Replace this formatter by supplying a Block to the initializer
|
145
163
|
def default_formatter
|
146
164
|
Proc.new do |log|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
thread_name: log.thread_name,
|
152
|
-
name: log.name,
|
153
|
-
level: log.level,
|
154
|
-
level_index: log.level_index,
|
155
|
-
}
|
156
|
-
document[:application] = application if application
|
157
|
-
document[:message] = log.cleansed_message if log.message
|
158
|
-
document[:duration] = log.duration if log.duration
|
159
|
-
document[:tags] = log.tags if log.tags && (log.tags.size > 0)
|
160
|
-
document[:payload] = log.payload if log.payload
|
161
|
-
if log.exception
|
162
|
-
root = document
|
163
|
-
log.each_exception do |exception, i|
|
164
|
-
name = i == 0 ? :exception : :cause
|
165
|
-
root[name] = {
|
166
|
-
name: exception.class.name,
|
167
|
-
message: exception.message,
|
168
|
-
stack_trace: exception.backtrace
|
169
|
-
}
|
170
|
-
root = root[name]
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
file, line = log.file_name_and_line
|
175
|
-
if file
|
176
|
-
document[:file_name] = file
|
177
|
-
document[:line_number] = line.to_i
|
178
|
-
end
|
179
|
-
document
|
165
|
+
h = log.to_h
|
166
|
+
h[:host] = host
|
167
|
+
h[:application] = application
|
168
|
+
h
|
180
169
|
end
|
181
170
|
end
|
182
171
|
|
183
|
-
# Default host_name to use if none is supplied to the appenders initializer
|
184
|
-
def self.host_name
|
185
|
-
@@host_name ||= Socket.gethostname.split('.').first
|
186
|
-
end
|
187
|
-
|
188
|
-
# Override the default host_name
|
189
|
-
def self.host_name=(host_name)
|
190
|
-
@@host_name = host_name
|
191
|
-
end
|
192
|
-
|
193
172
|
# Log the message to MongoDB
|
194
173
|
def log(log)
|
195
174
|
# Ensure minimum log level is met, and check filter
|
196
175
|
return false if (level_index > (log.level_index || 0)) || !include_message?(log)
|
197
176
|
|
198
177
|
# Insert log entry into Mongo
|
199
|
-
collection.insert(formatter.call(log), w: @write_concern)
|
178
|
+
collection.insert(formatter.call(log, self), w: @write_concern)
|
200
179
|
true
|
201
180
|
end
|
202
181
|
|
@@ -1,81 +1,47 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
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
|
1
|
+
begin
|
2
|
+
require 'newrelic_rpm'
|
3
|
+
rescue LoadError
|
4
|
+
raise 'Gem newrelic_rpm is required for logging to New Relic. Please add the gem "newrelic_rpm" to your Gemfile.'
|
49
5
|
end
|
50
6
|
|
51
|
-
#
|
52
|
-
|
53
|
-
|
54
|
-
#
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
require 'newrelic_rpm'
|
60
|
-
|
7
|
+
# Send log messages to NewRelic
|
8
|
+
#
|
9
|
+
# The :error and :fatal log entries will show up under
|
10
|
+
# "Applications" > "Application Name" > "Events" > "Errors" in New Relic.
|
11
|
+
#
|
12
|
+
# Example:
|
13
|
+
# SemanticLogger.add_appender(SemanticLogger::Appender::NewRelic.new)
|
14
|
+
#
|
61
15
|
class SemanticLogger::Appender::NewRelic < SemanticLogger::Appender::Base
|
62
|
-
#
|
63
|
-
#
|
64
|
-
#
|
65
|
-
|
66
|
-
|
16
|
+
# Create Appender
|
17
|
+
#
|
18
|
+
# Parameters
|
19
|
+
# level: [:trace | :debug | :info | :warn | :error | :fatal]
|
20
|
+
# Override the log level for this appender.
|
21
|
+
# Default: :error
|
22
|
+
#
|
23
|
+
# filter: [Regexp|Proc]
|
24
|
+
# RegExp: Only include log messages where the class name matches the supplied.
|
25
|
+
# regular expression. All other messages will be ignored.
|
26
|
+
# Proc: Only include log messages where the supplied Proc returns true
|
27
|
+
# The Proc must return true or false.
|
28
|
+
def initialize(options = {}, &block)
|
29
|
+
# Backward compatibility
|
30
|
+
options = {level: options} unless options.is_a?(Hash)
|
31
|
+
@options = options.dup
|
32
|
+
level = @options.delete(:level) || :error
|
33
|
+
filter = @options.delete(:filter)
|
34
|
+
|
35
|
+
super(level, filter, &block)
|
67
36
|
end
|
68
37
|
|
69
38
|
# Returns [Hash] of parameters to send to New Relic.
|
70
39
|
def default_formatter
|
71
|
-
Proc.new do |log|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
custom_params[:message] = log.message if log.exception
|
77
|
-
|
78
|
-
{metric: log.metric, custom_params: custom_params}
|
40
|
+
Proc.new do |log, logger|
|
41
|
+
h = log.to_h
|
42
|
+
h.delete(:time)
|
43
|
+
h.delete(:exception)
|
44
|
+
{metric: log.metric, custom_params: h}
|
79
45
|
end
|
80
46
|
end
|
81
47
|
|
@@ -96,7 +62,7 @@ class SemanticLogger::Appender::NewRelic < SemanticLogger::Appender::Base
|
|
96
62
|
# For more documentation on the NewRelic::Agent.notice_error method see:
|
97
63
|
# http://rubydoc.info/github/newrelic/rpm/NewRelic/Agent#notice_error-instance_method
|
98
64
|
# and https://docs.newrelic.com/docs/ruby/ruby-agent-api
|
99
|
-
NewRelic::Agent.notice_error(exception, formatter.call(log))
|
65
|
+
NewRelic::Agent.notice_error(exception, formatter.call(log, self))
|
100
66
|
true
|
101
67
|
end
|
102
68
|
|