Upgrading to 0.2.0
0.2.0 changes the shape of some lines, filters more, and fixes integrations that logged nothing or logged twice. This page lists what you may need to change. The changelog has every change.
Requirements
- Ruby 3.3 or newer, and Rails 7.1 or newer.
- LogStruct depends on
railties,actionpack,activesupport,globalidandlogrageinstead ofrails, so it no longer installs every Rails framework. - Pin
gem "logstruct", "~> 0.2.0". Below 1.0, a minor version can change lines.
Lines that look different
Update the log queries, dashboards and alerts that read these fields.
- A logged Hash's keys are at the top level, not under
payload. A Hash can set its ownevtandsrc:
Rails.logger.info({evt: "signup", user_id: 1, plan: "pro"}){"evt":"signup","user_id":1,"plan":"pro","src":"app","ts":"2026-10-17T03:14:21.865Z","lvl":"info","prog":"Rails"}- Email hashes changed. A hash is now an HMAC-SHA256, 16 characters long, keyed with
filters.hash_saltor, without it, a key derived from your app'ssecret_key_base. Every hash differs from 0.1's, and they change again ifsecret_key_basedoes: sethash_saltto keep them. An address has one hash however it's capitalized. - Filtered values have no length. A filtered String is
{"_filtered":{"_class":"String"}}, without_bytes. Keys infilter_keys_with_hashes(email,email_address) are filtered too, with a_hash:
{"msg":"Invited","email":{"_filtered":{"_class":"String","_hash":"537d1000c07039b3"}},"invite_token":{"_filtered":{"_class":"String"}},"src":"app","evt":"log","ts":"2026-10-17T03:14:21.865Z","lvl":"info","prog":"Rails"}- More keys are filtered. Sensitive keys are matched by their words in any case or separator style, so
apiKey,X-Api-Key,Set-Cookie,passwords,session,passcodeandcard_numberare filtered by default. Rails'filter_parametersmatch as Rails matches them::secretfiltersclientSecret. See Filtering Sensitive Data. - Request lines have
source_ipandhost. If yourlograge_custom_optionsadded them, remove them there. - Tags are written:
logger.taggedandconfig.log_tagsgive atagsarray, and named tags go inctx. - Times have milliseconds in every field (
scheduled_at,started_at,finished_at, ...), likets. A finish line'stsis when the job finished. - Size limits. Hashes write their first 100 keys, backtraces 20 lines (an error line's backtrace had 5), other Arrays 10 items, and strings are cut at 32 KB. See Limits.
- Error lines from Sidekiq, Puma and ActiveJob have typed fields instead of fields in
additional_data, and the request'spath,method,source_ip,user_agentandreferer. A message logged with an exception (logger.error("Import failed", e)) has the message inmsgand the exception's inerror_message. - Mail error lines say
Email delivery failed, notEmail delivery error, will retry. Update alerts that match the old text. - Removed fields that were never written: ActiveStorage's
url(a signed URL anyone could use), ActiveJob'sretries, the blocked host line'sallowed_hostsandallow_ip_hosts(it hasblocked_hostsinstead), Shrine'smetadataandexist, and ActiveStorage'sfilename,mime_type,sizeandmetadataon upload and download lines (metadata lines have themime_typeinstead).
Behavior changes
- Production writes to STDOUT only. LogStruct no longer adds a
log/production.logthat nothing rotated. Development writes STDOUT and its log file, and test its log file. See Where lines go. - Sorbet type errors raise in development and test.
error_handling_modes.type_checking_errorsdefaults toReportProduction: raised inlocal_environments, logged and reported in production. They were only logged, so a test suite can find type errors it missed before. Set it toLogto only log them while you fix them. - Integrations are set up after your initializers, so the settings in
config/initializers/logstruct.rbapply. LogStruct's Lograge settings and Sidekiq logger replace ones another initializer set: configure them through LogStruct (lograge_custom_options, for example). - IP spoofing isn't checked on every request any more. Rails raises
IpSpoofAttackErrorwhere your app readsremote_ipand responds as it would without LogStruct, and LogStruct logs it there. - Blocked hosts get your app's own
config.host_authorization[:response_app]if you set one. - Mail delivery jobs log only the mailer, the mail and the delivery method as their
arguments, because mailers are often passed tokens (Devise's reset password token) as plain Strings. - A failed Sidekiq job is one structured
warnline without its arguments, instead of Sidekiq's three text lines. - Mailers keep their logger, so
logger.infoin a mailer is written.
Code that may need changing
LogStruct::Log::Sidekiqis nowLogStruct::Log::Sidekiq::Log.LogStruct::Log::Error'serror_classtakes anyExceptionclass.LogStruct.handle_exceptiontakeslog:. If your tests stub it with a lambda, acceptlog:too.- Removed:
LogStruct::Integrations::GoodJob::LogSubscriberandLog::GoodJob::Enqueue,Start,FinishandSchedule(GoodJob's jobs are logged by the ActiveJob integration),LogStruct::Integrations::Rack,Log::ActiveStorage::StreamandEvent::Stream. - Removed:
config.integrations.enable_semantic_logger, which did nothing (LogStruct always writes through SemanticLogger). Remove it from your initializer: setting it raisesNoMethodError.
New in 0.2.0
config.limits,filters.url_query_params,filters.path_matchersandfilters.authorization_credentials.- ActiveJob
retry,retry_stoppedanddiscardlines, and lines forperform_all_later. - CarrierWave download and delete lines, and ActiveStorage deletes by prefix.
- Every line logged during a Sidekiq job has the job's
jidand class inctx. - Forked children (Puma and Unicorn workers, Passenger, Pitchfork, Resque) write their lines with no setup.
If you use the Terraform provider, upgrade it with the gem: its catalog of sources and events is built in, and 0.2.0 removes GoodJob's enqueue, start, finish and schedule events and ActiveStorage's stream event.