What I want to have: all logs in JSON format, with additional tags and per-request tags (similar to java's MDC)
How to make it:
- Use
lograge
withlogstash-event
to change default request logs - Make custom log formatter
- Override
ActiveSupport::TaggedLogging
format - Add own error handlers with
config.exceptions_app = self.routes
- Override default logger at the right time
Use environment variables JSON_LOGGER = true/false
and RAILS_LOG_LEVEL = info / debug / etc
Note that kibana by default use @timestamp
attribute and it will be rounded to seconds. Since most of request will be handled within 1 second, then log messages to be in random order and very hard to use.
I added time
attribute that we can use as time field and it will be used with millisecond precision
We can use Thread.current.thread_variable_get(:logger_tags)
to add extra attributes, e.g.
# in controller
add_logging_context(:user_email, current_user.email)
before
Started GET "/app?test_user_phone=081339372389" for 127.0.0.1 at 2022-08-16 02:45:28 +0800
ActiveRecord::SchemaMigration Pluck (0.6ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
Processing by WebappController#index as HTML
Parameters: {"test_user_phone"=>"081339372389"}
Rendering layout layouts/application.haml
Rendering webapp/index.haml within layouts/application
Rendered webapp/index.haml within layouts/application (Duration: 4.8ms | Allocations: 2793)
[Webpacker] Everything's up-to-date. Nothing to do
Rendered layout layouts/application.haml (Duration: 58.6ms | Allocations: 14356)
Completed 200 OK in 67ms (Views: 60.7ms | ActiveRecord: 0.0ms | Allocations: 18325)
after
{"time":"2022-08-16T03:04:33.985160+08:00","level":"DEBUG","type":"default","request_id":"924c9ccd-8a83-476a-af25-3972c0143520","path":"/app","message":"\u001b[1m\u001b[36mActiveRecord::SchemaMigration Pluck (0.5ms)\u001b[0m \u001b[1m\u001b[34mSELECT \"schema_migrations\".\"version\" FROM \"schema_migrations\" ORDER BY \"schema_migrations\".\"version\" ASC\u001b[0m"}
{"time":"2022-08-16T03:04:34.030925+08:00","level":"DEBUG","type":"default","request_id":"924c9ccd-8a83-476a-af25-3972c0143520","path":"/app","message":"Everything's up-to-date. Nothing to do"}
{"time":"2022-08-16T03:04:34.049406+08:00","level":"INFO","type":"default","request_id":"924c9ccd-8a83-476a-af25-3972c0143520","path":"/app","method":"GET","format":"*/*","controller":"WebappController","action":"index","status":200,"duration":39.37,"view":35.45,"db":0.0,"process_id":6064,"remote_ip":"127.0.0.1","host":"Pavel-MBP2.local","params":"{\"test_user_phone\":\"081339372389\"}","@timestamp":"2022-08-15T19:04:34.049Z","@version":"1","message":"[200] GET /app (WebappController#index)"}
SQL queries and Webpacker logs can be disabled with RAILS_LOG_LEVEL = INFO
before
Started GET "/foo" for 127.0.0.1 at 2022-08-16 03:01:22 +0800
ActionController::RoutingError (No route matches [GET] "/foo"):
Processing by ErrorsController#not_found as HTML
Rendering public/404.html
Rendered public/404.html (Duration: 0.8ms | Allocations: 55)
Completed 404 Not Found in 6ms (Views: 4.8ms | Allocations: 974)
after
{"time":"2022-08-16T03:02:22.677391+08:00","level":"FATAL","type":"default","request_id":"f9f80671-5e15-495f-81c2-b6ba14f52768","path":"/foo","message":"ActionController::RoutingError (No route matches [GET] \"/foo\"):"}
before
Started GET "/app?test_user_phone=081339372389" for 127.0.0.1 at 2022-08-16 03:13:54 +0800
Processing by WebappController#index as */*
Parameters: {"test_user_phone"=>"081339372389"}
Completed 500 Internal Server Error in 9ms (ActiveRecord: 0.0ms | Allocations: 3535)
NameError (undefined local variable or method `some_wrong_name' for #<WebappController:0x00000000007c38>
some_wrong_name
^^^^^^^^^^^^^^^):
app/controllers/webapp_controller.rb:7:in `index'
Processing by ErrorsController#internal_server as */*
Parameters: {"test_user_phone"=>"081339372389"}
Exception - /500?test_user_phone=081339372389
Completed 500 Internal Server Error in 1ms (Views: 0.3ms | ActiveRecord: 0.0ms | Allocations: 374)
after
{"time":"2022-08-16T03:12:38.090739+08:00","level":"INFO","type":"default","request_id":"d629de0c-7419-404d-8e1f-fc9df33564f4","path":"/app","method":"GET","format":"*/*","controller":"WebappController","action":"index","status":500,"duration":8.47,"view":0.0,"process_id":6064,"remote_ip":"127.0.0.1","host":"Pavel-MBP2.local","params":"{\"test_user_phone\":\"081339372389\"}","exception":"NameError","@timestamp":"2022-08-15T19:12:38.090Z","@version":"1","message":"[500] GET /app (WebappController#index)"}
{"time":"2022-08-16T03:12:38.095591+08:00","level":"FATAL","type":"default","request_id":"d629de0c-7419-404d-8e1f-fc9df33564f4","path":"/app","message":"NameError (undefined local variable or method `some_wrong_name' for #\u003cWebappController:0x0000000000ab18\u003e\n\n some_wrong_name\n ^^^^^^^^^^^^^^^):\n \napp/controllers/webapp_controller.rb:7:in `index'"}
{"time":"2022-08-16T03:12:38.096933+08:00","level":"INFO","type":"default","request_id":"d629de0c-7419-404d-8e1f-fc9df33564f4","path":"/app","message":"Exception - /500?test_user_phone=081339372389"}
{"time":"2022-08-16T03:12:38.097802+08:00","level":"INFO","type":"default","request_id":"d629de0c-7419-404d-8e1f-fc9df33564f4","path":"/500","method":"GET","format":"*/*","controller":"ErrorsController","action":"internal_server","status":500,"duration":0.88,"view":0.35,"process_id":6064,"remote_ip":"127.0.0.1","host":"Pavel-MBP2.local","params":"{\"test_user_phone\":\"081339372389\"}","@timestamp":"2022-08-15T19:12:38.097Z","@version":"1","message":"[500] GET /500 (ErrorsController#internal_server)"}