Skip to main content

Search the Audit Log

Review the steps to search the Audit Log

Use the following ways to search the Audit Log.

  1. Perform a search via the SDK.
  2. Paginate search results.
  3. Restrict search results.
  4. Search syntax.

Perform a search via the SDK

Each SDK provides a search method that can be used to search the audit log.

The following shows an example of searching the audit logs.

POST/v1/search
import os

import pangea.exceptions as pe
from pangea.config import PangeaConfig
from pangea.response import PangeaResponse
from pangea.services import Audit
from pangea.services.audit.audit import SearchOutput, SearchResultOutput
from pangea.tools import logger_set_pangea_config

# This example shows how to perform an audit log, and then search for thats results

token = os.getenv("PANGEA_AUDIT_TOKEN")
domain = os.getenv("PANGEA_DOMAIN")
config = PangeaConfig(domain=domain)
audit = Audit(token, config=config, private_key_file="./key/privkey", logger_name="audit")
logger_set_pangea_config(logger_name=audit.logger.name)


def main():
    print("Log Data...")
    msg = "python-sdk-standard-schema-example"

    try:
        log_response = audit.log(
            message=msg,
            actor="Someone",
            action="Testing",
            source="monitor",
            status="Good",
            target="Another spot",
            new="New updated message",
            old="Old message that it's been updated",
            verify=True,
            verbose=False,
            sign_local=True,
        )
        print(f"Log Request ID: {log_response.request_id}, Status: {log_response.status}")
    except pe.PangeaAPIException as e:
        print(f"Request Error: {e.response.summary}")
        for err in e.errors:
            print(f"\t{err.detail} \n")
        exit()

    print("Search Data...")

    page_size = 10
    query = "message:" + msg

    try:
        search_res: PangeaResponse[SearchOutput] = audit.search(
            query=query, limit=page_size, verify_consistency=True, verify_events=True
        )

        result_id = search_res.result.id
        count = search_res.result.count
        print(f"Search Request ID: {search_res.request_id}, Success: {search_res.status}, Results: {count}")
        offset = 0

        print_header_results()
        while offset < count:
            print_page_results(search_res, offset, count)
            offset += page_size

            if offset < count:
                search_res = audit.results(
                    id=result_id, limit=page_size, offset=offset, verify_consistency=True, verify_events=True
                )

    except pe.PangeaAPIException as e:
        print("Search Failed:", e.response.summary)
        for err in e.errors:
            print(f"\t{err.detail} \n")


def print_header_results():
    print(f"\n\nreceived_at\t\t\t\tMessage \tSource " f"\t\tActor \t\tMembership \tConsistency \tSignature\t")


def print_page_results(search_res: PangeaResponse[SearchResultOutput], offset, count):
    print("\n--------------------------------------------------------------------\n")
    for row in search_res.result.events:
        print(
            f"{row.envelope.received_at}\t{row.envelope.event['message']}\t{row.envelope.event['source']}\t\t"
            f"{row.envelope.event['actor']}\t\t{row.membership_verification}\t\t {row.consistency_verification}\t\t {row.signature_verification}\t\t"
        )
    print(
        f"\nResults: {offset+1}-{offset+len(search_res.result.events)} of {count}",
    )


if __name__ == "__main__":
    main()
note

Setting the optional parameter verify to true will automatically verify the membership and consistency proofs of each returned result.

Paginate search results

Audit results can be paginated using a combination of offset, count, and results_id. The results_id is returned by the search method and is a unique id corresponding to the search results. Search results don't live indefinitely; they have a defined expiration date that's also returned with the search results. Offset is used to determine at which record number results should be returned, and Count indicates how many records in total have been returned by the search. Continuing the previous example, paging through the results would look like this:

if search_res.success:
result_id = search_res.result.id
count = search_res.result.count
offset = 0

while offset < count and search_res.success:
for row in search_res.result.events:
print(f"{row.event.received_at}\t{row.event.message}\t{row.event.source}")
offset += page_size

search_res = audit.results(result_id, limit=page_size, offset=offset)

Restrict search results

In some cases, it may be desirable to partition search results. The search_restriction provided as restriction to the Python SDK can facilitate this need. A search_restriction can limit queries to the data described by the search restriction.

As an example, consider the following restriction:

{
"actor":"Dennis Nedry"
}

In this case, no matter the results included in the query, only results containing "Dennis Nedry" in the actor field will be returned.

This could be useful in an app that exposes the search interface to its users, providing the users with a way to search for auditable actions performed by themselves. A search restriction could restrict them to such actions without allowing them to see activities performed by other users.

Search syntax

The Search capability provides a simple search grammar to use when searching the logs for specific events.

note

The search queries are case sensitive.

The search query can be provided as either key-value pairs to specify searching for the query text in a specific field or just the query text to search for the text across all audit fields. For example, a query of deactivated will search for the term deactivated across all fields.

A simple search string should be provided as <field_name>:<value>. The field name should be an exact match of the field name to be searched. The /search API will perform a partial match of data in the specified field matching the search term.

For example: actor:"Dennis" will initiate the search for any audit event where the actor field contains the word Dennis.

You can exclude specific values and return everything that does not match the search term by including a minus (-) prefix. For example, -actor:"Dennis" returns all results where the actor field does not include the word Dennis.

tip

Certain field types, including integers, booleans, and datetimes (in the case of Custom schemas), offer the following functionalities:

  • Comparison operators: For fields that support comparison (like datetime and integer), you can use > or < instead of : to compare values that are greater than or less than the second part, respectively. For example, if value is a field in your schema, both value>10 and value<10 are valid expressions.
  • Negative values: For integer fields, you can add minus (-) prefix to the number. For example, value<-11 would search for everything with a value less than negative 11.
  • Boolean field filtering: You can filter boolean fields using fieldname:true to find values that are true or fieldname:false to locate values that are false.

Using multiple search terms

Multiple search terms can be joined using the AND and OR operators. For example, to search for events where the actor field contains Dennis and the target contains Security, the following search string would be used:

actor:"Dennis" AND target:"Security"

Grouping search terms

Search terms can be logically grouped using parentheses. As an example, to search for events where the actor is "Dennis" or "Grant" and the target is "Security" the following would be used:

(actor:"Dennis" OR target:"Grant") AND target "Security"

Was this article helpful?

Contact us