logstruct 0.0.1 → 0.0.2.pre.rc2
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 +26 -2
- data/LICENSE +21 -0
- data/README.md +67 -0
- data/lib/log_struct/concerns/configuration.rb +93 -0
- data/lib/log_struct/concerns/error_handling.rb +94 -0
- data/lib/log_struct/concerns/logging.rb +45 -0
- data/lib/log_struct/config_struct/error_handling_modes.rb +25 -0
- data/lib/log_struct/config_struct/filters.rb +80 -0
- data/lib/log_struct/config_struct/integrations.rb +89 -0
- data/lib/log_struct/configuration.rb +59 -0
- data/lib/log_struct/enums/error_handling_mode.rb +22 -0
- data/lib/log_struct/enums/error_reporter.rb +14 -0
- data/lib/log_struct/enums/event.rb +48 -0
- data/lib/log_struct/enums/level.rb +66 -0
- data/lib/log_struct/enums/source.rb +26 -0
- data/lib/log_struct/enums.rb +9 -0
- data/lib/log_struct/formatter.rb +224 -0
- data/lib/log_struct/handlers.rb +27 -0
- data/lib/log_struct/hash_utils.rb +21 -0
- data/lib/log_struct/integrations/action_mailer/callbacks.rb +100 -0
- data/lib/log_struct/integrations/action_mailer/error_handling.rb +173 -0
- data/lib/log_struct/integrations/action_mailer/event_logging.rb +90 -0
- data/lib/log_struct/integrations/action_mailer/metadata_collection.rb +78 -0
- data/lib/log_struct/integrations/action_mailer.rb +50 -0
- data/lib/log_struct/integrations/active_job/log_subscriber.rb +104 -0
- data/lib/log_struct/integrations/active_job.rb +38 -0
- data/lib/log_struct/integrations/active_record.rb +258 -0
- data/lib/log_struct/integrations/active_storage.rb +94 -0
- data/lib/log_struct/integrations/carrierwave.rb +111 -0
- data/lib/log_struct/integrations/good_job/log_subscriber.rb +228 -0
- data/lib/log_struct/integrations/good_job/logger.rb +73 -0
- data/lib/log_struct/integrations/good_job.rb +111 -0
- data/lib/log_struct/integrations/host_authorization.rb +81 -0
- data/lib/log_struct/integrations/integration_interface.rb +21 -0
- data/lib/log_struct/integrations/lograge.rb +114 -0
- data/lib/log_struct/integrations/rack.rb +31 -0
- data/lib/log_struct/integrations/rack_error_handler/middleware.rb +146 -0
- data/lib/log_struct/integrations/rack_error_handler.rb +32 -0
- data/lib/log_struct/integrations/shrine.rb +75 -0
- data/lib/log_struct/integrations/sidekiq/logger.rb +43 -0
- data/lib/log_struct/integrations/sidekiq.rb +39 -0
- data/lib/log_struct/integrations/sorbet.rb +49 -0
- data/lib/log_struct/integrations.rb +41 -0
- data/lib/log_struct/log/action_mailer.rb +55 -0
- data/lib/log_struct/log/active_job.rb +64 -0
- data/lib/log_struct/log/active_storage.rb +78 -0
- data/lib/log_struct/log/carrierwave.rb +82 -0
- data/lib/log_struct/log/error.rb +76 -0
- data/lib/log_struct/log/good_job.rb +151 -0
- data/lib/log_struct/log/interfaces/additional_data_field.rb +20 -0
- data/lib/log_struct/log/interfaces/common_fields.rb +42 -0
- data/lib/log_struct/log/interfaces/message_field.rb +20 -0
- data/lib/log_struct/log/interfaces/request_fields.rb +36 -0
- data/lib/log_struct/log/plain.rb +53 -0
- data/lib/log_struct/log/request.rb +76 -0
- data/lib/log_struct/log/security.rb +80 -0
- data/lib/log_struct/log/shared/add_request_fields.rb +29 -0
- data/lib/log_struct/log/shared/merge_additional_data_fields.rb +28 -0
- data/lib/log_struct/log/shared/serialize_common.rb +36 -0
- data/lib/log_struct/log/shrine.rb +70 -0
- data/lib/log_struct/log/sidekiq.rb +50 -0
- data/lib/log_struct/log/sql.rb +126 -0
- data/lib/log_struct/log.rb +43 -0
- data/lib/log_struct/log_keys.rb +102 -0
- data/lib/log_struct/monkey_patches/active_support/tagged_logging/formatter.rb +36 -0
- data/lib/log_struct/multi_error_reporter.rb +149 -0
- data/lib/log_struct/param_filters.rb +89 -0
- data/lib/log_struct/railtie.rb +31 -0
- data/lib/log_struct/semantic_logger/color_formatter.rb +209 -0
- data/lib/log_struct/semantic_logger/formatter.rb +94 -0
- data/lib/log_struct/semantic_logger/logger.rb +129 -0
- data/lib/log_struct/semantic_logger/setup.rb +219 -0
- data/lib/log_struct/sorbet/serialize_symbol_keys.rb +23 -0
- data/lib/log_struct/sorbet.rb +13 -0
- data/lib/log_struct/string_scrubber.rb +84 -0
- data/lib/log_struct/version.rb +6 -0
- data/lib/log_struct.rb +37 -0
- data/lib/logstruct.rb +2 -6
- data/logstruct.gemspec +52 -0
- metadata +221 -5
- data/Rakefile +0 -5
@@ -0,0 +1,129 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "semantic_logger"
|
5
|
+
|
6
|
+
module LogStruct
|
7
|
+
module SemanticLogger
|
8
|
+
# High-Performance Logger with LogStruct Integration
|
9
|
+
#
|
10
|
+
# This logger extends SemanticLogger::Logger to provide optimal logging performance
|
11
|
+
# while seamlessly integrating with LogStruct's typed logging system.
|
12
|
+
#
|
13
|
+
# ## Key Benefits Over Rails.logger:
|
14
|
+
#
|
15
|
+
# ### Performance
|
16
|
+
# - **10-100x faster** than Rails' default logger for high-volume applications
|
17
|
+
# - **Non-blocking I/O**: Uses background threads for actual log writes
|
18
|
+
# - **Minimal memory allocation**: Efficient object reuse and zero-copy operations
|
19
|
+
# - **Batched writes**: Reduces system calls by batching multiple log entries
|
20
|
+
#
|
21
|
+
# ### Reliability
|
22
|
+
# - **Thread-safe operations**: Safe for use in multi-threaded environments
|
23
|
+
# - **Error resilience**: Logger failures don't crash your application
|
24
|
+
# - **Graceful fallbacks**: Continues operating even if appenders fail
|
25
|
+
#
|
26
|
+
# ### Features
|
27
|
+
# - **Structured logging**: Native support for LogStruct types and hashes
|
28
|
+
# - **Rich metadata**: Automatic inclusion of process ID, thread ID, timestamps
|
29
|
+
# - **Tagged context**: Hierarchical tagging for request/job tracking
|
30
|
+
# - **Multiple destinations**: Simultaneously log to files, STDOUT, cloud services
|
31
|
+
#
|
32
|
+
# ### Development Experience
|
33
|
+
# - **Colorized output**: Beautiful ANSI-colored logs in development
|
34
|
+
# - **Detailed timing**: Built-in measurement of log processing time
|
35
|
+
# - **Context preservation**: Maintains Rails.logger compatibility
|
36
|
+
#
|
37
|
+
# ## Usage Examples
|
38
|
+
#
|
39
|
+
# The logger automatically handles LogStruct types, hashes, and plain messages:
|
40
|
+
#
|
41
|
+
# ```ruby
|
42
|
+
# logger = LogStruct::SemanticLogger::Logger.new("MyApp")
|
43
|
+
#
|
44
|
+
# # LogStruct typed logging (optimal performance)
|
45
|
+
# log_entry = LogStruct::Log::Plain.new(
|
46
|
+
# message: "User authenticated",
|
47
|
+
# source: LogStruct::Source::App,
|
48
|
+
# event: LogStruct::Event::Security
|
49
|
+
# )
|
50
|
+
# logger.info(log_entry)
|
51
|
+
#
|
52
|
+
# # Hash logging (automatically structured)
|
53
|
+
# logger.info({
|
54
|
+
# action: "user_login",
|
55
|
+
# user_id: 123,
|
56
|
+
# ip_address: "192.168.1.1"
|
57
|
+
# })
|
58
|
+
#
|
59
|
+
# # Plain string logging (backward compatibility)
|
60
|
+
# logger.info("User logged in successfully")
|
61
|
+
# ```
|
62
|
+
#
|
63
|
+
# The logger is a drop-in replacement for Rails.logger and maintains full
|
64
|
+
# API compatibility while providing significantly enhanced performance.
|
65
|
+
class Logger < ::SemanticLogger::Logger
|
66
|
+
extend T::Sig
|
67
|
+
|
68
|
+
sig { params(name: T.any(String, Symbol, Module, T::Class[T.anything]), level: T.nilable(Symbol), filter: T.untyped).void }
|
69
|
+
def initialize(name = "Application", level: nil, filter: nil)
|
70
|
+
# SemanticLogger::Logger expects positional arguments, not named arguments
|
71
|
+
super(name, level, filter)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Override log methods to handle LogStruct types
|
75
|
+
%i[debug info warn error fatal].each do |level|
|
76
|
+
define_method(level) do |message = nil, payload = nil, &block|
|
77
|
+
# If message is a LogStruct type, use it as payload
|
78
|
+
if message.is_a?(LogStruct::Log::Interfaces::CommonFields) ||
|
79
|
+
message.is_a?(T::Struct) ||
|
80
|
+
message.is_a?(Hash)
|
81
|
+
payload = message
|
82
|
+
message = nil
|
83
|
+
super(message, payload: payload, &block)
|
84
|
+
else
|
85
|
+
# For plain string messages, pass them through normally
|
86
|
+
super(message, payload, &block)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Support for tagged logging
|
92
|
+
sig { params(tags: T.untyped, block: T.proc.returns(T.untyped)).returns(T.untyped) }
|
93
|
+
def tagged(*tags, &block)
|
94
|
+
# Convert tags to array and pass individually to avoid splat issues
|
95
|
+
tag_array = tags.flatten
|
96
|
+
if tag_array.empty?
|
97
|
+
super(&block)
|
98
|
+
else
|
99
|
+
super(*T.unsafe(tag_array), &block)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Ensure compatibility with Rails.logger interface
|
104
|
+
sig { returns(T::Array[T.any(String, Symbol)]) }
|
105
|
+
def current_tags
|
106
|
+
::SemanticLogger.tags
|
107
|
+
end
|
108
|
+
|
109
|
+
sig { void }
|
110
|
+
def clear_tags!
|
111
|
+
# SemanticLogger doesn't have clear_tags!, use pop_tags instead
|
112
|
+
count = ::SemanticLogger.tags.length
|
113
|
+
::SemanticLogger.pop_tags(count) if count > 0
|
114
|
+
end
|
115
|
+
|
116
|
+
sig { params(tags: T.untyped).returns(T::Array[T.untyped]) }
|
117
|
+
def push_tags(*tags)
|
118
|
+
flat = tags.flatten.compact
|
119
|
+
flat.each { |tag| ::SemanticLogger.push_tags(tag) }
|
120
|
+
flat
|
121
|
+
end
|
122
|
+
|
123
|
+
sig { params(count: Integer).void }
|
124
|
+
def pop_tags(count = 1)
|
125
|
+
::SemanticLogger.pop_tags(count)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,219 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "semantic_logger"
|
5
|
+
require_relative "formatter"
|
6
|
+
require_relative "color_formatter"
|
7
|
+
require_relative "logger"
|
8
|
+
|
9
|
+
module LogStruct
|
10
|
+
# SemanticLogger Integration
|
11
|
+
#
|
12
|
+
# LogStruct uses SemanticLogger as its core logging engine, providing significant
|
13
|
+
# performance and functionality benefits over Rails' default logger:
|
14
|
+
#
|
15
|
+
# ## Performance Benefits
|
16
|
+
# - **Asynchronous logging**: Logs are written in a background thread, eliminating
|
17
|
+
# I/O blocking in your main application threads
|
18
|
+
# - **High throughput**: Can handle 100,000+ log entries per second
|
19
|
+
# - **Memory efficient**: Structured data processing with minimal allocations
|
20
|
+
# - **Zero-copy serialization**: Direct JSON generation without intermediate objects
|
21
|
+
#
|
22
|
+
# ## Reliability Benefits
|
23
|
+
# - **Thread-safe**: All operations are thread-safe by design
|
24
|
+
# - **Graceful degradation**: Continues logging even if appenders fail
|
25
|
+
# - **Error isolation**: Logging errors don't crash your application
|
26
|
+
# - **Buffered writes**: Reduces disk I/O with intelligent batching
|
27
|
+
#
|
28
|
+
# ## Feature Benefits
|
29
|
+
# - **Multiple appenders**: Log to files, STDOUT, databases, cloud services simultaneously
|
30
|
+
# - **Structured metadata**: Rich context including process ID, thread ID, tags, and more
|
31
|
+
# - **Log filtering**: Runtime filtering by logger name, level, or custom rules
|
32
|
+
# - **Formatters**: Pluggable output formatting (JSON, colorized, custom)
|
33
|
+
# - **Metrics integration**: Built-in performance metrics and timing data
|
34
|
+
#
|
35
|
+
# ## Development Experience
|
36
|
+
# - **Colorized output**: Beautiful, readable logs in development with ANSI colors
|
37
|
+
# - **Tagged logging**: Hierarchical context tracking (requests, jobs, etc.)
|
38
|
+
# - **Debugging tools**: Detailed timing and memory usage information
|
39
|
+
# - **Hot reloading**: Configuration changes without application restart
|
40
|
+
#
|
41
|
+
# ## Production Benefits
|
42
|
+
# - **Log rotation**: Automatic file rotation with size/time-based policies
|
43
|
+
# - **Compression**: Automatic log compression to save disk space
|
44
|
+
# - **Cloud integration**: Direct integration with CloudWatch, Splunk, etc.
|
45
|
+
# - **Alerting**: Built-in support for error alerting and monitoring
|
46
|
+
#
|
47
|
+
# ## LogStruct Specific Enhancements
|
48
|
+
# - **Type safety**: Full Sorbet type annotations for compile-time error detection
|
49
|
+
# - **Structured data**: Native support for LogStruct's typed log structures
|
50
|
+
# - **Filtering integration**: Seamless integration with LogStruct's data filters
|
51
|
+
# - **Error handling**: Enhanced error reporting with full stack traces and context
|
52
|
+
#
|
53
|
+
# SemanticLogger is a production-grade logging framework used by companies processing
|
54
|
+
# millions of requests per day. It provides the performance and reliability needed
|
55
|
+
# for high-traffic Rails applications while maintaining an elegant developer experience.
|
56
|
+
module SemanticLogger
|
57
|
+
# Handles setup and configuration of SemanticLogger for Rails applications
|
58
|
+
#
|
59
|
+
# This module provides the core integration between LogStruct and SemanticLogger,
|
60
|
+
# configuring appenders, formatters, and logger replacement to provide optimal
|
61
|
+
# logging performance while maintaining full compatibility with Rails conventions.
|
62
|
+
module Setup
|
63
|
+
extend T::Sig
|
64
|
+
|
65
|
+
# Configures SemanticLogger as the primary logging engine for the Rails application
|
66
|
+
#
|
67
|
+
# This method replaces Rails' default logger with SemanticLogger, providing:
|
68
|
+
# - **10-100x performance improvement** for high-volume logging
|
69
|
+
# - **Non-blocking I/O** through background thread processing
|
70
|
+
# - **Enhanced reliability** with graceful error handling
|
71
|
+
# - **Multiple output destinations** (files, STDOUT, cloud services)
|
72
|
+
# - **Structured metadata** including process/thread IDs and timing
|
73
|
+
#
|
74
|
+
# The configuration automatically:
|
75
|
+
# - Determines optimal log levels based on environment
|
76
|
+
# - Sets up appropriate appenders (console, file, etc.)
|
77
|
+
# - Enables colorized output in development
|
78
|
+
# - Replaces Rails.logger and component loggers
|
79
|
+
# - Preserves full Rails.logger API compatibility
|
80
|
+
#
|
81
|
+
# @param app [Rails::Application] The Rails application instance
|
82
|
+
sig { params(app: T.untyped).void }
|
83
|
+
def self.configure_semantic_logger(app)
|
84
|
+
# Set SemanticLogger configuration
|
85
|
+
::SemanticLogger.application = Rails.application.class.module_parent_name
|
86
|
+
::SemanticLogger.environment = Rails.env
|
87
|
+
|
88
|
+
# Determine log level from Rails config
|
89
|
+
log_level = determine_log_level(app)
|
90
|
+
::SemanticLogger.default_level = log_level
|
91
|
+
|
92
|
+
# Clear existing appenders
|
93
|
+
::SemanticLogger.clear_appenders!
|
94
|
+
|
95
|
+
# Add appropriate appenders based on environment
|
96
|
+
add_appenders(app)
|
97
|
+
|
98
|
+
# Replace Rails.logger with SemanticLogger
|
99
|
+
replace_rails_logger(app)
|
100
|
+
end
|
101
|
+
|
102
|
+
sig { params(app: T.untyped).returns(Symbol) }
|
103
|
+
def self.determine_log_level(app)
|
104
|
+
if app.config.log_level
|
105
|
+
app.config.log_level
|
106
|
+
elsif Rails.env.production?
|
107
|
+
:info
|
108
|
+
elsif Rails.env.test?
|
109
|
+
:warn
|
110
|
+
else
|
111
|
+
:debug
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
sig { params(app: T.untyped).void }
|
116
|
+
def self.add_appenders(app)
|
117
|
+
config = LogStruct.config
|
118
|
+
|
119
|
+
# Determine output destination
|
120
|
+
io = determine_output(app)
|
121
|
+
|
122
|
+
if Rails.env.development? && config.integrations.enable_color_output
|
123
|
+
# Use our colorized LogStruct formatter for development
|
124
|
+
::SemanticLogger.add_appender(
|
125
|
+
io: io,
|
126
|
+
formatter: LogStruct::SemanticLogger::ColorFormatter.new(
|
127
|
+
color_map: config.integrations.color_map
|
128
|
+
),
|
129
|
+
filter: determine_filter
|
130
|
+
)
|
131
|
+
else
|
132
|
+
# Use our custom JSON formatter
|
133
|
+
::SemanticLogger.add_appender(
|
134
|
+
io: io,
|
135
|
+
formatter: LogStruct::SemanticLogger::Formatter.new,
|
136
|
+
filter: determine_filter
|
137
|
+
)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Add file appender if configured and not already logging to STDOUT/StringIO
|
141
|
+
if app.config.paths["log"].first && io != $stdout && !io.is_a?(StringIO)
|
142
|
+
::SemanticLogger.add_appender(
|
143
|
+
file_name: app.config.paths["log"].first,
|
144
|
+
formatter: LogStruct::SemanticLogger::Formatter.new,
|
145
|
+
filter: determine_filter
|
146
|
+
)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
sig { params(app: T.untyped).returns(T.any(IO, StringIO)) }
|
151
|
+
def self.determine_output(app)
|
152
|
+
if ENV["RAILS_LOG_TO_STDOUT"].present?
|
153
|
+
$stdout
|
154
|
+
elsif Rails.env.test?
|
155
|
+
# Use StringIO for tests to avoid cluttering test output
|
156
|
+
StringIO.new
|
157
|
+
else
|
158
|
+
# Prefer file logging when not explicitly configured for STDOUT
|
159
|
+
$stdout
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
sig { returns(T.nilable(Regexp)) }
|
164
|
+
def self.determine_filter
|
165
|
+
# Filter out noisy loggers if configured
|
166
|
+
config = LogStruct.config
|
167
|
+
return nil unless config.integrations.filter_noisy_loggers
|
168
|
+
|
169
|
+
# Common noisy loggers to filter
|
170
|
+
/\A(ActionView|ActionController::RoutingError|ActiveRecord::SchemaMigration)/
|
171
|
+
end
|
172
|
+
|
173
|
+
# Replaces Rails.logger and all component loggers with LogStruct's SemanticLogger
|
174
|
+
#
|
175
|
+
# This method provides seamless integration by replacing the default Rails logger
|
176
|
+
# throughout the entire Rails stack, ensuring all logging flows through the
|
177
|
+
# high-performance SemanticLogger system.
|
178
|
+
#
|
179
|
+
# ## Benefits of Complete Logger Replacement:
|
180
|
+
# - **Consistent performance**: All Rails components benefit from SemanticLogger speed
|
181
|
+
# - **Unified formatting**: All logs use the same structured JSON format
|
182
|
+
# - **Centralized configuration**: Single point of control for all logging
|
183
|
+
# - **Complete compatibility**: Maintains all Rails.logger API contracts
|
184
|
+
#
|
185
|
+
# ## Components Updated:
|
186
|
+
# - Rails.logger (framework core)
|
187
|
+
# - ActiveRecord::Base.logger (database queries)
|
188
|
+
# - ActionController::Base.logger (request processing)
|
189
|
+
# - ActionMailer::Base.logger (email delivery)
|
190
|
+
# - ActiveJob::Base.logger (background jobs)
|
191
|
+
# - ActionView::Base.logger (template rendering)
|
192
|
+
# - ActionCable.server.config.logger (WebSocket connections)
|
193
|
+
#
|
194
|
+
# After replacement, all Rails logging maintains API compatibility while gaining
|
195
|
+
# SemanticLogger's performance, reliability, and feature benefits.
|
196
|
+
#
|
197
|
+
# @param app [Rails::Application] The Rails application instance
|
198
|
+
sig { params(app: T.untyped).void }
|
199
|
+
def self.replace_rails_logger(app)
|
200
|
+
# Create new SemanticLogger instance
|
201
|
+
logger = LogStruct::SemanticLogger::Logger.new("Rails")
|
202
|
+
|
203
|
+
# Replace Rails.logger
|
204
|
+
Rails.logger = logger
|
205
|
+
|
206
|
+
# Also replace various component loggers
|
207
|
+
ActiveRecord::Base.logger = logger if defined?(ActiveRecord::Base)
|
208
|
+
ActionController::Base.logger = logger if defined?(ActionController::Base)
|
209
|
+
ActionMailer::Base.logger = logger if defined?(ActionMailer::Base)
|
210
|
+
ActiveJob::Base.logger = logger if defined?(ActiveJob::Base)
|
211
|
+
ActionView::Base.logger = logger if defined?(ActionView::Base)
|
212
|
+
ActionCable.server.config.logger = logger if defined?(ActionCable)
|
213
|
+
|
214
|
+
# Store reference in app config
|
215
|
+
app.config.logger = logger
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module LogStruct
|
5
|
+
module Sorbet
|
6
|
+
module SerializeSymbolKeys
|
7
|
+
extend T::Sig
|
8
|
+
extend T::Helpers
|
9
|
+
|
10
|
+
requires_ancestor { T::Struct }
|
11
|
+
|
12
|
+
sig { params(strict: T::Boolean).returns(T::Hash[Symbol, T.untyped]) }
|
13
|
+
def serialize(strict = true)
|
14
|
+
super.deep_symbolize_keys
|
15
|
+
end
|
16
|
+
|
17
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
18
|
+
def to_h
|
19
|
+
serialize
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Note: We use T::Struct for our Log classes so Sorbet is a hard requirement,
|
5
|
+
# not an optional dependency.
|
6
|
+
require "sorbet-runtime"
|
7
|
+
require "log_struct/sorbet/serialize_symbol_keys"
|
8
|
+
|
9
|
+
# Don't extend T::Sig to all modules! We're just a library, not a private Rails application
|
10
|
+
# See: https://sorbet.org/docs/sigs
|
11
|
+
# class Module
|
12
|
+
# include T::Sig
|
13
|
+
# end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "digest"
|
5
|
+
|
6
|
+
module LogStruct
|
7
|
+
# StringScrubber is inspired by logstop by @ankane: https://github.com/ankane/logstop
|
8
|
+
# Enhancements:
|
9
|
+
# - Shows which type of data was filtered
|
10
|
+
# - Includes an SHA256 hash with filtered emails for request tracing
|
11
|
+
# - Uses configuration options from LogStruct.config
|
12
|
+
module StringScrubber
|
13
|
+
class << self
|
14
|
+
extend T::Sig
|
15
|
+
|
16
|
+
# Also supports URL-encoded URLs like https%3A%2F%2Fuser%3Asecret%40example.com
|
17
|
+
# cspell:ignore Fuser Asecret
|
18
|
+
URL_PASSWORD_REGEX = /((?:\/\/|%2F%2F)[^\s\/]+(?::|%3A))[^\s\/]+(@|%40)/
|
19
|
+
URL_PASSWORD_REPLACEMENT = '\1[PASSWORD]\2'
|
20
|
+
|
21
|
+
EMAIL_REGEX = /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/i
|
22
|
+
|
23
|
+
CREDIT_CARD_REGEX_SHORT = /\b[3456]\d{15}\b/
|
24
|
+
CREDIT_CARD_REGEX_DELIMITERS = /\b[3456]\d{3}[\s-]\d{4}[\s-]\d{4}[\s-]\d{4}\b/
|
25
|
+
CREDIT_CARD_REPLACEMENT = "[CREDIT_CARD]"
|
26
|
+
|
27
|
+
PHONE_REGEX = /\b\d{3}[\s-]\d{3}[\s-]\d{4}\b/
|
28
|
+
PHONE_REPLACEMENT = "[PHONE]"
|
29
|
+
|
30
|
+
SSN_REGEX = /\b\d{3}[\s-]\d{2}[\s-]\d{4}\b/
|
31
|
+
SSN_REPLACEMENT = "[SSN]"
|
32
|
+
|
33
|
+
IP_REGEX = /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/
|
34
|
+
IP_REPLACEMENT = "[IP]"
|
35
|
+
|
36
|
+
MAC_REGEX = /\b[0-9a-f]{2}(:[0-9a-f]{2}){5}\b/i
|
37
|
+
MAC_REPLACEMENT = "[MAC]"
|
38
|
+
|
39
|
+
# Scrub sensitive information from a string
|
40
|
+
sig { params(string: String).returns(String) }
|
41
|
+
def scrub(string)
|
42
|
+
return string if string.empty?
|
43
|
+
|
44
|
+
string = string.to_s.dup
|
45
|
+
config = LogStruct.config.filters
|
46
|
+
|
47
|
+
# Passwords in URLs
|
48
|
+
string.gsub!(URL_PASSWORD_REGEX, URL_PASSWORD_REPLACEMENT) if config.url_passwords
|
49
|
+
|
50
|
+
# Emails
|
51
|
+
if config.email_addresses
|
52
|
+
string.gsub!(EMAIL_REGEX) do |email|
|
53
|
+
email_hash = HashUtils.hash_value(email)
|
54
|
+
"[EMAIL:#{email_hash}]"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Credit card numbers
|
59
|
+
if config.credit_card_numbers
|
60
|
+
string.gsub!(CREDIT_CARD_REGEX_SHORT, CREDIT_CARD_REPLACEMENT)
|
61
|
+
string.gsub!(CREDIT_CARD_REGEX_DELIMITERS, CREDIT_CARD_REPLACEMENT)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Phone numbers
|
65
|
+
string.gsub!(PHONE_REGEX, PHONE_REPLACEMENT) if config.phone_numbers
|
66
|
+
|
67
|
+
# SSNs
|
68
|
+
string.gsub!(SSN_REGEX, SSN_REPLACEMENT) if config.ssns
|
69
|
+
|
70
|
+
# IPs
|
71
|
+
string.gsub!(IP_REGEX, IP_REPLACEMENT) if config.ip_addresses
|
72
|
+
|
73
|
+
# MAC addresses
|
74
|
+
string.gsub!(MAC_REGEX, MAC_REPLACEMENT) if config.mac_addresses
|
75
|
+
|
76
|
+
# Custom scrubber
|
77
|
+
custom_scrubber = LogStruct.config.string_scrubbing_handler
|
78
|
+
string = custom_scrubber.call(string) if !custom_scrubber.nil?
|
79
|
+
|
80
|
+
string
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/lib/log_struct.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Core library files
|
5
|
+
require "log_struct/sorbet"
|
6
|
+
require "log_struct/version"
|
7
|
+
require "log_struct/enums" # All enums are now in the enums directory
|
8
|
+
require "log_struct/configuration"
|
9
|
+
require "log_struct/formatter"
|
10
|
+
require "log_struct/railtie"
|
11
|
+
require "log_struct/concerns/error_handling"
|
12
|
+
require "log_struct/concerns/configuration"
|
13
|
+
require "log_struct/concerns/logging"
|
14
|
+
|
15
|
+
# Monkey-patch ActiveSupport::TaggedLogging::Formatter to support hash input/output
|
16
|
+
require "log_struct/monkey_patches/active_support/tagged_logging/formatter"
|
17
|
+
|
18
|
+
# Require integrations
|
19
|
+
require "log_struct/integrations"
|
20
|
+
|
21
|
+
# SemanticLogger integration - core feature for high-performance logging
|
22
|
+
require "log_struct/semantic_logger/formatter"
|
23
|
+
require "log_struct/semantic_logger/color_formatter"
|
24
|
+
require "log_struct/semantic_logger/logger"
|
25
|
+
require "log_struct/semantic_logger/setup"
|
26
|
+
|
27
|
+
module LogStruct
|
28
|
+
class Error < StandardError; end
|
29
|
+
|
30
|
+
extend Concerns::ErrorHandling::ClassMethods
|
31
|
+
extend Concerns::Configuration::ClassMethods
|
32
|
+
extend Concerns::Logging::ClassMethods
|
33
|
+
|
34
|
+
# Set enabled at require time based on current Rails environment.
|
35
|
+
# (Users can disable or enable LogStruct later in an initializer.)
|
36
|
+
set_enabled_from_rails_env!
|
37
|
+
end
|
data/lib/logstruct.rb
CHANGED
data/logstruct.gemspec
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/log_struct/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "logstruct"
|
7
|
+
spec.version = LogStruct::VERSION
|
8
|
+
spec.authors = ["DocSpring"]
|
9
|
+
spec.email = ["support@docspring.com"]
|
10
|
+
|
11
|
+
spec.summary = "Type-Safe JSON Structured Logging for Rails Apps"
|
12
|
+
spec.description = "An opinionated and type-safe structured logging solution. " \
|
13
|
+
"Configures any Rails app to log JSON to stdout. " \
|
14
|
+
"Structured logging is automatically configured for many gems, including Sidekiq, Carrierwave, Shrine, etc. " \
|
15
|
+
"Log messages, params, and job args are automatically filtered and scrubbed to remove any sensitive info."
|
16
|
+
spec.homepage = "https://logstruct.com"
|
17
|
+
spec.license = "MIT"
|
18
|
+
spec.required_ruby_version = ">= 3.2.0"
|
19
|
+
|
20
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
21
|
+
spec.metadata["source_code_uri"] = "https://github.com/DocSpring/logstruct"
|
22
|
+
spec.metadata["changelog_uri"] = "#{spec.metadata["source_code_uri"]}/blob/main/CHANGELOG.md"
|
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
|
+
]
|
32
|
+
spec.bindir = "exe"
|
33
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
34
|
+
spec.require_paths = ["lib"]
|
35
|
+
|
36
|
+
spec.add_dependency "lograge", ">= 0.11"
|
37
|
+
spec.add_dependency "rails", ">= 7.0"
|
38
|
+
spec.add_dependency "semantic_logger", "~> 4.15"
|
39
|
+
spec.add_dependency "sorbet-runtime", ">= 0.5"
|
40
|
+
|
41
|
+
# Optional integrations
|
42
|
+
spec.add_development_dependency "bugsnag", "~> 6.26"
|
43
|
+
spec.add_development_dependency "carrierwave", "~> 3.0"
|
44
|
+
spec.add_development_dependency "honeybadger", "~> 5.4"
|
45
|
+
spec.add_development_dependency "rollbar", "~> 3.4"
|
46
|
+
spec.add_development_dependency "sentry-ruby", "~> 5.15"
|
47
|
+
spec.add_development_dependency "shrine", "~> 3.5"
|
48
|
+
spec.add_development_dependency "sidekiq", "~> 7.2"
|
49
|
+
spec.add_development_dependency "sorbet", "~> 0.5"
|
50
|
+
|
51
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
52
|
+
end
|