Query logs

You can use query logs to monitor the performance of your Fauna databases and diagnose issues.

Requirements

To use query logs, you must have a Fauna Pro or Enterprise plan.

Log format

Fauna outputs query logs in a JSON+L format. Each line contains a JSON object that represents a log entry for a single query.

An example query log object formatted for readability:

{
  "API_VERSION": "10",
  "BYTES_IN": 43,
  "BYTES_OUT": 2692,
  "BYTE_READ_OPS": 21,
  "BYTE_WRITE_OPS": 0,
  "COMPUTE_OPS": 1,
  "DATABASE": ["ECommerce"],
  "FQL_SHAPE":"Product.sortedByPriceLowToHigh()\n",
  "QUERY_TIME_MS": 64,
  "REGION_GROUP": "us-std",
  "REQUEST_HEADERS": "{\"user_agent\":\"curl/8.4.0\",\"x_forwarded_for\":\"108.83.200.91, 10.1.1.124\"}",
  "REQUEST_METHOD": "POST",
  "RESPONSE_CODE": "200",
  "TAGS": { "request_type": "product_search", "sort_by": "price" },
  "TRACEPARENT": "00-00000000000000000992df31c81cc1c8-481003b57ce0897f-00",
  "TS": "2024-05-10 18:05:42.871 Z",
  "TXN_RETRIES": 0
}
Reference: Log record format

Instrument queries for query logs

You can instrument your queries for query logs by including query tags and traces in query requests.

Query tags

You can instrument your queries with query tags to better identify their source and context in query logs.

A query tag is an arbitrary key-value pair. Query log entries include query tags in the TAGS property:

{
  ...
  // Query tags
  "TAGS": { "request_type": "product_search", "sort_by": "price" },
  ...
}

Add query tags in the query HTTP API

If you use the query HTTP API, you can pass query tags for a request using the x-query-tags request header:

curl -X POST \
  'https://db.fauna.com/query/1' \
  -H 'Authorization: Bearer $FAUNA_SECRET' \
  -H 'Content-Type: application/json' \
  -H 'x-query-tags: request_type=product_search,sort_by=price' \
  -d '{"query": "Product.sortedByPriceLowToHigh()"}'

Query API responses include query tags in the query_tags property:

{
  "data": {
    ...
  },
  "static_type": "Set<Product>",
  "summary": "",
  "txn_ts": 1720713675611973,
  "stats": {
    ...
  },
  // Query tags are returned in the response.
  "query_tags": "sort_by=price,request_type=product_search",
  "schema_version": 1720712455226000
}

Add query tags using a client driver

The Fauna client drivers let you pass query tags as query options:

JavaScriptPythonGoC#

 

import { Client, fql } from "fauna";

const client = new Client({
  secret: 'FAUNA_SECRET'
});

const query = fql`Product.sortedByPriceLowToHigh()`;

// Define the query options.
const options = {
  // Define the query tags.
  query_tags: {
    request_type: "product_search",
    sort_by: "price"
  }
};

// Pass the query options to the query.
const response = await client.query(query, options);
// Print the query tags from the response.
console.log(response.query_tags);

client.close();
from fauna import fql
from fauna.client import Client, QueryOptions


client = Client(secret='FAUNA_SECRET')

query = fql("Product.sortedByPriceLowToHigh()")

# Define the query options.
options = QueryOptions(
    # Define the query tags.
    query_tags={
        'request_type': 'product_search',
        'sort_by': 'price'
    }
)

# Pass the query options to the query.
res  = client.query(query, options)
# Print the query tags from the response.
print(res.query_tags)

client.close()
package main

import (
	"encoding/json"
	"fmt"

	"github.com/fauna/fauna-go/v2"
)

func main() {
	client := fauna.NewClient(
		"FAUNA_SECRET",
		fauna.DefaultTimeouts(),
	)

	query, _ := fauna.FQL(`Product.sortedByPriceLowToHigh()`, nil)

	// Define the query options.
	options := []fauna.QueryOptFn{
		// Define the query tags.
		fauna.Tags(map[string]string{
			"request_type": "product_search",
			"sort_by":      "price",
		}),
	}

	// Pass the query options to the query.
	res, err := client.Query(query, options...)
	if err != nil {
		panic(err)
	}

	// Serialize the response to JSON.
	jsonData, _ := json.Marshal(res.QueryTags)
	// Print the query tags from the response.
	fmt.Println(string(jsonData))
}
using Fauna;
using static Fauna.Query;
using System.Text.Json;

var client = new Client("FAUNA_SECRET");

var query = FQL($@"Product.sortedByPriceLowToHigh()");

// Define the query options.
var queryOptions = new QueryOptions
{
    // Define the query tags.
    QueryTags = new Dictionary<string, string>
    {
        { "request_type", "product_search" },
        { "sort_by", "price" },
    }
};

// Pass the query options to the query.
var response = await client.QueryAsync(query, queryOptions);

// Serialize the response to JSON.
var jsonOptions = new JsonSerializerOptions
{
    WriteIndented = true,
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
string jsonString = JsonSerializer.Serialize(response.QueryTags, jsonOptions);

// Print the query tags from the response.
Console.WriteLine(jsonString);
{
  "sort_by": "price",
  "request_type": "product_search"
}

You can use your programming language’s native variables to dynamically populate the keys and values of query tags. For example, you can include an end user ID as a query tag:

import { Client, fql } from "fauna";

const client = new Client({
  secret: 'FAUNA_SECRET'
});

// Uses the FQL `Query.identity()` method to get the
// document ID for the token secret used to authenticate queries.
const userIdQuery = fql`Query.identity() { id }`;
const userIdRes = await client.query(userIdQuery);
// Store the identity document ID as the `userId`.
// If the query is authenticated using a key or JWT,
// set `userId` to "None."
const userId = userIdRes.data?.id ?? "None";

const query = fql`Product.sortedByPriceLowToHigh()`;

// Define the query options.
const options = {
  // Define the query tags.
  query_tags: {
    // Set the `user_id` query tag to the `userId`.
    user_id: userId
  }
};

// Pass the query options to the query.
const response = await client.query(query, options);
// Print the query tags from the response.
console.log(response.query_tags);

client.close();
from fauna import fql
from fauna.client import Client, QueryOptions


client = Client(secret='FAUNA_SECRET')

# Uses the FQL `Query.identity()` method to get the
# document ID for the token secret used to authenticate queries.
user_id_query = fql("Query.identity() { id }")
user_id_res = client.query(user_id_query )
# Store the identity document ID as the `user_id`.
# If the query is authenticated using a key or JWT,
# set `user_id` to "None."
user_id = user_id_res.data.get("id") or "None"


query = fql("Product.sortedByPriceLowToHigh()")

# Define the query options.
options = QueryOptions(
    # Define the query tags.
    query_tags={
        # Set the `user_id` query tag to the `user_id`.
        'user_id': user_id
    }
)

# Pass the query options to the query.
res  = client.query(query, options)
# Print the query tags from the response.
print(res.query_tags)

client.close()
package main

import (
	"encoding/json"
	"fmt"

	"github.com/fauna/fauna-go/v2"
)

func main() {
	client := fauna.NewClient(
		"FAUNA_SECRET",
		fauna.DefaultTimeouts(),
	)

	// Uses the FQL `Query.identity()` method to get the
	// document ID for the token secret used to authenticate queries.
	userIdQuery, _ := fauna.FQL(`Query.identity() { id }`, nil)
	userIdRes, userIdErr := client.Query(userIdQuery)
	if userIdErr != nil {
		panic(userIdErr)
	}
	// Store the identity document ID as the `userId`.
	// If the query is authenticated using a key or JWT,
	// set `userId` to "None."
	var userId string
	if userIdRes.Data != nil {
		if id, ok := userIdRes.Data.(map[string]interface{})["id"]; ok {
			userId = id.(string)
		} else {
			userId = "None"
		}
	} else {
		userId = "None"
	}

	query, _ := fauna.FQL(`Product.sortedByPriceLowToHigh()`, nil)

	// Define the query options.
	options := []fauna.QueryOptFn{
		// Define the query tags.
		fauna.Tags(map[string]string{
			// Set the `user_id` query tag to the `userId`.
			"user_id": userId,
		}),
	}

	// Pass the query options to the query.
	res, err := client.Query(query, options...)
	if err != nil {
		panic(err)
	}

	// Serialize the response to JSON.
	jsonData, _ := json.Marshal(res.QueryTags)
	// Print the query tags from the response.
	fmt.Println(string(jsonData))
}
using Fauna;
using static Fauna.Query;
using System.Text.Json;

var client = new Client("FAUNA_SECRET");

// Uses the FQL `Query.identity()` method to get the
// document ID for the token secret used to authenticate queries.
var userIdQuery = FQL($@"Query.identity() {{ id }}");
var userIdRes = await client.QueryAsync<Dictionary<string, string?>>(userIdQuery);

// Store the identity document ID as the `userId`.
// If the query is authenticated using a key or JWT,
// set `userId` to "None."
var userId = userIdRes.Data["id"] ?? "None";

var query = FQL($@"Product.sortedByPriceLowToHigh()");

// Define the query options.
var queryOptions = new QueryOptions
{
    // Define the query tags.
    QueryTags = new Dictionary<string, string>
    {
        // Set the `user_id` query tag to the `userId`.
        { "user_id", userId },
    }
};

// Pass the query options to the query.
var response = await client.QueryAsync(query, queryOptions);

// Serialize the response to JSON.
var jsonOptions = new JsonSerializerOptions
{
    WriteIndented = true,
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
string jsonString = JsonSerializer.Serialize(response.QueryTags, jsonOptions);

// Print the query tags from the response.
Console.WriteLine(jsonString);

Query tag constraints

  • A query can include up to 25 query tags.

  • Query tag keys and values must be alphanumeric strings. The strings can include the underscore (_) character. Empty strings are not supported.

  • A tag key can be up to 40 characters. A tag value can be up to 80 characters.

A query with invalid query tags fails and returns an error with a 400 HTTP status code.

Traces

You can identify queries triggered by a specific service, component, or process using a traceparent header.

Query log entries include the header’s traceparent identifier in the TRACEPARENT property. If you don’t include a traceparent identifier in a query request or use an invalid identifier, Fauna generates a valid identifier.

Add a traceparent in the query HTTP API

If you use the query HTTP API, you can include a traceparent header in the request. For example:

traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01

Add a traceparent using a client driver

The Fauna client drivers let you pass a traceparent as a query option:

import { Client, fql } from "fauna";

const client = new Client({
  secret: 'FAUNA_SECRET'
});

const query = fql`Product.sortedByPriceLowToHigh()`;

// Define the query options.
const options = {
  // Define the traceparent.
  traceparent: "00-750efa5fb6a131eb2cf4db39f28366cb-000000000000000b-00"
};

// Pass the query options to the query.
const response = await client.query(query, options);

console.log(response);

client.close();
from fauna import fql
from fauna.client import Client, QueryOptions


client = Client(secret='FAUNA_SECRET')

query = fql("Product.sortedByPriceLowToHigh()")

# Define the query options.
options = QueryOptions(
    # Define the traceparent.
    traceparent='00-750efa5fb6a131eb2cf4db39f28366cb-000000000000000b-00'
)

# Pass the query options to the query.
res  = client.query(query, options)

print(res)

client.close()
package main

import (
	"encoding/json"
	"fmt"

	"github.com/fauna/fauna-go/v2"
)

func main() {

	client := fauna.NewClient(
		"FAUNA_SECRET",
		fauna.DefaultTimeouts(),
	)

	query, _ := fauna.FQL(`Product.sortedByPriceLowToHigh()`, nil)

	// Define the query options.
	options := []fauna.QueryOptFn{
		// Define the traceparent.
		fauna.Traceparent("00-750efa5fb6a131eb2cf4db39f28366cb-000000000000000b-00"),
	}

	// Pass the query options to the query.
	res, err := client.Query(query, options...)
	if err != nil {
		panic(err)
	}

	jsonData, _ := json.Marshal(res)
	fmt.Println(string(jsonData))
}
using Fauna;
using static Fauna.Query;
using System.Text.Json;

var client = new Client("FAUNA_SECRET");

var query = FQL($@"Product.sortedByPriceLowToHigh()");

// Define the query options.
var queryOptions = new QueryOptions
{
    // Define the traceparent.
    TraceParent = "00-750efa5fb6a131eb2cf4db39f28366cb-000000000000000b-00",
};

// Pass the query options to the query.
var response = await client.QueryAsync(query, queryOptions);

// Serialize the response to JSON.
var jsonOptions = new JsonSerializerOptions
{
    WriteIndented = true,
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
string jsonString = JsonSerializer.Serialize(response, jsonOptions);

Console.WriteLine(jsonString);

Access query logs

You can access query logs using the:

Query logs HTTP API

You can use the query logs HTTP API to download query logs programmatically. For an example implementation, see Query logs API conventions.

Reference: Query log HTTP API reference

Fauna dashboard

You can manually request and download raw query logs using the Fauna Dashboard. If wanted, you can then upload the logs to a third-party service for visualization or analysis.

See Access query logs in the Fauna Dashboard

Aggregate query logs

You can aggregate query logs on entry properties using a script or JSON-processing tool, such as jq.

Example: Aggregate logs by query tags

The following example uses jq to aggregate query log entries by the user_id query tag in the TAGS property:

jq -s '
  # Filter out log entries without a `user_id` query tag.
  map(select(.TAGS.user_id != null)) |

  # Group all entries by `user_id`.
  group_by(.TAGS.user_id) |

  # For each group, create an object with aggregated data
  map({
    user_id: .[0].TAGS.user_id,
    query_count: length,
    total_bytes_in: map(.BYTES_IN // 0) | add,
    total_bytes_out: map(.BYTES_OUT // 0) | add,
    total_query_time_ms: map(.QUERY_TIME_MS // 0) | add,
    databases_accessed: map(.DATABASE[]) | unique
  }) |

  # Transform the array into an object with `user_id`s as keys.
  map({key: .user_id, value: .}) |
  from_entries

  # Replace `input.jsonl` with the path to your query log file.
' input.jsonl > output.json
{
  "403221241075335241": {
    "user_id": "403221241075335241",
    "query_count": 25,
    "total_bytes_in": 12194,
    "total_bytes_out": 43789,
    "total_query_time_ms": 1157,
    "databases_accessed": [
      "ECommerce"
    ]
  },
  ...
}

Example: Aggregate logs by query shape

The following example aggregates query logs that include the Product collection name in the FQL_SHAPE property:

jq -s '
  # Filter entries where `FQL_SHAPE` references the "Product" collection.
  map(select(.FQL_SHAPE | contains("Product"))) |

  # Group all entries. In this case, it will be a single group.
  group_by(true) |

  # Create a single object with aggregated data
  map({
    query_count: length,
    total_bytes_in: map(.BYTES_IN // 0) | add,
    total_bytes_out: map(.BYTES_OUT // 0) | add,
    total_query_time_ms: map(.QUERY_TIME_MS // 0) | add,
    databases_accessed: map(.DATABASE[]) | unique,
    unique_shapes: map(.FQL_SHAPE) | unique,
    response_codes: map(.RESPONSE_CODE) | group_by(.) | map({key: .[0], value: length}) | from_entries
  }) |

  # Extract the single group.
  .[0]

  # Replace `input.jsonl` with the path to your query log file.
' input.jsonl > output.json
{
  "query_count": 6,
  "total_bytes_in": 4663,
  "total_bytes_out": 8981,
  "total_query_time_ms": 340,
  "databases_accessed": [
    "ECommerce"
  ],
  "unique_shapes": [
    "Product.sortedByPriceLowToHigh()\n",
    ...
  ],
  "response_codes": {
    "200": 4,
    "400": 2
  }
}

Constraints

  • A log entry is available approximately 30 seconds to 10 minutes after a query’s execution.

  • You can request up to 90 days of query logs. You can specify future times in the request.

  • You can’t get query logs for queries run more than one year ago.

Is this article helpful? 

Tell Fauna how the article can be improved:
Visit Fauna's forums or email docs@fauna.com

Thank you for your feedback!