semantic_logger 4.11.0 → 4.14.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 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