Skip to content

Commit 0ac8d64

Browse files
committed
fix jscpd duplication
1 parent ec64c2c commit 0ac8d64

File tree

23 files changed

+467
-1059
lines changed

23 files changed

+467
-1059
lines changed

.cspell/project-words.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ mswin
4444
nilability
4545
nilable
4646
nonblock
47+
nonblocking
4748
optparse
4849
parseable
4950
pentester

lib/log_struct/integrations/action_mailer/error_handling.rb

Lines changed: 16 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -150,38 +150,8 @@ def handle_error_notifications(error, notify, report, reraise)
150150

151151
# Report to error reporting service if requested
152152
if report
153-
# Get message if available
154-
mailer_message = respond_to?(:message) ? message : nil
155-
156-
# Prepare universal mailer fields
157-
message_data = {}
158-
MetadataCollection.add_message_metadata(self, message_data)
159-
160-
# Prepare app-specific context data
161153
context_data = {recipients: recipients(error)}
162-
MetadataCollection.add_context_metadata(self, context_data)
163-
164-
# Extract email fields
165-
to = mailer_message&.to
166-
from = mailer_message&.from&.first
167-
subject = mailer_message&.subject
168-
message_id = extract_message_id_from_mailer(self)
169-
170-
# Create ActionMailer-specific error struct
171-
exception_data = Log::ActionMailer::Error.new(
172-
to: to,
173-
from: from,
174-
subject: subject,
175-
message_id: message_id,
176-
mailer_class: self.class.to_s,
177-
mailer_action: respond_to?(:action_name) ? action_name&.to_s : nil,
178-
attachment_count: message_data[:attachment_count],
179-
error_class: error.class,
180-
message: error.message,
181-
backtrace: error.backtrace,
182-
additional_data: context_data.presence,
183-
timestamp: Time.now
184-
)
154+
exception_data = build_exception_data(error, Level::Error, context_data)
185155

186156
# Log the exception with structured data
187157
LogStruct.error(exception_data)
@@ -202,29 +172,32 @@ def handle_error_notifications(error, notify, report, reraise)
202172
# Log a notification event that can be picked up by external systems
203173
sig { params(error: StandardError).void }
204174
def log_notification_event(error)
205-
# Get message if available
206-
mailer_message = respond_to?(:message) ? message : nil
207-
208-
# Prepare universal mailer fields
209-
message_data = {}
210-
MetadataCollection.add_message_metadata(self, message_data)
211-
212-
# Prepare app-specific context data
213175
context_data = {
214176
mailer: self.class.to_s,
215177
action: action_name&.to_s,
216178
recipients: recipients(error)
217179
}
180+
exception_data = build_exception_data(error, Level::Info, context_data)
181+
182+
# Log the error at info level since it's not a critical error
183+
LogStruct.info(exception_data)
184+
end
185+
186+
sig { params(error: StandardError, level: Level, context_data: T::Hash[Symbol, T.untyped]).returns(Log::ActionMailer::Error) }
187+
def build_exception_data(error, level, context_data)
188+
mailer_message = respond_to?(:message) ? message : nil
189+
190+
message_data = {}
191+
MetadataCollection.add_message_metadata(self, message_data)
192+
218193
MetadataCollection.add_context_metadata(self, context_data)
219194

220-
# Extract email fields
221195
to = mailer_message&.to
222196
from = mailer_message&.from&.first
223197
subject = mailer_message&.subject
224198
message_id = extract_message_id_from_mailer(self)
225199

226-
# Create ActionMailer-specific error struct
227-
exception_data = Log::ActionMailer::Error.new(
200+
Log::ActionMailer::Error.new(
228201
to: to,
229202
from: from,
230203
subject: subject,
@@ -237,11 +210,8 @@ def log_notification_event(error)
237210
backtrace: error.backtrace,
238211
additional_data: context_data.presence,
239212
timestamp: Time.now,
240-
level: Level::Info
213+
level: level
241214
)
242-
243-
# Log the error at info level since it's not a critical error
244-
LogStruct.info(exception_data)
245215
end
246216

247217
sig { params(error: StandardError).returns(String) }

lib/log_struct/integrations/dotenv.rb

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,20 @@ def self.setup(config)
3434
class << self
3535
extend T::Sig
3636

37+
sig { params(env: T.untyped).returns(String) }
38+
def relative_env_path(env)
39+
abs = env.filename
40+
begin
41+
if defined?(::Rails) && ::Rails.respond_to?(:root) && ::Rails.root
42+
Pathname.new(abs).relative_path_from(Pathname.new(::Rails.root.to_s)).to_s
43+
else
44+
abs
45+
end
46+
rescue
47+
abs
48+
end
49+
end
50+
3751
sig { void }
3852
def subscribe!
3953
# Guard against double subscription
@@ -47,16 +61,7 @@ def subscribe!
4761
LogStruct::Log::Dotenv.new
4862
event = ::ActiveSupport::Notifications::Event.new(*args)
4963
env = event.payload[:env]
50-
abs = env.filename
51-
file = begin
52-
if defined?(::Rails) && ::Rails.respond_to?(:root) && ::Rails.root
53-
Pathname.new(abs).relative_path_from(Pathname.new(::Rails.root.to_s)).to_s
54-
else
55-
abs
56-
end
57-
rescue
58-
abs
59-
end
64+
file = relative_env_path(env)
6065

6166
ts = event.time ? Time.at(event.time) : Time.now
6267
LogStruct.info(Log::Dotenv::Load.new(file: file, timestamp: ts))
@@ -134,16 +139,7 @@ def self.setup_boot
134139
instrumenter.subscribe("load.dotenv") do |*args|
135140
event = ::ActiveSupport::Notifications::Event.new(*args)
136141
env = event.payload[:env]
137-
abs = env.filename
138-
file = begin
139-
if defined?(::Rails) && ::Rails.respond_to?(:root) && ::Rails.root
140-
Pathname.new(abs).relative_path_from(Pathname.new(::Rails.root.to_s)).to_s
141-
else
142-
abs
143-
end
144-
rescue
145-
abs
146-
end
142+
file = relative_env_path(env)
147143
ts = event.time ? Time.at(event.time) : Time.now
148144
LogStruct::BootBuffer.add(Log::Dotenv::Load.new(file: file, timestamp: ts))
149145
rescue => e

lib/log_struct/integrations/puma.rb

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,7 @@ def setup(config)
5050
if ARGV.include?("server")
5151
# Emit deterministic boot/started events based on CLI args
5252
begin
53-
port = T.let(nil, T.nilable(String))
54-
ARGV.each_with_index do |arg, idx|
55-
if arg == "-p" || arg == "--port"
56-
port = ARGV[idx + 1]
57-
break
58-
elsif arg.start_with?("--port=")
59-
port = arg.split("=", 2)[1]
60-
break
61-
end
62-
end
53+
port = port_from_argv(ARGV)
6354
si = T.cast(STATE[:start_info], T::Hash[Symbol, T.untyped])
6455
si[:pid] ||= Process.pid
6556
si[:environment] ||= ((defined?(::Rails) && ::Rails.respond_to?(:env)) ? ::Rails.env : nil)
@@ -175,6 +166,18 @@ def state_reset!
175166
}
176167
end
177168

169+
sig { params(argv: T::Array[String]).returns(T.nilable(String)) }
170+
def port_from_argv(argv)
171+
argv.each_with_index do |arg, idx|
172+
if arg == "-p" || arg == "--port"
173+
return argv[idx + 1]
174+
elsif arg.start_with?("--port=")
175+
return arg.split("=", 2)[1]
176+
end
177+
end
178+
nil
179+
end
180+
178181
sig { params(line: String).returns(T::Boolean) }
179182
def process_line(line)
180183
l = line.to_s.strip
@@ -243,16 +246,7 @@ def process_line(line)
243246
# Fallback: if no listening address captured yet, infer from ARGV
244247
if T.cast(si[:listening], T::Array[T.untyped]).empty?
245248
begin
246-
port = T.let(nil, T.untyped)
247-
ARGV.each_with_index do |arg, idx|
248-
if arg == "-p" || arg == "--port"
249-
port = ARGV[idx + 1]
250-
break
251-
elsif arg.start_with?("--port=")
252-
port = arg.split("=", 2)[1]
253-
break
254-
end
255-
end
249+
port = port_from_argv(ARGV)
256250
if port
257251
si[:listening] << "tcp://127.0.0.1:#{port}"
258252
end

lib/log_struct/integrations/rack.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
require "rack"
55
require "action_dispatch/middleware/show_exceptions"
66
require_relative "rack/error_handling_middleware"
7+
require_relative "rack_setup"
78

89
module LogStruct
910
module Integrations
@@ -15,12 +16,11 @@ module Rack
1516
# Set up Rack middleware for structured error logging
1617
sig { override.params(config: LogStruct::Configuration).returns(T.nilable(T::Boolean)) }
1718
def self.setup(config)
18-
return nil unless config.enabled
19-
return nil unless config.integrations.enable_rack_error_handler
19+
return nil unless RackSetup.enabled?(config)
2020

2121
# Add structured logging middleware for security violations and errors
2222
# Need to insert after ShowExceptions to catch IP spoofing errors
23-
::Rails.application.middleware.insert_after(
23+
RackSetup.insert_after(
2424
::ActionDispatch::ShowExceptions,
2525
Integrations::RackErrorHandler::Middleware
2626
)

lib/log_struct/integrations/rack_error_handler.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
require "rack"
55
require "action_dispatch/middleware/show_exceptions"
66
require_relative "rack_error_handler/middleware"
7+
require_relative "rack_setup"
78

89
module LogStruct
910
module Integrations
@@ -15,12 +16,11 @@ module RackErrorHandler
1516
# Set up Rack middleware for structured error logging
1617
sig { override.params(config: LogStruct::Configuration).returns(T.nilable(T::Boolean)) }
1718
def self.setup(config)
18-
return nil unless config.enabled
19-
return nil unless config.integrations.enable_rack_error_handler
19+
return nil unless RackSetup.enabled?(config)
2020

2121
# Add structured logging middleware for security violations and errors
2222
# Need to insert before RemoteIp to catch IP spoofing errors it raises
23-
::Rails.application.middleware.insert_before(
23+
RackSetup.insert_before(
2424
::ActionDispatch::RemoteIp,
2525
Integrations::RackErrorHandler::Middleware
2626
)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# typed: strict
2+
# frozen_string_literal: true
3+
4+
module LogStruct
5+
module Integrations
6+
module RackSetup
7+
extend T::Sig
8+
9+
sig { params(config: LogStruct::Configuration).returns(T::Boolean) }
10+
def self.enabled?(config)
11+
return false unless config.enabled
12+
return false unless config.integrations.enable_rack_error_handler
13+
14+
true
15+
end
16+
17+
sig { params(anchor: T.untyped, middleware: T.untyped).void }
18+
def self.insert_after(anchor, middleware)
19+
::Rails.application.middleware.insert_after(anchor, middleware)
20+
end
21+
22+
sig { params(anchor: T.untyped, middleware: T.untyped).void }
23+
def self.insert_before(anchor, middleware)
24+
::Rails.application.middleware.insert_before(anchor, middleware)
25+
end
26+
end
27+
end
28+
end

lib/log_struct/railtie.rb

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,7 @@ class Railtie < ::Rails::Railtie
4646
next unless is_server
4747
begin
4848
require "log_struct/log/puma"
49-
port = T.let(nil, T.nilable(String))
50-
ARGV.each_with_index do |arg, idx|
51-
if arg == "-p" || arg == "--port"
52-
port = ARGV[idx + 1]
53-
break
54-
elsif arg.start_with?("--port=")
55-
port = arg.split("=", 2)[1]
56-
break
57-
end
58-
end
49+
port = LogStruct::Integrations::Puma.port_from_argv(ARGV)
5950
started = LogStruct::Log::Puma::Start.new(
6051
mode: "single",
6152
environment: (defined?(::Rails) && ::Rails.respond_to?(:env)) ? ::Rails.env : nil,

lib/log_struct/semantic_logger/color_formatter.rb

Lines changed: 22 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -62,44 +62,11 @@ def initialize(color_map: nil, **args)
6262
def call(log, logger)
6363
# Handle LogStruct types specially with colorization
6464
if log.payload.is_a?(LogStruct::Log::Interfaces::CommonFields)
65-
# Get the LogStruct formatted JSON
66-
logstruct_json = @logstruct_formatter.call(log.level, log.time, log.name, log.payload)
67-
68-
# Parse and colorize it
69-
begin
70-
parsed_data = T.let(JSON.parse(logstruct_json), T::Hash[String, T.untyped])
71-
colorized_json = colorize_json(parsed_data)
72-
73-
# Use SemanticLogger's prefix formatting but with our colorized content
74-
prefix = format("%<time>s %<level>s [%<process>s] %<name>s -- ",
75-
time: format_time(log.time),
76-
level: format_level(log.level),
77-
process: log.process_info,
78-
name: format_name(log.name))
79-
80-
"#{prefix}#{colorized_json}\n"
81-
rescue JSON::ParserError
82-
# Fallback to standard formatting
83-
super
84-
end
65+
formatted = format_logstruct_payload(log)
66+
formatted if formatted
8567
elsif log.payload.is_a?(Hash) || log.payload.is_a?(T::Struct)
86-
# Process hashes through our formatter then colorize
87-
begin
88-
logstruct_json = @logstruct_formatter.call(log.level, log.time, log.name, log.payload)
89-
parsed_data = T.let(JSON.parse(logstruct_json), T::Hash[String, T.untyped])
90-
colorized_json = colorize_json(parsed_data)
91-
92-
prefix = format("%<time>s %<level>s [%<process>s] %<name>s -- ",
93-
time: format_time(log.time),
94-
level: format_level(log.level),
95-
process: log.process_info,
96-
name: format_name(log.name))
97-
98-
"#{prefix}#{colorized_json}\n"
99-
rescue JSON::ParserError
100-
# Fallback to standard formatting
101-
super
102-
end
68+
formatted = format_logstruct_payload(log)
69+
formatted if formatted
10370
else
10471
# For plain messages, use SemanticLogger's default colorization
10572
super
@@ -139,6 +106,24 @@ def colorize_json(data)
139106
.gsub(": null", ": " + colorize_text("null", :nil))
140107
end
141108

109+
sig { params(log: ::SemanticLogger::Log).returns(T.nilable(String)) }
110+
def format_logstruct_payload(log)
111+
logstruct_json = @logstruct_formatter.call(log.level, log.time, log.name, log.payload)
112+
113+
parsed_data = T.let(JSON.parse(logstruct_json), T::Hash[String, T.untyped])
114+
colorized_json = colorize_json(parsed_data)
115+
116+
prefix = format("%<time>s %<level>s [%<process>s] %<name>s -- ",
117+
time: format_time(log.time),
118+
level: format_level(log.level),
119+
process: log.process_info,
120+
name: format_name(log.name))
121+
122+
"#{prefix}#{colorized_json}\n"
123+
rescue JSON::ParserError
124+
nil
125+
end
126+
142127
# Add ANSI color codes to text
143128
sig { params(text: String, color_type: Symbol).returns(String) }
144129
def colorize_text(text, color_type)

0 commit comments

Comments
 (0)