Navigation

Access Logs Through the Admin API

Overview

You can request an application’s logs programmatically by accessing the logging endpoints of the Stitch Admin API.

The examples in this section use the following helper functions in a Function:

async function authenticate(publicApiKey, privateApiKey) {
  const result = await context.http.post({
    url: `${ADMIN_API_BASE_URL}/auth/providers/mongodb-cloud/login`,
    headers: {
      "Content-Type": ["application/json"],
      "Accept": ["application/json"],
    },
    body: {
      "username": publicApiKey,
      "apiKey": privateApiKey,
    },
    encodeBodyAsJSON: true
  })
  return EJSON.parse(result.body.text());
}

function formatQueryString(queryParams) {
  const params = Object.entries(queryParams);
  return params.length > 0
    ? "?" + params.map(([a, b]) => `${a}=${b}`).join("&")
    : ""
}

Get Recent Logs

To return the 100 most recent log entries for your application, call the Logging endpoint with no additional parameters:

const ADMIN_API_BASE_URL = "https://stitch.mongodb.com/api/admin/v3.0";
exports = async function() {
  // Get values that you need for requests
  const projectId = "<Atlas Project ID>";
  const appId = "<Stitch App ID>";
  const publicApiKey = "<Atlas Public API Key>";
  const privateApiKey = "<Atlas Private API Key>";

  // Authenticate with the Atlas API Key
  const { access_token } = await authenticate(publicApiKey, privateApiKey);

  // Get logs for your Stitch App
  const logsEndpoint = `${ADMIN_API_BASE_URL}/groups/${projectId}/apps/${appId}/logs`;
  const  request = {
    "url": logsEndpoint,
    "headers": {
      "Authorization": [`Bearer ${access_token}`]
    }
  };
  const result = await context.http.get(request);
  const logs = EJSON.parse(result.body.text());
  return logs;
}

Get Logs for a Date Range

To return log entries for a specific date range, call the Logging endpoint with either or both of the start_date and end_date fields:

Result Pagination

If the date range that you specify includes more than 100 log entries, you will need to run paginated queries to access all of the entries.

const ADMIN_API_BASE_URL = "https://stitch.mongodb.com/api/admin/v3.0";
exports = async function() {
  // Get values that you need for requests
  const projectId = "<Atlas Project ID>";
  const appId = "<Stitch App ID>";
  const publicApiKey = "<Atlas Public API Key>";
  const privateApiKey = "<Atlas Private API Key>";

  // Authenticate with the Atlas API Key
  const { access_token } = await authenticate(publicApiKey, privateApiKey);

  // Get logs for your Stitch App
  const logsEndpoint = `${ADMIN_API_BASE_URL}/groups/${projectId}/apps/${appId}/logs`;
  const  request = {
    "url": logsEndpoint + formatQueryString({
      start_date: "2019-07-01",
      end_date: "2019-07-31",
    }),
    "headers": {
      "Authorization": [`Bearer ${access_token}`]
    }
  };
  const result = await context.http.get(request);
  const logs = EJSON.parse(result.body.text());
  return logs;
}

Get Paginated Logs

Stitch returns a maximum of 100 log entries for each request. If a request applies to more than 100 log entries, the API will return the first “page” of 100 results and include additional parameters in the response that you can provide to get the next page of up to 100 entries.

Paginated Responses

A paginated response resembles the following document, where nextEndDate and nextSkip are optional:

{
  logs: [<Log Entry>, ...],
  nextEndDate: "<End date of the next page>",
  nextSkip: <Offset of the next page>,
}
const ADMIN_API_BASE_URL = "https://stitch.mongodb.com/api/admin/v3.0";
exports = async function() {
  // Get values that you need for requests
  const projectId = "<Atlas Project ID>";
  const appId = "<Stitch App ID>";
  const publicApiKey = "<Atlas Public API Key>";
  const privateApiKey = "<Atlas Private API Key>";

  // Authenticate with the Atlas API Key
  const { access_token } = await authenticate(publicApiKey, privateApiKey);

  // Get logs for your Stitch App
  const pager = new LogPager(projectId, appId, access_token);
  console.log("First page", await pager.getNextPage());
  console.log("Second page", await pager.getNextPage(firstPage));
  console.log("All logs", await pager.getAllLogs());
}

class LogPager {
  constructor(projectId, appId, access_token, queryParams={}) {
    this.logsEndpoint = `${ADMIN_API_BASE_URL}/groups/${projectId}/apps/${appId}/logs`;
    this.queryParams = queryParams;
    this.authHeaders = { Authorization: [`Bearer ${access_token}`] }
  }

  async getNextPage(prevPage) {
    const { nextEndDate, nextSkip } = prevPage || {};
    if(prevPage && !nextEndDate) {
      throw new Error("Paginated API does not have any more pages.")
    }
    const request = {
      "headers": this.authHeaders,
      "url": this.logsEndpoint + formatQueryString({
        ...this.queryParams,
        end_date: nextEndDate,
        skip: nextSkip,
      }),
    }
    const result = await context.http.get(request);
    const nextPage = EJSON.parse(result.body.text());
    return nextPage
  }

  async getAllLogs() {
    // Note: If your query parameters match too many logs this might time out
    let logs = []
    let hasNext = true;
    let prevPage = null
    while(hasNext) {
      const page = await getNextPage(prevPage);
      logs = logs.concat(page.logs);
      hasNext = page.nextEndDate
      prevPage = page
    }
    return logs;
  }
}