logstruct 0.1.2 → 0.1.3
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 +12 -1
- data/README.md +4 -6
- data/lib/log_struct/concerns/configuration.rb +2 -2
- data/lib/log_struct/config_struct/integrations.rb +5 -0
- data/lib/log_struct/enums/log_field.rb +12 -1
- data/lib/log_struct/integrations/action_mailer/error_handling.rb +121 -27
- data/lib/log_struct/integrations/action_mailer/event_logging.rb +30 -14
- data/lib/log_struct/integrations/action_mailer/metadata_collection.rb +18 -24
- data/lib/log_struct/integrations/action_mailer.rb +13 -6
- data/lib/log_struct/integrations/active_job/log_subscriber.rb +2 -2
- data/lib/log_struct/integrations/active_storage.rb +8 -8
- data/lib/log_struct/integrations/ahoy.rb +2 -3
- data/lib/log_struct/integrations/carrierwave.rb +8 -10
- data/lib/log_struct/integrations/good_job/log_subscriber.rb +5 -5
- data/lib/log_struct/integrations/good_job/logger.rb +2 -6
- data/lib/log_struct/integrations/good_job.rb +1 -1
- data/lib/log_struct/integrations/host_authorization.rb +27 -36
- data/lib/log_struct/integrations/lograge.rb +1 -1
- data/lib/log_struct/integrations/shrine.rb +21 -24
- data/lib/log_struct/integrations/sidekiq/logger.rb +8 -1
- data/lib/log_struct/log/action_mailer/delivered.rb +14 -49
- data/lib/log_struct/log/action_mailer/delivery.rb +14 -49
- data/lib/log_struct/log/action_mailer/error.rb +72 -0
- data/lib/log_struct/log/action_mailer.rb +15 -2
- data/lib/log_struct/log/active_job/enqueue.rb +9 -73
- data/lib/log_struct/log/active_job/finish.rb +9 -76
- data/lib/log_struct/log/active_job/schedule.rb +9 -73
- data/lib/log_struct/log/active_job/start.rb +9 -76
- data/lib/log_struct/log/active_job.rb +2 -2
- data/lib/log_struct/log/active_model_serializers.rb +5 -45
- data/lib/log_struct/log/active_storage/delete.rb +8 -46
- data/lib/log_struct/log/active_storage/download.rb +9 -55
- data/lib/log_struct/log/active_storage/exist.rb +9 -49
- data/lib/log_struct/log/active_storage/metadata.rb +9 -49
- data/lib/log_struct/log/active_storage/stream.rb +9 -49
- data/lib/log_struct/log/active_storage/upload.rb +9 -64
- data/lib/log_struct/log/active_storage/url.rb +9 -49
- data/lib/log_struct/log/active_storage.rb +2 -2
- data/lib/log_struct/log/ahoy.rb +5 -43
- data/lib/log_struct/log/carrierwave/delete.rb +15 -69
- data/lib/log_struct/log/carrierwave/download.rb +15 -77
- data/lib/log_struct/log/carrierwave/upload.rb +15 -83
- data/lib/log_struct/log/carrierwave.rb +13 -4
- data/lib/log_struct/log/dotenv/load.rb +5 -33
- data/lib/log_struct/log/dotenv/restore.rb +5 -33
- data/lib/log_struct/log/dotenv/save.rb +5 -33
- data/lib/log_struct/log/dotenv/update.rb +5 -33
- data/lib/log_struct/log/error.rb +7 -40
- data/lib/log_struct/log/good_job/enqueue.rb +9 -72
- data/lib/log_struct/log/good_job/error.rb +9 -89
- data/lib/log_struct/log/good_job/finish.rb +9 -78
- data/lib/log_struct/log/good_job/log.rb +11 -75
- data/lib/log_struct/log/good_job/schedule.rb +7 -78
- data/lib/log_struct/log/good_job/start.rb +7 -78
- data/lib/log_struct/log/good_job.rb +2 -2
- data/lib/log_struct/log/plain.rb +5 -32
- data/lib/log_struct/log/puma/shutdown.rb +5 -32
- data/lib/log_struct/log/puma/start.rb +5 -56
- data/lib/log_struct/log/request.rb +7 -90
- data/lib/log_struct/log/security/blocked_host.rb +12 -73
- data/lib/log_struct/log/security/csrf_violation.rb +6 -67
- data/lib/log_struct/log/security/ip_spoof.rb +6 -73
- data/lib/log_struct/log/shrine/delete.rb +6 -41
- data/lib/log_struct/log/shrine/download.rb +6 -44
- data/lib/log_struct/log/shrine/exist.rb +6 -44
- data/lib/log_struct/log/shrine/metadata.rb +8 -46
- data/lib/log_struct/log/shrine/upload.rb +6 -53
- data/lib/log_struct/log/sidekiq.rb +5 -42
- data/lib/log_struct/log/sql.rb +5 -65
- data/lib/log_struct/log.rb +2 -2
- data/lib/log_struct/monkey_patches/active_support/tagged_logging/formatter.rb +12 -1
- data/lib/log_struct/railtie.rb +0 -22
- data/lib/log_struct/semantic_logger/concerns/log_methods.rb +100 -0
- data/lib/log_struct/semantic_logger/logger.rb +46 -15
- data/lib/log_struct/semantic_logger/setup.rb +11 -7
- data/lib/log_struct/shared/{shared/add_request_fields.rb → add_request_fields.rb} +2 -2
- data/lib/log_struct/shared/{shared/merge_additional_data_fields.rb → merge_additional_data_fields.rb} +1 -1
- data/lib/log_struct/shared/{shared/serialize_common.rb → serialize_common.rb} +9 -3
- data/lib/log_struct/{log/shared → shared}/serialize_common_public.rb +2 -2
- data/lib/log_struct/version.rb +1 -1
- data/lib/log_struct.rb +4 -1
- data/logstruct.gemspec +1 -1
- metadata +9 -11
- data/lib/log_struct/integrations/action_mailer/callbacks.rb +0 -100
- data/lib/log_struct/log/shared/add_request_fields.rb +0 -4
- data/lib/log_struct/log/shared/merge_additional_data_fields.rb +0 -4
- data/lib/log_struct/log/shared/serialize_common.rb +0 -4
@@ -9,9 +9,9 @@
|
|
9
9
|
require "log_struct/shared/interfaces/common_fields"
|
10
10
|
require "log_struct/shared/interfaces/additional_data_field"
|
11
11
|
require "log_struct/shared/interfaces/request_fields"
|
12
|
-
require "log_struct/shared/
|
13
|
-
require "log_struct/shared/
|
14
|
-
require "log_struct/shared/
|
12
|
+
require "log_struct/shared/serialize_common"
|
13
|
+
require "log_struct/shared/merge_additional_data_fields"
|
14
|
+
require "log_struct/shared/add_request_fields"
|
15
15
|
require_relative "../../enums/source"
|
16
16
|
require_relative "../../enums/event"
|
17
17
|
require_relative "../../enums/level"
|
@@ -21,11 +21,6 @@ module LogStruct
|
|
21
21
|
module Log
|
22
22
|
class Shrine
|
23
23
|
class Upload < T::Struct
|
24
|
-
# typed: strict
|
25
|
-
# frozen_string_literal: true
|
26
|
-
|
27
|
-
extend T::Sig
|
28
|
-
|
29
24
|
extend T::Sig
|
30
25
|
|
31
26
|
# Shared/common fields
|
@@ -35,48 +30,20 @@ module LogStruct
|
|
35
30
|
const :level, Level, default: Level::Info
|
36
31
|
|
37
32
|
# Event-specific fields
|
38
|
-
const :storage,
|
33
|
+
const :storage, Symbol
|
39
34
|
const :location, String
|
40
35
|
const :upload_options, T.nilable(T::Hash[Symbol, T.untyped]), default: nil
|
41
36
|
const :options, T.nilable(T::Hash[Symbol, T.untyped]), default: nil
|
42
37
|
const :uploader, T.nilable(String), default: nil
|
43
38
|
const :duration_ms, T.nilable(Float), default: nil
|
44
39
|
|
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
40
|
# Serialize shared fields
|
53
41
|
include LogStruct::Log::Interfaces::CommonFields
|
54
42
|
include LogStruct::Log::Shared::SerializeCommon
|
55
43
|
|
56
44
|
sig { returns(T::Hash[LogStruct::LogField, T.untyped]) }
|
57
|
-
def
|
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
|
45
|
+
def to_h
|
46
|
+
h = T.let({}, T::Hash[LogStruct::LogField, T.untyped])
|
80
47
|
h[LogField::Storage] = storage
|
81
48
|
h[LogField::Location] = location
|
82
49
|
h[LogField::UploadOptions] = upload_options unless upload_options.nil?
|
@@ -85,20 +52,6 @@ module LogStruct
|
|
85
52
|
h[LogField::DurationMs] = duration_ms unless duration_ms.nil?
|
86
53
|
h
|
87
54
|
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
55
|
end
|
103
56
|
end
|
104
57
|
end
|
@@ -9,9 +9,9 @@
|
|
9
9
|
require "log_struct/shared/interfaces/common_fields"
|
10
10
|
require "log_struct/shared/interfaces/additional_data_field"
|
11
11
|
require "log_struct/shared/interfaces/request_fields"
|
12
|
-
require "log_struct/shared/
|
13
|
-
require "log_struct/shared/
|
14
|
-
require "log_struct/shared/
|
12
|
+
require "log_struct/shared/serialize_common"
|
13
|
+
require "log_struct/shared/merge_additional_data_fields"
|
14
|
+
require "log_struct/shared/add_request_fields"
|
15
15
|
require_relative "../enums/source"
|
16
16
|
require_relative "../enums/event"
|
17
17
|
require_relative "../enums/level"
|
@@ -20,11 +20,6 @@ require_relative "../enums/log_field"
|
|
20
20
|
module LogStruct
|
21
21
|
module Log
|
22
22
|
class Sidekiq < T::Struct
|
23
|
-
# typed: strict
|
24
|
-
# frozen_string_literal: true
|
25
|
-
|
26
|
-
extend T::Sig
|
27
|
-
|
28
23
|
extend T::Sig
|
29
24
|
|
30
25
|
# Shared/common fields
|
@@ -39,51 +34,19 @@ module LogStruct
|
|
39
34
|
const :process_id, T.nilable(Integer), default: nil
|
40
35
|
const :thread_id, T.nilable(T.any(Integer, String)), default: nil
|
41
36
|
|
42
|
-
# Additional data
|
43
|
-
|
44
|
-
# Request fields (optional)
|
45
|
-
|
46
37
|
# Serialize shared fields
|
47
38
|
include LogStruct::Log::Interfaces::CommonFields
|
48
39
|
include LogStruct::Log::Shared::SerializeCommon
|
49
40
|
|
50
41
|
sig { returns(T::Hash[LogStruct::LogField, T.untyped]) }
|
51
|
-
def
|
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
|
42
|
+
def to_h
|
43
|
+
h = T.let({}, T::Hash[LogStruct::LogField, T.untyped])
|
70
44
|
h[LogField::Message] = message unless message.nil?
|
71
45
|
h[LogField::Context] = context unless context.nil?
|
72
46
|
h[LogField::ProcessId] = process_id unless process_id.nil?
|
73
47
|
h[LogField::ThreadId] = thread_id unless thread_id.nil?
|
74
48
|
h
|
75
49
|
end
|
76
|
-
|
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
|
-
)
|
86
|
-
end
|
87
50
|
end
|
88
51
|
end
|
89
52
|
end
|
data/lib/log_struct/log/sql.rb
CHANGED
@@ -9,9 +9,9 @@
|
|
9
9
|
require "log_struct/shared/interfaces/common_fields"
|
10
10
|
require "log_struct/shared/interfaces/additional_data_field"
|
11
11
|
require "log_struct/shared/interfaces/request_fields"
|
12
|
-
require "log_struct/shared/
|
13
|
-
require "log_struct/shared/
|
14
|
-
require "log_struct/shared/
|
12
|
+
require "log_struct/shared/serialize_common"
|
13
|
+
require "log_struct/shared/merge_additional_data_fields"
|
14
|
+
require "log_struct/shared/add_request_fields"
|
15
15
|
require_relative "../enums/source"
|
16
16
|
require_relative "../enums/event"
|
17
17
|
require_relative "../enums/level"
|
@@ -20,11 +20,6 @@ require_relative "../enums/log_field"
|
|
20
20
|
module LogStruct
|
21
21
|
module Log
|
22
22
|
class SQL < T::Struct
|
23
|
-
# typed: strict
|
24
|
-
# frozen_string_literal: true
|
25
|
-
|
26
|
-
extend T::Sig
|
27
|
-
|
28
23
|
extend T::Sig
|
29
24
|
|
30
25
|
# Shared/common fields
|
@@ -52,48 +47,13 @@ module LogStruct
|
|
52
47
|
const :additional_data, T.nilable(T::Hash[T.any(String, Symbol), T.untyped]), default: nil
|
53
48
|
include LogStruct::Log::Shared::MergeAdditionalDataFields
|
54
49
|
|
55
|
-
# Request fields (optional)
|
56
|
-
|
57
50
|
# Serialize shared fields
|
58
51
|
include LogStruct::Log::Interfaces::CommonFields
|
59
52
|
include LogStruct::Log::Shared::SerializeCommon
|
60
53
|
|
61
54
|
sig { returns(T::Hash[LogStruct::LogField, T.untyped]) }
|
62
|
-
def
|
63
|
-
{}
|
64
|
-
end
|
65
|
-
|
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
|
55
|
+
def to_h
|
56
|
+
h = T.let({}, T::Hash[LogStruct::LogField, T.untyped])
|
97
57
|
h[LogField::Message] = message
|
98
58
|
h[LogField::Sql] = sql
|
99
59
|
h[LogField::Name] = name
|
@@ -108,26 +68,6 @@ module LogStruct
|
|
108
68
|
h[LogField::TableNames] = table_names unless table_names.nil?
|
109
69
|
h
|
110
70
|
end
|
111
|
-
|
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
|
-
)
|
130
|
-
end
|
131
71
|
end
|
132
72
|
end
|
133
73
|
end
|
data/lib/log_struct/log.rb
CHANGED
@@ -7,7 +7,7 @@ require_relative "enums/event"
|
|
7
7
|
require_relative "enums/level"
|
8
8
|
require_relative "enums/log_field"
|
9
9
|
require_relative "log/interfaces/public_common_fields"
|
10
|
-
require_relative "
|
10
|
+
require_relative "shared/serialize_common_public"
|
11
11
|
|
12
12
|
# Dynamically require all top-level log structs under log/*
|
13
13
|
# Nested per-event files are required by their parent files.
|
@@ -31,7 +31,7 @@ module LogStruct
|
|
31
31
|
def self.from_exception(source, ex, additional_data = {}, timestamp = Time.now)
|
32
32
|
LogStruct::Log::Error.new(
|
33
33
|
source: source,
|
34
|
-
|
34
|
+
error_class: ex.class,
|
35
35
|
message: ex.message,
|
36
36
|
backtrace: ex.backtrace,
|
37
37
|
additional_data: additional_data,
|
@@ -8,6 +8,16 @@ require "active_support/tagged_logging"
|
|
8
8
|
# directly into the hash instead of being prepended as strings
|
9
9
|
module ActiveSupport
|
10
10
|
module TaggedLogging
|
11
|
+
extend T::Sig
|
12
|
+
|
13
|
+
# Add class-level current_tags method for compatibility with Rails code
|
14
|
+
# that expects to call ActiveSupport::TaggedLogging.current_tags
|
15
|
+
# Use thread-local storage directly like Rails does internally
|
16
|
+
sig { returns(T::Array[T.any(String, Symbol)]) }
|
17
|
+
def self.current_tags
|
18
|
+
Thread.current[:activesupport_tagged_logging_tags] || []
|
19
|
+
end
|
20
|
+
|
11
21
|
module FormatterExtension
|
12
22
|
extend T::Sig
|
13
23
|
extend T::Helpers
|
@@ -23,7 +33,8 @@ module ActiveSupport
|
|
23
33
|
data = {message: data.to_s} unless data.is_a?(Hash)
|
24
34
|
|
25
35
|
# Add current tags to the hash if present
|
26
|
-
|
36
|
+
# Use thread-local storage directly as fallback if current_tags method doesn't exist
|
37
|
+
tags = T.unsafe(self).respond_to?(:current_tags) ? current_tags : (Thread.current[:activesupport_tagged_logging_tags] || [])
|
27
38
|
data[:tags] = tags if tags.present?
|
28
39
|
|
29
40
|
# Call the original formatter with our enhanced data
|
data/lib/log_struct/railtie.rb
CHANGED
@@ -10,32 +10,10 @@ require_relative "integrations"
|
|
10
10
|
module LogStruct
|
11
11
|
# Railtie to integrate with Rails
|
12
12
|
class Railtie < ::Rails::Railtie
|
13
|
-
# Ensure test hosts are allowed early enough for middleware build
|
14
|
-
initializer "logstruct.allow_test_hosts", before: :build_middleware_stack do |app|
|
15
|
-
if ::Rails.env.test? && app.config.respond_to?(:hosts)
|
16
|
-
begin
|
17
|
-
app.config.hosts << /.*\z/
|
18
|
-
rescue
|
19
|
-
# best-effort
|
20
|
-
end
|
21
|
-
begin
|
22
|
-
app.config.middleware.delete(::ActionDispatch::HostAuthorization)
|
23
|
-
rescue
|
24
|
-
# best-effort
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
# After ActionDispatch is configured, remove HostAuthorization in test to prevent 403s
|
30
|
-
# (No late deletion needed; handled above before middleware stack is built)
|
31
|
-
|
32
13
|
# Configure early, right after logger initialization
|
33
14
|
initializer "logstruct.configure_logger", after: :initialize_logger do |app|
|
34
15
|
next unless LogStruct.enabled?
|
35
16
|
|
36
|
-
# Apply TaggedLogging monkey patch only when enabled
|
37
|
-
require_relative "monkey_patches/active_support/tagged_logging/formatter"
|
38
|
-
|
39
17
|
# Use SemanticLogger for powerful logging features
|
40
18
|
LogStruct::SemanticLogger::Setup.configure_semantic_logger(app)
|
41
19
|
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module LogStruct
|
5
|
+
module SemanticLogger
|
6
|
+
module Concerns
|
7
|
+
module LogMethods
|
8
|
+
extend T::Sig
|
9
|
+
extend T::Helpers
|
10
|
+
requires_ancestor { LogStruct::SemanticLogger::Logger }
|
11
|
+
|
12
|
+
# Override log methods to handle LogStruct types and broadcast
|
13
|
+
sig { params(message: T.untyped, payload: T.untyped, block: T.nilable(T.proc.returns(String))).returns(T::Boolean) }
|
14
|
+
def debug(message = nil, payload = nil, &block)
|
15
|
+
instrument_log(message, :debug)
|
16
|
+
result = if message.is_a?(LogStruct::Log::Interfaces::CommonFields) || message.is_a?(T::Struct) || message.is_a?(Hash)
|
17
|
+
super(nil, payload: message, &block)
|
18
|
+
else
|
19
|
+
super
|
20
|
+
end
|
21
|
+
broadcasts.each do |logger|
|
22
|
+
next unless logger.respond_to?(:debug)
|
23
|
+
message.is_a?(String) ? logger.debug(message) : (logger.debug(&block) if block)
|
24
|
+
end
|
25
|
+
result
|
26
|
+
end
|
27
|
+
|
28
|
+
sig { params(message: T.untyped, payload: T.untyped, block: T.nilable(T.proc.returns(String))).returns(T::Boolean) }
|
29
|
+
def info(message = nil, payload = nil, &block)
|
30
|
+
instrument_log(message, :info)
|
31
|
+
result = if message.is_a?(LogStruct::Log::Interfaces::CommonFields) || message.is_a?(T::Struct) || message.is_a?(Hash)
|
32
|
+
super(nil, payload: message, &block)
|
33
|
+
else
|
34
|
+
super
|
35
|
+
end
|
36
|
+
broadcasts.each do |logger|
|
37
|
+
next unless logger.respond_to?(:info)
|
38
|
+
message.is_a?(String) ? logger.info(message) : (logger.info(&block) if block)
|
39
|
+
end
|
40
|
+
result
|
41
|
+
end
|
42
|
+
|
43
|
+
sig { params(message: T.untyped, payload: T.untyped, block: T.nilable(T.proc.returns(String))).returns(T::Boolean) }
|
44
|
+
def warn(message = nil, payload = nil, &block)
|
45
|
+
instrument_log(message, :warn)
|
46
|
+
result = if message.is_a?(LogStruct::Log::Interfaces::CommonFields) || message.is_a?(T::Struct) || message.is_a?(Hash)
|
47
|
+
super(nil, payload: message, &block)
|
48
|
+
else
|
49
|
+
super
|
50
|
+
end
|
51
|
+
broadcasts.each do |logger|
|
52
|
+
next unless logger.respond_to?(:warn)
|
53
|
+
message.is_a?(String) ? logger.warn(message) : (logger.warn(&block) if block)
|
54
|
+
end
|
55
|
+
result
|
56
|
+
end
|
57
|
+
|
58
|
+
sig { params(message: T.untyped, payload: T.untyped, block: T.nilable(T.proc.returns(String))).returns(T::Boolean) }
|
59
|
+
def error(message = nil, payload = nil, &block)
|
60
|
+
instrument_log(message, :error)
|
61
|
+
result = if message.is_a?(LogStruct::Log::Interfaces::CommonFields) || message.is_a?(T::Struct) || message.is_a?(Hash)
|
62
|
+
super(nil, payload: message, &block)
|
63
|
+
else
|
64
|
+
super
|
65
|
+
end
|
66
|
+
broadcasts.each do |logger|
|
67
|
+
next unless logger.respond_to?(:error)
|
68
|
+
message.is_a?(String) ? logger.error(message) : (logger.error(&block) if block)
|
69
|
+
end
|
70
|
+
result
|
71
|
+
end
|
72
|
+
|
73
|
+
sig { params(message: T.untyped, payload: T.untyped, block: T.nilable(T.proc.returns(String))).returns(T::Boolean) }
|
74
|
+
def fatal(message = nil, payload = nil, &block)
|
75
|
+
instrument_log(message, :fatal)
|
76
|
+
result = if message.is_a?(LogStruct::Log::Interfaces::CommonFields) || message.is_a?(T::Struct) || message.is_a?(Hash)
|
77
|
+
super(nil, payload: message, &block)
|
78
|
+
else
|
79
|
+
super
|
80
|
+
end
|
81
|
+
broadcasts.each do |logger|
|
82
|
+
next unless logger.respond_to?(:fatal)
|
83
|
+
message.is_a?(String) ? logger.fatal(message) : (logger.fatal(&block) if block)
|
84
|
+
end
|
85
|
+
result
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
# Instrument log events for subscribers
|
91
|
+
sig { params(message: T.untyped, level: Symbol).void }
|
92
|
+
def instrument_log(message, level)
|
93
|
+
return unless message.is_a?(LogStruct::Log::Interfaces::CommonFields) || message.is_a?(T::Struct)
|
94
|
+
|
95
|
+
::ActiveSupport::Notifications.instrument("log.logstruct", log: message, level: level)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "semantic_logger"
|
5
|
+
require_relative "concerns/log_methods"
|
5
6
|
|
6
7
|
module LogStruct
|
7
8
|
module SemanticLogger
|
@@ -69,25 +70,37 @@ module LogStruct
|
|
69
70
|
def initialize(name = "Application", level: nil, filter: nil)
|
70
71
|
# SemanticLogger::Logger expects positional arguments, not named arguments
|
71
72
|
super(name, level, filter)
|
73
|
+
# T.untyped because users can pass any logger: ::Logger, ActiveSupport::Logger,
|
74
|
+
# custom loggers (FakeLogger in tests), or third-party loggers
|
75
|
+
@broadcasts = T.let([], T::Array[T.untyped])
|
76
|
+
# ActiveJob expects logger.formatter to exist and respond to current_tags
|
77
|
+
@formatter = T.let(FormatterProxy.new, FormatterProxy)
|
72
78
|
end
|
73
79
|
|
74
|
-
#
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
80
|
+
# ActiveSupport::BroadcastLogger compatibility
|
81
|
+
# These methods allow Rails.logger to broadcast to multiple loggers
|
82
|
+
sig { returns(T::Array[T.untyped]) }
|
83
|
+
attr_reader :broadcasts
|
84
|
+
|
85
|
+
# ActiveJob compatibility - expects logger.formatter.current_tags
|
86
|
+
sig { returns(FormatterProxy) }
|
87
|
+
attr_reader :formatter
|
88
|
+
|
89
|
+
# T.untyped for logger param because we accept any logger-like object:
|
90
|
+
# ::Logger, ActiveSupport::Logger, test doubles, etc.
|
91
|
+
sig { params(logger: T.untyped).returns(T.untyped) }
|
92
|
+
def broadcast_to(logger)
|
93
|
+
@broadcasts << logger
|
94
|
+
logger
|
95
|
+
end
|
96
|
+
|
97
|
+
sig { params(logger: T.untyped).void }
|
98
|
+
def stop_broadcasting_to(logger)
|
99
|
+
@broadcasts.delete(logger)
|
89
100
|
end
|
90
101
|
|
102
|
+
include Concerns::LogMethods
|
103
|
+
|
91
104
|
# Support for tagged logging
|
92
105
|
sig { params(tags: T.untyped, block: T.proc.returns(T.untyped)).returns(T.untyped) }
|
93
106
|
def tagged(*tags, &block)
|
@@ -124,6 +137,24 @@ module LogStruct
|
|
124
137
|
def pop_tags(count = 1)
|
125
138
|
::SemanticLogger.pop_tags(count)
|
126
139
|
end
|
140
|
+
|
141
|
+
# Support for << operator (used by RailsLogSplitter)
|
142
|
+
sig { params(msg: String).returns(T.self_type) }
|
143
|
+
def <<(msg)
|
144
|
+
info(msg)
|
145
|
+
@broadcasts.each { |logger| logger << msg if logger.respond_to?(:<<) }
|
146
|
+
self
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Proxy object to provide ActiveJob-compatible formatter interface
|
151
|
+
class FormatterProxy
|
152
|
+
extend T::Sig
|
153
|
+
|
154
|
+
sig { returns(T::Array[T.any(String, Symbol)]) }
|
155
|
+
def current_tags
|
156
|
+
Thread.current[:activesupport_tagged_logging_tags] || []
|
157
|
+
end
|
127
158
|
end
|
128
159
|
end
|
129
160
|
end
|
@@ -152,8 +152,8 @@ module LogStruct
|
|
152
152
|
)
|
153
153
|
end
|
154
154
|
|
155
|
-
# Add file appender if
|
156
|
-
if app.config.paths["log"].first
|
155
|
+
# Add file appender if Rails has a log path configured (normal Rails behavior)
|
156
|
+
if app.config.paths["log"].first
|
157
157
|
::SemanticLogger.add_appender(
|
158
158
|
file_name: app.config.paths["log"].first,
|
159
159
|
formatter: LogStruct::SemanticLogger::Formatter.new,
|
@@ -164,14 +164,14 @@ module LogStruct
|
|
164
164
|
|
165
165
|
sig { params(app: T.untyped).returns(T.untyped) }
|
166
166
|
def self.determine_output(app)
|
167
|
-
# Always honor explicit STDOUT directive
|
168
|
-
return $stdout if ENV["RAILS_LOG_TO_STDOUT"].present?
|
167
|
+
# Always honor explicit STDOUT directive
|
168
|
+
return $stdout if ENV["RAILS_LOG_TO_STDOUT"].present?
|
169
169
|
|
170
170
|
if Rails.env.test?
|
171
|
-
#
|
171
|
+
# Use StringIO in test to keep stdout clean
|
172
172
|
StringIO.new
|
173
173
|
else
|
174
|
-
#
|
174
|
+
# Use STDOUT for app logs in dev/production
|
175
175
|
$stdout
|
176
176
|
end
|
177
177
|
end
|
@@ -222,7 +222,11 @@ module LogStruct
|
|
222
222
|
# Also replace various component loggers
|
223
223
|
ActiveRecord::Base.logger = logger if defined?(ActiveRecord::Base)
|
224
224
|
ActionController::Base.logger = logger if defined?(ActionController::Base)
|
225
|
-
|
225
|
+
if defined?(ActionMailer::Base)
|
226
|
+
ActionMailer::Base.logger = logger
|
227
|
+
# Ensure ActionMailer.logger is also set (it might be accessed directly)
|
228
|
+
T.unsafe(::ActionMailer).logger = logger if T.unsafe(::ActionMailer).respond_to?(:logger=)
|
229
|
+
end
|
226
230
|
ActiveJob::Base.logger = logger if defined?(ActiveJob::Base)
|
227
231
|
ActionView::Base.logger = logger if defined?(ActionView::Base)
|
228
232
|
ActionCable.server.config.logger = logger if defined?(ActionCable)
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require_relative "
|
5
|
-
require_relative "
|
4
|
+
require_relative "../enums/log_field"
|
5
|
+
require_relative "interfaces/request_fields"
|
6
6
|
|
7
7
|
module LogStruct
|
8
8
|
module Log
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require_relative "
|
5
|
-
require_relative "
|
6
|
-
require_relative "
|
4
|
+
require_relative "../enums/log_field"
|
5
|
+
require_relative "interfaces/common_fields"
|
6
|
+
require_relative "merge_additional_data_fields"
|
7
7
|
|
8
8
|
module LogStruct
|
9
9
|
module Log
|
@@ -25,6 +25,12 @@ module LogStruct
|
|
25
25
|
field_hash.each do |log_field, value|
|
26
26
|
next if value.nil?
|
27
27
|
key = log_field.serialize
|
28
|
+
|
29
|
+
# Limit backtrace to first 5 lines
|
30
|
+
if key == :backtrace && value.is_a?(Array)
|
31
|
+
value = value.first(5)
|
32
|
+
end
|
33
|
+
|
28
34
|
out[key] = value.is_a?(::Time) ? value.iso8601 : value
|
29
35
|
end
|
30
36
|
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require_relative "
|
5
|
-
require_relative "
|
4
|
+
require_relative "../enums/log_field"
|
5
|
+
require_relative "interfaces/public_common_fields"
|
6
6
|
|
7
7
|
module LogStruct
|
8
8
|
module Log
|
data/lib/log_struct/version.rb
CHANGED
data/lib/log_struct.rb
CHANGED
@@ -22,6 +22,9 @@ require "log_struct/semantic_logger/logger"
|
|
22
22
|
require "log_struct/semantic_logger/setup"
|
23
23
|
require "log_struct/rails_boot_banner_silencer"
|
24
24
|
|
25
|
+
# Monkey patches for Rails compatibility
|
26
|
+
require "log_struct/monkey_patches/active_support/tagged_logging/formatter"
|
27
|
+
|
25
28
|
module LogStruct
|
26
29
|
extend T::Sig
|
27
30
|
|
@@ -44,7 +47,7 @@ module LogStruct
|
|
44
47
|
end
|
45
48
|
|
46
49
|
# Set enabled at require time based on current Rails environment.
|
47
|
-
# (Users can
|
50
|
+
# (Users can override this in their initializer which runs before the Railtie checks enabled)
|
48
51
|
set_enabled_from_rails_env!
|
49
52
|
|
50
53
|
# Silence Rails boot banners for cleaner server output
|