This guide shows how developers can retrieve diagnostics reports, out of memory (OOM) crash reports (iOS only), and application logs via API requests.
An example use case is tracking how often app users run into OOM errors.
Runtime/container requirements
- Runtime version 4.84+
- For out of memory crash reports, only iOS is currently supported, and container version 21.12.1+ is a requirement
API Endpoints
POST
https://flight-recorder.journeyapps.com/api/v1/logs/list - Lists an app’s console logs when a user presses “Upload Report”POST
https://flight-recorder.journeyapps.com/api/v1/reports/list-diagnostics-reports - These are the vanilla diagnostics reports that are generated when a user presses “Upload Report”. Note: these do not contain the application console logs - they are now separately retrieved via the above listed endpointPOST
https://flight-recorder.journeyapps.com/api/v1/reports/list-crash-reports - These contain the same information as diagnostics reports, and are automatically generated when an iOS device runs into an OOM error. They differ from the vanilla diagnostics reports in that they contain the timestamp of the crash, e.g.
“crashDetails”: {
“crashType”: “webview”,
“crashTimestamp”: “2022-01-11T07:49:14.000Z”
},
Authorization
Authorization is a Bearer Token. Please contact JourneyApps support to retrieve this token for your organization.
The Body of the request needs to contain the deployment_id
, example:
In OXIDE this deployment_id
can be copied from a deployment’s card:
The response will be as follows (this example uses the logs/list
endpoint):
Example implementation
This CloudCode task generates a report that is emailed to stakeholders and contains all crash reports for a time period.
/**
* To test: Update `emailRecipient` below and run Test Task
*/
const sgMail = require("@sendgrid/mail");
const emailRecipient = "user@example.com"; // Update as required
// To send email, a SendGrid API key is required
sgMail.setApiKey(process.env.sendgrid_api_key);
// The endpoint of the API. Replace with integration endpoint:
const URL_BASE = 'https://flight-recorder.journeyapps.com/api/v1';
/*
HTTP headers to include in every request.
Can include Authorization headers here, e.g.
"Authorization": "Bearer 123456"
*/
const DEFAULT_HEADERS = {
"Content-Type": "application/json",
"Authorization": "Bearer " + process.env.flight_recorder
};
export async function run() {
/*
POST request:
In this example, the Content-Type is JSON, so we need to use JSON.stringify for the body:
*/
let res = await postRequest('/reports/list-crash-reports', {body: JSON.stringify({deployment_id: process.env.deployment_id})});
let response = await res.json();
console.log(`API Response: ${JSON.stringify(response)}`);
let items = response.data.items;
console.log(`items.length ${items.length}`);
if (response.data.total > response.data.count) {
// TODO: get remaining data
}
// Generate a CSV file
const csv_entries = await generateCSVRows(items);
const csv = toCSVArray(csv_entries);
let file = toBase64(csv);
// Send the CSV via email
await sendEmail(file);
}
function generateCSVRows(items) {
let csv_lines = [['Created At', 'Device ID', 'Device name', 'Crash timestamp', 'Deployment ID', 'Databrowser ID', 'Databrowser label']];
const csv_entries = items.map(item => [
item.created_at,
item.userInfo.app_user_id,
item.userInfo.user_name,
item.crashDetails.crashTimestamp,
item.deployment_id,
item.backend_id,
item.userInfo.account_label
]);
csv_lines = csv_lines.concat(csv_entries);
return csv_lines;
}
function postRequest(path, options) {
return fetchRequest(path, options, 'POST');
}
function fetchRequest(path, options, method) {
let opts = options || {};
let headers = DEFAULT_HEADERS;
Object.assign(headers, opts.headers);
Object.assign(opts, {headers: headers, method: method});
return fetch(URL_BASE + path, opts);
}
function getBasicAuth(username, password) {
return "Basic " + Buffer.from(username + ":" + password, 'binary').toString('base64');
}
function toCSVArray(array) {
let csv = array
.map(function(a) {
return a.join(",");
})
.join("\n");
console.log(csv);
return csv;
}
function toBase64(data) {
return Buffer.from(data).toString("base64");
}
async function sendEmail(generatedCsv) {
const email = {
from: "noreply@journeyapps.com",
to: emailRecipient,
subject: "Report: Out of memory crashes",
text: "Find the generated report attached.",
attachments: [
{
content: generatedCsv,
filename: "attachment.csv"
}
]
};
try {
await sgMail.send(email);
} catch(error) {
// This helps for debugging
if (error.response) {
console.error(error.response.body);
}
throw error;
}
}
The deployment_id
for the request body is currently not available programatically. So in this example it is hardcoded as an environment variable.
Other notes:
- Apps with runtime version of 4.84 or greater, the “new” diagnostics reports in this API are also accessible via the backend data browser (and now also OXIDE).
- For apps with runtime version lower than 4.84, the “old” diagnostics reports are still available via the backend data browser.