semantic_logger 4.11.0 → 4.14.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ef345374458ca40a1c31ab82b43fa040974c586fd089a3a5a2a5ab6fe0393c77
4
- data.tar.gz: a7d13c0f42e7571b3ddcc47303f83a4b28a2b46929134be7d02e9c5af23d2963
3
+ metadata.gz: 966d1357f842de3f83f7b0c479152a401906da47588a8f64525ea0fee406a017
4
+ data.tar.gz: 3528480641795abc6f38fe4617f4599be81b276ab4184c719b1f6ee7e555fb07
5
5
  SHA512:
6
- metadata.gz: 0c4913a0f50f89e9b92b03b89c01b8a02f71f29efd6c06ae0c3f59b49c290b5878569cc6e5a946a7cc29943974d822a29481e6bd83d33c0de66f87f4b524e05c
7
- data.tar.gz: ffe8ca5c2ce97b4bfe67bd79b4ae562c509c577cc70daca9128fe2c28cb7e92f1f6e567e4fb5199f89927653b6a1a3d32d53d6287e5f96ec935207534027746f
6
+ metadata.gz: 6e75514558375d84db0943fbca39295c1011f9def7a86ddc2592c1de7cd0a460c61158ecbdfa35954ec768baa3bfc7b7b068ca802723b6daf64a050a31814c60
7
+ data.tar.gz: 9c351b2d1eb79c5cfbde72304e6557e0d18526746be4ef3777bb1130a2a675a069321954e617d799c7d6dc2ddc60fd425cc33b9bd9ec148d04931336aa048286
data/README.md CHANGED
@@ -55,6 +55,7 @@ and are therefore not automatically included by this gem:
55
55
  - Bugsnag Appender: gem 'bugsnag'
56
56
  - MongoDB Appender: gem 'mongo' 1.9.2 or above
57
57
  - NewRelic Appender: gem 'newrelic_rpm'
58
+ - NewRelicLogs Appender: gem 'newrelic_rpm'
58
59
  - Syslog Appender: gem 'syslog_protocol' 0.9.2 or above
59
60
  - Syslog Appender to a remote syslogng server over TCP or UDP: gem 'net_tcp_client'
60
61
  - Splunk Appender: gem 'splunk-sdk-ruby'
@@ -59,7 +59,7 @@ module SemanticLogger
59
59
 
60
60
  appender.reopen if appender.respond_to?(:reopen)
61
61
 
62
- @thread.kill if @thread&.alive?
62
+ @thread&.kill if @thread&.alive?
63
63
  @thread = Thread.new { process }
64
64
  end
65
65
 
@@ -1,5 +1,3 @@
1
- require "concurrent"
2
-
3
1
  module SemanticLogger
4
2
  module Appender
5
3
  # Log asynchronously in batches using a separate thread.
@@ -21,7 +21,7 @@ module SemanticLogger
21
21
  class Http < SemanticLogger::Subscriber
22
22
  attr_accessor :username, :compress, :header,
23
23
  :open_timeout, :read_timeout, :continue_timeout
24
- attr_reader :http, :url, :server, :port, :path, :ssl_options
24
+ attr_reader :http, :url, :server, :port, :path, :ssl_options, :proxy_url
25
25
 
26
26
  # Create HTTP(S) log appender
27
27
  #
@@ -48,6 +48,11 @@ module SemanticLogger
48
48
  # password: [String]
49
49
  # Password for basic Authentication.
50
50
  #
51
+ # header: [Hash]
52
+ # Custom HTTP headers to send with each request.
53
+ # Default: {} ( do not send any custom headers)
54
+ # Example: {"Authorization" => "Bearer BEARER_TOKEN"}
55
+ #
51
56
  # compress: [true|false]
52
57
  # Whether to compress the JSON string with GZip.
53
58
  # Default: false
@@ -57,6 +62,16 @@ module SemanticLogger
57
62
  # ca_file, ca_path, cert, cert_store, ciphers, key, ssl_timeout,
58
63
  # ssl_version, verify_callback, verify_depth and verify_mode.
59
64
  #
65
+ # proxy_url: [String]
66
+ # URL of proxy server to use for HTTP(s) connections. Should
67
+ # include username and password if required.
68
+ # Example: http://user@pass:example.com/some_path
69
+ # To enable SSL include https in the URL.
70
+ # Example: https://example.com/some_path
71
+ # If this is set to :ENV, Net::HTTP will use the environment http_proxy*
72
+ # variables if they are set. If set to nil then no proxy will be used,
73
+ # even if the environment variables are set.
74
+ #
60
75
  # level: [:trace | :debug | :info | :warn | :error | :fatal]
61
76
  # Override the log level for this appender.
62
77
  # Default: SemanticLogger.default_level
@@ -85,6 +100,8 @@ module SemanticLogger
85
100
  ssl: {},
86
101
  username: nil,
87
102
  password: nil,
103
+ header: {},
104
+ proxy_url: :ENV,
88
105
  open_timeout: 2.0,
89
106
  read_timeout: 1.0,
90
107
  continue_timeout: 1.0,
@@ -92,6 +109,7 @@ module SemanticLogger
92
109
  &block)
93
110
 
94
111
  @url = url
112
+ @proxy_url = proxy_url
95
113
  @ssl_options = ssl
96
114
  @username = username
97
115
  @password = password
@@ -106,7 +124,7 @@ module SemanticLogger
106
124
  "Content-Type" => "application/json",
107
125
  "Connection" => "keep-alive",
108
126
  "Keep-Alive" => "300"
109
- }
127
+ }.merge(header)
110
128
  @header["Content-Encoding"] = "gzip" if @compress
111
129
 
112
130
  uri = URI.parse(@url)
@@ -129,6 +147,9 @@ module SemanticLogger
129
147
  else
130
148
  @port ||= HTTP.http_default_port
131
149
  end
150
+
151
+ @proxy_uri = URI.parse(@proxy_url) if @proxy_url && @proxy_url != :ENV
152
+
132
153
  @http = nil
133
154
 
134
155
  super(**args, &block)
@@ -144,7 +165,11 @@ module SemanticLogger
144
165
  nil
145
166
  end
146
167
 
147
- @http = Net::HTTP.new(server, port)
168
+ @http = if @proxy_uri
169
+ Net::HTTP.new(server, port, @proxy_uri.host, @proxy_uri.port, @proxy_uri.user, @proxy_uri.password)
170
+ else
171
+ Net::HTTP.new(server, port, @proxy_url)
172
+ end
148
173
 
149
174
  if @ssl_options
150
175
  @http.methods.grep(/\A(\w+)=\z/) do |meth|
@@ -207,7 +232,7 @@ module SemanticLogger
207
232
  end
208
233
  request.basic_auth(@username, @password) if @username
209
234
  response = @http.request(request)
210
- if response.code == "200" || response.code == "201"
235
+ if response.is_a?(Net::HTTPSuccess)
211
236
  true
212
237
  else
213
238
  # Failures are logged to the global semantic logger failsafe logger (Usually stderr or file)
@@ -24,7 +24,7 @@ module SemanticLogger
24
24
  class Kafka < SemanticLogger::Subscriber
25
25
  attr_accessor :seed_brokers, :client_id, :connect_timeout, :socket_timeout,
26
26
  :ssl_ca_cert, :ssl_client_cert, :ssl_client_cert_key, :ssl_ca_certs_from_system,
27
- :delivery_threshold, :delivery_interval,
27
+ :delivery_threshold, :delivery_interval, :required_acks,
28
28
  :topic, :partition, :partition_key, :key
29
29
 
30
30
  # Send log messages to Kafka in JSON format.
@@ -90,6 +90,10 @@ module SemanticLogger
90
90
  # Number of seconds between triggering a delivery of messages to Apache Kafka.
91
91
  # Default: 5
92
92
  #
93
+ # required_acks: [Integer]
94
+ # Number of replicas that must acknowledge receipt of each log message to the topic
95
+ # Default: 1
96
+ #
93
97
  # Semantic Logger Parameters:
94
98
  #
95
99
  # level: [:trace | :debug | :info | :warn | :error | :fatal]
@@ -121,7 +125,7 @@ module SemanticLogger
121
125
  def initialize(seed_brokers:, client_id: "semantic-logger", connect_timeout: nil, socket_timeout: nil,
122
126
  ssl_ca_cert: nil, ssl_client_cert: nil, ssl_client_cert_key: nil, ssl_ca_certs_from_system: false,
123
127
  topic: "log_messages", partition: nil, partition_key: nil, key: nil,
124
- delivery_threshold: 100, delivery_interval: 10,
128
+ delivery_threshold: 100, delivery_interval: 10, required_acks: 1,
125
129
  metrics: true, **args, &block)
126
130
 
127
131
  @seed_brokers = seed_brokers
@@ -138,6 +142,7 @@ module SemanticLogger
138
142
  @key = key
139
143
  @delivery_threshold = delivery_threshold
140
144
  @delivery_interval = delivery_interval
145
+ @required_acks = required_acks
141
146
 
142
147
  super(metrics: metrics, **args, &block)
143
148
  reopen
@@ -158,7 +163,8 @@ module SemanticLogger
158
163
 
159
164
  @producer = @kafka.async_producer(
160
165
  delivery_threshold: delivery_threshold,
161
- delivery_interval: delivery_interval
166
+ delivery_interval: delivery_interval,
167
+ required_acks: required_acks
162
168
  )
163
169
  end
164
170
 
@@ -0,0 +1,57 @@
1
+ begin
2
+ require "newrelic_rpm"
3
+ rescue LoadError
4
+ raise LoadError, 'Gem newrelic_rpm is required for logging to New Relic. Please add the gem "newrelic_rpm" to your Gemfile.'
5
+ end
6
+
7
+ require "semantic_logger/formatters/new_relic_logs"
8
+
9
+ # Send log messages to NewRelic
10
+ #
11
+ # All log entries will appear under
12
+ # "Logs" in New Relic
13
+ #
14
+ # == Caveats
15
+ #
16
+ # * The NewRelic agent only sends logs to NewRelic when log forwarding is enabled. There is however an open
17
+ # issue to get this fixed: https://github.com/newrelic/newrelic-ruby-agent/issues/1614. Please see the guide
18
+ # for a workaround.
19
+ #
20
+ # Example:
21
+ # SemanticLogger.add_appender(appender: :new_relic_logs)
22
+ module SemanticLogger
23
+ module Appender
24
+ class NewRelicLogs < SemanticLogger::Subscriber
25
+ # Create Appender
26
+ #
27
+ # Parameters
28
+ # level: [:trace | :debug | :info | :warn | :error | :fatal]
29
+ # Override the log level for this appender.
30
+ # Default: SemanticLogger.default_level
31
+ #
32
+ # formatter: [Object|Proc]
33
+ # An instance of a class that implements #call, or a Proc to be used to format
34
+ # the output from this appender
35
+ # Default: SemanticLogger::Formatters::NewRelicLogs
36
+ #
37
+ # filter: [Regexp|Proc]
38
+ # RegExp: Only include log messages where the class name matches the supplied.
39
+ # regular expression. All other messages will be ignored.
40
+ # Proc: Only include log messages where the supplied Proc returns true
41
+ # The Proc must return true or false.
42
+ def initialize(formatter: SemanticLogger::Formatters::NewRelicLogs.new, **args, &block)
43
+ super(formatter: formatter, **args, &block)
44
+ end
45
+
46
+ # Send an error notification to New Relic
47
+ def log(log)
48
+ self.class.log_newrelic(formatter.call(log, self).to_json, log.level.to_s.upcase)
49
+ true
50
+ end
51
+
52
+ def self.log_newrelic(message, level)
53
+ ::NewRelic::Agent.agent.log_event_aggregator.record(message, level)
54
+ end
55
+ end
56
+ end
57
+ end
@@ -40,7 +40,7 @@ module SemanticLogger
40
40
  def initialize(level: :error, **args, &block)
41
41
  # Replace the Sentry Ruby logger so that we can identify its log
42
42
  # messages and not forward them to Sentry
43
- ::Sentry.init { |config| config.logger = SemanticLogger[::Sentry] }
43
+ ::Sentry.init { |config| config.logger = SemanticLogger[::Sentry] } unless ::Sentry.initialized?
44
44
  super(level: level, **args, &block)
45
45
  end
46
46
 
@@ -15,6 +15,7 @@ module SemanticLogger
15
15
  autoload :Http, "semantic_logger/appender/http"
16
16
  autoload :MongoDB, "semantic_logger/appender/mongodb"
17
17
  autoload :NewRelic, "semantic_logger/appender/new_relic"
18
+ autoload :NewRelicLogs, "semantic_logger/appender/new_relic_logs"
18
19
  autoload :Rabbitmq, "semantic_logger/appender/rabbitmq"
19
20
  autoload :Splunk, "semantic_logger/appender/splunk"
20
21
  autoload :SplunkHttp, "semantic_logger/appender/splunk_http"
@@ -77,7 +77,7 @@ module SemanticLogger
77
77
  # # Log an exception in a semantic way
78
78
  # logger.info("Parsing received XML", exc)
79
79
  #
80
- SemanticLogger::LEVELS.each_with_index do |level, index|
80
+ SemanticLogger::Levels::LEVELS.each_with_index do |level, index|
81
81
  class_eval <<~METHODS, __FILE__, __LINE__ + 1
82
82
  def #{level}(message=nil, payload=nil, exception=nil, &block)
83
83
  if level_index <= #{index}
@@ -23,7 +23,6 @@ module SemanticLogger
23
23
  # See Time#strftime for the format of this string.
24
24
  # :iso_8601 Outputs an ISO8601 Formatted timestamp.
25
25
  # :ms Output in miliseconds since epoch.
26
- # nil: Returns Empty string for time ( no time is output ).
27
26
  # Default: '%Y-%m-%d %H:%M:%S.%<precision>N'
28
27
  # log_host: [Boolean]
29
28
  # Whether or not to include hostname in logs
@@ -62,7 +62,12 @@ module SemanticLogger
62
62
 
63
63
  def flatten_log
64
64
  flattened = @parsed.map do |key, value|
65
- "#{key}=#{value.to_json}"
65
+ case value
66
+ when Hash, Array
67
+ "#{key}=#{value.to_s.to_json}"
68
+ else
69
+ "#{key}=#{value.to_json}"
70
+ end
66
71
  end
67
72
 
68
73
  flattened.join(" ")
@@ -0,0 +1,109 @@
1
+ require "json"
2
+
3
+ begin
4
+ require "newrelic_rpm"
5
+ rescue LoadError
6
+ raise LoadError, 'Gem newrelic_rpm is required for logging to New Relic. Please add the gem "newrelic_rpm" to your Gemfile.'
7
+ end
8
+
9
+ raise "NewRelic::Agent.linking_metadata is not defined. Please update newrelic_rpm gem version" unless NewRelic::Agent.respond_to?(:linking_metadata)
10
+
11
+ raise "NewRelic::Agent::Tracer.current_span_id is not defined. Please update newrelic_rpm gem version" unless NewRelic::Agent::Tracer.respond_to?(:current_span_id)
12
+
13
+ raise "NewRelic::Agent::Tracer.current_trace_id is not defined. Please update newrelic_rpm gem version" unless NewRelic::Agent::Tracer.respond_to?(:current_trace_id)
14
+
15
+ module SemanticLogger
16
+ module Formatters
17
+ # Formatter for reporting to NewRelic's Logger
18
+ #
19
+ # New Relic's logs do not support custom attributes out of the box, and therefore these
20
+ # have to be put into a single JSON serialized string under the +message+ key.
21
+ #
22
+ # In particular the following fields of the log object are serialized under the +message+
23
+ # key that's sent to NewRelic:
24
+ #
25
+ # * message
26
+ # * tags
27
+ # * named_tags
28
+ # * payload
29
+ # * metric
30
+ # * metric_amount
31
+ # * environment
32
+ # * application
33
+ #
34
+ # == New Relic Attributes not Supported
35
+ # * thread.id
36
+ # * class.name
37
+ # * method.name
38
+ #
39
+ # == Reference
40
+ # * Logging specification
41
+ # * https://github.com/newrelic/newrelic-exporter-specs/tree/master/logging
42
+ #
43
+ # * Metadata APIs
44
+ # * https://www.rubydoc.info/gems/newrelic_rpm/NewRelic/Agent#linking_metadata-instance_method
45
+ # * https://www.rubydoc.info/gems/newrelic_rpm/NewRelic/Agent/Tracer#current_trace_id-class_method
46
+ # * https://www.rubydoc.info/gems/newrelic_rpm/NewRelic/Agent/Tracer#current_span_id-class_method
47
+ #
48
+ class NewRelicLogs < Raw
49
+ def initialize(**args)
50
+ args.delete(:time_key)
51
+ args.delete(:time_format)
52
+
53
+ super(time_key: :timestamp, time_format: :ms, **args)
54
+ end
55
+
56
+ def call(log, logger)
57
+ hash = super(log, logger)
58
+
59
+ message = {
60
+ message: hash[:message].to_s,
61
+ tags: hash[:tags] || [],
62
+ named_tags: hash[:named_tags] || {},
63
+
64
+ **hash.slice(:metric, :metric_amount, :environment, :application, :payload)
65
+ }
66
+
67
+ message.merge!(duration: hash[:duration_ms]) if hash.key?(:duration_ms)
68
+ message.merge!(duration_human: hash[:duration]) if hash.key?(:duration)
69
+
70
+ result = {
71
+ **new_relic_metadata,
72
+ message: message.to_json,
73
+ timestamp: hash[:timestamp].to_i,
74
+ "log.level": log.level.to_s.upcase,
75
+ "logger.name": log.name,
76
+ "thread.name": log.thread_name.to_s
77
+ }
78
+
79
+ if hash[:exception]
80
+ result.merge!(
81
+ "error.message": hash[:exception][:message],
82
+ "error.class": hash[:exception][:name],
83
+ "error.stack": hash[:exception][:stack_trace].join("\n")
84
+ )
85
+ end
86
+
87
+ if hash[:file]
88
+ result.merge!(
89
+ "file.name": hash[:file],
90
+ "line.number": hash[:line].to_s
91
+ )
92
+ end
93
+
94
+ result
95
+ end
96
+
97
+ private
98
+
99
+ def new_relic_metadata
100
+ {
101
+ "trace.id": NewRelic::Agent::Tracer.current_trace_id,
102
+ "span.id": NewRelic::Agent::Tracer.current_span_id,
103
+ **NewRelic::Agent.linking_metadata
104
+ }.reject { |_k, v| v.nil? }.
105
+ map { |k, v| [k.to_sym, v] }.to_h
106
+ end
107
+ end
108
+ end
109
+ end
@@ -23,7 +23,7 @@ module SemanticLogger
23
23
  #
24
24
  # Example:
25
25
  # # Log via udp to a remote syslog server on host: `server1` and port `8514`, using the CEE format.
26
- # SemanticLogger.add_appender(appender: :syslog, formatter: syslog_cee, url: 'udp://server1:8514')
26
+ # SemanticLogger.add_appender(appender: :syslog, formatter: :syslog_cee, url: 'udp://server1:8514')
27
27
  def initialize(facility: ::Syslog::LOG_USER, level_map: SemanticLogger::Formatters::Syslog::LevelMap.new, max_size: Integer)
28
28
  @facility = facility
29
29
  @level_map = level_map.is_a?(SemanticLogger::Formatters::Syslog::LevelMap) ? level_map : SemanticLogger::Formatters::Syslog::LevelMap.new(level_map)
@@ -1,15 +1,17 @@
1
1
  module SemanticLogger
2
2
  module Formatters
3
- autoload :Base, "semantic_logger/formatters/base"
4
- autoload :Color, "semantic_logger/formatters/color"
5
- autoload :Default, "semantic_logger/formatters/default"
6
- autoload :Json, "semantic_logger/formatters/json"
7
- autoload :Raw, "semantic_logger/formatters/raw"
8
- autoload :OneLine, "semantic_logger/formatters/one_line"
9
- autoload :Signalfx, "semantic_logger/formatters/signalfx"
10
- autoload :Syslog, "semantic_logger/formatters/syslog"
11
- autoload :Fluentd, "semantic_logger/formatters/fluentd"
12
- autoload :Logfmt, "semantic_logger/formatters/logfmt"
3
+ autoload :Base, "semantic_logger/formatters/base"
4
+ autoload :Color, "semantic_logger/formatters/color"
5
+ autoload :Default, "semantic_logger/formatters/default"
6
+ autoload :Json, "semantic_logger/formatters/json"
7
+ autoload :Raw, "semantic_logger/formatters/raw"
8
+ autoload :OneLine, "semantic_logger/formatters/one_line"
9
+ autoload :Signalfx, "semantic_logger/formatters/signalfx"
10
+ autoload :Syslog, "semantic_logger/formatters/syslog"
11
+ autoload :Fluentd, "semantic_logger/formatters/fluentd"
12
+ autoload :Logfmt, "semantic_logger/formatters/logfmt"
13
+ autoload :SyslogCee, "semantic_logger/formatters/syslog_cee"
14
+ autoload :NewRelicLogs, "semantic_logger/formatters/new_relic_logs"
13
15
 
14
16
  # Return formatter that responds to call.
15
17
  #
@@ -1,35 +1,31 @@
1
+ require "logger"
2
+
1
3
  module SemanticLogger
2
4
  module Levels
3
5
  # Logging levels in order of most detailed to most severe
4
6
  LEVELS = %i[trace debug info warn error fatal].freeze
5
7
 
8
+ # Map the built-in `Logger` levels to SemanticLogger levels.
9
+ MAPPED_LEVELS =
10
+ ::Logger::Severity.constants.each_with_object([]) do |constant, levels|
11
+ logger_value = ::Logger::Severity.const_get(constant)
12
+ levels[logger_value] = LEVELS.find_index(constant.downcase.to_sym) || LEVELS.find_index(:error)
13
+ end.freeze
14
+
6
15
  # Internal method to return the log level as an internal index
7
16
  # Also supports mapping the ::Logger levels to SemanticLogger levels
8
17
  def self.index(level)
9
18
  return if level.nil?
10
19
 
11
- index =
12
- if level.is_a?(Symbol)
13
- LEVELS.index(level)
14
- elsif level.is_a?(String)
15
- level = level.downcase.to_sym
16
- LEVELS.index(level)
17
- elsif level.is_a?(Integer) && defined?(::Logger::Severity)
18
- # Mapping of Rails and Ruby Logger levels to SemanticLogger levels
19
- @map_levels ||=
20
- begin
21
- levels = []
22
- ::Logger::Severity.constants.each do |constant|
23
- levels[::Logger::Severity.const_get(constant)] =
24
- LEVELS.find_index(constant.downcase.to_sym) || LEVELS.find_index(:error)
25
- end
26
- levels
27
- end
28
- @map_levels[level]
29
- end
30
- raise "Invalid level:#{level.inspect} being requested. Must be one of #{LEVELS.inspect}" unless index
31
-
32
- index
20
+ case level
21
+ when Symbol
22
+ LEVELS.index(level)
23
+ when String
24
+ LEVELS.index(level.downcase.to_sym)
25
+ when Integer
26
+ MAPPED_LEVELS[level]
27
+ end ||
28
+ raise(ArgumentError, "Invalid level:#{level.inspect} being requested. Must be one of #{LEVELS.inspect}")
33
29
  end
34
30
 
35
31
  # Returns the symbolic level for the supplied level index
@@ -219,11 +219,11 @@ module SemanticLogger
219
219
 
220
220
  seconds = duration / 1000
221
221
  if seconds >= 86_400.0 # 1 day
222
- "#{(seconds / 86_400).to_i}d #{Time.at(seconds).strftime('%-Hh %-Mm')}"
222
+ "#{(seconds / 86_400).to_i}d #{Time.at(seconds).utc.strftime('%-Hh %-Mm')}"
223
223
  elsif seconds >= 3600.0 # 1 hour
224
- Time.at(seconds).strftime("%-Hh %-Mm")
224
+ Time.at(seconds).utc.strftime("%-Hh %-Mm")
225
225
  elsif seconds >= 60.0 # 1 minute
226
- Time.at(seconds).strftime("%-Mm %-Ss")
226
+ Time.at(seconds).utc.strftime("%-Mm %-Ss")
227
227
  elsif seconds >= 1.0 # 1 second
228
228
  "#{format('%.3f', seconds)}s"
229
229
  else
@@ -1,4 +1,3 @@
1
- require "concurrent"
2
1
  module SemanticLogger
3
2
  # Logger stores the class name to be used for all log messages so that every
4
3
  # log message written by this instance will include the class name
@@ -1,4 +1,3 @@
1
- require "concurrent"
2
1
  require "socket"
3
2
 
4
3
  module SemanticLogger
@@ -253,9 +252,9 @@ module SemanticLogger
253
252
  # When the log_level_signal is raised on this process, the global default log level
254
253
  # rotates through the following log levels in the following order, starting
255
254
  # from the current global default level:
256
- # :warn, :info, :debug, :trace
255
+ # :fatal, :error, :warn, :info, :debug, :trace
257
256
  #
258
- # If the current level is :trace it wraps around back to :warn
257
+ # If the current level is :trace it wraps around back to :fatal
259
258
  #
260
259
  # 2. Logging a Ruby thread dump
261
260
  #
@@ -279,10 +278,11 @@ module SemanticLogger
279
278
  def self.add_signal_handler(log_level_signal = "USR2", thread_dump_signal = "TTIN", gc_log_microseconds = 100_000)
280
279
  if log_level_signal
281
280
  Signal.trap(log_level_signal) do
282
- index = default_level == :trace ? LEVELS.find_index(:error) : LEVELS.find_index(default_level)
283
- new_level = LEVELS[index - 1]
284
- self["SemanticLogger"].warn "Changed global default log level to #{new_level.inspect}"
281
+ current_level_index = LEVELS.find_index(default_level)
282
+ new_level_index = current_level_index == 0 ? LEVELS.size - 1 : current_level_index - 1
283
+ new_level = LEVELS[new_level_index]
285
284
  self.default_level = new_level
285
+ self["SemanticLogger"].warn "Changed global default log level to #{new_level.inspect}"
286
286
  end
287
287
  end
288
288
 
@@ -516,4 +516,27 @@ module SemanticLogger
516
516
  @backtrace_level = :error
517
517
  @backtrace_level_index = Levels.index(@backtrace_level)
518
518
  @sync = false
519
+
520
+ # @formatter:off
521
+ module Metric
522
+ autoload :NewRelic, "semantic_logger/metric/new_relic"
523
+ autoload :Signalfx, "semantic_logger/metric/signalfx"
524
+ autoload :Statsd, "semantic_logger/metric/statsd"
525
+ end
526
+
527
+ module Reporters
528
+ autoload :Minitest, "semantic_logger/reporters/minitest"
529
+ end
530
+
531
+ module Test
532
+ autoload :CaptureLogEvents, "semantic_logger/test/capture_log_events"
533
+ autoload :Minitest, "semantic_logger/test/minitest"
534
+ end
535
+
536
+ if defined?(JRuby)
537
+ module JRuby
538
+ autoload :GarbageCollectionLogger, "semantic_logger/jruby/garbage_collection_logger"
539
+ end
540
+ end
541
+ # @formatter:on
519
542
  end
@@ -1,14 +1,28 @@
1
1
  module SemanticLogger
2
- # Thread that submits and processes log requests
2
+ # The SyncProcessor performs logging in the current thread.
3
+ #
4
+ # Appenders are designed to only be used by one thread at a time, so all calls
5
+ # are mutex protected in case SyncProcessor is being used in a multi-threaded environment.
3
6
  class SyncProcessor
4
- extend Forwardable
7
+ def add(*args, &block)
8
+ @mutex.synchronize { @appenders.add(*args, &block) }
9
+ end
10
+
11
+ def log(*args, &block)
12
+ @mutex.synchronize { @appenders.log(*args, &block) }
13
+ end
14
+
15
+ def flush
16
+ @mutex.synchronize { @appenders.flush }
17
+ end
18
+
19
+ def close
20
+ @mutex.synchronize { @appenders.close }
21
+ end
5
22
 
6
- # Forward methods that can be called directly
7
- def_delegator :@appenders, :add
8
- def_delegator :@appenders, :log
9
- def_delegator :@appenders, :flush
10
- def_delegator :@appenders, :close
11
- def_delegator :@appenders, :reopen
23
+ def reopen(*args)
24
+ @mutex.synchronize { @appenders.reopen(*args) }
25
+ end
12
26
 
13
27
  # Allow the internal logger to be overridden from its default of $stderr
14
28
  # Can be replaced with another Ruby logger or Rails logger, but never to
@@ -33,6 +47,7 @@ module SemanticLogger
33
47
  attr_reader :appenders
34
48
 
35
49
  def initialize(appenders = nil)
50
+ @mutex = Mutex.new
36
51
  @appenders = appenders || Appenders.new(self.class.logger.dup)
37
52
  end
38
53
 
@@ -24,10 +24,15 @@ module SemanticLogger
24
24
  # By default collect all log levels, and collect metric only log events.
25
25
  def initialize(level: :trace, metrics: true)
26
26
  super(level: level, metrics: true)
27
+ @events = []
27
28
  end
28
29
 
29
30
  def log(log)
30
- (@events ||= []) << log
31
+ @events << log
32
+ end
33
+
34
+ def clear
35
+ @events.clear
31
36
  end
32
37
  end
33
38
  end
@@ -0,0 +1,53 @@
1
+ module SemanticLogger
2
+ module Test
3
+ module Minitest
4
+ # Returns [Array<SemanticLogger::Log>] the log events from Semantic Logger
5
+ # captured whilst executing the supplied block.
6
+ def semantic_logger_events(klass = nil, &block)
7
+ logger = SemanticLogger::Test::CaptureLogEvents.new
8
+ if klass
9
+ klass.stub(:logger, logger, &block)
10
+ else
11
+ SemanticLogger.silence(:trace) do
12
+ SemanticLogger::Logger.stub(:processor, logger, &block)
13
+ end
14
+ end
15
+ logger.events
16
+ end
17
+
18
+ # Verify a single log event has all the required attributes.
19
+ def assert_semantic_logger_event(event, level: nil, name: nil, message: nil, message_includes: nil,
20
+ payload: nil, payload_includes: nil,
21
+ thread_name: nil, tags: nil, named_tags: nil, context: nil,
22
+ metric: nil, metric_amount: nil, dimensions: nil)
23
+ msg = message || message_includes || "no message"
24
+ assert event, "Log event missing for message: '#{msg}'"
25
+ assert_equal message, event.message if message
26
+ assert_includes event.message, message_includes if message_includes
27
+ assert_equal name, event.name, -> { "Mismatched log name for message: '#{msg}'" } if name
28
+ assert_equal level, event.level, -> { "Mismatched log level for message: '#{msg}'" } if level
29
+
30
+ if payload_includes
31
+ payload_includes.each_pair do |key, expected_value|
32
+ value = event.payload[key]
33
+ if expected_value.nil?
34
+ assert_nil value, -> { "Mismatched key: #{key.inspect} in log payload: #{event.payload} for message: '#{msg}'" }
35
+ else
36
+ assert_equal expected_value, value, -> { "Mismatched key: #{key.inspect} in log payload: #{event.payload} for message: '#{msg}'" }
37
+ end
38
+ end
39
+ elsif payload
40
+ assert_equal payload, event.payload, -> { "Mismatched log payload: #{event.payload} for message: '#{msg}'" }
41
+ end
42
+
43
+ assert_equal thread_name, event.thread_name, -> { "Mismatched thread_name for message: '#{msg}'" } if thread_name
44
+ assert_equal tags, event.tags, -> { "Mismatched tags for message: '#{msg}'" } if tags
45
+ assert_equal named_tags, event.named_tags, -> { "Mismatched named_tags for message: '#{msg}'" } if named_tags
46
+ assert_equal context, event.context, -> { "Mismatched context for message: '#{msg}'" } if context
47
+ assert_equal metric, event.metric, -> { "Mismatched metric for message: '#{msg}'" } if metric
48
+ assert_equal metric_amount, event.metric_amount, -> { "Mismatched metric_amount for message: '#{msg}'" } if metric_amount
49
+ assert_equal dimensions, event.dimensions, -> { "Mismatched dimensions for message: '#{msg}'" } if dimensions
50
+ end
51
+ end
52
+ end
53
+ end
@@ -1,3 +1,3 @@
1
1
  module SemanticLogger
2
- VERSION = "4.11.0".freeze
2
+ VERSION = "4.14.0".freeze
3
3
  end
@@ -1,49 +1,22 @@
1
+ require "concurrent"
1
2
  require "semantic_logger/core_ext/thread"
2
3
  require "semantic_logger/version"
3
-
4
- # @formatter:off
5
- module SemanticLogger
6
- autoload :AnsiColors, "semantic_logger/ansi_colors"
7
- autoload :Appender, "semantic_logger/appender"
8
- autoload :Appenders, "semantic_logger/appenders"
9
- autoload :Base, "semantic_logger/base"
10
- autoload :DebugAsTraceLogger, "semantic_logger/debug_as_trace_logger"
11
- autoload :Formatters, "semantic_logger/formatters"
12
- autoload :Levels, "semantic_logger/levels"
13
- autoload :Log, "semantic_logger/log"
14
- autoload :Logger, "semantic_logger/logger"
15
- autoload :Loggable, "semantic_logger/loggable"
16
- autoload :Processor, "semantic_logger/processor"
17
- autoload :Subscriber, "semantic_logger/subscriber"
18
- autoload :SyncProcessor, "semantic_logger/sync_processor"
19
- autoload :Utils, "semantic_logger/utils"
20
-
21
- module Concerns
22
- autoload :Compatibility, "semantic_logger/concerns/compatibility"
23
- end
24
-
25
- module Metric
26
- autoload :NewRelic, "semantic_logger/metric/new_relic"
27
- autoload :Signalfx, "semantic_logger/metric/signalfx"
28
- autoload :Statsd, "semantic_logger/metric/statsd"
29
- end
30
-
31
- module Reporters
32
- autoload :Minitest, "semantic_logger/reporters/minitest"
33
- end
34
-
35
- module Test
36
- autoload :CaptureLogEvents, "semantic_logger/test/capture_log_events"
37
- end
38
-
39
- if defined?(JRuby)
40
- module JRuby
41
- autoload :GarbageCollectionLogger, "semantic_logger/jruby/garbage_collection_logger"
42
- end
43
- end
44
- end
4
+ require "semantic_logger/utils"
5
+ require "semantic_logger/ansi_colors"
6
+ require "semantic_logger/levels"
7
+ require "semantic_logger/base"
8
+ require "semantic_logger/formatters"
9
+ require "semantic_logger/log"
10
+ require "semantic_logger/subscriber"
11
+ require "semantic_logger/loggable"
12
+ require "semantic_logger/concerns/compatibility"
13
+ require "semantic_logger/appender"
14
+ require "semantic_logger/appenders"
15
+ require "semantic_logger/processor"
16
+ require "semantic_logger/sync_processor"
17
+ require "semantic_logger/logger"
18
+ require "semantic_logger/debug_as_trace_logger"
45
19
  require "semantic_logger/semantic_logger"
46
- # @formatter:on
47
20
 
48
21
  # Flush all appenders at exit, waiting for outstanding messages on the queue
49
22
  # to be written first.
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: 4.11.0
4
+ version: 4.14.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: 2022-05-13 00:00:00.000000000 Z
11
+ date: 2023-07-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -49,6 +49,7 @@ files:
49
49
  - lib/semantic_logger/appender/kafka.rb
50
50
  - lib/semantic_logger/appender/mongodb.rb
51
51
  - lib/semantic_logger/appender/new_relic.rb
52
+ - lib/semantic_logger/appender/new_relic_logs.rb
52
53
  - lib/semantic_logger/appender/rabbitmq.rb
53
54
  - lib/semantic_logger/appender/sentry.rb
54
55
  - lib/semantic_logger/appender/sentry_ruby.rb
@@ -70,6 +71,7 @@ files:
70
71
  - lib/semantic_logger/formatters/fluentd.rb
71
72
  - lib/semantic_logger/formatters/json.rb
72
73
  - lib/semantic_logger/formatters/logfmt.rb
74
+ - lib/semantic_logger/formatters/new_relic_logs.rb
73
75
  - lib/semantic_logger/formatters/one_line.rb
74
76
  - lib/semantic_logger/formatters/raw.rb
75
77
  - lib/semantic_logger/formatters/signalfx.rb
@@ -90,12 +92,17 @@ files:
90
92
  - lib/semantic_logger/sync.rb
91
93
  - lib/semantic_logger/sync_processor.rb
92
94
  - lib/semantic_logger/test/capture_log_events.rb
95
+ - lib/semantic_logger/test/minitest.rb
93
96
  - lib/semantic_logger/utils.rb
94
97
  - lib/semantic_logger/version.rb
95
98
  homepage: https://logger.rocketjob.io
96
99
  licenses:
97
100
  - Apache-2.0
98
- metadata: {}
101
+ metadata:
102
+ bug_tracker_uri: https://github.com/reidmorrison/semantic_logger/issues
103
+ documentation_uri: https://logger.rocketjob.io
104
+ source_code_uri: https://github.com/reidmorrison/semantic_logger/tree/4.14.0
105
+ rubygems_mfa_required: 'true'
99
106
  post_install_message:
100
107
  rdoc_options: []
101
108
  require_paths:
@@ -111,7 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
111
118
  - !ruby/object:Gem::Version
112
119
  version: '0'
113
120
  requirements: []
114
- rubygems_version: 3.3.7
121
+ rubygems_version: 3.4.9
115
122
  signing_key:
116
123
  specification_version: 4
117
124
  summary: Feature rich logging framework, and replacement for existing Ruby & Rails