How a query is built
Every Logging Search query starts with search and the logs to read, then pipes (|) into filters and aggregations.
search "<compartment_ocid>/<log_group_ocid>/<log_ocid>"
| where <condition>
| sort by datetime desc
- You can stop at the compartment (
"<compartment_ocid>") or the log group ("<compartment_ocid>/<log_group_ocid>") to search more widely. Several streams are separated by commas. - Log group and log names also work in place of OCIDs.
- Field names are case-sensitive. Log payloads live under
data., for exampledata.messageordata.statusCode. logContentis the whole original record. Use it for full-text searches with*wildcards.- Strings use single quotes. String comparison is
=(with wildcards); usenot (...)to negate.
The quickest way to learn a log's field names: open it in Logging > Search, expand one record, then switch to Advanced mode to edit the generated query.
General filters
-- Anything containing "error", any case
search "<compartment_ocid>/<log_group>"
| where contains_ci(logContent, 'error', true)
| sort by datetime desc
-- Exact text match with wildcards
search "<compartment_ocid>/<log_group>/<log>"
| where logContent = '*OutOfMemory*'
-- Exclude noisy records
search "<compartment_ocid>/<log_group>/<log>"
| where not (logContent = '*healthcheck*')
-- Only show the columns you need
search "<compartment_ocid>/<log_group>/<log>"
| select datetime, source, data.message
| sort by datetime desc
Counting and trending
-- Errors per 5 minutes (good for spotting when a problem started)
search "<compartment_ocid>/<log_group>/<log>"
| where contains_ci(logContent, 'error', true)
| summarize count() as errors by rounddown(datetime, '5m') as bucket
| sort by bucket
-- Which hosts or sources produce the most records
search "<compartment_ocid>/<log_group>"
| summarize count() as records by source
| sort by records desc
-- Top 10 messages
search "<compartment_ocid>/<log_group>/<log>"
| summarize count() as n by data.message
| top 10 by n
Audit log
Audit events are searched with the special _Audit log on a compartment (use the tenancy OCID to include the root compartment):
-- Who did what, most recent first
search "<compartment_ocid>/_Audit"
| select datetime, data.identity.principalName, data.eventName, data.resourceName
| sort by datetime desc
-- Every action by one user
search "<compartment_ocid>/_Audit"
| where data.identity.principalName = 'jane.doe@example.com'
| sort by datetime desc
-- Instance terminations
search "<compartment_ocid>/_Audit"
| where data.eventName = 'TerminateInstance'
| select datetime, data.identity.principalName, data.resourceName
-- Failed (denied) requests
search "<compartment_ocid>/_Audit"
| where data.response.status = '401' or data.response.status = '404'
| summarize count() as denied by data.identity.principalName, data.eventName
Event names follow the API operation (TerminateInstance, DeleteBucket, UpdatePolicy ...). Check one record to confirm the exact field names in your tenancy.
Load balancer access logs
Enable the access log on the load balancer first (Logging > Logs > Enable service log).
-- 5xx responses
search "<compartment_ocid>/<lb_log_group>/<lb_access_log>"
| where data.statusCode >= 500
| select datetime, data.clientAddr, data.request, data.statusCode, data.backendStatusCode
-- 5xx from the load balancer vs from the backend
search "<compartment_ocid>/<lb_log_group>/<lb_access_log>"
| where data.statusCode >= 500
| summarize count() as n by data.statusCode, data.backendStatusCode
A 502 with an empty backend status usually means the backend never answered (health, security list or timeout), not an application error. Field names for load balancer logs vary by log version; check one record.
VCN flow logs
-- Rejected traffic to a host
search "<compartment_ocid>/<flow_log_group>/<flow_log>"
| where data.action = 'REJECT' and data.destinationAddress = '10.0.1.25'
| select datetime, data.sourceAddress, data.destinationPort, data.protocolName
-- Top rejected source/port pairs
search "<compartment_ocid>/<flow_log_group>/<flow_log>"
| where data.action = 'REJECT'
| summarize count() as n by data.sourceAddress, data.destinationPort
| top 20 by n
Rejects at a known port usually point at a security list or NSG rule.
Running queries from the CLI
oci logging-search search-logs \
--search-query "search \"$COMP/_Audit\" | where data.eventName = 'DeleteBucket'" \
--time-start 2026-10-26T00:00:00Z \
--time-end 2026-10-27T00:00:00Z \
--query 'data.results[].data'
Useful options: --limit to cap results and --is-return-field-info to see the field types.
Tip
Searches cover a limited time window per request (Audit queries through the API are commonly capped at 14 days). For longer investigations, loop day by day in a script or archive logs to Object Storage with Connector Hub.
Quick reference
| Need | Query piece |
|---|---|
| Full-text match | where logContent = '*text*' |
| Case-insensitive contains | where contains_ci(logContent, 'text', true) |
| Negate | where not (expr) |
| Count by field | summarize count() as n by field |
| Time buckets | rounddown(datetime, '1h') |
| Top N | top 10 by n |
| Remove duplicates | dedup field1, field2 |
| Columns | select a, b / extend expr as name |