logstruct 0.1.11 → 0.2.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.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +146 -1
  3. data/README.md +76 -68
  4. data/lib/log_struct/concerns/configuration.rb +139 -91
  5. data/lib/log_struct/concerns/error_handling.rb +36 -6
  6. data/lib/log_struct/config_struct/error_handling_modes.rb +3 -1
  7. data/lib/log_struct/config_struct/filters.rb +126 -22
  8. data/lib/log_struct/config_struct/integrations.rb +0 -4
  9. data/lib/log_struct/config_struct/limits.rb +29 -0
  10. data/lib/log_struct/configuration.rb +2 -0
  11. data/lib/log_struct/enums/event.rb +3 -1
  12. data/lib/log_struct/enums/log_field.rb +5 -0
  13. data/lib/log_struct/formatter.rb +304 -56
  14. data/lib/log_struct/hash_utils.rb +33 -4
  15. data/lib/log_struct/integrations/action_mailer/error_handling.rb +78 -20
  16. data/lib/log_struct/integrations/action_mailer/fallback_error_handlers.rb +81 -0
  17. data/lib/log_struct/integrations/action_mailer/metadata_collection.rb +0 -20
  18. data/lib/log_struct/integrations/action_mailer.rb +22 -17
  19. data/lib/log_struct/integrations/active_job/log_subscriber.rb +177 -25
  20. data/lib/log_struct/integrations/active_job.rb +2 -7
  21. data/lib/log_struct/integrations/active_model_serializers.rb +9 -3
  22. data/lib/log_struct/integrations/active_record.rb +45 -17
  23. data/lib/log_struct/integrations/active_storage.rb +31 -31
  24. data/lib/log_struct/integrations/ahoy.rb +19 -15
  25. data/lib/log_struct/integrations/carrierwave.rb +55 -51
  26. data/lib/log_struct/integrations/dotenv.rb +22 -32
  27. data/lib/log_struct/integrations/good_job/logger.rb +30 -24
  28. data/lib/log_struct/integrations/good_job.rb +111 -40
  29. data/lib/log_struct/integrations/host_authorization.rb +47 -36
  30. data/lib/log_struct/integrations/lograge.rb +39 -4
  31. data/lib/log_struct/integrations/puma.rb +74 -34
  32. data/lib/log_struct/integrations/rack_error_handler/debug_exceptions_logging.rb +35 -0
  33. data/lib/log_struct/integrations/rack_error_handler/error_lines.rb +160 -0
  34. data/lib/log_struct/integrations/rack_error_handler/middleware.rb +14 -169
  35. data/lib/log_struct/integrations/rack_error_handler/parse_error_logging.rb +36 -0
  36. data/lib/log_struct/integrations/rack_error_handler.rb +20 -4
  37. data/lib/log_struct/integrations/rack_middleware.rb +23 -0
  38. data/lib/log_struct/integrations/rails_log_subscribers.rb +32 -0
  39. data/lib/log_struct/integrations/request_context/middleware.rb +4 -9
  40. data/lib/log_struct/integrations/shrine.rb +48 -73
  41. data/lib/log_struct/integrations/sidekiq/job_context_middleware.rb +32 -0
  42. data/lib/log_struct/integrations/sidekiq/logger.rb +17 -9
  43. data/lib/log_struct/integrations/sidekiq.rb +98 -7
  44. data/lib/log_struct/integrations/sorbet.rb +10 -46
  45. data/lib/log_struct/integrations.rb +3 -2
  46. data/lib/log_struct/log/{good_job/enqueue.rb → active_job/discard.rb} +13 -13
  47. data/lib/log_struct/log/active_job/enqueue.rb +0 -4
  48. data/lib/log_struct/log/{good_job/schedule.rb → active_job/error.rb} +15 -15
  49. data/lib/log_struct/log/{good_job/start.rb → active_job/retry.rb} +13 -13
  50. data/lib/log_struct/log/{good_job/finish.rb → active_job/retry_stopped.rb} +13 -17
  51. data/lib/log_struct/log/active_job.rb +4 -0
  52. data/lib/log_struct/log/active_storage/delete.rb +6 -2
  53. data/lib/log_struct/log/active_storage/download.rb +2 -4
  54. data/lib/log_struct/log/active_storage/exist.rb +2 -2
  55. data/lib/log_struct/log/active_storage/metadata.rb +4 -4
  56. data/lib/log_struct/log/active_storage/upload.rb +2 -10
  57. data/lib/log_struct/log/active_storage/url.rb +2 -6
  58. data/lib/log_struct/log/active_storage.rb +2 -3
  59. data/lib/log_struct/log/error.rb +24 -1
  60. data/lib/log_struct/log/good_job.rb +0 -4
  61. data/lib/log_struct/log/puma/error.rb +63 -0
  62. data/lib/log_struct/log/puma.rb +1 -0
  63. data/lib/log_struct/log/security/blocked_host.rb +0 -4
  64. data/lib/log_struct/log/shrine/exist.rb +0 -2
  65. data/lib/log_struct/log/shrine/metadata.rb +2 -4
  66. data/lib/log_struct/log/shrine/upload.rb +4 -0
  67. data/lib/log_struct/log/sidekiq/error.rb +63 -0
  68. data/lib/log_struct/log/{active_storage/stream.rb → sidekiq/log.rb} +17 -11
  69. data/lib/log_struct/log/sidekiq.rb +3 -45
  70. data/lib/log_struct/log.rb +4 -2
  71. data/lib/log_struct/logged_errors.rb +25 -0
  72. data/lib/log_struct/monkey_patches/active_support/tagged_logging/formatter.rb +2 -3
  73. data/lib/log_struct/param_filters.rb +51 -38
  74. data/lib/log_struct/params_errors.rb +78 -0
  75. data/lib/log_struct/rails_boot_banner_silencer.rb +7 -2
  76. data/lib/log_struct/railtie.rb +29 -14
  77. data/lib/log_struct/semantic_logger/color_formatter.rb +60 -53
  78. data/lib/log_struct/semantic_logger/concerns/log_methods.rb +65 -57
  79. data/lib/log_struct/semantic_logger/formatter.rb +79 -57
  80. data/lib/log_struct/semantic_logger/logger.rb +29 -53
  81. data/lib/log_struct/semantic_logger/reopen_after_fork.rb +40 -0
  82. data/lib/log_struct/semantic_logger/setup.rb +115 -112
  83. data/lib/log_struct/shared/merge_additional_data_fields.rb +14 -1
  84. data/lib/log_struct/shared/serialize_common.rb +2 -7
  85. data/lib/log_struct/string_scrubber.rb +229 -20
  86. data/lib/log_struct/version.rb +1 -1
  87. data/lib/log_struct.rb +6 -4
  88. data/logstruct.gemspec +19 -10
  89. metadata +63 -11
  90. data/lib/log_struct/builders/active_job.rb +0 -84
  91. data/lib/log_struct/integrations/good_job/log_subscriber.rb +0 -179
  92. data/lib/log_struct/integrations/rack.rb +0 -31
data/lib/log_struct.rb CHANGED
@@ -7,6 +7,7 @@ require "log_struct/version"
7
7
  require "log_struct/enums"
8
8
  require "log_struct/configuration"
9
9
  require "log_struct/formatter"
10
+ require "log_struct/logged_errors"
10
11
  require "log_struct/railtie"
11
12
  require "log_struct/concerns/error_handling"
12
13
  require "log_struct/concerns/configuration"
@@ -15,7 +16,7 @@ require "log_struct/concerns/logging"
15
16
  # Require integrations
16
17
  require "log_struct/integrations"
17
18
 
18
- # SemanticLogger integration - core feature for high-performance logging
19
+ # SemanticLogger: the logger, formatters and setup LogStruct writes its lines with
19
20
  require "log_struct/semantic_logger/formatter"
20
21
  require "log_struct/semantic_logger/color_formatter"
21
22
  require "log_struct/semantic_logger/logger"
@@ -54,8 +55,9 @@ module LogStruct
54
55
  LogStruct::RailsBootBannerSilencer.install!
55
56
 
56
57
  # Patch Puma immediately for server runs so we can convert its lifecycle
57
- # messages into structured logs reliably.
58
- if ARGV.include?("server")
58
+ # messages into structured logs reliably. Not when LogStruct is disabled, and not
59
+ # when Puma isn't installed (install_patches! skips it).
60
+ if LogStruct.enabled? && ARGV.include?("server")
59
61
  begin
60
62
  require "log_struct/integrations/puma"
61
63
  LogStruct::Integrations::Puma.install_patches!
@@ -65,7 +67,7 @@ module LogStruct
65
67
  if defined?(::Rails) && ::Rails.respond_to?(:env) && ::Rails.env.test?
66
68
  raise e
67
69
  else
68
- LogStruct.handle_exception(e, source: LogStruct::Source::Puma)
70
+ LogStruct.handle_exception(e, source: LogStruct::Source::Internal, context: {integration: :puma})
69
71
  end
70
72
  end
71
73
  end
data/logstruct.gemspec CHANGED
@@ -15,26 +15,35 @@ Gem::Specification.new do |spec|
15
15
  "Log messages, params, and job args are automatically filtered and scrubbed to remove any sensitive info."
16
16
  spec.homepage = "https://logstruct.com"
17
17
  spec.license = "MIT"
18
- spec.required_ruby_version = ">= 3.2.0"
18
+ spec.required_ruby_version = ">= 3.3.0"
19
19
 
20
20
  spec.metadata["homepage_uri"] = spec.homepage
21
21
  spec.metadata["source_code_uri"] = "https://github.com/DocSpring/logstruct"
22
22
  spec.metadata["changelog_uri"] = "#{spec.metadata["source_code_uri"]}/blob/main/CHANGELOG.md"
23
23
 
24
- # Specify which files should be added to the gem when it is released.
25
- spec.files = Dir[
26
- "lib/**/*",
27
- "README.md",
28
- "CHANGELOG.md",
29
- "LICENSE",
30
- "*.gemspec",
31
- ]
24
+ # Specify which files should be added to the gem when it is released: only files committed to git, so a
25
+ # local build can't pick up untracked files. Without git (an unpacked gem), it falls back to Dir.
26
+ patterns = ["lib/**/*", "README.md", "CHANGELOG.md", "LICENSE", "*.gemspec"]
27
+ tracked_files = begin
28
+ output = IO.popen(%w[git ls-files -z], chdir: __dir__, err: File::NULL, &:read)
29
+ output.split("\x0") if $?.success?
30
+ rescue Errno::ENOENT
31
+ nil
32
+ end
33
+ spec.files = if tracked_files
34
+ tracked_files.select { |file| patterns.any? { |pattern| File.fnmatch?(pattern, file, File::FNM_PATHNAME) } }
35
+ else
36
+ Dir[*patterns].select { |file| File.file?(file) }
37
+ end
32
38
  spec.bindir = "exe"
33
39
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
34
40
  spec.require_paths = ["lib"]
35
41
 
42
+ spec.add_dependency "actionpack", ">= 7.1"
43
+ spec.add_dependency "activesupport", ">= 7.1"
44
+ spec.add_dependency "globalid", ">= 1.0"
36
45
  spec.add_dependency "lograge", ">= 0.11"
37
- spec.add_dependency "rails", ">= 7.1"
46
+ spec.add_dependency "railties", ">= 7.1"
38
47
  spec.add_dependency "semantic_logger", "~> 4.15"
39
48
  spec.add_dependency "sorbet-runtime", ">= 0.5"
40
49
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstruct
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.11
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - DocSpring
@@ -9,6 +9,48 @@ bindir: exe
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: actionpack
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '7.1'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '7.1'
26
+ - !ruby/object:Gem::Dependency
27
+ name: activesupport
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '7.1'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '7.1'
40
+ - !ruby/object:Gem::Dependency
41
+ name: globalid
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '1.0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '1.0'
12
54
  - !ruby/object:Gem::Dependency
13
55
  name: lograge
14
56
  requirement: !ruby/object:Gem::Requirement
@@ -24,7 +66,7 @@ dependencies:
24
66
  - !ruby/object:Gem::Version
25
67
  version: '0.11'
26
68
  - !ruby/object:Gem::Dependency
27
- name: rails
69
+ name: railties
28
70
  requirement: !ruby/object:Gem::Requirement
29
71
  requirements:
30
72
  - - ">="
@@ -240,13 +282,13 @@ files:
240
282
  - README.md
241
283
  - lib/log_struct.rb
242
284
  - lib/log_struct/boot_buffer.rb
243
- - lib/log_struct/builders/active_job.rb
244
285
  - lib/log_struct/concerns/configuration.rb
245
286
  - lib/log_struct/concerns/error_handling.rb
246
287
  - lib/log_struct/concerns/logging.rb
247
288
  - lib/log_struct/config_struct/error_handling_modes.rb
248
289
  - lib/log_struct/config_struct/filters.rb
249
290
  - lib/log_struct/config_struct/integrations.rb
291
+ - lib/log_struct/config_struct/limits.rb
250
292
  - lib/log_struct/configuration.rb
251
293
  - lib/log_struct/enums.rb
252
294
  - lib/log_struct/enums/error_handling_mode.rb
@@ -262,6 +304,7 @@ files:
262
304
  - lib/log_struct/integrations/action_mailer.rb
263
305
  - lib/log_struct/integrations/action_mailer/error_handling.rb
264
306
  - lib/log_struct/integrations/action_mailer/event_logging.rb
307
+ - lib/log_struct/integrations/action_mailer/fallback_error_handlers.rb
265
308
  - lib/log_struct/integrations/action_mailer/metadata_collection.rb
266
309
  - lib/log_struct/integrations/active_job.rb
267
310
  - lib/log_struct/integrations/active_job/log_subscriber.rb
@@ -273,20 +316,24 @@ files:
273
316
  - lib/log_struct/integrations/dotenv.rb
274
317
  - lib/log_struct/integrations/event_time.rb
275
318
  - lib/log_struct/integrations/good_job.rb
276
- - lib/log_struct/integrations/good_job/log_subscriber.rb
277
319
  - lib/log_struct/integrations/good_job/logger.rb
278
320
  - lib/log_struct/integrations/host_authorization.rb
279
321
  - lib/log_struct/integrations/integration_interface.rb
280
322
  - lib/log_struct/integrations/lograge.rb
281
323
  - lib/log_struct/integrations/puma.rb
282
- - lib/log_struct/integrations/rack.rb
283
324
  - lib/log_struct/integrations/rack_error_handler.rb
325
+ - lib/log_struct/integrations/rack_error_handler/debug_exceptions_logging.rb
326
+ - lib/log_struct/integrations/rack_error_handler/error_lines.rb
284
327
  - lib/log_struct/integrations/rack_error_handler/middleware.rb
328
+ - lib/log_struct/integrations/rack_error_handler/parse_error_logging.rb
329
+ - lib/log_struct/integrations/rack_middleware.rb
285
330
  - lib/log_struct/integrations/rack_setup.rb
331
+ - lib/log_struct/integrations/rails_log_subscribers.rb
286
332
  - lib/log_struct/integrations/request_context.rb
287
333
  - lib/log_struct/integrations/request_context/middleware.rb
288
334
  - lib/log_struct/integrations/shrine.rb
289
335
  - lib/log_struct/integrations/sidekiq.rb
336
+ - lib/log_struct/integrations/sidekiq/job_context_middleware.rb
290
337
  - lib/log_struct/integrations/sidekiq/logger.rb
291
338
  - lib/log_struct/integrations/sorbet.rb
292
339
  - lib/log_struct/log.rb
@@ -295,8 +342,12 @@ files:
295
342
  - lib/log_struct/log/action_mailer/delivery.rb
296
343
  - lib/log_struct/log/action_mailer/error.rb
297
344
  - lib/log_struct/log/active_job.rb
345
+ - lib/log_struct/log/active_job/discard.rb
298
346
  - lib/log_struct/log/active_job/enqueue.rb
347
+ - lib/log_struct/log/active_job/error.rb
299
348
  - lib/log_struct/log/active_job/finish.rb
349
+ - lib/log_struct/log/active_job/retry.rb
350
+ - lib/log_struct/log/active_job/retry_stopped.rb
300
351
  - lib/log_struct/log/active_job/schedule.rb
301
352
  - lib/log_struct/log/active_job/start.rb
302
353
  - lib/log_struct/log/active_model_serializers.rb
@@ -305,7 +356,6 @@ files:
305
356
  - lib/log_struct/log/active_storage/download.rb
306
357
  - lib/log_struct/log/active_storage/exist.rb
307
358
  - lib/log_struct/log/active_storage/metadata.rb
308
- - lib/log_struct/log/active_storage/stream.rb
309
359
  - lib/log_struct/log/active_storage/upload.rb
310
360
  - lib/log_struct/log/active_storage/url.rb
311
361
  - lib/log_struct/log/ahoy.rb
@@ -320,18 +370,15 @@ files:
320
370
  - lib/log_struct/log/dotenv/update.rb
321
371
  - lib/log_struct/log/error.rb
322
372
  - lib/log_struct/log/good_job.rb
323
- - lib/log_struct/log/good_job/enqueue.rb
324
373
  - lib/log_struct/log/good_job/error.rb
325
- - lib/log_struct/log/good_job/finish.rb
326
374
  - lib/log_struct/log/good_job/log.rb
327
- - lib/log_struct/log/good_job/schedule.rb
328
- - lib/log_struct/log/good_job/start.rb
329
375
  - lib/log_struct/log/interfaces/additional_data_field.rb
330
376
  - lib/log_struct/log/interfaces/common_fields.rb
331
377
  - lib/log_struct/log/interfaces/public_common_fields.rb
332
378
  - lib/log_struct/log/interfaces/request_fields.rb
333
379
  - lib/log_struct/log/plain.rb
334
380
  - lib/log_struct/log/puma.rb
381
+ - lib/log_struct/log/puma/error.rb
335
382
  - lib/log_struct/log/puma/shutdown.rb
336
383
  - lib/log_struct/log/puma/start.rb
337
384
  - lib/log_struct/log/request.rb
@@ -346,16 +393,21 @@ files:
346
393
  - lib/log_struct/log/shrine/metadata.rb
347
394
  - lib/log_struct/log/shrine/upload.rb
348
395
  - lib/log_struct/log/sidekiq.rb
396
+ - lib/log_struct/log/sidekiq/error.rb
397
+ - lib/log_struct/log/sidekiq/log.rb
349
398
  - lib/log_struct/log/sql.rb
399
+ - lib/log_struct/logged_errors.rb
350
400
  - lib/log_struct/monkey_patches/active_support/tagged_logging/formatter.rb
351
401
  - lib/log_struct/multi_error_reporter.rb
352
402
  - lib/log_struct/param_filters.rb
403
+ - lib/log_struct/params_errors.rb
353
404
  - lib/log_struct/rails_boot_banner_silencer.rb
354
405
  - lib/log_struct/railtie.rb
355
406
  - lib/log_struct/semantic_logger/color_formatter.rb
356
407
  - lib/log_struct/semantic_logger/concerns/log_methods.rb
357
408
  - lib/log_struct/semantic_logger/formatter.rb
358
409
  - lib/log_struct/semantic_logger/logger.rb
410
+ - lib/log_struct/semantic_logger/reopen_after_fork.rb
359
411
  - lib/log_struct/semantic_logger/setup.rb
360
412
  - lib/log_struct/shared/add_request_fields.rb
361
413
  - lib/log_struct/shared/interfaces/additional_data_field.rb
@@ -387,7 +439,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
387
439
  requirements:
388
440
  - - ">="
389
441
  - !ruby/object:Gem::Version
390
- version: 3.2.0
442
+ version: 3.3.0
391
443
  required_rubygems_version: !ruby/object:Gem::Requirement
392
444
  requirements:
393
445
  - - ">="
@@ -1,84 +0,0 @@
1
- # typed: strict
2
- # frozen_string_literal: true
3
-
4
- require_relative "../log/active_job/enqueue"
5
- require_relative "../log/active_job/schedule"
6
- require_relative "../log/active_job/start"
7
- require_relative "../log/active_job/finish"
8
-
9
- module LogStruct
10
- module Builders
11
- module ActiveJob
12
- extend T::Sig
13
-
14
- sig { params(job: T.untyped).returns(T.nilable(String)) }
15
- def self.safe_provider_job_id(job)
16
- job.respond_to?(:provider_job_id) ? job.provider_job_id : nil
17
- end
18
-
19
- sig { params(job: T.untyped).returns(T.nilable(Integer)) }
20
- def self.safe_executions(job)
21
- job.respond_to?(:executions) ? job.executions : nil
22
- end
23
-
24
- sig { params(job: T.untyped).returns(T.nilable(T::Array[T.untyped])) }
25
- def self.safe_arguments(job)
26
- return nil unless job.class.respond_to?(:log_arguments?)
27
- job.class.log_arguments? ? job.arguments : nil
28
- end
29
-
30
- sig { params(job: T.untyped).returns(Log::ActiveJob::Enqueue) }
31
- def self.enqueue(job)
32
- Log::ActiveJob::Enqueue.new(
33
- job_id: job.job_id,
34
- job_class: job.class.to_s,
35
- queue_name: job.queue_name,
36
- arguments: safe_arguments(job),
37
- executions: safe_executions(job),
38
- provider_job_id: safe_provider_job_id(job)
39
- )
40
- end
41
-
42
- sig { params(job: T.untyped, scheduled_at: Time).returns(Log::ActiveJob::Schedule) }
43
- def self.schedule(job, scheduled_at:)
44
- Log::ActiveJob::Schedule.new(
45
- job_id: job.job_id,
46
- job_class: job.class.to_s,
47
- queue_name: job.queue_name,
48
- arguments: safe_arguments(job),
49
- executions: safe_executions(job),
50
- provider_job_id: safe_provider_job_id(job),
51
- scheduled_at: scheduled_at
52
- )
53
- end
54
-
55
- sig { params(job: T.untyped, started_at: Time, attempt: T.nilable(Integer)).returns(Log::ActiveJob::Start) }
56
- def self.start(job, started_at:, attempt:)
57
- Log::ActiveJob::Start.new(
58
- job_id: job.job_id,
59
- job_class: job.class.to_s,
60
- queue_name: job.queue_name,
61
- arguments: safe_arguments(job),
62
- executions: safe_executions(job),
63
- provider_job_id: safe_provider_job_id(job),
64
- started_at: started_at,
65
- attempt: attempt
66
- )
67
- end
68
-
69
- sig { params(job: T.untyped, duration_ms: Float, finished_at: Time).returns(Log::ActiveJob::Finish) }
70
- def self.finish(job, duration_ms:, finished_at:)
71
- Log::ActiveJob::Finish.new(
72
- job_id: job.job_id,
73
- job_class: job.class.to_s,
74
- queue_name: job.queue_name,
75
- arguments: safe_arguments(job),
76
- executions: safe_executions(job),
77
- provider_job_id: safe_provider_job_id(job),
78
- duration_ms: duration_ms,
79
- finished_at: finished_at
80
- )
81
- end
82
- end
83
- end
84
- end
@@ -1,179 +0,0 @@
1
- # typed: strict
2
- # frozen_string_literal: true
3
-
4
- begin
5
- require "active_support/log_subscriber"
6
- rescue LoadError
7
- # ActiveSupport is not available, log subscriber will be skipped
8
- end
9
-
10
- require_relative "../../log/good_job"
11
- require_relative "../../enums/event"
12
- require_relative "../../enums/level"
13
- require_relative "../event_time"
14
-
15
- module LogStruct
16
- module Integrations
17
- module GoodJob
18
- # LogSubscriber for GoodJob ActiveSupport notifications
19
- #
20
- # This subscriber captures GoodJob's ActiveSupport notifications and converts
21
- # them into structured LogStruct::Log::GoodJob entries. It provides detailed
22
- # logging for job lifecycle events, performance metrics, and error tracking.
23
- #
24
- # ## Supported Events:
25
- # - job.enqueue - Job queued for execution
26
- # - job.start - Job execution started
27
- # - job.finish - Job completed successfully
28
- # - job.error - Job failed with error
29
- # - job.retry - Job retry initiated
30
- # - job.schedule - Job scheduled for future execution
31
- #
32
- # ## Event Data Captured:
33
- # - Job identification (ID, class, queue)
34
- # - Execution context (arguments, priority, scheduled time)
35
- # - Performance metrics (execution time, wait time)
36
- # - Error information (class, message, backtrace)
37
- # - Process and thread information
38
- class LogSubscriber < ::ActiveSupport::LogSubscriber
39
- extend T::Sig
40
-
41
- # Job enqueued event
42
- sig { params(event: ::ActiveSupport::Notifications::Event).void }
43
- def enqueue(event)
44
- payload = T.let(event.payload, T::Hash[Symbol, T.untyped])
45
- job = payload[:job]
46
- base_fields = build_base_fields(job, payload)
47
- ts = EventTime.coerce_event_time(event.time)
48
-
49
- logger.info(Log::GoodJob::Enqueue.new(
50
- **base_fields.to_kwargs,
51
- scheduled_at: (job&.scheduled_at ? Time.at(job.scheduled_at.to_i) : nil),
52
- duration_ms: event.duration.to_f,
53
- enqueue_caller: job&.enqueue_caller_location,
54
- timestamp: ts
55
- ))
56
- end
57
-
58
- # Job execution started event
59
- sig { params(event: ::ActiveSupport::Notifications::Event).void }
60
- def start(event)
61
- payload = T.let(event.payload, T::Hash[Symbol, T.untyped])
62
- job = payload[:job]
63
- execution = payload[:execution] || payload[:good_job_execution]
64
- base_fields = build_base_fields(job, payload)
65
- ts = EventTime.coerce_event_time(event.time)
66
-
67
- logger.info(Log::GoodJob::Start.new(
68
- **base_fields.to_kwargs,
69
- wait_ms: begin
70
- wt = execution&.wait_time || calculate_wait_time(execution)
71
- wt ? (wt.to_f * 1000.0) : nil
72
- end,
73
- scheduled_at: (job&.scheduled_at ? Time.at(job.scheduled_at.to_i) : nil),
74
- process_id: ::Process.pid,
75
- thread_id: Thread.current.object_id.to_s(36),
76
- timestamp: ts
77
- ))
78
- end
79
-
80
- # Job completed successfully event
81
- sig { params(event: ::ActiveSupport::Notifications::Event).void }
82
- def finish(event)
83
- payload = T.let(event.payload, T::Hash[Symbol, T.untyped])
84
- job = payload[:job]
85
- base_fields = build_base_fields(job, payload)
86
- start_ts = EventTime.coerce_event_time(event.time)
87
- end_ts = EventTime.coerce_event_time(event.end)
88
-
89
- logger.info(Log::GoodJob::Finish.new(
90
- **base_fields.to_kwargs,
91
- duration_ms: event.duration.to_f,
92
- finished_at: end_ts,
93
- process_id: ::Process.pid,
94
- thread_id: Thread.current.object_id.to_s(36),
95
- result: payload[:result]&.to_s,
96
- timestamp: start_ts
97
- ))
98
- end
99
-
100
- # Job failed with error event
101
- sig { params(event: ::ActiveSupport::Notifications::Event).void }
102
- def error(event)
103
- payload = T.let(event.payload, T::Hash[Symbol, T.untyped])
104
- job = payload[:job]
105
- execution = payload[:execution] || payload[:good_job_execution]
106
- exception = payload[:exception] || payload[:error]
107
- ts = EventTime.coerce_event_time(event.time)
108
- base_fields = build_base_fields(job, payload)
109
-
110
- logger.error(Log::GoodJob::Error.new(
111
- **base_fields.to_kwargs,
112
- exception_executions: execution&.exception_executions,
113
- error_class: exception&.class&.name,
114
- error_message: exception&.message,
115
- backtrace: exception&.backtrace,
116
- duration_ms: event.duration.to_f,
117
- process_id: ::Process.pid,
118
- thread_id: Thread.current.object_id.to_s(36),
119
- timestamp: ts
120
- ))
121
- end
122
-
123
- # Job scheduled for future execution event
124
- sig { params(event: ::ActiveSupport::Notifications::Event).void }
125
- def schedule(event)
126
- payload = T.let(event.payload, T::Hash[Symbol, T.untyped])
127
- job = payload[:job]
128
- base_fields = build_base_fields(job, payload)
129
- ts = EventTime.coerce_event_time(event.time)
130
-
131
- logger.info(Log::GoodJob::Schedule.new(
132
- **base_fields.to_kwargs,
133
- scheduled_at: (job&.scheduled_at ? Time.at(job.scheduled_at.to_i) : nil),
134
- priority: job&.priority,
135
- cron_key: job&.cron_key,
136
- duration_ms: event.duration.to_f,
137
- timestamp: ts
138
- ))
139
- end
140
-
141
- private
142
-
143
- # Build BaseFields from job + payload (execution)
144
- sig { params(job: T.untyped, payload: T::Hash[Symbol, T.untyped]).returns(Log::GoodJob::BaseFields) }
145
- def build_base_fields(job, payload)
146
- execution = payload[:execution] || payload[:good_job_execution]
147
- Log::GoodJob::BaseFields.new(
148
- job_id: job&.job_id,
149
- job_class: job&.job_class,
150
- queue_name: job&.queue_name&.to_sym,
151
- arguments: job&.arguments,
152
- executions: execution&.executions
153
- )
154
- end
155
-
156
- # Calculate wait time from job creation to execution start
157
- sig { params(execution: T.untyped).returns(T.nilable(Float)) }
158
- def calculate_wait_time(execution)
159
- return nil unless execution.respond_to?(:created_at)
160
- return nil unless execution.respond_to?(:performed_at)
161
- return nil unless execution.created_at && execution.performed_at
162
-
163
- (execution.performed_at - execution.created_at).to_f
164
- rescue
165
- # Return nil if calculation fails
166
- nil
167
- end
168
-
169
- # Get the appropriate logger for GoodJob events
170
- sig { returns(T.untyped) }
171
- def logger
172
- # Always use Rails.logger - in production it will be configured by the integration setup,
173
- # in tests it will be set up by the test harness
174
- Rails.logger
175
- end
176
- end
177
- end
178
- end
179
- end
@@ -1,31 +0,0 @@
1
- # typed: strict
2
- # frozen_string_literal: true
3
-
4
- require "rack"
5
- require "action_dispatch/middleware/show_exceptions"
6
- require_relative "rack/error_handling_middleware"
7
- require_relative "rack_setup"
8
-
9
- module LogStruct
10
- module Integrations
11
- # Rack middleware integration for structured logging
12
- module Rack
13
- extend T::Sig
14
- extend IntegrationInterface
15
-
16
- # Set up Rack middleware for structured error logging
17
- sig { override.params(config: LogStruct::Configuration).returns(T.nilable(T::Boolean)) }
18
- def self.setup(config)
19
- return nil unless RackSetup.enabled?(config)
20
-
21
- # Add structured logging middleware for security violations and errors
22
- # Need to insert after ShowExceptions to catch IP spoofing errors
23
- RackSetup.insert_after(
24
- ::ActionDispatch::ShowExceptions,
25
- Integrations::RackErrorHandler::Middleware
26
- )
27
- true
28
- end
29
- end
30
- end
31
- end