rails_semantic_logger 4.13.0 → 4.17.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: b82f4b18da96047400658caaf0cbdb073ac21d0d9dfc9f8e89a270fae80550af
4
- data.tar.gz: fcef56ce1f917619d302b3dced8680748f0945c3a5a16763aadf21007ef1a00d
3
+ metadata.gz: 9a96d9dc4eb844eb8561d52f66dc27e2e07edb6b61da2163b0351ce3b12759ff
4
+ data.tar.gz: 34145d723bd89d14e5377200862df57829739b2ee5e5422a58f1d0edd4cbff0f
5
5
  SHA512:
6
- metadata.gz: 6ca5b2a92ca494065abb52d7d44eb71e06ba044f190c10b710549b609f78924542eb4f5eb126c54f98bc1248daaa7e1e29b35139c527081f178c1119be76acdc
7
- data.tar.gz: 604c669579b6bf2e25b718008406fd2a5bfcacd32f39a5714b05656773e59634d15d10d3afd0277c098bc73f5836e577ad1412669947598b206f22e5af4379d4
6
+ metadata.gz: e99c63ad9e4d02377d5ea25ba528f5de4fa8f83a2aadd7ef19ef2e3b8f52fc67cd2d08130ec18478ab4e5a2785657928f4e863024d8214a76de3bff4618b6891
7
+ data.tar.gz: 2d6ccc0cb9e385a2ca3a0fdc65023948f968a83b044768fc9bc9ccfb78452d7d4325731cdc4c5397bb097efa1ad72a1ce05d5576429cb2eb05b8c984fa7f06bb
data/README.md CHANGED
@@ -3,12 +3,116 @@
3
3
 
4
4
  Rails Semantic Logger replaces the Rails default logger with [Semantic Logger](https://logger.rocketjob.io/)
5
5
 
6
+ When any large Rails application is deployed to production one of the first steps is to move to centralized logging, so that logs can be viewed and searched from a central location.
7
+
8
+ Centralized logging quickly falls apart when trying to consume the current human readable log files:
9
+ - Log entries often span multiple lines, resulting in unrelated log lines in the centralized logging system. For example, stack traces.
10
+ - Complex Regular Expressions are needed to parse the text lines and make them machine readable. For example to build queries, or alerts that are looking for specific elements in the message.
11
+ - Writing searches, alerts, or dashboards based on text logs is incredibly brittle, since a small change to the text logged can often break the parsing of those logs.
12
+ - Every log entry often has a completely different format, making it difficult to make consistent searches against the data.
13
+
14
+ For these and many other reasons switching to structured logging, or logs in JSON format, in testing and production makes centralized logging incredibly powerful.
15
+
16
+ For example, adding these lines to `config/application.rb` and removing any other log overrides from other environments, will switch automatically to structured logging when running inside Kubernetes:
17
+ ~~~ruby
18
+ # Setup structured logging
19
+ config.semantic_logger.application = "my_application"
20
+ config.semantic_logger.environment = ENV["STACK_NAME"] || Rails.env
21
+ config.log_level = ENV["LOG_LEVEL"] || :info
22
+
23
+ # Switch to JSON Logging output to stdout when running on Kubernetes
24
+ if ENV["LOG_TO_CONSOLE"] || ENV["KUBERNETES_SERVICE_HOST"]
25
+ config.rails_semantic_logger.add_file_appender = false
26
+ config.semantic_logger.add_appender(io: $stdout, formatter: :json)
27
+ end
28
+ ~~~
29
+
30
+ Then configure the centralized logging system to tell it that the data is in JSON format, so that it will parse it for you into a hierarchy.
31
+
32
+ For example, the following will instruct [Observe](https://www.observeinc.com/) to parse the JSON data and create machine readable data from it:
33
+ ~~~ruby
34
+ interface "log", "log":log
35
+
36
+ make_col event:parse_json(log)
37
+
38
+ make_col
39
+ time:parse_isotime(event.timestamp),
40
+ application:string(event.application),
41
+ environment:string(event.environment),
42
+ duration:duration_ms(event.duration_ms),
43
+ level:string(event.level),
44
+ name:string(event.name),
45
+ message:string(event.message),
46
+ named_tags:event.named_tags,
47
+ payload:event.payload,
48
+ metric:string(event.metric),
49
+ metric_amount:float64(event.metric_amount),
50
+ tags:array(event.tags),
51
+ exception:event.exception,
52
+ host:string(event.host),
53
+ pid:int64(event.pid),
54
+ thread:string(event.thread),
55
+ file:string(event.file),
56
+ line:int64(event.line),
57
+ dimensions:event.dimensions,
58
+ backtrace:array(event.backtrace),
59
+ level_index:int64(event.level_index)
60
+
61
+ set_valid_from(time)
62
+ drop_col timestamp, log, event, stream
63
+ rename_col timestamp:time
64
+ ~~~
65
+
66
+ Now queries can be built to drill down into each of these fields, including `payload` which is a nested object.
67
+
68
+ For example to find all failed Sidekiq job calls where the causing exception class name is `NoMethodError`:
69
+ ~~~ruby
70
+ filter environment = "uat2"
71
+ filter level = "error"
72
+ filter metric = "sidekiq.job.perform"
73
+ filter (string(exception.cause.name) = "NoMethodError")
74
+ ~~~
75
+
76
+ Example: create a dashboard showing the duration of all successful Sidekiq jobs:
77
+ ~~~ruby
78
+ filter environment = "production"
79
+ filter level = "info"
80
+ filter metric = "sidekiq.job.perform"
81
+ timechart duration:avg(duration), group_by(name)
82
+ ~~~
83
+
84
+ Example: create a dashboard showing the queue latency of all Sidekiq jobs.
85
+ The queue latency is the time between when the job was enqueued and when it was started:
86
+ ~~~ruby
87
+ filter environment = "production"
88
+ filter level = "info"
89
+ filter metric = "sidekiq.queue.latency"
90
+ timechart latency:avg(metric_amount/1000), group_by(string(named_tags.queue))
91
+ ~~~
92
+
6
93
  * http://github.com/reidmorrison/rails_semantic_logger
7
94
 
8
95
  ## Documentation
9
96
 
10
97
  For complete documentation see: https://logger.rocketjob.io/rails
11
98
 
99
+ ## Upgrading to Semantic Logger V4.16 - Sidekiq Metrics Support
100
+
101
+ Rails Semantic Logger now supports Sidekiq metrics.
102
+ Below are the metrics that are now available when the JSON logging format is used:
103
+ - `sidekiq.job.perform`
104
+ - The duration of each Sidekiq job.
105
+ - `duration` contains the time in milliseconds that the job took to run.
106
+ - `sidekiq.queue.latency`
107
+ - The time between when a Sidekiq job was enqueued and when it was started.
108
+ - `metric_amount` contains the time in milliseconds that the job was waiting in the queue.
109
+
110
+ ## Upgrading to Semantic Logger v4.15 & V4.16 - Sidekiq Support
111
+
112
+ Rails Semantic Logger introduces direct support for Sidekiq v4, v5, v6, and v7.
113
+ Please remove any previous custom patches or configurations to make Sidekiq work with Semantic Logger.
114
+ To see the complete list of patches being made, and to contribute your own changes, see: [Sidekiq Patches](https://github.com/reidmorrison/rails_semantic_logger/blob/master/lib/rails_semantic_logger/extensions/sidekiq/sidekiq.rb)
115
+
12
116
  ## Upgrading to Semantic Logger v4.4
13
117
 
14
118
  With some forking frameworks it is necessary to call `reopen` after the fork. With v4.4 the
@@ -19,7 +123,18 @@ I.e. Please remove the following line if being called anywhere:
19
123
  SemanticLogger::Processor.instance.instance_variable_set(:@queue, Queue.new)
20
124
  ~~~
21
125
 
22
- ## Supports
126
+ ## New Versions of Rails, etc.
127
+
128
+ The primary purpose of the Rails Semantic Logger gem is to patch other gems, primarily Rails, to make them support structured logging though Semantic Logger.
129
+
130
+ When new versions of Rails and other gems are published they often make changes to the internals, so the existing patches stop working.
131
+
132
+ Rails Semantic Logger survives only when someone in the community upgrades to a newer Rails or other supported libraries, runs into problems,
133
+ and then contributes the fix back to the community by means of a pull request.
134
+
135
+ Additionally, when new popular gems come out, we rely only the community to supply the necessary patches in Rails Semantic Logger to make those gems support structured logging.
136
+
137
+ ## Supported Platforms
23
138
 
24
139
  For the complete list of supported Ruby and Rails versions, see the [Testing file](https://github.com/reidmorrison/rails_semantic_logger/blob/master/.github/workflows/ci.yml).
25
140
 
@@ -14,10 +14,10 @@ module RailsSemanticLogger
14
14
 
15
15
  # Unused, but needed for Devise 401 status code monkey patch to still work.
16
16
  ::ActionController::Base.log_process_action(payload)
17
-
17
+
18
18
  params = payload[:params]
19
19
 
20
- if params.kind_of?(Hash) || params.kind_of?(::ActionController::Parameters)
20
+ if params.is_a?(Hash) || params.is_a?(::ActionController::Parameters)
21
21
  # According to PR https://github.com/reidmorrison/rails_semantic_logger/pull/37/files
22
22
  # params is not always a Hash.
23
23
  payload[:params] = params.to_unsafe_h unless params.is_a?(Hash)
@@ -79,7 +79,9 @@ module RailsSemanticLogger
79
79
  end
80
80
 
81
81
  def send_data(event)
82
- controller_logger(event).info(message: "Sent data", payload: {file_name: event.payload[:filename]}, duration: event.duration)
82
+ controller_logger(event).info(message: "Sent data",
83
+ payload: {file_name: event.payload[:filename]},
84
+ duration: event.duration)
83
85
  end
84
86
 
85
87
  def unpermitted_parameters(event)
@@ -9,22 +9,22 @@ module RailsSemanticLogger
9
9
  message_id = event.payload[:message_id]
10
10
  duration = event.duration.round(1)
11
11
  if ex
12
- log_with_formatter event: event, log_duration: true, level: :error do |fmt|
12
+ log_with_formatter event: event, log_duration: true, level: :error do |_fmt|
13
13
  {
14
- message: "Error delivering mail #{message_id} (#{duration}ms)",
14
+ message: "Error delivering mail #{message_id} (#{duration}ms)",
15
15
  exception: ex
16
16
  }
17
17
  end
18
18
  else
19
- message = begin
19
+ message =
20
20
  if event.payload[:perform_deliveries]
21
21
  "Delivered mail #{message_id} (#{duration}ms)"
22
22
  else
23
23
  "Skipped delivery of mail #{message_id} as `perform_deliveries` is false"
24
24
  end
25
- end
26
- log_with_formatter event: event, log_duration: true do |fmt|
27
- { message: message }
25
+
26
+ log_with_formatter event: event, log_duration: true do |_fmt|
27
+ {message: message}
28
28
  end
29
29
  end
30
30
  end
@@ -34,8 +34,8 @@ module RailsSemanticLogger
34
34
  mailer = event.payload[:mailer]
35
35
  action = event.payload[:action]
36
36
  duration = event.duration.round(1)
37
- log_with_formatter event: event do |fmt|
38
- { message: "#{mailer}##{action}: processed outbound mail in #{duration}ms" }
37
+ log_with_formatter event: event do |_fmt|
38
+ {message: "#{mailer}##{action}: processed outbound mail in #{duration}ms"}
39
39
  end
40
40
  end
41
41
 
@@ -74,8 +74,6 @@ module RailsSemanticLogger
74
74
  event.payload[:date].to_time.utc
75
75
  elsif event.payload[:date].is_a?(String)
76
76
  Time.parse(date).utc
77
- else
78
- nil
79
77
  end
80
78
  end
81
79
 
@@ -94,8 +92,8 @@ module RailsSemanticLogger
94
92
  def formatted_args
95
93
  if defined?(mailer.constantize.log_arguments?) && !mailer.constantize.log_arguments?
96
94
  ""
97
- else
98
- JSON.pretty_generate(event.payload[:args].map { |arg| format(arg) }) if event.payload[:args].present?
95
+ elsif event.payload[:args].present?
96
+ JSON.pretty_generate(event.payload[:args].map { |arg| format(arg) })
99
97
  end
100
98
  end
101
99
 
@@ -19,7 +19,7 @@ module RailsSemanticLogger
19
19
  def render_template(event)
20
20
  return unless should_log?
21
21
 
22
- payload = {
22
+ payload = {
23
23
  template: from_rails_root(event.payload[:identifier])
24
24
  }
25
25
  payload[:within] = from_rails_root(event.payload[:layout]) if event.payload[:layout]
@@ -36,7 +36,7 @@ module RailsSemanticLogger
36
36
  def render_partial(event)
37
37
  return unless should_log?
38
38
 
39
- payload = {
39
+ payload = {
40
40
  partial: from_rails_root(event.payload[:identifier])
41
41
  }
42
42
  payload[:within] = from_rails_root(event.payload[:layout]) if event.payload[:layout]
@@ -56,7 +56,7 @@ module RailsSemanticLogger
56
56
 
57
57
  identifier = event.payload[:identifier] || "templates"
58
58
 
59
- payload = {
59
+ payload = {
60
60
  template: from_rails_root(identifier),
61
61
  count: event.payload[:count]
62
62
  }
@@ -72,9 +72,9 @@ module RailsSemanticLogger
72
72
  end
73
73
 
74
74
  def start(name, id, payload)
75
- if (name == "render_template.action_view" || name == "render_layout.action_view") && should_log?
75
+ if ["render_template.action_view", "render_layout.action_view"].include?(name) && should_log?
76
76
  qualifier = " layout" if name == "render_layout.action_view"
77
- payload = { template: from_rails_root(payload[:identifier]) }
77
+ payload = {template: from_rails_root(payload[:identifier])}
78
78
  payload[:within] = from_rails_root(payload[:layout]) if payload[:layout]
79
79
 
80
80
  logger.send(self.class.rendered_log_level, message: "Rendering#{qualifier}", payload: payload)
@@ -84,18 +84,19 @@ module RailsSemanticLogger
84
84
  end
85
85
 
86
86
  if (Rails::VERSION::MAJOR == 7 && Rails::VERSION::MINOR >= 1) || Rails::VERSION::MAJOR > 7
87
- class Start # :nodoc:
88
- def start(name, id, payload)
87
+ class Start
88
+ def start(name, _id, payload)
89
89
  return unless %w[render_template.action_view render_layout.action_view].include?(name)
90
90
 
91
91
  qualifier = " layout" if name == "render_layout.action_view"
92
- payload = { template: from_rails_root(payload[:identifier]) }
92
+ payload = {template: from_rails_root(payload[:identifier])}
93
93
  payload[:within] = from_rails_root(payload[:layout]) if payload[:layout]
94
94
 
95
95
  logger.debug(message: "Rendering#{qualifier}", payload: payload)
96
96
  end
97
97
 
98
- def finish(name, id, payload) end
98
+ def finish(name, id, payload)
99
+ end
99
100
 
100
101
  private
101
102
 
@@ -105,7 +106,7 @@ module RailsSemanticLogger
105
106
  string
106
107
  end
107
108
 
108
- def rails_root # :doc:
109
+ def rails_root
109
110
  @root ||= "#{Rails.root}/"
110
111
  end
111
112
 
@@ -117,8 +118,10 @@ module RailsSemanticLogger
117
118
  def self.attach_to(*)
118
119
  ActiveSupport::Notifications.unsubscribe("render_template.action_view")
119
120
  ActiveSupport::Notifications.unsubscribe("render_layout.action_view")
120
- ActiveSupport::Notifications.subscribe("render_template.action_view", RailsSemanticLogger::ActionView::LogSubscriber::Start.new)
121
- ActiveSupport::Notifications.subscribe("render_layout.action_view", RailsSemanticLogger::ActionView::LogSubscriber::Start.new)
121
+ ActiveSupport::Notifications.subscribe("render_template.action_view",
122
+ RailsSemanticLogger::ActionView::LogSubscriber::Start.new)
123
+ ActiveSupport::Notifications.subscribe("render_layout.action_view",
124
+ RailsSemanticLogger::ActionView::LogSubscriber::Start.new)
122
125
 
123
126
  super
124
127
  end
@@ -9,17 +9,17 @@ module RailsSemanticLogger
9
9
  if ex
10
10
  log_with_formatter level: :error, event: event do |fmt|
11
11
  {
12
- message: "Failed enqueuing #{fmt.job_info} (#{ex.class} (#{ex.message})",
12
+ message: "Failed enqueuing #{fmt.job_info} (#{ex.class} (#{ex.message})",
13
13
  exception: ex
14
14
  }
15
15
  end
16
16
  elsif event.payload[:aborted]
17
17
  log_with_formatter level: :info, event: event do |fmt|
18
- { message: "Failed enqueuing #{fmt.job_info}, a before_enqueue callback halted the enqueuing execution." }
18
+ {message: "Failed enqueuing #{fmt.job_info}, a before_enqueue callback halted the enqueuing execution."}
19
19
  end
20
20
  else
21
21
  log_with_formatter event: event do |fmt|
22
- { message: "Enqueued #{fmt.job_info}" }
22
+ {message: "Enqueued #{fmt.job_info}"}
23
23
  end
24
24
  end
25
25
  end
@@ -30,13 +30,13 @@ module RailsSemanticLogger
30
30
  if ex
31
31
  log_with_formatter level: :error, event: event do |fmt|
32
32
  {
33
- message: "Failed enqueuing #{fmt.job_info} (#{ex.class} (#{ex.message})",
33
+ message: "Failed enqueuing #{fmt.job_info} (#{ex.class} (#{ex.message})",
34
34
  exception: ex
35
35
  }
36
36
  end
37
37
  elsif event.payload[:aborted]
38
38
  log_with_formatter level: :info, event: event do |fmt|
39
- { message: "Failed enqueuing #{fmt.job_info}, a before_enqueue callback halted the enqueuing execution." }
39
+ {message: "Failed enqueuing #{fmt.job_info}, a before_enqueue callback halted the enqueuing execution."}
40
40
  end
41
41
  else
42
42
  log_with_formatter event: event do |fmt|
@@ -56,7 +56,7 @@ module RailsSemanticLogger
56
56
  if ex
57
57
  log_with_formatter event: event, log_duration: true, level: :error do |fmt|
58
58
  {
59
- message: "Error performing #{fmt.job_info} in #{event.duration.round(2)}ms",
59
+ message: "Error performing #{fmt.job_info} in #{event.duration.round(2)}ms",
60
60
  exception: ex
61
61
  }
62
62
  end
@@ -169,9 +169,7 @@ module RailsSemanticLogger
169
169
  def render_bind_v6_1(attr, value)
170
170
  case attr
171
171
  when ActiveModel::Attribute
172
- if attr.type.binary? && attr.value
173
- value = "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
174
- end
172
+ value = "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>" if attr.type.binary? && attr.value
175
173
  when Array
176
174
  attr = attr.first
177
175
  else
@@ -77,7 +77,7 @@ module RailsSemanticLogger
77
77
  logger = SemanticLogger[Rails]
78
78
  logger.warn(
79
79
  "Rails Error: Unable to access log file. Please ensure that #{path} exists and is chmod 0666. " \
80
- "The log level has been raised to WARN and the output directed to STDERR until the problem is fixed.",
80
+ "The log level has been raised to WARN and the output directed to STDERR until the problem is fixed.",
81
81
  e
82
82
  )
83
83
  logger
@@ -108,17 +108,36 @@ module RailsSemanticLogger
108
108
  Resque.logger = SemanticLogger[Resque] if defined?(Resque) && Resque.respond_to?(:logger=)
109
109
 
110
110
  # Replace the Sidekiq logger
111
- if defined?(Sidekiq)
112
- if Sidekiq.respond_to?(:logger=)
113
- Sidekiq.logger = SemanticLogger[Sidekiq]
114
- elsif Sidekiq::VERSION[0..1] == '7.'
115
- method = Sidekiq.server? ? :configure_server : :configure_client
116
- Sidekiq.public_send(method) { |cfg| cfg.logger = SemanticLogger[Sidekiq] }
111
+ if defined?(::Sidekiq)
112
+ ::Sidekiq.configure_client do |config|
113
+ config.logger = ::SemanticLogger[::Sidekiq]
114
+ end
115
+
116
+ ::Sidekiq.configure_server do |config|
117
+ config.logger = ::SemanticLogger[::Sidekiq]
118
+ if config.respond_to?(:options)
119
+ config.options[:job_logger] = RailsSemanticLogger::Sidekiq::JobLogger
120
+ else
121
+ config[:job_logger] = RailsSemanticLogger::Sidekiq::JobLogger
122
+ end
123
+
124
+ # Add back the default console logger unless already added
125
+ SemanticLogger.add_appender(io: $stdout, formatter: :color) unless SemanticLogger.appenders.console_output?
126
+
127
+ # Replace default error handler when present
128
+ existing = RailsSemanticLogger::Sidekiq::Defaults.delete_default_error_handler(config.error_handlers)
129
+ config.error_handlers << RailsSemanticLogger::Sidekiq::Defaults::ERROR_HANDLER if existing
130
+ end
131
+
132
+ if defined?(::Sidekiq::Job) && (::Sidekiq::VERSION.to_i != 5)
133
+ ::Sidekiq::Job.singleton_class.prepend(RailsSemanticLogger::Sidekiq::Loggable)
134
+ else
135
+ ::Sidekiq::Worker.singleton_class.prepend(RailsSemanticLogger::Sidekiq::Loggable)
117
136
  end
118
137
  end
119
138
 
120
139
  # Replace the Sidetiq logger
121
- Sidetiq.logger = SemanticLogger[Sidetiq] if defined?(Sidetiq) && Sidetiq.respond_to?(:logger=)
140
+ Sidetiq.logger = SemanticLogger[Sidetiq] if defined?(Sidetiq) && Sidetiq.respond_to?(:logger=)
122
141
 
123
142
  # Replace the DelayedJob logger
124
143
  if defined?(Delayed::Worker)
@@ -143,7 +162,9 @@ module RailsSemanticLogger
143
162
  # Rails Patches
144
163
  require("rails_semantic_logger/extensions/action_cable/tagged_logger_proxy") if defined?(::ActionCable)
145
164
  require("rails_semantic_logger/extensions/action_controller/live") if defined?(::ActionController::Live)
146
- require("rails_semantic_logger/extensions/action_dispatch/debug_exceptions") if defined?(::ActionDispatch::DebugExceptions)
165
+ if defined?(::ActionDispatch::DebugExceptions)
166
+ require("rails_semantic_logger/extensions/action_dispatch/debug_exceptions")
167
+ end
147
168
  if defined?(::ActionView::StreamingTemplateRenderer::Body)
148
169
  require("rails_semantic_logger/extensions/action_view/streaming_template_renderer")
149
170
  end
@@ -152,7 +173,7 @@ module RailsSemanticLogger
152
173
 
153
174
  if config.rails_semantic_logger.semantic
154
175
  # Active Job
155
- if defined?(::ActiveJob) && defined?(::ActiveJob::Logging::LogSubscriber)
176
+ if defined?(::ActiveJob::Logging::LogSubscriber)
156
177
  RailsSemanticLogger.swap_subscriber(
157
178
  ::ActiveJob::Logging::LogSubscriber,
158
179
  RailsSemanticLogger::ActiveJob::LogSubscriber,
@@ -160,7 +181,7 @@ module RailsSemanticLogger
160
181
  )
161
182
  end
162
183
 
163
- if defined?(::ActiveJob) && defined?(::ActiveJob::LogSubscriber)
184
+ if defined?(::ActiveJob::LogSubscriber)
164
185
  RailsSemanticLogger.swap_subscriber(
165
186
  ::ActiveJob::LogSubscriber,
166
187
  RailsSemanticLogger::ActiveJob::LogSubscriber,
@@ -222,6 +243,8 @@ module RailsSemanticLogger
222
243
  :action_mailer
223
244
  )
224
245
  end
246
+
247
+ require("rails_semantic_logger/extensions/sidekiq/sidekiq") if defined?(::Sidekiq)
225
248
  end
226
249
 
227
250
  #
@@ -237,7 +260,7 @@ module RailsSemanticLogger
237
260
  end
238
261
 
239
262
  # Re-open appenders after Resque has forked a worker
240
- Resque.after_fork { |_job| ::SemanticLogger.reopen } if defined?(Resque)
263
+ Resque.after_fork { |_job| ::SemanticLogger.reopen } if defined?(Resque.after_fork)
241
264
 
242
265
  # Re-open appenders after Spring has forked a process
243
266
  Spring.after_fork { |_job| ::SemanticLogger.reopen } if defined?(Spring.after_fork)
@@ -6,9 +6,17 @@ module ActionDispatch
6
6
  private
7
7
 
8
8
  undef_method :log_error
9
- def log_error(_request, wrapper)
10
- ActiveSupport::Deprecation.silence do
11
- ActionController::Base.logger.fatal(wrapper.exception)
9
+ if (Rails::VERSION::MAJOR == 7 && Rails::VERSION::MINOR >= 1) || Rails::VERSION::MAJOR > 7
10
+ def log_error(_request, wrapper)
11
+ Rails.application.deprecators.silence do
12
+ ActionController::Base.logger.fatal(wrapper.exception)
13
+ end
14
+ end
15
+ else
16
+ def log_error(_request, wrapper)
17
+ ActiveSupport::Deprecation.silence do
18
+ ActionController::Base.logger.fatal(wrapper.exception)
19
+ end
12
20
  end
13
21
  end
14
22
  end
@@ -1,11 +1,13 @@
1
- require "active_support/log_subscriber"
1
+ if ActiveSupport::VERSION::STRING == "7.1.1"
2
+ require "active_support/log_subscriber"
2
3
 
3
- module ActiveSupport
4
- class LogSubscriber
5
- # @override Rails 7.1
6
- def silenced?(event)
7
- native_log_level = @event_levels.fetch(event, ::Logger::Severity::FATAL)
8
- logger.nil? || SemanticLogger::Levels.index(logger.level) > SemanticLogger::Levels.index(native_log_level)
4
+ module ActiveSupport
5
+ class LogSubscriber
6
+ # @override Rails 7.1
7
+ def silenced?(event)
8
+ native_log_level = @event_levels.fetch(event, ::Logger::Severity::FATAL)
9
+ logger.nil? || SemanticLogger::Levels.index(logger.level) > SemanticLogger::Levels.index(native_log_level)
10
+ end
9
11
  end
10
12
  end
11
13
  end
@@ -9,18 +9,18 @@ module ActiveSupport
9
9
  # Prevent broadcasting since SemanticLogger already supports multiple loggers
10
10
  if method_defined?(:broadcast)
11
11
  undef :broadcast
12
- def broadcast(logger)
12
+ def broadcast(_logger)
13
13
  Module.new
14
14
  end
15
15
  end
16
16
  end
17
17
 
18
18
  # Prevent Console from trying to merge loggers
19
- def self.logger_outputs_to?(*args)
19
+ def self.logger_outputs_to?(*_args)
20
20
  true
21
21
  end
22
22
 
23
- def self.new(*args, **kwargs)
23
+ def self.new(*_args, **_kwargs)
24
24
  SemanticLogger[self]
25
25
  end
26
26
  end
@@ -0,0 +1,12 @@
1
+ module RailsSemanticLogger
2
+ module Rackup
3
+ module Server
4
+ def daemonize_app
5
+ super
6
+ SemanticLogger.reopen
7
+ end
8
+ end
9
+ end
10
+ end
11
+
12
+ Rackup::Server.prepend(RailsSemanticLogger::Rackup::Server)
@@ -4,7 +4,7 @@ require "rails"
4
4
  module Rails
5
5
  class Server
6
6
  private
7
-
7
+
8
8
  undef_method :log_to_stdout if method_defined?(:log_to_stdout)
9
9
  def log_to_stdout
10
10
  wrapped_app # touch the app so the logger is set up
@@ -0,0 +1,70 @@
1
+ # Sidekiq patches
2
+ if Sidekiq::VERSION.to_i == 4
3
+ require "sidekiq/logging"
4
+ require "sidekiq/middleware/server/logging"
5
+ require "sidekiq/processor"
6
+ elsif Sidekiq::VERSION.to_i == 5
7
+ require "sidekiq/logging"
8
+ end
9
+
10
+ module Sidekiq
11
+ # Sidekiq v4 & v5
12
+ if defined?(::Sidekiq::Logging)
13
+ # Replace Sidekiq logging context
14
+ module Logging
15
+ def self.with_context(msg, &block)
16
+ SemanticLogger.tagged(msg, &block)
17
+ end
18
+
19
+ def self.job_hash_context(job_hash)
20
+ h = {jid: job_hash["jid"]}
21
+ h[:bid] = job_hash["bid"] if job_hash["bid"]
22
+ h[:queue] = job_hash["queue"] if job_hash["queue"]
23
+ h
24
+ end
25
+ end
26
+ end
27
+
28
+ # Sidekiq v4
29
+ if defined?(::Sidekiq::Middleware::Server::Logging)
30
+ # Convert string to machine readable format
31
+ class Processor
32
+ def log_context(job_hash)
33
+ h = {jid: job_hash["jid"]}
34
+ h[:bid] = job_hash["bid"] if job_hash["bid"]
35
+ h[:queue] = job_hash["queue"] if job_hash["queue"]
36
+ h
37
+ end
38
+ end
39
+
40
+ # Let Semantic Logger handle duration logging
41
+ module Middleware
42
+ module Server
43
+ class Logging
44
+ # rubocop:disable Style/ExplicitBlockArgument
45
+ def call(worker, item, queue)
46
+ SemanticLogger.tagged(queue: queue) do
47
+ worker.logger.info(
48
+ "Start #perform",
49
+ metric: "sidekiq.queue.latency",
50
+ metric_amount: job_latency_ms(item)
51
+ )
52
+ worker.logger.measure_info(
53
+ "Completed #perform",
54
+ on_exception_level: :error,
55
+ log_exception: :full,
56
+ metric: "sidekiq.job.perform"
57
+ ) { yield }
58
+ end
59
+ end
60
+
61
+ def job_latency_ms(job)
62
+ return unless job && job["enqueued_at"]
63
+
64
+ (Time.now.to_f - job["enqueued_at"].to_f) * 1000
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -37,7 +37,7 @@ module RailsSemanticLogger
37
37
  def call_app(request, env)
38
38
  instrumenter = ActiveSupport::Notifications.instrumenter
39
39
  instrumenter_state = instrumenter.start "request.action_dispatch", request: request
40
- instrumenter_finish = -> () {
40
+ instrumenter_finish = lambda {
41
41
  instrumenter.finish_with_state(instrumenter_state, "request.action_dispatch", request: request)
42
42
  }
43
43
 
@@ -0,0 +1,40 @@
1
+ module RailsSemanticLogger
2
+ module Sidekiq
3
+ module Defaults
4
+ # Prevent exception logging during standard error handling since the Job Logger below already logs the exception.
5
+ ERROR_HANDLER =
6
+ if ::Sidekiq::VERSION.to_f < 7.1 ||
7
+ (::Sidekiq::VERSION.to_f == 7.1 && ::Sidekiq::VERSION.split(".").last.to_i < 6)
8
+ lambda do |_ex, ctx|
9
+ unless ctx.empty?
10
+ job_hash = ctx[:job] || {}
11
+ klass = job_hash["display_class"] || job_hash["wrapped"] || job_hash["class"]
12
+ logger = klass ? SemanticLogger[klass] : Sidekiq.logger
13
+ ctx[:context] ? logger.warn(ctx[:context], ctx) : logger.warn(ctx)
14
+ end
15
+ end
16
+ else
17
+ lambda do |_ex, ctx, _default_configuration|
18
+ unless ctx.empty?
19
+ job_hash = ctx[:job] || {}
20
+ klass = job_hash["display_class"] || job_hash["wrapped"] || job_hash["class"]
21
+ logger = klass ? SemanticLogger[klass] : Sidekiq.logger
22
+ ctx[:context] ? logger.warn(ctx[:context], ctx) : logger.warn(ctx)
23
+ end
24
+ end
25
+ end
26
+
27
+ # Returns the default logger after removing from the supplied list.
28
+ # Returns [nil] when the default logger was not present.
29
+ def self.delete_default_error_handler(error_handlers)
30
+ return error_handlers.delete(::Sidekiq::Config::ERROR_HANDLER) if defined?(::Sidekiq::Config::ERROR_HANDLER)
31
+ return error_handlers.delete(::Sidekiq::DEFAULT_ERROR_HANDLER) if defined?(::Sidekiq::DEFAULT_ERROR_HANDLER)
32
+
33
+ return unless defined?(::Sidekiq::ExceptionHandler)
34
+
35
+ existing = error_handlers.find { |handler| handler.is_a?(::Sidekiq::ExceptionHandler::Logger) }
36
+ error_handlers.delete(existing) if existing
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,59 @@
1
+ module RailsSemanticLogger
2
+ module Sidekiq
3
+ class JobLogger
4
+ # Sidekiq 6.5 does not take any arguments, whereas v7 is given a logger
5
+ def initialize(*_args)
6
+ end
7
+
8
+ def call(item, queue, &block)
9
+ klass = item["wrapped"] || item["class"]
10
+ logger = klass ? SemanticLogger[klass] : Sidekiq.logger
11
+
12
+ SemanticLogger.tagged(queue: queue) do
13
+ # Latency is the time between when the job was enqueued and when it started executing.
14
+ logger.info(
15
+ "Start #perform",
16
+ metric: "sidekiq.queue.latency",
17
+ metric_amount: job_latency_ms(item)
18
+ )
19
+
20
+ # Measure the duration of running the job
21
+ logger.measure_info(
22
+ "Completed #perform",
23
+ on_exception_level: :error,
24
+ log_exception: :full,
25
+ metric: "sidekiq.job.perform",
26
+ &block
27
+ )
28
+ end
29
+ end
30
+
31
+ def prepare(job_hash, &block)
32
+ level = job_hash["log_level"]
33
+ if level
34
+ SemanticLogger.silence(level) do
35
+ SemanticLogger.tagged(job_hash_context(job_hash), &block)
36
+ end
37
+ else
38
+ SemanticLogger.tagged(job_hash_context(job_hash), &block)
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def job_hash_context(job_hash)
45
+ h = {jid: job_hash["jid"]}
46
+ h[:bid] = job_hash["bid"] if job_hash["bid"]
47
+ h[:tags] = job_hash["tags"] if job_hash["tags"]
48
+ h[:queue] = job_hash["queue"] if job_hash["queue"]
49
+ h
50
+ end
51
+
52
+ def job_latency_ms(job)
53
+ return unless job && job["enqueued_at"]
54
+
55
+ (Time.now.to_f - job["enqueued_at"].to_f) * 1000
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,10 @@
1
+ module RailsSemanticLogger
2
+ module Sidekiq
3
+ module Loggable
4
+ def included(base)
5
+ super
6
+ base.include(SemanticLogger::Loggable)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -1,3 +1,3 @@
1
1
  module RailsSemanticLogger
2
- VERSION = "4.13.0".freeze
2
+ VERSION = "4.17.0".freeze
3
3
  end
@@ -6,25 +6,37 @@ module RailsSemanticLogger
6
6
  module ActionController
7
7
  autoload :LogSubscriber, "rails_semantic_logger/action_controller/log_subscriber"
8
8
  end
9
+
9
10
  module ActionMailer
10
11
  autoload :LogSubscriber, "rails_semantic_logger/action_mailer/log_subscriber"
11
12
  end
13
+
12
14
  module ActionView
13
15
  autoload :LogSubscriber, "rails_semantic_logger/action_view/log_subscriber"
14
16
  end
17
+
15
18
  module ActiveJob
16
19
  autoload :LogSubscriber, "rails_semantic_logger/active_job/log_subscriber"
17
20
  end
21
+
18
22
  module ActiveRecord
19
23
  autoload :LogSubscriber, "rails_semantic_logger/active_record/log_subscriber"
20
24
  end
25
+
21
26
  module Rack
22
27
  autoload :Logger, "rails_semantic_logger/rack/logger"
23
28
  end
29
+
24
30
  module DelayedJob
25
31
  autoload :Plugin, "rails_semantic_logger/delayed_job/plugin"
26
32
  end
27
33
 
34
+ module Sidekiq
35
+ autoload :Defaults, "rails_semantic_logger/sidekiq/defaults"
36
+ autoload :JobLogger, "rails_semantic_logger/sidekiq/job_logger"
37
+ autoload :Loggable, "rails_semantic_logger/sidekiq/loggable"
38
+ end
39
+
28
40
  autoload :Options, "rails_semantic_logger/options"
29
41
 
30
42
  # Swap an existing subscriber with a new one
@@ -48,9 +60,11 @@ module RailsSemanticLogger
48
60
  end
49
61
 
50
62
  def self.subscriber_patterns(subscriber)
51
- subscriber.patterns.respond_to?(:keys) ?
52
- subscriber.patterns.keys :
63
+ if subscriber.patterns.respond_to?(:keys)
64
+ subscriber.patterns.keys
65
+ else
53
66
  subscriber.patterns
67
+ end
54
68
  end
55
69
 
56
70
  private_class_method :subscriber_patterns, :unattach
@@ -59,4 +73,14 @@ end
59
73
  require("rails_semantic_logger/extensions/mongoid/config") if defined?(Mongoid)
60
74
  require("rails_semantic_logger/extensions/active_support/logger") if defined?(ActiveSupport::Logger)
61
75
  require("rails_semantic_logger/extensions/active_support/log_subscriber") if defined?(ActiveSupport::LogSubscriber)
62
- require("rails_semantic_logger/extensions/rack/server") if defined?(Rack::Server)
76
+
77
+ begin
78
+ require "rackup"
79
+ rescue LoadError
80
+ # No need to do anything, will fall back to Rack
81
+ end
82
+ if defined?(Rackup::Server)
83
+ require("rails_semantic_logger/extensions/rackup/server")
84
+ elsif defined?(Rack::Server)
85
+ require("rails_semantic_logger/extensions/rack/server")
86
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_semantic_logger
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.13.0
4
+ version: 4.17.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: 2023-11-09 00:00:00.000000000 Z
11
+ date: 2024-07-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '4.13'
47
+ version: '4.16'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '4.13'
54
+ version: '4.16'
55
55
  description:
56
56
  email:
57
57
  executables: []
@@ -80,9 +80,14 @@ files:
80
80
  - lib/rails_semantic_logger/extensions/active_support/tagged_logging.rb
81
81
  - lib/rails_semantic_logger/extensions/mongoid/config.rb
82
82
  - lib/rails_semantic_logger/extensions/rack/server.rb
83
+ - lib/rails_semantic_logger/extensions/rackup/server.rb
83
84
  - lib/rails_semantic_logger/extensions/rails/server.rb
85
+ - lib/rails_semantic_logger/extensions/sidekiq/sidekiq.rb
84
86
  - lib/rails_semantic_logger/options.rb
85
87
  - lib/rails_semantic_logger/rack/logger.rb
88
+ - lib/rails_semantic_logger/sidekiq/defaults.rb
89
+ - lib/rails_semantic_logger/sidekiq/job_logger.rb
90
+ - lib/rails_semantic_logger/sidekiq/loggable.rb
86
91
  - lib/rails_semantic_logger/version.rb
87
92
  homepage: https://logger.rocketjob.io
88
93
  licenses:
@@ -90,7 +95,7 @@ licenses:
90
95
  metadata:
91
96
  bug_tracker_uri: https://github.com/reidmorrison/rails_semantic_logger/issues
92
97
  documentation_uri: https://logger.rocketjob.io
93
- source_code_uri: https://github.com/reidmorrison/rails_semantic_logger/tree/v4.13.0
98
+ source_code_uri: https://github.com/reidmorrison/rails_semantic_logger/tree/v4.17.0
94
99
  rubygems_mfa_required: 'true'
95
100
  post_install_message:
96
101
  rdoc_options: []
@@ -107,7 +112,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
107
112
  - !ruby/object:Gem::Version
108
113
  version: '0'
109
114
  requirements: []
110
- rubygems_version: 3.4.9
115
+ rubygems_version: 3.5.3
111
116
  signing_key:
112
117
  specification_version: 4
113
118
  summary: Feature rich logging framework that replaces the Rails logger.