Skip to main content

Logs

Stream live logs from devices, or query historical logs over a time range. Logs are emitted by devices via the Device SDK Logs module at three severity levels: info, warn, and error.

Access via app.log.

API

stream()

Subscribe to live logs from a device. Optionally filter by severity level.

await app.log.stream(params)
ParameterTypeDescription
device_identstringThe device identifier
levelsstring[] or "*"Optional. Subset of ["info", "warn", "error"], or "*" for all. Defaults to all.
callbackfunctionCalled with each incoming log entry

The callback receives an object with level, data, and timestamp:

FieldTypeDescription
levelstringOne of "info", "warn", "error"
datastringThe formatted log message
timestampnumberUnix milliseconds, set by the device
// all levels
await app.log.stream({
device_ident: "sensor_01",
callback: (entry) => {
console.log(`[${entry.level}] ${entry.data}`);
},
});

// errors only
await app.log.stream({
device_ident: "sensor_01",
levels: ["error"],
callback: (entry) => {
console.error(entry.data);
},
});

You can call stream() multiple times for the same device with different level filters and callbacks. Each call creates an independent subscription.

off()

Tear down all live log subscriptions for a device.

await app.log.off(params)
ParameterTypeDescription
device_identstringThe device identifier

off() removes every active subscription for the given device. There is no per-level removal. To change a level filter, call off() and then stream() again with the new levels.

await app.log.off({ device_ident: "sensor_01" });

history()

Query historical logs over a time range.

await app.log.history(params)
ParameterTypeDescription
device_identstringThe device identifier
levelsstring[]Optional. Subset of ["info", "warn", "error"]. Defaults to all three.
startstringStart time (ISO 8601, UTC)
endstringEnd time (ISO 8601, UTC)
intervalstringOptional. Bucket size as a Flux duration ("1h", "5m"). Pair with aggregate_fn.
aggregate_fn"count"Optional. "count" is the only valid value.

Returns an object keyed by level, each containing an array of entries.

const logs = await app.log.history({
device_ident: "sensor_01",
levels: ["error"],
start: "2026-04-28T00:00:00.000Z",
end: "2026-04-29T00:00:00.000Z",
});

logs.error.forEach((entry) => {
console.error(`${entry.timestamp}: ${entry.value}`);
});

Example response:

{
"info": [
{ "value": "Boot complete: firmware=1.2.0", "timestamp": 1774690200000 }
],
"warn": [
{ "value": "Retrying transport publish", "timestamp": 1774690260000 }
],
"error": [
{ "value": "Sensor read failed: EIO", "timestamp": 1774690320000 }
]
}

Levels you did not request are still included in the response object as empty arrays.

Counting logs over time

Use interval and aggregate_fn: "count" to bucket logs into time intervals. This is useful for charting error rates or detecting spikes.

const hourlyErrors = await app.log.history({
device_ident: "sensor_01",
levels: ["error"],
start: "2026-04-28T00:00:00.000Z",
end: "2026-04-29T00:00:00.000Z",
interval: "1h",
aggregate_fn: "count",
});
History retention

Log retention depends on your plan. Queries outside your retention window will return empty arrays.