SQL Queries
SQL logging is off by default. With config.integrations.enable_sql_logging = true, LogStruct subscribes to ActiveRecord's sql.active_record events and writes an app database line for each query that takes at least sql_slow_query_threshold milliseconds (100 by default). It detaches Rails' own SQL log lines while it's on.
Lines
Each query is one line at info level. These two were logged with sql_slow_query_threshold = 0 in the test environment, where bind values are logged:
{
"src": "app",
"evt": "database",
"lvl": "info",
"ts": "2026-10-17T03:21:04.676+00:00",
"msg": "User Create executed",
"sql": "INSERT INTO \"users\" (\"email\", \"name\", \"password_digest\", \"reset_token\", \"avatar\") VALUES (?, ?, ?, ?, ?) RETURNING \"id\"",
"name": "User Create",
"duration_ms": 0.18,
"row_count": 1,
"adapter": "SQLite3Adapter",
"bind_params": [
"[FILTERED]",
"Test User",
"[FILTERED]",
"[FILTERED]",
"<3 bytes of binary data>"
],
"pool_size": 5,
"active_count": 1,
"op_type": "INSERT",
"table_names": [
"users"
],
"prog": "Rails"
}{
"src": "app",
"evt": "database",
"lvl": "info",
"ts": "2026-10-17T03:21:04.681+00:00",
"msg": "User Load executed",
"sql": "SELECT \"users\".* FROM \"users\" WHERE \"users\".\"email\" = ? ORDER BY \"users\".\"id\" ASC LIMIT ?",
"name": "User Load",
"duration_ms": 0.04,
"row_count": 1,
"adapter": "SQLite3Adapter",
"bind_params": [
"[FILTERED]",
1
],
"pool_size": 5,
"active_count": 1,
"op_type": "SELECT",
"table_names": [
"users"
],
"prog": "Rails"
}Fields
| Field | Value |
|---|---|
| msg | <name> executed. |
| sql | The SQL, scrubbed like any other string (email addresses, card numbers and so on), but not filtered. |
| name | ActiveRecord's name for the query, such as User Load, or SQL Query when it has none. |
| duration_ms | How long the query took, in milliseconds, to two decimal places. |
| row_count | The number of rows, when ActiveRecord reports it. |
| adapter | The connection adapter's class name, such as PostgreSQLAdapter. |
| bind_params | The bind values, filtered (see Bind values). Left out when sql_log_bind_params is off. |
| db_name | The database name, from adapters that have current_database (PostgreSQL and MySQL). |
| pool_size | The size of the connection's pool. |
| active_count | How many connections in the pool are in use. |
| op_type | The SQL's first word, in capitals (SELECT, INSERT, BEGIN, ...). |
| table_names | The tables named after FROM, JOIN, UPDATE and INTO, found with a regular expression: for a schema-qualified name such as "public"."users" it's the schema's name. |
Which queries are logged
- Queries that take at least
sql_slow_query_thresholdmilliseconds (default100.0). With0ornil, every query is logged, transactions'BEGINandCOMMITincluded. - Not logged: queries named
SCHEMAorCACHE(query cache hits), queries onschema_migrationsorar_internal_metadata, andSHOW,DESCRIBEandEXPLAINstatements.
Rails' own SQL lines
While SQL logging is on, LogStruct detaches Rails' ActiveRecord::LogSubscriber and turns off ActiveRecord.verbose_query_logs (the "↳ caller" lines), so each query is logged once. With SQL logging off, Rails' own lines stay: debug-level lines with the query as text in msg, which aren't written at the info level that Rails' generated production config sets.
Bind values
sql_log_bind_paramsdefaults totrueunlessRails.envisproduction. An environment with another name, such asstaging, logs bind values unless you turn it off.- Each value is filtered by its column name, like a request parameter: a column that matches your filter keys, LogStruct's default sensitive keys, Rails'
filter_parametersorfilter_keys_with_hashes(emailby default) is written as[FILTERED]. In the lines above, that'semail,password_digestandreset_token. - Binary columns are written as their size (
<3 bytes of binary data>). - Other strings are written as
[FILTERED]when they look like a secret: longer than 50 characters, 32 or more hex digits, 20 or more Base64 characters, a JWT, aBearertoken, or text containingpassword,secret,token,keyorauth. - What's left is scrubbed like any other value.
Settings
config.integrations.enable_sql_logging(defaultfalse).config.integrations.sql_slow_query_threshold, in milliseconds, as a Float (default100.0).config.integrations.sql_log_bind_params(defaulttrueunlessRails.envisproduction).
See Configuration for the other settings.
LogStruct.configure do |config|
# Off by default
config.integrations.enable_sql_logging = true
# Log queries that take at least this many milliseconds, as a Float
# (default: 100.0; 0 or nil logs every query)
config.integrations.sql_slow_query_threshold = 250.0
# Log bind values (default: true unless Rails.env is "production")
config.integrations.sql_log_bind_params = false
endLimits
- The
sqlfield isn't filtered. Values written into the SQL itself (a string condition such aswhere("token = '...'"), or every value whenprepared_statementsis off) are logged, scrubbed but not filtered. - A low threshold in a busy app writes a line for nearly every query.
req_id, so a query can be matched to the request that ran it.