logstruct 0.0.2 → 0.1.1

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 (114) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -22
  3. data/README.md +25 -2
  4. data/lib/log_struct/boot_buffer.rb +28 -0
  5. data/lib/log_struct/builders/active_job.rb +84 -0
  6. data/lib/log_struct/concerns/configuration.rb +126 -13
  7. data/lib/log_struct/concerns/error_handling.rb +3 -7
  8. data/lib/log_struct/concerns/logging.rb +5 -5
  9. data/lib/log_struct/config_struct/filters.rb +18 -0
  10. data/lib/log_struct/config_struct/integrations.rb +16 -12
  11. data/lib/log_struct/configuration.rb +13 -0
  12. data/lib/log_struct/enums/event.rb +13 -0
  13. data/lib/log_struct/enums/log_field.rb +154 -0
  14. data/lib/log_struct/enums/source.rb +4 -1
  15. data/lib/log_struct/formatter.rb +29 -17
  16. data/lib/log_struct/integrations/action_mailer/error_handling.rb +3 -11
  17. data/lib/log_struct/integrations/action_mailer/event_logging.rb +22 -12
  18. data/lib/log_struct/integrations/active_job/log_subscriber.rb +52 -48
  19. data/lib/log_struct/integrations/active_model_serializers.rb +49 -0
  20. data/lib/log_struct/integrations/active_record.rb +35 -5
  21. data/lib/log_struct/integrations/active_storage.rb +59 -20
  22. data/lib/log_struct/integrations/ahoy.rb +54 -0
  23. data/lib/log_struct/integrations/carrierwave.rb +13 -16
  24. data/lib/log_struct/integrations/dotenv.rb +278 -0
  25. data/lib/log_struct/integrations/good_job/log_subscriber.rb +86 -136
  26. data/lib/log_struct/integrations/good_job/logger.rb +8 -10
  27. data/lib/log_struct/integrations/good_job.rb +5 -7
  28. data/lib/log_struct/integrations/host_authorization.rb +25 -4
  29. data/lib/log_struct/integrations/lograge.rb +20 -14
  30. data/lib/log_struct/integrations/puma.rb +482 -0
  31. data/lib/log_struct/integrations/rack_error_handler/middleware.rb +11 -18
  32. data/lib/log_struct/integrations/shrine.rb +44 -19
  33. data/lib/log_struct/integrations/sorbet.rb +48 -0
  34. data/lib/log_struct/integrations.rb +25 -0
  35. data/lib/log_struct/log/action_mailer/delivered.rb +99 -0
  36. data/lib/log_struct/log/action_mailer/delivery.rb +99 -0
  37. data/lib/log_struct/log/action_mailer.rb +30 -45
  38. data/lib/log_struct/log/active_job/enqueue.rb +125 -0
  39. data/lib/log_struct/log/active_job/finish.rb +130 -0
  40. data/lib/log_struct/log/active_job/schedule.rb +125 -0
  41. data/lib/log_struct/log/active_job/start.rb +130 -0
  42. data/lib/log_struct/log/active_job.rb +41 -54
  43. data/lib/log_struct/log/active_model_serializers.rb +94 -0
  44. data/lib/log_struct/log/active_storage/delete.rb +87 -0
  45. data/lib/log_struct/log/active_storage/download.rb +103 -0
  46. data/lib/log_struct/log/active_storage/exist.rb +93 -0
  47. data/lib/log_struct/log/active_storage/metadata.rb +93 -0
  48. data/lib/log_struct/log/active_storage/stream.rb +93 -0
  49. data/lib/log_struct/log/active_storage/upload.rb +118 -0
  50. data/lib/log_struct/log/active_storage/url.rb +93 -0
  51. data/lib/log_struct/log/active_storage.rb +32 -68
  52. data/lib/log_struct/log/ahoy.rb +88 -0
  53. data/lib/log_struct/log/carrierwave/delete.rb +115 -0
  54. data/lib/log_struct/log/carrierwave/download.rb +131 -0
  55. data/lib/log_struct/log/carrierwave/upload.rb +141 -0
  56. data/lib/log_struct/log/carrierwave.rb +37 -72
  57. data/lib/log_struct/log/dotenv/load.rb +76 -0
  58. data/lib/log_struct/log/dotenv/restore.rb +76 -0
  59. data/lib/log_struct/log/dotenv/save.rb +76 -0
  60. data/lib/log_struct/log/dotenv/update.rb +76 -0
  61. data/lib/log_struct/log/dotenv.rb +12 -0
  62. data/lib/log_struct/log/error.rb +58 -46
  63. data/lib/log_struct/log/good_job/enqueue.rb +126 -0
  64. data/lib/log_struct/log/good_job/error.rb +151 -0
  65. data/lib/log_struct/log/good_job/finish.rb +136 -0
  66. data/lib/log_struct/log/good_job/log.rb +131 -0
  67. data/lib/log_struct/log/good_job/schedule.rb +136 -0
  68. data/lib/log_struct/log/good_job/start.rb +136 -0
  69. data/lib/log_struct/log/good_job.rb +40 -141
  70. data/lib/log_struct/log/interfaces/additional_data_field.rb +1 -17
  71. data/lib/log_struct/log/interfaces/common_fields.rb +1 -39
  72. data/lib/log_struct/log/interfaces/public_common_fields.rb +4 -0
  73. data/lib/log_struct/log/interfaces/request_fields.rb +1 -33
  74. data/lib/log_struct/log/plain.rb +59 -34
  75. data/lib/log_struct/log/puma/shutdown.rb +80 -0
  76. data/lib/log_struct/log/puma/start.rb +120 -0
  77. data/lib/log_struct/log/puma.rb +10 -0
  78. data/lib/log_struct/log/request.rb +132 -48
  79. data/lib/log_struct/log/security/blocked_host.rb +141 -0
  80. data/lib/log_struct/log/security/csrf_violation.rb +131 -0
  81. data/lib/log_struct/log/security/ip_spoof.rb +141 -0
  82. data/lib/log_struct/log/security.rb +40 -70
  83. data/lib/log_struct/log/shared/add_request_fields.rb +1 -26
  84. data/lib/log_struct/log/shared/merge_additional_data_fields.rb +1 -25
  85. data/lib/log_struct/log/shared/serialize_common.rb +1 -33
  86. data/lib/log_struct/log/shared/serialize_common_public.rb +44 -0
  87. data/lib/log_struct/log/shrine/delete.rb +85 -0
  88. data/lib/log_struct/log/shrine/download.rb +90 -0
  89. data/lib/log_struct/log/shrine/exist.rb +90 -0
  90. data/lib/log_struct/log/shrine/metadata.rb +90 -0
  91. data/lib/log_struct/log/shrine/upload.rb +105 -0
  92. data/lib/log_struct/log/shrine.rb +10 -67
  93. data/lib/log_struct/log/sidekiq.rb +65 -26
  94. data/lib/log_struct/log/sql.rb +113 -106
  95. data/lib/log_struct/log.rb +31 -32
  96. data/lib/log_struct/multi_error_reporter.rb +80 -22
  97. data/lib/log_struct/param_filters.rb +50 -7
  98. data/lib/log_struct/rails_boot_banner_silencer.rb +123 -0
  99. data/lib/log_struct/railtie.rb +71 -0
  100. data/lib/log_struct/semantic_logger/formatter.rb +4 -2
  101. data/lib/log_struct/semantic_logger/setup.rb +34 -18
  102. data/lib/log_struct/shared/interfaces/additional_data_field.rb +22 -0
  103. data/lib/log_struct/shared/interfaces/common_fields.rb +39 -0
  104. data/lib/log_struct/shared/interfaces/public_common_fields.rb +29 -0
  105. data/lib/log_struct/shared/interfaces/request_fields.rb +39 -0
  106. data/lib/log_struct/shared/shared/add_request_fields.rb +28 -0
  107. data/lib/log_struct/shared/shared/merge_additional_data_fields.rb +27 -0
  108. data/lib/log_struct/shared/shared/serialize_common.rb +58 -0
  109. data/lib/log_struct/version.rb +1 -1
  110. data/lib/log_struct.rb +22 -4
  111. data/logstruct.gemspec +3 -0
  112. metadata +108 -5
  113. data/lib/log_struct/log/interfaces/message_field.rb +0 -20
  114. data/lib/log_struct/log_keys.rb +0 -102
@@ -0,0 +1,105 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ # AUTO-GENERATED: DO NOT EDIT
5
+ # Generated by scripts/generate_structs.rb
6
+ # Schemas dir: schemas/log_sources/
7
+ # Template: tools/codegen/templates/sorbet/event.rb.erb
8
+
9
+ require "log_struct/shared/interfaces/common_fields"
10
+ require "log_struct/shared/interfaces/additional_data_field"
11
+ require "log_struct/shared/interfaces/request_fields"
12
+ require "log_struct/shared/shared/serialize_common"
13
+ require "log_struct/shared/shared/merge_additional_data_fields"
14
+ require "log_struct/shared/shared/add_request_fields"
15
+ require_relative "../../enums/source"
16
+ require_relative "../../enums/event"
17
+ require_relative "../../enums/level"
18
+ require_relative "../../enums/log_field"
19
+
20
+ module LogStruct
21
+ module Log
22
+ class Shrine
23
+ class Upload < T::Struct
24
+ # typed: strict
25
+ # frozen_string_literal: true
26
+
27
+ extend T::Sig
28
+
29
+ extend T::Sig
30
+
31
+ # Shared/common fields
32
+ const :source, Source::Shrine, default: Source::Shrine
33
+ const :event, Event, default: Event::Upload
34
+ const :timestamp, Time, factory: -> { Time.now }
35
+ const :level, Level, default: Level::Info
36
+
37
+ # Event-specific fields
38
+ const :storage, String
39
+ const :location, String
40
+ const :upload_options, T.nilable(T::Hash[Symbol, T.untyped]), default: nil
41
+ const :options, T.nilable(T::Hash[Symbol, T.untyped]), default: nil
42
+ const :uploader, T.nilable(String), default: nil
43
+ const :duration_ms, T.nilable(Float), default: nil
44
+
45
+ # Additional data
46
+ include LogStruct::Log::Interfaces::AdditionalDataField
47
+ const :additional_data, T.nilable(T::Hash[T.any(String, Symbol), T.untyped]), default: nil
48
+ include LogStruct::Log::Shared::MergeAdditionalDataFields
49
+
50
+ # Request fields (optional)
51
+
52
+ # Serialize shared fields
53
+ include LogStruct::Log::Interfaces::CommonFields
54
+ include LogStruct::Log::Shared::SerializeCommon
55
+
56
+ sig { returns(T::Hash[LogStruct::LogField, T.untyped]) }
57
+ def self.base_hash
58
+ {}
59
+ end
60
+
61
+ sig {
62
+ params(storage: T.untyped,
63
+ location: T.untyped,
64
+ upload_options: T.untyped,
65
+ options: T.untyped,
66
+ uploader: T.untyped,
67
+ duration_ms: T.untyped,
68
+ additional_data: T.untyped,
69
+ timestamp: T.untyped).returns(T::Hash[LogStruct::LogField, T.untyped])
70
+ }
71
+ def self.build(storage:,
72
+ location:,
73
+ upload_options: nil,
74
+ options: nil,
75
+ uploader: nil,
76
+ duration_ms: nil,
77
+ additional_data: nil,
78
+ timestamp: Time.now)
79
+ h = base_hash
80
+ h[LogField::Storage] = storage
81
+ h[LogField::Location] = location
82
+ h[LogField::UploadOptions] = upload_options unless upload_options.nil?
83
+ h[LogField::Options] = options unless options.nil?
84
+ h[LogField::Uploader] = uploader unless uploader.nil?
85
+ h[LogField::DurationMs] = duration_ms unless duration_ms.nil?
86
+ h
87
+ end
88
+
89
+ sig { returns(T::Hash[LogStruct::LogField, T.untyped]) }
90
+ def to_h
91
+ self.class.build(
92
+ storage: storage,
93
+ location: location,
94
+ upload_options: upload_options,
95
+ options: options,
96
+ uploader: uploader,
97
+ duration_ms: duration_ms,
98
+ additional_data: additional_data,
99
+ timestamp: timestamp
100
+ )
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -1,70 +1,13 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require_relative "interfaces/common_fields"
5
- require_relative "interfaces/additional_data_field"
6
- require_relative "shared/serialize_common"
7
- require_relative "shared/merge_additional_data_fields"
8
- require_relative "../enums/source"
9
- require_relative "../enums/event"
10
- require_relative "../enums/level"
11
- require_relative "../log_keys"
12
-
13
- module LogStruct
14
- module Log
15
- # Shrine log entry for structured logging
16
- class Shrine < T::Struct
17
- extend T::Sig
18
-
19
- include Interfaces::CommonFields
20
- include Interfaces::AdditionalDataField
21
- include SerializeCommon
22
- include MergeAdditionalDataFields
23
-
24
- ShrineEvent = T.type_alias {
25
- T.any(
26
- Event::Upload,
27
- Event::Download,
28
- Event::Delete,
29
- Event::Metadata,
30
- Event::Exist,
31
- Event::Unknown
32
- )
33
- }
34
-
35
- # Common fields
36
- const :source, Source::Shrine, default: T.let(Source::Shrine, Source::Shrine)
37
- const :event, ShrineEvent
38
- const :timestamp, Time, factory: -> { Time.now }
39
- const :level, Level, default: T.let(Level::Info, Level)
40
-
41
- # Shrine-specific fields
42
- const :storage, T.nilable(String), default: nil
43
- const :location, T.nilable(String), default: nil
44
- const :upload_options, T.nilable(T::Hash[Symbol, T.untyped]), default: nil
45
- const :download_options, T.nilable(T::Hash[Symbol, T.untyped]), default: nil
46
- const :options, T.nilable(T::Hash[Symbol, T.untyped]), default: nil
47
- const :uploader, T.nilable(String), default: nil
48
- const :duration, T.nilable(Float), default: nil
49
- const :additional_data, T::Hash[Symbol, T.untyped], default: {}
50
-
51
- # Convert the log entry to a hash for serialization
52
- sig { override.params(strict: T::Boolean).returns(T::Hash[Symbol, T.untyped]) }
53
- def serialize(strict = true)
54
- hash = serialize_common(strict)
55
- merge_additional_data_fields(hash)
56
-
57
- # Add Shrine-specific fields if they're present
58
- hash[LOG_KEYS.fetch(:storage)] = storage if storage
59
- hash[LOG_KEYS.fetch(:location)] = location if location
60
- hash[LOG_KEYS.fetch(:upload_options)] = upload_options if upload_options
61
- hash[LOG_KEYS.fetch(:download_options)] = download_options if download_options
62
- hash[LOG_KEYS.fetch(:options)] = options if options
63
- hash[LOG_KEYS.fetch(:uploader)] = uploader if uploader
64
- hash[LOG_KEYS.fetch(:duration)] = duration if duration
65
-
66
- hash
67
- end
68
- end
69
- end
70
- end
4
+ # AUTO-GENERATED: DO NOT EDIT
5
+ # Generated by scripts/generate_structs.rb
6
+ # Schemas dir: schemas/log_sources/
7
+ # Template: tools/codegen/templates/sorbet/source_parent.rb.erb
8
+
9
+ require_relative "shrine/upload"
10
+ require_relative "shrine/download"
11
+ require_relative "shrine/delete"
12
+ require_relative "shrine/metadata"
13
+ require_relative "shrine/exist"
@@ -1,49 +1,88 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require_relative "interfaces/common_fields"
5
- require_relative "shared/serialize_common"
4
+ # AUTO-GENERATED: DO NOT EDIT
5
+ # Generated by scripts/generate_structs.rb
6
+ # Schemas dir: schemas/log_sources/
7
+ # Template: tools/codegen/templates/sorbet/event.rb.erb
8
+
9
+ require "log_struct/shared/interfaces/common_fields"
10
+ require "log_struct/shared/interfaces/additional_data_field"
11
+ require "log_struct/shared/interfaces/request_fields"
12
+ require "log_struct/shared/shared/serialize_common"
13
+ require "log_struct/shared/shared/merge_additional_data_fields"
14
+ require "log_struct/shared/shared/add_request_fields"
6
15
  require_relative "../enums/source"
7
16
  require_relative "../enums/event"
8
17
  require_relative "../enums/level"
9
- require_relative "../log_keys"
18
+ require_relative "../enums/log_field"
10
19
 
11
20
  module LogStruct
12
21
  module Log
13
- # Sidekiq log entry for structured logging
14
22
  class Sidekiq < T::Struct
15
- extend T::Sig
23
+ # typed: strict
24
+ # frozen_string_literal: true
16
25
 
17
- include Interfaces::CommonFields
18
- include SerializeCommon
26
+ extend T::Sig
19
27
 
20
- # Define valid event types for Sidekiq (currently only Log is used)
21
- SidekiqEvent = T.type_alias { Event::Log }
28
+ extend T::Sig
22
29
 
23
- # Common fields
24
- const :source, Source::Sidekiq, default: T.let(Source::Sidekiq, Source::Sidekiq)
25
- const :event, SidekiqEvent, default: T.let(Event::Log, SidekiqEvent)
30
+ # Shared/common fields
31
+ const :source, Source::Sidekiq, default: Source::Sidekiq
32
+ const :event, Event, default: Event::Log
26
33
  const :timestamp, Time, factory: -> { Time.now }
27
- const :level, Level, default: T.let(Level::Info, Level)
34
+ const :level, Level, default: Level::Info
28
35
 
29
- # Sidekiq-specific fields
30
- const :process_id, T.nilable(Integer), default: nil
31
- const :thread_id, T.nilable(T.any(Integer, String)), default: nil
36
+ # Event-specific fields
32
37
  const :message, T.nilable(String), default: nil
33
38
  const :context, T.nilable(T::Hash[Symbol, T.untyped]), default: nil
39
+ const :process_id, T.nilable(Integer), default: nil
40
+ const :thread_id, T.nilable(T.any(Integer, String)), default: nil
41
+
42
+ # Additional data
43
+
44
+ # Request fields (optional)
34
45
 
35
- # Convert the log entry to a hash for serialization
36
- sig { override.params(strict: T::Boolean).returns(T::Hash[Symbol, T.untyped]) }
37
- def serialize(strict = true)
38
- hash = serialize_common(strict)
46
+ # Serialize shared fields
47
+ include LogStruct::Log::Interfaces::CommonFields
48
+ include LogStruct::Log::Shared::SerializeCommon
39
49
 
40
- # Add Sidekiq-specific fields if they're present
41
- hash[LOG_KEYS.fetch(:message)] = message if message
42
- hash[LOG_KEYS.fetch(:context)] = context if context
43
- hash[LOG_KEYS.fetch(:process_id)] = process_id if process_id
44
- hash[LOG_KEYS.fetch(:thread_id)] = thread_id if thread_id
50
+ sig { returns(T::Hash[LogStruct::LogField, T.untyped]) }
51
+ def self.base_hash
52
+ {}
53
+ end
54
+
55
+ sig {
56
+ params(message: T.untyped,
57
+ context: T.untyped,
58
+ process_id: T.untyped,
59
+ thread_id: T.untyped,
60
+ additional_data: T.untyped,
61
+ timestamp: T.untyped).returns(T::Hash[LogStruct::LogField, T.untyped])
62
+ }
63
+ def self.build(message: nil,
64
+ context: nil,
65
+ process_id: nil,
66
+ thread_id: nil,
67
+ additional_data: nil,
68
+ timestamp: Time.now)
69
+ h = base_hash
70
+ h[LogField::Message] = message unless message.nil?
71
+ h[LogField::Context] = context unless context.nil?
72
+ h[LogField::ProcessId] = process_id unless process_id.nil?
73
+ h[LogField::ThreadId] = thread_id unless thread_id.nil?
74
+ h
75
+ end
45
76
 
46
- hash
77
+ sig { returns(T::Hash[LogStruct::LogField, T.untyped]) }
78
+ def to_h
79
+ self.class.build(
80
+ message: message,
81
+ context: context,
82
+ process_id: process_id,
83
+ thread_id: thread_id,
84
+ timestamp: timestamp
85
+ )
47
86
  end
48
87
  end
49
88
  end
@@ -1,125 +1,132 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require_relative "interfaces/common_fields"
5
- require_relative "interfaces/additional_data_field"
6
- require_relative "shared/serialize_common"
7
- require_relative "shared/merge_additional_data_fields"
4
+ # AUTO-GENERATED: DO NOT EDIT
5
+ # Generated by scripts/generate_structs.rb
6
+ # Schemas dir: schemas/log_sources/
7
+ # Template: tools/codegen/templates/sorbet/event.rb.erb
8
+
9
+ require "log_struct/shared/interfaces/common_fields"
10
+ require "log_struct/shared/interfaces/additional_data_field"
11
+ require "log_struct/shared/interfaces/request_fields"
12
+ require "log_struct/shared/shared/serialize_common"
13
+ require "log_struct/shared/shared/merge_additional_data_fields"
14
+ require "log_struct/shared/shared/add_request_fields"
15
+ require_relative "../enums/source"
16
+ require_relative "../enums/event"
17
+ require_relative "../enums/level"
18
+ require_relative "../enums/log_field"
8
19
 
9
20
  module LogStruct
10
21
  module Log
11
- # SQL Query Log Structure
12
- #
13
- # Captures detailed information about SQL queries executed through ActiveRecord.
14
- # This provides structured logging for database operations, including:
15
- # - Query text and operation name
16
- # - Execution timing and performance metrics
17
- # - Row counts and connection information
18
- # - Safely filtered bind parameters
19
- #
20
- # ## Use Cases:
21
- # - Development debugging of N+1 queries
22
- # - Production performance monitoring
23
- # - Database query analysis and optimization
24
- # - Audit trails for data access patterns
25
- #
26
- # ## Security:
27
- # - SQL queries are safe (always parameterized with ?)
28
- # - Bind parameters are filtered through LogStruct's param filters
29
- # - Sensitive data like passwords, tokens are automatically scrubbed
30
- #
31
- # ## Example Usage:
32
- #
33
- # ```ruby
34
- # # Automatically captured when SQL query integration is enabled
35
- # LogStruct.config.integrations.enable_sql_logging = true
36
- #
37
- # # Manual logging (rare)
38
- # sql_log = LogStruct::Log::SQL.new(
39
- # message: "User lookup query",
40
- # sql: "SELECT * FROM users WHERE id = ?",
41
- # name: "User Load",
42
- # duration: 2.3,
43
- # row_count: 1,
44
- # bind_params: [123]
45
- # )
46
- # LogStruct.info(sql_log)
47
- # ```
48
22
  class SQL < T::Struct
23
+ # typed: strict
24
+ # frozen_string_literal: true
25
+
49
26
  extend T::Sig
50
- include Interfaces::CommonFields
51
- include Interfaces::AdditionalDataField
52
- include SerializeCommon
53
- include MergeAdditionalDataFields
54
27
 
55
- SQLEvent = T.type_alias {
56
- Event::Database
57
- }
28
+ extend T::Sig
58
29
 
59
- # Common fields
60
- const :source, Source, default: T.let(Source::App, Source)
61
- const :event, SQLEvent, default: T.let(Event::Database, SQLEvent)
62
- const :level, Level, default: T.let(Level::Info, Level)
30
+ # Shared/common fields
31
+ const :source, Source::App, default: Source::App
32
+ const :event, Event, default: Event::Database
63
33
  const :timestamp, Time, factory: -> { Time.now }
64
- const :message, String
34
+ const :level, Level, default: Level::Info
65
35
 
66
- # The SQL query that was executed (parameterized, safe to log)
36
+ # Event-specific fields
37
+ const :message, String
67
38
  const :sql, String
68
-
69
- # The name of the database operation (e.g., "User Load", "Post Create")
70
39
  const :name, String
40
+ const :duration_ms, Float
41
+ const :row_count, T.nilable(Integer), default: nil
42
+ const :adapter, T.nilable(String), default: nil
43
+ const :bind_params, T.nilable(T::Array[T.untyped]), default: nil
44
+ const :database_name, T.nilable(String), default: nil
45
+ const :connection_pool_size, T.nilable(Integer), default: nil
46
+ const :active_connections, T.nilable(Integer), default: nil
47
+ const :operation_type, T.nilable(String), default: nil
48
+ const :table_names, T.nilable(T::Array[String]), default: nil
49
+
50
+ # Additional data
51
+ include LogStruct::Log::Interfaces::AdditionalDataField
52
+ const :additional_data, T.nilable(T::Hash[T.any(String, Symbol), T.untyped]), default: nil
53
+ include LogStruct::Log::Shared::MergeAdditionalDataFields
54
+
55
+ # Request fields (optional)
56
+
57
+ # Serialize shared fields
58
+ include LogStruct::Log::Interfaces::CommonFields
59
+ include LogStruct::Log::Shared::SerializeCommon
60
+
61
+ sig { returns(T::Hash[LogStruct::LogField, T.untyped]) }
62
+ def self.base_hash
63
+ {}
64
+ end
71
65
 
72
- # Duration of the query execution in milliseconds
73
- const :duration, Float
74
-
75
- # Number of rows affected or returned by the query
76
- const :row_count, T.nilable(Integer)
77
-
78
- # Database connection information (adapter name)
79
- const :connection_adapter, T.nilable(String)
80
-
81
- # Filtered bind parameters (sensitive data removed)
82
- const :bind_params, T.nilable(T::Array[T.untyped])
83
-
84
- # Database name (if available)
85
- const :database_name, T.nilable(String)
86
-
87
- # Connection pool size information (for monitoring)
88
- const :connection_pool_size, T.nilable(Integer)
89
-
90
- # Active connection count (for monitoring)
91
- const :active_connections, T.nilable(Integer)
92
-
93
- # SQL operation type (SELECT, INSERT, UPDATE, DELETE, etc.)
94
- const :operation_type, T.nilable(String)
95
-
96
- # Table names involved in the query (extracted from SQL)
97
- const :table_names, T.nilable(T::Array[String])
98
-
99
- # Allow additional custom data
100
- const :additional_data, T::Hash[Symbol, T.untyped], default: {}
101
-
102
- # Convert the log entry to a hash for serialization
103
- sig { override.params(strict: T::Boolean).returns(T::Hash[Symbol, T.untyped]) }
104
- def serialize(strict = true)
105
- hash = serialize_common(strict)
106
- merge_additional_data_fields(hash)
107
-
108
- # Add SQL-specific fields using LOG_KEYS mapping for consistency
109
- hash[LOG_KEYS.fetch(:message)] = message
110
- hash[LOG_KEYS.fetch(:sql)] = sql
111
- hash[LOG_KEYS.fetch(:name)] = name
112
- hash[LOG_KEYS.fetch(:duration)] = duration
113
- hash[LOG_KEYS.fetch(:row_count)] = row_count
114
- hash[LOG_KEYS.fetch(:connection_adapter)] = connection_adapter
115
- hash[LOG_KEYS.fetch(:bind_params)] = bind_params
116
- hash[LOG_KEYS.fetch(:database_name)] = database_name
117
- hash[LOG_KEYS.fetch(:connection_pool_size)] = connection_pool_size
118
- hash[LOG_KEYS.fetch(:active_connections)] = active_connections
119
- hash[LOG_KEYS.fetch(:operation_type)] = operation_type
120
- hash[LOG_KEYS.fetch(:table_names)] = table_names
66
+ sig {
67
+ params(message: T.untyped,
68
+ sql: T.untyped,
69
+ name: T.untyped,
70
+ duration_ms: T.untyped,
71
+ row_count: T.untyped,
72
+ adapter: T.untyped,
73
+ bind_params: T.untyped,
74
+ database_name: T.untyped,
75
+ connection_pool_size: T.untyped,
76
+ active_connections: T.untyped,
77
+ operation_type: T.untyped,
78
+ table_names: T.untyped,
79
+ additional_data: T.untyped,
80
+ timestamp: T.untyped).returns(T::Hash[LogStruct::LogField, T.untyped])
81
+ }
82
+ def self.build(message:,
83
+ sql:,
84
+ name:,
85
+ duration_ms:,
86
+ row_count: nil,
87
+ adapter: nil,
88
+ bind_params: nil,
89
+ database_name: nil,
90
+ connection_pool_size: nil,
91
+ active_connections: nil,
92
+ operation_type: nil,
93
+ table_names: nil,
94
+ additional_data: nil,
95
+ timestamp: Time.now)
96
+ h = base_hash
97
+ h[LogField::Message] = message
98
+ h[LogField::Sql] = sql
99
+ h[LogField::Name] = name
100
+ h[LogField::DurationMs] = duration_ms
101
+ h[LogField::RowCount] = row_count unless row_count.nil?
102
+ h[LogField::Adapter] = adapter unless adapter.nil?
103
+ h[LogField::BindParams] = bind_params unless bind_params.nil?
104
+ h[LogField::DatabaseName] = database_name unless database_name.nil?
105
+ h[LogField::ConnectionPoolSize] = connection_pool_size unless connection_pool_size.nil?
106
+ h[LogField::ActiveConnections] = active_connections unless active_connections.nil?
107
+ h[LogField::OperationType] = operation_type unless operation_type.nil?
108
+ h[LogField::TableNames] = table_names unless table_names.nil?
109
+ h
110
+ end
121
111
 
122
- hash
112
+ sig { returns(T::Hash[LogStruct::LogField, T.untyped]) }
113
+ def to_h
114
+ self.class.build(
115
+ message: message,
116
+ sql: sql,
117
+ name: name,
118
+ duration_ms: duration_ms,
119
+ row_count: row_count,
120
+ adapter: adapter,
121
+ bind_params: bind_params,
122
+ database_name: database_name,
123
+ connection_pool_size: connection_pool_size,
124
+ active_connections: active_connections,
125
+ operation_type: operation_type,
126
+ table_names: table_names,
127
+ additional_data: additional_data,
128
+ timestamp: timestamp
129
+ )
123
130
  end
124
131
  end
125
132
  end
@@ -1,43 +1,42 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- # Common Enums
4
+ # Common enums and shared interfaces
5
5
  require_relative "enums/source"
6
6
  require_relative "enums/event"
7
7
  require_relative "enums/level"
8
+ require_relative "enums/log_field"
9
+ require_relative "log/interfaces/public_common_fields"
10
+ require_relative "log/shared/serialize_common_public"
8
11
 
9
- # Log Structs
10
- require_relative "log/carrierwave"
11
- require_relative "log/action_mailer"
12
- require_relative "log/active_storage"
13
- require_relative "log/active_job"
14
- require_relative "log/error"
15
- require_relative "log/good_job"
16
- require_relative "log/plain"
17
- require_relative "log/request"
18
- require_relative "log/security"
19
- require_relative "log/shrine"
20
- require_relative "log/sidekiq"
21
- require_relative "log/sql"
12
+ # Dynamically require all top-level log structs under log/*
13
+ # Nested per-event files are required by their parent files.
14
+ Dir[File.join(__dir__, "log", "*.rb")].sort.each do |file|
15
+ require file
16
+ end
22
17
 
23
18
  module LogStruct
24
- # Type aliases for all possible log types
25
- # This should be updated whenever a new log type is added
26
- # (Can't use sealed! unless we want to put everything in one giant file.)
27
- LogClassType = T.type_alias do
28
- T.any(
29
- T.class_of(LogStruct::Log::CarrierWave),
30
- T.class_of(LogStruct::Log::ActionMailer),
31
- T.class_of(LogStruct::Log::ActiveStorage),
32
- T.class_of(LogStruct::Log::ActiveJob),
33
- T.class_of(LogStruct::Log::Error),
34
- T.class_of(LogStruct::Log::GoodJob),
35
- T.class_of(LogStruct::Log::Plain),
36
- T.class_of(LogStruct::Log::Request),
37
- T.class_of(LogStruct::Log::Security),
38
- T.class_of(LogStruct::Log::Shrine),
39
- T.class_of(LogStruct::Log::Sidekiq),
40
- T.class_of(LogStruct::Log::SQL)
41
- )
19
+ module Log
20
+ extend T::Sig
21
+
22
+ # Build an Error log from an exception with optional context and timestamp
23
+ sig do
24
+ params(
25
+ source: Source,
26
+ ex: StandardError,
27
+ additional_data: T::Hash[T.any(String, Symbol), T.untyped],
28
+ timestamp: Time
29
+ ).returns(LogStruct::Log::Error)
30
+ end
31
+ def self.from_exception(source, ex, additional_data = {}, timestamp = Time.now)
32
+ LogStruct::Log::Error.new(
33
+ source: source,
34
+ err_class: ex.class,
35
+ message: ex.message,
36
+ backtrace: ex.backtrace,
37
+ additional_data: additional_data,
38
+ timestamp: timestamp
39
+ )
40
+ end
42
41
  end
43
42
  end