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 follows a simple philosophy: machines get JSON, humans get readable logs. By default:

  • Production servers → JSON logs (for parsing and analysis)
  • Test runs → JSON logs (to catch production bugs in both local and CI environments)
  • Local development → Human-readable logs (disabled by default)
  • Rails console → Human-readable logs (always)
  • Rake tasks → Human-readable logs

Overriding the Defaults

You can override this behavior in several ways, with the following precedence (highest to lowest):

  1. Environment variable override
    Set LOGSTRUCT_ENABLED=true to force JSON logs, or LOGSTRUCT_ENABLED=false to disable completely.
    # Force JSON logs in console for debugging
    LOGSTRUCT_ENABLED=true rails console
    
    # Disable JSON logs in tests (not recommended)
    LOGSTRUCT_ENABLED=false rails test
  2. Initializer configuration
    Manually set config.enabled = true in your initializer to enable in all development processes.
    LogStruct.configure do |c|
      c.enabled = true
    end
  3. Environment list
    Modify config.enabled_environments to change which environments have JSON logs by default (currently [:test, :production]).
The CI environment variable is automatically detected by most CI systems (GitHub Actions, GitLab CI, CircleCI, Travis, etc.). If your CI doesn't set it, add CI=true to your CI configuration.

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

Preview Production Logs in Development

LogStruct is disabled by default in development. When you explicitly enable it (for example, LOGSTRUCT_ENABLED=true rails s or setting config.enabled = true), LogStruct uses the same JSON formatter as production so you can validate structured logs locally. If you prefer the colorful human formatter for day‑to‑day debugging, set:

LogStruct.configure do |c|
  c.prefer_json_in_development = false
  c.enable_color_output = true
end
The ActiveSupport::TaggedLogging compatibility patch is only applied when LogStruct is enabled. This ensures third‑party gems that write Hashes to Rails.logger (e.g., dotenv‑rails) do not affect your default development logging when LogStruct is disabled.
We enable LogStruct in test as well as production by default. Keeping test and production behavior as close as possible helps catch logging issues early (for example, unexpected serialization errors, missing fields, or broken integrations) before they reach prod. If you prefer to disable it in test, remove :test from config.enabled_environments.
To force JSON logs in console or other Rake tasks (e.g., for debugging or inspecting the exact JSON output), set LOGSTRUCT_ENABLED=true when running the command:
LOGSTRUCT_ENABLED=true rails console
LOGSTRUCT_ENABLED=true rake db:migrate

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_actionmailer = true
  config.integrations.enable_active_model_serializers = true
  config.integrations.enable_activejob = true
  config.integrations.enable_activestorage = true
  config.integrations.enable_ahoy = true
  config.integrations.enable_carrierwave = true
  config.integrations.enable_dotenv = true
  config.integrations.enable_goodjob = true
  config.integrations.enable_host_authorization = true
  config.integrations.enable_lograge = true
  config.integrations.enable_rack_error_handler = true
  config.integrations.enable_semantic_logger = true
  config.integrations.enable_shrine = true
  config.integrations.enable_sidekiq = true
  config.integrations.enable_puma = true
  config.integrations.enable_sorbet_error_handlers = true
  config.integrations.enable_sql_logging = 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.credit_card_numbers = true  # Filter credit card numbers
  config.filters.email_addresses = true      # Filter email addresses
  config.filters.ip_addresses = false        # Filter IP addresses (off by default)
  config.filters.mac_addresses = false       # Filter MAC addresses (off by default)
  config.filters.phone_numbers = true        # Filter phone numbers
  config.filters.ssns = true                 # Filter social security numbers
  config.filters.url_passwords = true        # Filter passwords in URLs

  # Configure additional matchers for custom filtering rules
  config.filters.filter_matchers = [
    LogStruct::ConfigStruct::FilterMatcher.new(
      callable: ->(key, _value) { key.start_with?("secret_") },
      label: "secret prefix matcher"
    )
  ]

  # 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

ActionMailer ID Mapping

Configure which instance variables on your mailers should be logged as IDs in additional_data. This is useful for tracking which account, user, organization, etc. sent an email.

LogStruct.configure do |c|
  # Default mapping
  c.integrations.actionmailer_id_mapping = {
    account: :account_id,
    user: :user_id
  }

  # Custom mapping for your app
  c.integrations.actionmailer_id_mapping = {
    organization: :org_id,
    company: :company_id,
    tenant: :tenant_id
  }
end

In your mailer, set the instance variables and they'll automatically be logged:

class UserMailer < ApplicationMailer
  def welcome_email(user, account)
    @user = user      # Logs user_id
    @account = account  # Logs account_id
    mail(to: user.email, subject: 'Welcome')
  end
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