The loggers module exposes the historical data layer of the Weble IoT Universal Gateway. It lets you query timeseries (raw or aggregated), subscribe to live value streams, and manage logger instances — the database connectors (MySQL, PostgreSQL, InfluxDB, …) that persist address values to disk.
Load it with:
peer('supervision').require('loggers', function(err, loggers){
// loggers.query(...), loggers.live(...), loggers.getLoggers(...)
})
query(queryObject, callback)Retrieve historical values for one or more addresses, optionally aggregated. The callback is called multiple times as a stream — each call delivers a chunk of rows. When the stream is finished, the callback is called once more with err = 'end'.
loggers.query({
address : '4/7/25',
start : new Date(Date.now() - 86400000), // 24h ago
end : new Date(),
aggregate : '15m'
}, function(err, rows){
if(err === 'end') return console.log('done')
if(err) return console.error(err)
rows.forEach(function(r){
console.log(r.ts, r.value, r.value_string)
})
})
| Field | Type | Description |
|---|---|---|
address |
string / array | Address key (id, name, alias) or array of address keys to query |
start |
Date | Start of the time window. If no row exists at exactly start, the previous row is included. |
end |
Date | End of the time window. If no row exists at exactly end, the next row is included. |
aggregate |
string | Optional. Aggregation interval. When set, returns (min, avg, max) per bucket; when omitted, returns the raw rows. |
Aggregation units for the aggregate field:
| Suffix | Unit |
|---|---|
s |
seconds (e.g. '30s') |
m |
minutes (e.g. '15m') |
h |
hours (e.g. '1h') |
d |
days (e.g. '1d') |
w |
weeks |
o |
months |
y |
years |
Each row has the shape:
{
id : 23055, // address id
ts : Date, // timestamp (rounded to bucket if aggregated)
value : 21.5, // numeric value (or avg in aggregate mode)
value_string : '21.5°C', // string representation
min : 20.8, // only in aggregate mode
max : 22.1 // only in aggregate mode
}
The query call returns a handle with a cancel() method:
var handle = loggers.query({...}, function(err, rows){...})
// later, to abort:
handle.cancel()
REST equivalent:
GET /loggers/data— see REST loggers endpoints.
Pass an array as the address field. Each row in the callback identifies its address via the id field.
loggers.query({
address : ['4/7/25', '4/7/26', '%M1'],
start : new Date(Date.now() - 3600000),
end : new Date(),
aggregate : '1m'
}, function(err, rows){
if(err === 'end') return
if(err) return console.error(err)
rows.forEach(function(r){
console.log(r.id, r.ts, r.value)
})
})
live(queryObject, callback)Subscribe to live updates of one or more addresses. The callback fires every time a new value is received from the field bus, until the subscription is canceled.
var cancel = loggers.live({
address : ['4/7/25', '4/7/26']
}, function(err, update){
if(err) return console.error(err)
console.log(update.id, '=', update.value, 'at', update.value_date)
})
// Later, to stop receiving updates:
cancel()
The update object has the same shape as a row from query:
{
id : 23055,
value : 21.5,
value_string : '21.5°C',
value_date : Date
}
live is the recommended way to drive dashboards, mobile apps, and any UI that needs to react to value changes. It is more efficient than calling gateways.getLastValue in a loop, and it correctly multiplexes updates from many addresses over a single subscription.
REST equivalent:
GET /loggers/live(Server-Sent Events).
A logger is a configured database connector (local MySQL/PostgreSQL, remote InfluxDB, log cloud server, …) that persists address values. The gateway can run several loggers in parallel — for example one for local storage and one for cloud federation.
getLoggers(callback)Return all logger instances as an object keyed by logger id.
loggers.getLoggers(function(err, all){
Object.keys(all).forEach(function(id){
console.log(id, all[id].name, all[id].driver, all[id].state)
})
})
getLoggersLight(callback)Same as getLoggers but returns only minimal fields (id, name, description, cluster, log medium, log level). Use this for UI listings to avoid transferring the full configuration.
getLogger(loggerKey, callback)Return a single logger by id, name, or object.
insertLogger(logger, callback) / updateLogger(logger, callback) / replaceLogger(logger, callback) / deleteLogger(loggerKey, callback)Standard CRUD on logger instances.
loggers.insertLogger({
name : 'Local MySQL',
driver : 'mysql',
json : {
licence : '...',
host : 'localhost',
port : 3306,
database : 'weble',
user : 'weble',
password : '...'
}
}, function(err, newId){
console.log('logger created:', newId)
})
The json field's structure depends on the logger driver. A licence is required.
startLogger(loggerKey, callback) / stopLogger(loggerKey, callback) / restartLogger(loggerKey, callback)Lifecycle control. Starting a logger triggers a connection attempt to the underlying database. The loggerState event is fired with 'connecting', then 'connected' on success or 'disconnected' on failure (the process is restarted automatically after a short delay).
A task is a scheduled job attached to a logger — typically a periodic export or a deferred query. Tasks are persisted on disk so they survive restarts and continue where they left off.
getTasks(loggerKey, callback) / getTask(loggerKey, taskKey, callback)List the tasks of a logger, or fetch one by key. If loggerKey is omitted on getTasks, returns the tasks of all loggers.
loggers.getTasks(function(err, tasks){
tasks.forEach(function(t){ console.log(t.id, t.start, t.end) })
})
insertTask(loggerKey, task, callback)Create a task on a logger. The logger must be connected.
loggers.insertTask(loggerId, {
id : 'monthly_export',
address : ['4/7/25', '4/7/26'],
start : new Date('2026-01-01'),
end : new Date('2026-02-01')
}, function(err){
if(err) console.error(err)
})
Required fields: id, address (single key or array), start, end.
deleteTask(loggerKey, taskKey, callback) / pauseTask(loggerKey, taskKey, callback) / resumeTask(loggerKey, taskKey, callback)Remove, pause, or resume a task. Paused tasks remain on disk but stop running until resumed.
requireDriver(driverName, callback)Load the driver-specific API of a logger driver — same pattern as gateways.requireDriver. Use this to access methods that are not part of the generic loggers API.
loggers.requireDriver('mysql', function(err, mysqlApi){
// driver-specific methods here
})
| Event | Arguments | Description |
|---|---|---|
loggerInserted |
(logger) |
New logger created |
loggerUpdated |
(newLogger, oldLogger) |
Logger modified |
loggerDeleted |
(logger) |
Logger removed |
loggerState <loggerId> |
(state) |
Connection state changed ('connecting', 'connected', 'disconnected') |
taskInserted / taskInserted <loggerId> |
(task) |
Task created |
taskDeleted / taskDeleted <loggerId> |
(task) |
Task removed |
getStats(callback)Return aggregated statistics across all loggers (rows persisted, last write timestamps, error counts).
getCrashLog(loggerKey, callback)Retrieve the last crash dump for a logger. Useful when a logger keeps disconnecting and you want to know why.
loggers.query({
address : 'tempLiving',
start : new Date(Date.now() - 86400000),
end : new Date(),
aggregate : '15m'
}, function(err, rows){
if(err === 'end') return console.log('done')
if(err) return console.error(err)
rows.forEach(function(r){ console.log(r.ts, r.min, r.value, r.max) })
})
var sub = loggers.live({
address : ['tempLiving', 'tempBedroom']
}, function(err, update){
if(err) return ws.close()
ws.send(JSON.stringify(update))
})
ws.on('close', sub) // cancel the subscription when the client disconnects
loggers.query({
address : ['%M1', '%M2', '%M3'],
start : new Date('2026-01-01'),
end : new Date('2026-02-01')
// no aggregate → raw rows
}, function(err, rows){
if(err === 'end') return finalize()
if(err) return console.error(err)
rows.forEach(writeCsvRow)
})