Configuration

LogStruct is designed to be highly configurable while working with sensible defaults. You can customize how and where logs are generated, which integrations are enabled, and how errors are handled.

Create a file at config/initializers/logstruct.rb with your desired configuration.

LogStruct.configure do |config|
  # your configuration here
end

Enabling LogStruct

LogStruct can be enabled or disabled in the following ways:

  • LogStruct will be enabled if the LOGSTRUCT_ENABLED environment variable is set to "true".
  • LogStruct will be disabled if LOGSTRUCT_ENABLED is set to any other value.
  • If LOGSTRUCT_ENABLED is undefined, LogStruct will be enabled if the current Rails environment is listed in config.enabled_environments.
  • Finally, you can manually set config.enabled in an initializer. This will override all other configuration methods.

First, we check if config.enabled_environments includes the current Rails environment. If it does, we use that value. Otherwise, we check the LOGSTRUCT_ENABLED environment variable. If that is not set, we fall back to config.enabled.

Environment Configuration

LogStruct supports different environments and handles them appropriately:

LogStruct.configure do |config|
  config.enabled_environments = [:test, :production]

  # LogStruct will raise errors in local environments,
  # and log or report errors in production.
  # (This can be configured with config.error_handling_modes)
  config.local_environments = [:development, :test]
end

Integration Configuration

LogStruct integrates with many popular gems. You can enable or disable specific integrations:

LogStruct.configure do |config|
  # Enable/disable specific integrations
  config.integrations.enable_lograge = true
  config.integrations.enable_actionmailer = true
  config.integrations.enable_activejob = true
  config.integrations.enable_activestorage = true
  config.integrations.enable_carrierwave = true
  config.integrations.enable_host_authorization = true
  config.integrations.enable_rack_error_handler = true
  config.integrations.enable_shrine = true
  config.integrations.enable_sidekiq = true
  config.integrations.enable_sorbet_error_handlers = true

  # Configure custom options for Lograge
  config.integrations.lograge_custom_options = ->(event, _) {
    {
      # Add custom fields to your Lograge output
      user_id: event.payload[:user_id],
      correlation_id: event.payload[:correlation_id]
    }
  }
end

Filtering Sensitive Data

LogStruct includes robust filtering for sensitive data to ensure privacy and security:

LogStruct.configure do |config|
  # Configure which params should be filtered
  config.filters.filter_keys = [
    :password, :password_confirmation, :token, :secret,
    :credit_card, :ssn, :social_security
  ]

  # Configure which params should include hashes for values
  config.filters.filter_keys_with_hashes = [
    :email, :email_address
  ]

  # Configure sensitive data filtering for all strings
  config.filters.email_addresses = true      # Filter email addresses
  config.filters.url_passwords = true        # Filter passwords in URLs
  config.filters.credit_card_numbers = true  # Filter credit card numbers
  config.filters.phone_numbers = true        # Filter phone numbers
  config.filters.ssns = true                 # Filter social security numbers
  config.filters.ip_addresses = false        # Filter IP addresses (off by default)
  config.filters.mac_addresses = false       # Filter MAC addresses (off by default)

  # Configure the salt used for hashing filtered email addresses
  config.filters.hash_salt = ENV.fetch("EMAIL_HASH_SALT", "test_salt")

  # Configure the length of hash output for filtered emails (default: 12)
  config.filters.hash_length = 12
end
See the Filtering Sensitive Data docs for more information.

Error Handling Configuration

LogStruct provides customizable error handling modes to control how errors are processed. You probably don't want type-checking errors or internal logging-related errors to crash your application, so our default behavior is to log and report those errors without crashing. We automatically detect which error reporting service you use (Sentry, Bugsnag, Rollbar, etc.). If you use a service that we don't support yet, you can configure a custom error handler. (Or you can send a PR!)

LogStruct.configure do |config|
  # Configure error handling modes
  modes = config.error_handling_modes

  modes.type_checking_errors = LogStruct::ErrorHandlingMode::ReportProduction
  modes.logstruct_errors = LogStruct::ErrorHandlingMode::ReportProduction
  modes.security_errors = LogStruct::ErrorHandlingMode::Report
  modes.standard_errors = LogStruct::ErrorHandlingMode::Raise
end

Custom Lograge Options

You can extend Lograge request logging with custom fields:

# Provide a custom proc to extend Lograge options
LogStruct.configure do |config|
  config.integrations.lograge_custom_options = T.let(->(event, options) do
    # Add custom fields to the options hash
    options[:user_id] = event.payload[:user_id] if event.payload[:user_id]
    options[:account_id] = event.payload[:account_id] if event.payload[:account_id]
    options
  end,
    LogStruct::Handlers::LogrageCustomOptions)
end

Custom String Scrubbing

You can implement custom string scrubbers to filter out sensitive data that isn't caught by the built-in filters:

# Set a custom string scrubbing handler that will be called
# after the built-in scrubbers run
LogStruct.configure do |config|
  config.string_scrubbing_handler = T.let(->(value) {
    # Custom string scrubbing logic here
    # Example: Remove all bank account numbers that match the pattern
    value.gsub(/\b\d{10,12}\b/, "[BANK_ACCOUNT]")
  },
    LogStruct::Handlers::StringScrubber)
end

Custom Error Reporting

If LogStruct doesn't support your error reporting service, you can register a custom error reporting handler. (Or submit a PR!)

LogStruct.configure do |config|
  config.error_reporting_handler = T.let(->(error, context, source) {
    # Custom error reporting logic here
    # You could send errors to a custom service, log them specially, etc.
    # This is just a simple example:

    # Extract info from the error
    error_class = error.class.name
    error_message = error.message

    # Log to a custom target
    puts "[CUSTOM ERROR REPORTER] #{error_class}: #{error_message}"
    puts "Context: #{context.inspect}"
    puts "Backtrace: #{error.backtrace&.first(5)&.join("\n  ")}"
  },
    LogStruct::Handlers::ErrorReporter)
end

Sorbet Integration

LogStruct integrates with Sorbet to handle type checking errors based on the environment. We raise type errors or logging-related errors in test/development so you can catch them early, but we only log or report them in production. You can configure a different error handling mode to change this behavior.

config.integrations.enable_sorbet_error_handlers = true

# This configures the following error handlers for Sorbet:
# - T::Configuration.inline_type_error_handler
# - T::Configuration.call_validation_error_handler
# - T::Configuration.sig_builder_error_handler
# - T::Configuration.sig_validation_error_handler