Logging to STDOUT (12‑Factor)

In production, LogStruct follows The Twelve‑Factor App's advice on logs: it writes each line to STDOUT, without buffering, and leaves collecting and storing the lines to your platform. It writes no log file there. In development it writes to STDOUT and Rails' log file, and in test to the log file only. This page shows where lines go and how that differs from Rails on its own.

Rails Defaults vs. LogStruct

Where LogStruct is on, it replaces Rails' logger with its own and writes JSON lines. By default it's on in test and in the production processes it recognizes, and off in development (see When LogStruct Runs). Where it's off, Rails logs as it always does.

EnvironmentRails without LogStructLogStruct on
developmentlog/development.log, and rails server prints the same lines to the consoleSTDOUT and log/development.log
testlog/test.loglog/test.log only, so test output stays clean
productionSTDOUT in apps generated by Rails 7.1 or newer (their config/environments/production.rb sets it). Apps generated earlier write log/production.log, or STDOUT when RAILS_LOG_TO_STDOUT is set.STDOUT only
RAILS_LOG_TO_STDOUT setWhatever your environment file does with itSTDOUT only, in every environment
  • LogStruct writes the log file only in config.local_environments (default [:development, :test]). Every other environment, staging included, is treated like production: STDOUT only.
  • LogStruct checks only that RAILS_LOG_TO_STDOUT isn't empty, so RAILS_LOG_TO_STDOUT=false also means STDOUT only.

Make Development Match Production

Turn LogStruct on in development to see the lines your app will write in production:

# One-off
LOGSTRUCT_ENABLED=true bin/rails server

# Or for every command in this shell
export LOGSTRUCT_ENABLED=true
bin/rails server
LogStruct replaces the logger as Rails boots, so a logger you set with config.logger gets no lines from then on: it applies only where LogStruct is off. LogStruct uses config.log_level and RAILS_LOG_TO_STDOUT, and its own logger settings choose between JSON (the default) and colored lines on the development console. The log file always gets JSON.

Production Recommendations

  • LogStruct logs to STDOUT in production whether or not RAILS_LOG_TO_STDOUT is set, so your platform collects the lines from the process's output. There's no log file to fill the disk.
  • Check that LogStruct is on in each kind of process you run. It switches on for the web servers and job runners it recognizes; set LOGSTRUCT_ENABLED=true for the others, such as Rake tasks (see When LogStruct Runs).
  • Ship the lines to your log platform (CloudWatch, Elasticsearch, Datadog, Grafana Loki) as line-delimited JSON, and parse ts, lvl and msg as the timestamp, level and message. Some platforms expect other names (Google Cloud Logging reads severity and message), so map the fields in your log shipper.
  • LogStruct keeps any appender your app adds to SemanticLogger in an initializer, but filtering happens in LogStruct's formatter: an appender with another formatter gets the values unfiltered (see Filtering Sensitive Data).