When LogStruct Runs

LogStruct decides in each process whether to replace Rails' logger. Where it's off, the process logs exactly as it would without LogStruct: Rails' text lines, no filtering and no integrations.

The rule

  1. When the LOGSTRUCT_ENABLED environment variable is set, it decides: true, t, yes, y or 1 (in any case) turns LogStruct on, and any other value turns it off. This applies to consoles too.
  2. Otherwise LogStruct is on when all of these are true:
    • The Rails environment is in config.enabled_environments (default [:test, :production]).
    • The process isn't a console (rails console or rails c).
    • It's a server or job process LogStruct recognizes (below), or the environment is test.

So with the defaults, LogStruct is off in development, on for every test process except a console, and in production on only in the processes below.

Recognized processes

  • Puma, when the program's name or one of its arguments contains puma (bundle exec puma, or puma started by a wrapper script).
  • rails server, with any server.
  • Unicorn, Thin and Falcon, when their server class (Unicorn::HttpServer, Thin::Server, Falcon::Server) is defined as LogStruct loads.
  • Sidekiq, in the sidekiq process (Sidekiq.server?).
  • GoodJob, in good_job start. GoodJob's async mode runs inside your web server, which is already recognized.

Everything else keeps Rails' logger in production. That includes Rake tasks (db:migrate too), rails runner, Solid Queue's bin/jobs (Rails 8's default job runner), Resque, Delayed Job, Passenger and Pitchfork.

Unicorn, Thin and Falcon are recognized by their class names. If your app loads one of these gems in every process (it's in the Gemfile without require: false), Rake tasks and other processes may count as servers too.

Turning it on for other processes

Set LOGSTRUCT_ENABLED=true in the environment of each process that should write LogStruct lines, for example in a Procfile:

web: bundle exec puma -C config/puma.rb
jobs: LOGSTRUCT_ENABLED=true bin/jobs
release: bin/rails db:migrate

If you set LOGSTRUCT_ENABLED=true for a whole container image, every process in it gets LogStruct, consoles included. Set LOGSTRUCT_ENABLED=false to turn it off in a process LogStruct would otherwise recognize.

From an initializer

LogStruct first decides when the gem is loaded (by Bundler.require), and checks again after config/initializers run. An initializer can set config.enabled, or change config.enabled_environments to apply the rule above to other environments. LOGSTRUCT_ENABLED wins over both, and config.enabled wins over enabled_environments.

config/initializers/logstruct.rb
LogStruct.configure do |config|
  # config.enabled applies to every process that loads this initializer, in every
  # environment, so limit it to the environment you mean
  config.enabled = true if Rails.env.development?
end

A few things are set up as the gem loads, before initializers run: Puma's boot lines in rails server and the logger that early boot lines go to. Use LOGSTRUCT_ENABLED to turn LogStruct on or off for all of the boot.

In development

Start the server with LOGSTRUCT_ENABLED=true bin/rails server to see the lines your app writes in production. They're JSON by default; set prefer_json_in_development = false and enable_color_output = true for colored text instead (see Logger Settings).

Where lines go

EnvironmentOutput
production (and others)STDOUT only, unbuffered. No log file.
developmentSTDOUT and log/development.log
testlog/test.log only, so test output stays clean
RAILS_LOG_TO_STDOUT setSTDOUT only, in any environment

The environments that also write the log file are config.local_environments (default [:development, :test]). LogStruct honors config.log_level. A config.logger you set yourself applies only where LogStruct is off. Appenders your app adds to SemanticLogger in an initializer are kept. They write with the formatter you give them, so give them LogStruct::SemanticLogger::Formatter.new for filtered lines (see Where filtering applies).

Forking servers

LogStruct writes lines through SemanticLogger, which writes them from a background thread. A forked child doesn't inherit its parent's threads, so LogStruct hooks Process._fork, which Ruby calls for every fork, and starts a new writer thread in each child. This covers Puma's cluster mode, Unicorn, Passenger, Pitchfork, Resque and anything else that forks, with no on_worker_boot code of your own.

When it's off

  • Rails' logger, Lograge's settings and Sidekiq's logger are left alone.
  • Nothing is filtered or scrubbed: LogStruct's filters apply only to the lines it writes.
  • Mailers still have log_and_ignore_error, log_and_report_error and log_and_reraise_error, so a rescue_from that names one of them works everywhere. They log a plain text line to the mailer's logger (see ActionMailer).
  • LogStruct.info and the other logging methods still work, through Rails' logger.