Platform

Logging


The Supabase Platform includes a Logs Explorer that allows log tracing and debugging. Log retention is based on your project's pricing plan.

Product logs

Supabase provides a logging interface specific to each product. You can use simple regular expressions for keywords and patterns to search log event messages. You can also export and download the log events matching your query as a spreadsheet.

API logs show all network requests and response for the REST and GraphQL APIs. If Read Replicas are enabled, logs are automatically filtered between databases as well as the API Load Balancer endpoint. Logs for a specific endpoint can be toggled with the Source button on the upper-right section of the dashboard.

When viewing logs originating from the API Load Balancer endpoint, the upstream database or the one that eventually handles the request can be found under the Redirect Identifier field. This is equivalent to metadata.load_balancer_redirect_identifier when querying the underlying logs.

API Logs


Working with API logs

API logs run through the Cloudflare edge servers and will have attached Cloudflare metadata under the metadata.request.cf.* fields.

Allowed headers

A strict list of request and response headers are permitted in the API logs. Request and response headers will still be received by the server(s) and client(s), but will not be attached to the API logs generated.

Request headers:

  • accept
  • cf-connecting-ip
  • cf-ipcountry
  • host
  • user-agent
  • x-forwarded-proto
  • referer
  • content-length
  • x-real-ip
  • x-client-info
  • x-forwarded-user-agent
  • range
  • prefer

Response headers:

  • cf-cache-status
  • cf-ray
  • content-location
  • content-range
  • content-type
  • content-length
  • date
  • transfer-encoding
  • x-kong-proxy-latency
  • x-kong-upstream-latency
  • sb-gateway-mode
  • sb-gateway-version

Additional request metadata

To attach additional metadata to a request, it is recommended to use the User-Agent header for purposes such as device or version identification.

For example:


_10
node MyApp/1.2.3 (device-id:abc123)
_10
Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0 MyApp/1.2.3 (Foo v1.3.2; Bar v2.2.2)

Logging Postgres queries

To enable query logs for other categories of statements:

  1. Enable the pgAudit extension.
  2. Restart your project using the Fast database reboot option.
  3. Configure pgaudit.log (see below). Perform a fast reboot if needed.
  4. View your query logs under Logs > Postgres Logs.

Configuring pgaudit.log

The stored value under pgaudit.log determines the classes of statements that are logged by pgAudit extension. Refer to the pgAudit documentation for the full list of values.

To enable logging for function calls/do blocks, writes, and DDL statements for a single session, execute the following within the session:


_10
-- temporary single-session config update
_10
set pgaudit.log = 'function, write, ddl';

To permanently set a logging configuration (beyond a single session), execute the following, then perform a fast reboot:


_10
-- equivalent permanent config update.
_10
alter role postgres set pgaudit.log to 'function, write, ddl';

To help with debugging, we recommend adjusting the log scope to only relevant statements as having too wide of a scope would result in a lot of noise in your Postgres logs.

Note that in the above example, the role is set to postgres. To log user-traffic flowing through the HTTP APIs powered by PostgREST, set your configuration values for the authenticator.


_10
-- for API-related logs
_10
alter role authenticator set pgaudit.log to 'write';

By default, the log level will be set to log. To view other levels, run the following:


_10
-- adjust log level
_10
alter role postgres set pgaudit.log_level to 'info';
_10
alter role postgres set pgaudit.log_level to 'debug5';

Note that as per the pgAudit log_level documentation, error, fatal, and panic are not allowed.

To reset system-wide settings, execute the following, then perform a fast reboot:


_10
-- resets stored config.
_10
alter role postgres reset pgaudit.log

Logging realtime connections

Realtime doesn't log new WebSocket connections or Channel joins by default. Enable connection logging per client by including an info log_level parameter when instantiating the Supabase client.


_10
import { createClient } from '@supabase/supabase-js'
_10
_10
const options = {
_10
realtime: {
_10
params: {
_10
log_level: 'info',
_10
},
_10
},
_10
}
_10
const supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key', options)

Logs Explorer

The Logs Explorer exposes logs from each part of the Supabase stack as a separate table that can be queried and joined using SQL.

Logs Explorer

You can access the following logs from the Sources drop-down:

  • auth_logs: GoTrue server logs, containing authentication/authorization activity.
  • edge_logs: Edge network logs, containing request and response metadata retrieved from Cloudflare.
  • function_edge_logs: Edge network logs for only edge functions, containing network requests and response metadata for each execution.
  • function_logs: Function internal logs, containing any console logging from within the edge function.
  • postgres_logs: Postgres database logs, containing statements executed by connected applications.
  • realtime_logs: Realtime server logs, containing client connection information.
  • storage_logs: Storage server logs, containing object upload and retrieval information.

Querying with the Logs Explorer

The Logs Explorer uses BigQuery and supports all available SQL functions and operators.

Timestamp display and behavior

Each log entry is stored with a timestamp as a TIMESTAMP data type. Use the appropriate timestamp function to utilize the timestamp field in a query.

Raw top-level timestamp values are rendered as unix microsecond. To render the timestamps in a human-readable format, use the DATETIME() function to convert the unix timestamp display into an ISO-8601 timestamp.


_10
-- timestamp column without datetime()
_10
select timestamp from ....
_10
-- 1664270180000
_10
_10
-- timestamp column with datetime()
_10
select datetime(timestamp) from ....
_10
-- 2022-09-27T09:17:10.439Z

Unnesting arrays

Each log event stores metadata an array of objects with multiple levels, and can be seen by selecting single log events in the Logs Explorer. To query arrays, use unnest() on each array field and add it to the query as a join. This allows you to reference the nested objects with an alias and select their individual fields.

For example, to query the edge logs without any joins:


_10
select timestamp, metadata from edge_logs as t;

The resulting metadata key is rendered as an array of objects in the Logs Explorer. In the following diagram, each box represents a nested array of objects:

Without Unnesting

Perform a cross join unnest() to work with the keys nested in the metadata key.

To query for a nested value, add a join for each array level:


_10
select timestamp, request.method, header.cf_ipcountry
_10
from
_10
edge_logs as t
_10
cross join unnest(t.metadata) as metadata
_10
cross join unnest(metadata.request) as request
_10
cross join unnest(request.headers) as header;

This surfaces the following columns available for selection: With Two Level Unnesting

This allows you to select the method and cf_ipcountry columns. In JS dot notation, the full paths for each selected column are:

  • metadata[].request[].method
  • metadata[].request[].headers[].cf_ipcountry

LIMIT and result row limitations

The Logs Explorer has a maximum of 1000 rows per run. Use LIMIT to optimize your queries by reducing the number of rows returned further.

Best practices

  1. Include a filter over timestamp

Querying your entire log history might seem appealing. For Enterprise customers that have a large retention range, you run the risk of timeouts due additional time required to scan the larger dataset.

  1. Avoid selecting large nested objects. Select individual values instead.

When querying large objects, the columnar storage engine selects each column associated with each nested key, resulting in a large number of columns being selected. This inadvertently impacts the query speed and may result in timeouts or memory errors, especially for projects with a lot of logs.

Instead, select only the values required.


_16
-- ❌ Avoid doing this
_16
select
_16
datetime(timestamp),
_16
m as metadata -- <- metadata contains many nested keys
_16
from
_16
edge_logs as t
_16
cross join unnest(t.metadata) as m;
_16
_16
-- ✅ Do this
_16
select
_16
datetime(timestamp),
_16
r.method -- <- select only the required values
_16
from
_16
edge_logs as t
_16
cross join unnest(t.metadata) as m
_16
cross join unnest(m.request) as r;

Examples and templates

The Logs Explorer includes Templates (available in the Templates tab or the dropdown in the Query tab) to help you get started.

For example, you can enter the following query in the SQL Editor to retrieve each user's IP address:


_10
select datetime(timestamp), h.x_real_ip
_10
from
_10
edge_logs
_10
cross join unnest(metadata) as m
_10
cross join unnest(m.request) as r
_10
cross join unnest(r.headers) as h
_10
where h.x_real_ip is not null and r.method = "GET";

Logs field reference

Refer to the full field reference for each available source below. Do note that in order to access each nested key, you would need to perform the necessary unnesting joins

PathType
idstring
timestampdatetime
event_messagestring
identifierstring
metadata.load_balancer_redirect_identifierstring
metadata.request.cf.asOrganizationstring
metadata.request.cf.asnnumber
metadata.request.cf.botManagement.corporateProxyboolean
metadata.request.cf.botManagement.detectionIdsnumber[]
metadata.request.cf.botManagement.ja3Hashstring
metadata.request.cf.botManagement.scorenumber
metadata.request.cf.botManagement.staticResourceboolean
metadata.request.cf.botManagement.verifiedBotboolean
metadata.request.cf.citystring
metadata.request.cf.clientTcpRttnumber
metadata.request.cf.clientTrustScorenumber
metadata.request.cf.colostring
metadata.request.cf.continentstring
metadata.request.cf.countrystring
metadata.request.cf.edgeRequestKeepAliveStatusnumber
metadata.request.cf.httpProtocolstring
metadata.request.cf.latitudestring
metadata.request.cf.longitudestring
metadata.request.cf.metroCodestring
metadata.request.cf.postalCodestring
metadata.request.cf.regionstring
metadata.request.cf.timezonestring
metadata.request.cf.tlsCipherstring
metadata.request.cf.tlsClientAuth.certPresentedstring
metadata.request.cf.tlsClientAuth.certRevokedstring
metadata.request.cf.tlsClientAuth.certVerifiedstring
metadata.request.cf.tlsExportedAuthenticator.clientFinishedstring
metadata.request.cf.tlsExportedAuthenticator.clientHandshakestring
metadata.request.cf.tlsExportedAuthenticator.serverFinishedstring
metadata.request.cf.tlsExportedAuthenticator.serverHandshakestring
metadata.request.cf.tlsVersionstring
metadata.request.headers.cf_connecting_ipstring
metadata.request.headers.cf_ipcountrystring
metadata.request.headers.cf_raystring
metadata.request.headers.hoststring
metadata.request.headers.refererstring
metadata.request.headers.x_client_infostring
metadata.request.headers.x_forwarded_protostring
metadata.request.headers.x_real_ipstring
metadata.request.hoststring
metadata.request.methodstring
metadata.request.pathstring
metadata.request.protocolstring
metadata.request.searchstring
metadata.request.urlstring
metadata.response.headers.cf_cache_statusstring
metadata.response.headers.cf_raystring
metadata.response.headers.content_locationstring
metadata.response.headers.content_rangestring
metadata.response.headers.content_typestring
metadata.response.headers.datestring
metadata.response.headers.sb_gateway_versionstring
metadata.response.headers.transfer_encodingstring
metadata.response.headers.x_kong_proxy_latencystring
metadata.response.origin_timenumber
metadata.response.status_codenumber