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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -22
- data/README.md +25 -2
- data/lib/log_struct/boot_buffer.rb +28 -0
- data/lib/log_struct/builders/active_job.rb +84 -0
- data/lib/log_struct/concerns/configuration.rb +126 -13
- data/lib/log_struct/concerns/error_handling.rb +3 -7
- data/lib/log_struct/concerns/logging.rb +5 -5
- data/lib/log_struct/config_struct/filters.rb +18 -0
- data/lib/log_struct/config_struct/integrations.rb +16 -12
- data/lib/log_struct/configuration.rb +13 -0
- data/lib/log_struct/enums/event.rb +13 -0
- data/lib/log_struct/enums/log_field.rb +154 -0
- data/lib/log_struct/enums/source.rb +4 -1
- data/lib/log_struct/formatter.rb +29 -17
- data/lib/log_struct/integrations/action_mailer/error_handling.rb +3 -11
- data/lib/log_struct/integrations/action_mailer/event_logging.rb +22 -12
- data/lib/log_struct/integrations/active_job/log_subscriber.rb +52 -48
- data/lib/log_struct/integrations/active_model_serializers.rb +49 -0
- data/lib/log_struct/integrations/active_record.rb +35 -5
- data/lib/log_struct/integrations/active_storage.rb +59 -20
- data/lib/log_struct/integrations/ahoy.rb +54 -0
- data/lib/log_struct/integrations/carrierwave.rb +13 -16
- data/lib/log_struct/integrations/dotenv.rb +278 -0
- data/lib/log_struct/integrations/good_job/log_subscriber.rb +86 -136
- data/lib/log_struct/integrations/good_job/logger.rb +8 -10
- data/lib/log_struct/integrations/good_job.rb +5 -7
- data/lib/log_struct/integrations/host_authorization.rb +25 -4
- data/lib/log_struct/integrations/lograge.rb +20 -14
- data/lib/log_struct/integrations/puma.rb +482 -0
- data/lib/log_struct/integrations/rack_error_handler/middleware.rb +11 -18
- data/lib/log_struct/integrations/shrine.rb +44 -19
- data/lib/log_struct/integrations/sorbet.rb +48 -0
- data/lib/log_struct/integrations.rb +25 -0
- data/lib/log_struct/log/action_mailer/delivered.rb +99 -0
- data/lib/log_struct/log/action_mailer/delivery.rb +99 -0
- data/lib/log_struct/log/action_mailer.rb +30 -45
- data/lib/log_struct/log/active_job/enqueue.rb +125 -0
- data/lib/log_struct/log/active_job/finish.rb +130 -0
- data/lib/log_struct/log/active_job/schedule.rb +125 -0
- data/lib/log_struct/log/active_job/start.rb +130 -0
- data/lib/log_struct/log/active_job.rb +41 -54
- data/lib/log_struct/log/active_model_serializers.rb +94 -0
- data/lib/log_struct/log/active_storage/delete.rb +87 -0
- data/lib/log_struct/log/active_storage/download.rb +103 -0
- data/lib/log_struct/log/active_storage/exist.rb +93 -0
- data/lib/log_struct/log/active_storage/metadata.rb +93 -0
- data/lib/log_struct/log/active_storage/stream.rb +93 -0
- data/lib/log_struct/log/active_storage/upload.rb +118 -0
- data/lib/log_struct/log/active_storage/url.rb +93 -0
- data/lib/log_struct/log/active_storage.rb +32 -68
- data/lib/log_struct/log/ahoy.rb +88 -0
- data/lib/log_struct/log/carrierwave/delete.rb +115 -0
- data/lib/log_struct/log/carrierwave/download.rb +131 -0
- data/lib/log_struct/log/carrierwave/upload.rb +141 -0
- data/lib/log_struct/log/carrierwave.rb +37 -72
- data/lib/log_struct/log/dotenv/load.rb +76 -0
- data/lib/log_struct/log/dotenv/restore.rb +76 -0
- data/lib/log_struct/log/dotenv/save.rb +76 -0
- data/lib/log_struct/log/dotenv/update.rb +76 -0
- data/lib/log_struct/log/dotenv.rb +12 -0
- data/lib/log_struct/log/error.rb +58 -46
- data/lib/log_struct/log/good_job/enqueue.rb +126 -0
- data/lib/log_struct/log/good_job/error.rb +151 -0
- data/lib/log_struct/log/good_job/finish.rb +136 -0
- data/lib/log_struct/log/good_job/log.rb +131 -0
- data/lib/log_struct/log/good_job/schedule.rb +136 -0
- data/lib/log_struct/log/good_job/start.rb +136 -0
- data/lib/log_struct/log/good_job.rb +40 -141
- data/lib/log_struct/log/interfaces/additional_data_field.rb +1 -17
- data/lib/log_struct/log/interfaces/common_fields.rb +1 -39
- data/lib/log_struct/log/interfaces/public_common_fields.rb +4 -0
- data/lib/log_struct/log/interfaces/request_fields.rb +1 -33
- data/lib/log_struct/log/plain.rb +59 -34
- data/lib/log_struct/log/puma/shutdown.rb +80 -0
- data/lib/log_struct/log/puma/start.rb +120 -0
- data/lib/log_struct/log/puma.rb +10 -0
- data/lib/log_struct/log/request.rb +132 -48
- data/lib/log_struct/log/security/blocked_host.rb +141 -0
- data/lib/log_struct/log/security/csrf_violation.rb +131 -0
- data/lib/log_struct/log/security/ip_spoof.rb +141 -0
- data/lib/log_struct/log/security.rb +40 -70
- data/lib/log_struct/log/shared/add_request_fields.rb +1 -26
- data/lib/log_struct/log/shared/merge_additional_data_fields.rb +1 -25
- data/lib/log_struct/log/shared/serialize_common.rb +1 -33
- data/lib/log_struct/log/shared/serialize_common_public.rb +44 -0
- data/lib/log_struct/log/shrine/delete.rb +85 -0
- data/lib/log_struct/log/shrine/download.rb +90 -0
- data/lib/log_struct/log/shrine/exist.rb +90 -0
- data/lib/log_struct/log/shrine/metadata.rb +90 -0
- data/lib/log_struct/log/shrine/upload.rb +105 -0
- data/lib/log_struct/log/shrine.rb +10 -67
- data/lib/log_struct/log/sidekiq.rb +65 -26
- data/lib/log_struct/log/sql.rb +113 -106
- data/lib/log_struct/log.rb +31 -32
- data/lib/log_struct/multi_error_reporter.rb +80 -22
- data/lib/log_struct/param_filters.rb +50 -7
- data/lib/log_struct/rails_boot_banner_silencer.rb +123 -0
- data/lib/log_struct/railtie.rb +71 -0
- data/lib/log_struct/semantic_logger/formatter.rb +4 -2
- data/lib/log_struct/semantic_logger/setup.rb +34 -18
- data/lib/log_struct/shared/interfaces/additional_data_field.rb +22 -0
- data/lib/log_struct/shared/interfaces/common_fields.rb +39 -0
- data/lib/log_struct/shared/interfaces/public_common_fields.rb +29 -0
- data/lib/log_struct/shared/interfaces/request_fields.rb +39 -0
- data/lib/log_struct/shared/shared/add_request_fields.rb +28 -0
- data/lib/log_struct/shared/shared/merge_additional_data_fields.rb +27 -0
- data/lib/log_struct/shared/shared/serialize_common.rb +58 -0
- data/lib/log_struct/version.rb +1 -1
- data/lib/log_struct.rb +22 -4
- data/logstruct.gemspec +3 -0
- metadata +108 -5
- data/lib/log_struct/log/interfaces/message_field.rb +0 -20
- data/lib/log_struct/log_keys.rb +0 -102
@@ -38,168 +38,118 @@ module LogStruct
|
|
38
38
|
extend T::Sig
|
39
39
|
|
40
40
|
# Job enqueued event
|
41
|
-
sig { params(event:
|
41
|
+
sig { params(event: ::ActiveSupport::Notifications::Event).void }
|
42
42
|
def enqueue(event)
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
additional_data: {
|
56
|
-
enqueue_caller: job_data[:caller_location]
|
57
|
-
}
|
58
|
-
)
|
59
|
-
|
60
|
-
logger.info(log_entry)
|
43
|
+
payload = T.let(event.payload, T::Hash[Symbol, T.untyped])
|
44
|
+
job = payload[:job]
|
45
|
+
base_fields = build_base_fields(job, payload)
|
46
|
+
ts = event.time ? Time.at(event.time) : Time.now
|
47
|
+
|
48
|
+
logger.info(Log::GoodJob::Enqueue.new(
|
49
|
+
**base_fields.to_kwargs,
|
50
|
+
scheduled_at: (job&.scheduled_at ? Time.at(job.scheduled_at.to_i) : nil),
|
51
|
+
duration_ms: event.duration.to_f,
|
52
|
+
additional_data: {enqueue_caller: job&.enqueue_caller_location},
|
53
|
+
timestamp: ts
|
54
|
+
))
|
61
55
|
end
|
62
56
|
|
63
57
|
# Job execution started event
|
64
|
-
sig { params(event:
|
58
|
+
sig { params(event: ::ActiveSupport::Notifications::Event).void }
|
65
59
|
def start(event)
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
60
|
+
payload = T.let(event.payload, T::Hash[Symbol, T.untyped])
|
61
|
+
job = payload[:job]
|
62
|
+
execution = payload[:execution] || payload[:good_job_execution]
|
63
|
+
base_fields = build_base_fields(job, payload)
|
64
|
+
ts = event.time ? Time.at(event.time) : Time.now
|
65
|
+
|
66
|
+
logger.info(Log::GoodJob::Start.new(
|
67
|
+
**base_fields.to_kwargs,
|
68
|
+
wait_ms: begin
|
69
|
+
wt = execution&.wait_time || calculate_wait_time(execution)
|
70
|
+
wt ? (wt.to_f * 1000.0) : nil
|
71
|
+
end,
|
72
|
+
scheduled_at: (job&.scheduled_at ? Time.at(job.scheduled_at.to_i) : nil),
|
78
73
|
process_id: ::Process.pid,
|
79
|
-
thread_id: Thread.current.object_id.to_s(36)
|
80
|
-
|
81
|
-
|
82
|
-
logger.info(log_entry)
|
74
|
+
thread_id: Thread.current.object_id.to_s(36),
|
75
|
+
timestamp: ts
|
76
|
+
))
|
83
77
|
end
|
84
78
|
|
85
79
|
# Job completed successfully event
|
86
|
-
sig { params(event:
|
80
|
+
sig { params(event: ::ActiveSupport::Notifications::Event).void }
|
87
81
|
def finish(event)
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
finished_at: Time.now,
|
82
|
+
payload = T.let(event.payload, T::Hash[Symbol, T.untyped])
|
83
|
+
job = payload[:job]
|
84
|
+
base_fields = build_base_fields(job, payload)
|
85
|
+
start_ts = event.time ? Time.at(event.time) : Time.now
|
86
|
+
end_ts = event.end ? Time.at(event.end) : Time.now
|
87
|
+
|
88
|
+
logger.info(Log::GoodJob::Finish.new(
|
89
|
+
**base_fields.to_kwargs,
|
90
|
+
duration_ms: event.duration.to_f,
|
91
|
+
finished_at: end_ts,
|
99
92
|
process_id: ::Process.pid,
|
100
93
|
thread_id: Thread.current.object_id.to_s(36),
|
101
|
-
additional_data: {
|
102
|
-
|
103
|
-
|
104
|
-
)
|
105
|
-
|
106
|
-
logger.info(log_entry)
|
94
|
+
additional_data: {result: payload[:result]},
|
95
|
+
timestamp: start_ts
|
96
|
+
))
|
107
97
|
end
|
108
98
|
|
109
99
|
# Job failed with error event
|
110
|
-
sig { params(event:
|
100
|
+
sig { params(event: ::ActiveSupport::Notifications::Event).void }
|
111
101
|
def error(event)
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
exception_executions:
|
122
|
-
|
123
|
-
error_message:
|
124
|
-
|
125
|
-
|
102
|
+
payload = T.let(event.payload, T::Hash[Symbol, T.untyped])
|
103
|
+
job = payload[:job]
|
104
|
+
execution = payload[:execution] || payload[:good_job_execution]
|
105
|
+
exception = payload[:exception] || payload[:error]
|
106
|
+
ts = event.time ? Time.at(event.time) : Time.now
|
107
|
+
base_fields = build_base_fields(job, payload)
|
108
|
+
|
109
|
+
logger.error(Log::GoodJob::Error.new(
|
110
|
+
**base_fields.to_kwargs,
|
111
|
+
exception_executions: execution&.exception_executions,
|
112
|
+
err_class: exception&.class&.name,
|
113
|
+
error_message: exception&.message,
|
114
|
+
backtrace: exception&.backtrace&.first(20),
|
115
|
+
duration_ms: event.duration.to_f,
|
126
116
|
process_id: ::Process.pid,
|
127
|
-
thread_id: Thread.current.object_id.to_s(36)
|
128
|
-
|
129
|
-
|
130
|
-
logger.error(log_entry)
|
117
|
+
thread_id: Thread.current.object_id.to_s(36),
|
118
|
+
timestamp: ts
|
119
|
+
))
|
131
120
|
end
|
132
121
|
|
133
122
|
# Job scheduled for future execution event
|
134
|
-
sig { params(event:
|
123
|
+
sig { params(event: ::ActiveSupport::Notifications::Event).void }
|
135
124
|
def schedule(event)
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
)
|
150
|
-
|
151
|
-
logger.info(log_entry)
|
125
|
+
payload = T.let(event.payload, T::Hash[Symbol, T.untyped])
|
126
|
+
job = payload[:job]
|
127
|
+
base_fields = build_base_fields(job, payload)
|
128
|
+
ts = event.time ? Time.at(event.time) : Time.now
|
129
|
+
|
130
|
+
logger.info(Log::GoodJob::Schedule.new(
|
131
|
+
**base_fields.to_kwargs,
|
132
|
+
scheduled_at: (job&.scheduled_at ? Time.at(job.scheduled_at.to_i) : nil),
|
133
|
+
priority: job&.priority,
|
134
|
+
cron_key: job&.cron_key,
|
135
|
+
duration_ms: event.duration.to_f,
|
136
|
+
timestamp: ts
|
137
|
+
))
|
152
138
|
end
|
153
139
|
|
154
140
|
private
|
155
141
|
|
156
|
-
#
|
157
|
-
sig { params(
|
158
|
-
def
|
159
|
-
payload = event.payload || {}
|
160
|
-
job = payload[:job]
|
142
|
+
# Build BaseFields from job + payload (execution)
|
143
|
+
sig { params(job: T.untyped, payload: T::Hash[Symbol, T.untyped]).returns(Log::GoodJob::BaseFields) }
|
144
|
+
def build_base_fields(job, payload)
|
161
145
|
execution = payload[:execution] || payload[:good_job_execution]
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
data[:job_class] = job.job_class if job.respond_to?(:job_class)
|
170
|
-
data[:queue_name] = job.queue_name if job.respond_to?(:queue_name)
|
171
|
-
data[:arguments] = job.arguments if job.respond_to?(:arguments)
|
172
|
-
data[:priority] = job.priority if job.respond_to?(:priority)
|
173
|
-
data[:scheduled_at] = job.scheduled_at if job.respond_to?(:scheduled_at)
|
174
|
-
data[:cron_key] = job.cron_key if job.respond_to?(:cron_key)
|
175
|
-
data[:caller_location] = job.enqueue_caller_location if job.respond_to?(:enqueue_caller_location)
|
176
|
-
end
|
177
|
-
|
178
|
-
# Execution-specific information
|
179
|
-
if execution
|
180
|
-
data[:executions] = execution.executions if execution.respond_to?(:executions)
|
181
|
-
data[:exception_executions] = execution.exception_executions if execution.respond_to?(:exception_executions)
|
182
|
-
# Use existing wait_time if available, otherwise calculate it
|
183
|
-
if execution.respond_to?(:wait_time) && execution.wait_time
|
184
|
-
data[:wait_time] = execution.wait_time
|
185
|
-
elsif execution.respond_to?(:created_at)
|
186
|
-
data[:wait_time] = calculate_wait_time(execution)
|
187
|
-
end
|
188
|
-
data[:batch_id] = execution.batch_id if execution.respond_to?(:batch_id)
|
189
|
-
data[:cron_key] ||= execution.cron_key if execution.respond_to?(:cron_key)
|
190
|
-
end
|
191
|
-
|
192
|
-
# Error information
|
193
|
-
if exception
|
194
|
-
data[:error_class] = exception.class.name
|
195
|
-
data[:error_message] = exception.message
|
196
|
-
data[:error_backtrace] = exception.backtrace&.first(20) # Limit backtrace size
|
197
|
-
end
|
198
|
-
|
199
|
-
# Result information
|
200
|
-
data[:result] = payload[:result] if payload.key?(:result)
|
201
|
-
|
202
|
-
data
|
146
|
+
Log::GoodJob::BaseFields.new(
|
147
|
+
job_id: job&.job_id,
|
148
|
+
job_class: job&.job_class,
|
149
|
+
queue_name: job&.queue_name,
|
150
|
+
arguments: job&.arguments,
|
151
|
+
executions: execution&.executions
|
152
|
+
)
|
203
153
|
end
|
204
154
|
|
205
155
|
# Calculate wait time from job creation to execution start
|
@@ -46,24 +46,22 @@ module LogStruct
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
#
|
50
|
-
|
51
|
-
|
52
|
-
|
49
|
+
# Emit a GoodJob::Log event with context and extra fields as additional_data
|
50
|
+
extras = {}
|
51
|
+
extras[:scheduled_at] = job_context[:scheduled_at] if job_context.key?(:scheduled_at)
|
52
|
+
extras[:priority] = job_context[:priority] if job_context.key?(:priority)
|
53
|
+
|
54
|
+
log_struct = Log::GoodJob::Log.new(
|
55
|
+
message: message || (block ? block.call : ""),
|
53
56
|
process_id: ::Process.pid,
|
54
57
|
thread_id: Thread.current.object_id.to_s(36),
|
55
58
|
job_id: job_context[:job_id],
|
56
59
|
job_class: job_context[:job_class],
|
57
60
|
queue_name: job_context[:queue_name],
|
58
61
|
executions: job_context[:executions],
|
59
|
-
|
60
|
-
priority: job_context[:priority],
|
61
|
-
additional_data: {
|
62
|
-
message: message || (block ? block.call : "")
|
63
|
-
}
|
62
|
+
additional_data: extras
|
64
63
|
)
|
65
64
|
|
66
|
-
# Pass the struct to SemanticLogger
|
67
65
|
super(log_struct, payload, &nil)
|
68
66
|
end
|
69
67
|
end
|
@@ -81,15 +81,13 @@ module LogStruct
|
|
81
81
|
# Configure error handling for thread errors if GoodJob supports it
|
82
82
|
if goodjob_module.respond_to?(:on_thread_error=)
|
83
83
|
goodjob_module.on_thread_error = ->(exception) do
|
84
|
-
|
85
|
-
|
86
|
-
event: Event::Error,
|
87
|
-
level: Level::Error,
|
88
|
-
error_class: exception.class.name,
|
84
|
+
log_entry = LogStruct::Log::GoodJob::Error.new(
|
85
|
+
err_class: exception.class.name,
|
89
86
|
error_message: exception.message,
|
90
|
-
|
87
|
+
backtrace: exception.backtrace,
|
88
|
+
process_id: ::Process.pid,
|
89
|
+
thread_id: Thread.current.object_id.to_s(36)
|
91
90
|
)
|
92
|
-
|
93
91
|
goodjob_module.logger.error(log_entry)
|
94
92
|
end
|
95
93
|
end
|
@@ -34,6 +34,23 @@ module LogStruct
|
|
34
34
|
return nil unless config.enabled
|
35
35
|
return nil unless config.integrations.enable_host_authorization
|
36
36
|
|
37
|
+
# In test environment, ensure HostAuthorization does not block requests
|
38
|
+
# from the default integration test hosts. Allow all hosts explicitly.
|
39
|
+
if ::Rails.env.test? && ::Rails.application.config.respond_to?(:hosts)
|
40
|
+
begin
|
41
|
+
::Rails.application.config.hosts << /.*\z/
|
42
|
+
rescue
|
43
|
+
# best-effort; ignore if hosts not configurable
|
44
|
+
end
|
45
|
+
# Additionally, exclude all requests from HostAuthorization in test
|
46
|
+
begin
|
47
|
+
::Rails.application.config.host_authorization ||= {}
|
48
|
+
::Rails.application.config.host_authorization[:exclude] = ->(_request) { true }
|
49
|
+
rescue
|
50
|
+
# best-effort
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
37
54
|
# Define the response app as a separate variable to fix block alignment
|
38
55
|
response_app = lambda do |env|
|
39
56
|
request = ::ActionDispatch::Request.new(env)
|
@@ -69,10 +86,14 @@ module LogStruct
|
|
69
86
|
[FORBIDDEN_STATUS, RESPONSE_HEADERS, [RESPONSE_HTML]]
|
70
87
|
end
|
71
88
|
|
72
|
-
#
|
73
|
-
Rails.application.config.host_authorization
|
74
|
-
|
75
|
-
|
89
|
+
# Merge our response_app into existing host_authorization config to preserve excludes
|
90
|
+
existing = Rails.application.config.host_authorization
|
91
|
+
unless existing.is_a?(Hash)
|
92
|
+
existing = {}
|
93
|
+
end
|
94
|
+
existing = existing.dup
|
95
|
+
existing[:response_app] = response_app
|
96
|
+
Rails.application.config.host_authorization = existing
|
76
97
|
|
77
98
|
true
|
78
99
|
end
|
@@ -38,21 +38,27 @@ module LogStruct
|
|
38
38
|
# The struct is converted to JSON by our Formatter (after filtering, etc.)
|
39
39
|
config.lograge.formatter = T.let(
|
40
40
|
lambda do |data|
|
41
|
-
#
|
41
|
+
# Coerce common fields to expected types
|
42
|
+
status = ((s = data[:status]) && s.respond_to?(:to_i)) ? s.to_i : s
|
43
|
+
duration_ms = ((d = data[:duration]) && d.respond_to?(:to_f)) ? d.to_f : d
|
44
|
+
view = ((v = data[:view]) && v.respond_to?(:to_f)) ? v.to_f : v
|
45
|
+
db = ((b = data[:db]) && b.respond_to?(:to_f)) ? b.to_f : b
|
46
|
+
|
47
|
+
params = data[:params]
|
48
|
+
params = params.deep_symbolize_keys if params&.respond_to?(:deep_symbolize_keys)
|
49
|
+
|
42
50
|
Log::Request.new(
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
db: data[:db],
|
55
|
-
params: data[:params]
|
51
|
+
http_method: data[:method]&.to_s,
|
52
|
+
path: data[:path]&.to_s,
|
53
|
+
format: data[:format]&.to_s,
|
54
|
+
controller: data[:controller]&.to_s,
|
55
|
+
action: data[:action]&.to_s,
|
56
|
+
status: status,
|
57
|
+
duration_ms: duration_ms,
|
58
|
+
view: view,
|
59
|
+
database: db,
|
60
|
+
params: params,
|
61
|
+
timestamp: Time.now
|
56
62
|
)
|
57
63
|
end,
|
58
64
|
T.proc.params(hash: T::Hash[Symbol, T.untyped]).returns(Log::Request)
|