Skip to content

Configuration ​

Every setting in one place, with its default. Each one is explained on the page for the type that takes it, and the arguments shared by several types have pages of their own: dims, fields, Field types, Durations, Snapshot options and Flush options.

createHouse ​

ts
createHouse({
  driver,
  schema,
  delivery,
  defaults,
  now,
  onError,
  onWarn,
})
OptionTypeDefaultMeaning
driverDriverrequiredWhere running totals and staged records live
schemametrics or a modulenoneRegistered at startup
delivery'staged', 'immediate', 'auto''staged'When rows leave
defaults.flushdurationnoneCadence for metrics that declare none
defaults.graceduration'2s'Grace for metrics that declare none
now() => numberDate.nowThe clock every metric reads
onError(error, { metric }) => voidnoneFailures that cannot be thrown at a caller
onWarn(message, { metric? }) => voidnoneStartup warnings about the setup

Defaults are filled in, never overridden. A metric that declares its own flush keeps it.

counter ​

The page: counter.

ts
counter(name, { dims, resolution, flush, grace, value, write })
OptionTypeDefaultMeaning
dimsshapenoneLabels to break the number down by
resolutiondurationrequiredHow wide one window is
flushdurationhouse defaultThe fastest this may ship
gracedurationhouse default, then '2s'How long a window waits for writes on their way
valueint() or float()int()Whether fractions are allowed
writeWriteFnrequiredWhere the rows go

gauge ​

The page: gauge.

ts
gauge(name, { dims, resolution, flush, grace, aggregate, collect, collectLead, collectScope, write })
OptionTypeDefaultMeaning
dimsshapenoneLabels to break the value down by
resolutiondurationrequiredHow wide one window is
flushdurationhouse defaultThe fastest this may ship
gracedurationhouse default, then '2s'How long a window waits for observations on their way
aggregatearray['last','min','max','sum','count']Which columns reach your sink
collect(gauge) => void | Promise<void>noneCalled a little before each window closes, to read a value held elsewhere and set() it
collectLeadduration'1s', or a tenth of a shorter resolutionHow long before the end of each window the scheduler calls collect
collectScope'fleet' or 'process''fleet'On a shared driver, one process per window calls collect, or every process does
writeWriteFnrequiredWhere the rows go

collectLead has to be longer than zero and shorter than resolution, and collectLead and collectScope need collect. See Collecting before a window closes for when collect runs.

level ​

The page: level.

ts
level(name, { dims, resolution, flush, grace, holdFor, value, collect, collectLead, collectScope, write })
OptionTypeDefaultMeaning
dimsshapenoneLabels to break the value down by
resolutiondurationrequiredHow wide one window is
flushdurationhouse defaultThe fastest this may ship
gracedurationhouse default, then '2s'How long a window waits for writes on their way
holdFordurationforeverHow long a series keeps reporting after its last write
valuefloat() or int()float()Whether fractions are allowed
collect(level) => void | Promise<void>noneCalled a little before each window closes, to read a value held elsewhere and set() it
collectLeadduration'1s', or a tenth of a shorter resolutionHow long before the end of each window the scheduler calls collect
collectScope'fleet' or 'process''fleet'On a shared driver, one process per window calls collect, or every process does
writeWriteFnrequiredWhere the rows go

holdFor has to be at least one resolution. collectLead has to be longer than zero and shorter than resolution, as on a gauge.

timer ​

The page: timer.

ts
timer(name, { dims, resolution, flush, grace, aggregate, record, write })
OptionTypeDefaultMeaning
dimsshapenoneLabels to break the duration down by
resolutiondurationrequiredHow wide one window is
flushdurationhouse defaultThe fastest this may ship
gracedurationhouse default, then '2s'How long a window waits for timings on their way
aggregatearray['min','max','sum','count']Which columns reach your sink
recordevent namenoneAn event every timing is also written to
writeWriteFnrequiredWhere the rows go

A dimension may not be named duration_ms, ts, _ingested_at or _sample_rate.

event ​

The page: event.

ts
event(name, { fields, stage, durability, batch, flush, timestamp, sample, derive, claimLimit, write })
OptionTypeDefaultMeaning
fieldsshaperequiredThe record schema. json() is allowed
stage'driver' or 'local''driver'Where records wait
durability'relaxed' or 'durable''relaxed'Whether record() returns at once or a promise that resolves once the driver has the record. 'durable' needs stage: 'driver' and no sample
batch.maxSizenumber500Local staging: ship at this many
batch.maxAgeduration'10s'Local staging: ship this long after the first
batch.maxStagednumber100_000, or batch.maxSize when largerLocal staging: the most records this process holds before it refuses more
flushdurationthe house default, then '30s'The fastest this may ship
timestamp'auto' or a ts() field'auto'Where ts comes from
samplenumber or functionkeep everythingThe fraction to keep, 0 to 1
deriverecord of functionsnoneCounters this event also increments
claimLimitnumberunlimitedRecords one claim, and one call to write, may carry. A flush claims again while a claim comes back full
writeWriteFnrequiredWhere the rows go

A field may not be named id, ts, _ingested_at or _sample_rate.

log ​

The page: log.

ts
log(name, { fields, levels, minLevel, stage, batch, flush, claimLimit, write })
OptionTypeDefaultMeaning
fieldsshapenoneExtra fields beyond the reserved ones
levelsarray of strings['debug','info','warn','error']Ascending severity
minLevelone of levelsthe lowestDrop anything below this
stage'driver' or 'local''driver'Where lines wait
batch.maxSizenumber500Local staging: ship at this many
batch.maxAgeduration'10s'Local staging: ship this long after the first
batch.maxStagednumber100_000, or batch.maxSize when largerLocal staging: the most records this process holds before it refuses more
flushdurationthe house default, then '30s'The fastest this may ship
claimLimitnumberunlimitedLines one claim, and one call to write, may carry. A flush claims again while a claim comes back full
writeWriteFnrequiredWhere the rows go

A field may not be named id, ts, level, message, error_stack, _ingested_at or _sample_rate. A level may not shadow a method on the logger.

memory ​

ts
memory({ maxSeries, maxStaged })
OptionTypeDefaultMeaning
maxSeriesnumber100_000Distinct label combinations per metric
maxStagednumber100_000Staged records per metric

Each has to be a positive whole number, or the driver throws when it is created. Set either to Number.POSITIVE_INFINITY to disable. Leave them on. They turn a runaway dimension into a loud error instead of a crash with no warning.

ioredis ​

ts
ioredis(clientOrFactory, { namespace, maxPipelineSize, recoverAfter })
OptionTypeDefaultMeaning
namespacestring'mh'Key prefix, with no colon, no whitespace and no half of a surrogate pair. Two houses sharing one Redis need different ones
maxPipelineSizenumber1000Commands or Lua scripts per round trip. A positive whole number, or the driver throws when it is created
recoverAfterduration'5m'How long a claim may be held before a flush treats it as abandoned

Pass a function rather than a client to delay connecting until the first write. A client made that way belongs to the driver, so close it with driver.close() after house.stop(). A client you pass in yourself is yours to close.

Keep recoverAfter above your sink's timeout. It is what separates a flusher that crashed from one that is merely slow, and taking a claim back from a slow one ships those rows twice. See Recovering a crashed flush.

Durations ​

Every time based setting takes a whole number followed by a lowercase unit, or a plain number of milliseconds.

ts
'500ms'   '30s'   '5m'   '2h'   '7d'   5_000

Durations has the units, the rejected inputs and the rule that resolution must divide flush.

Flush options ​

ts
metric.flush({ force })
house.flush({ force, only, strict })
OptionTypeDefaultMeaning
forcebooleanfalseIgnore the cadence and ship everything finished
onlyarray of namesevery metricRestrict the flush to these metrics. House only
strictbooleanfalseReject an only naming a metric the house does not hold. House only

Flush options also covers every field of the report that comes back.

Snapshot options ​

ts
metric.snapshot({ dims, from, to, complete, rollup, groupBy, orderBy, direction, limit })
house.snapshot({ only, strict, ...theSame })
OptionTypeDefaultMeaning
dimspartial valuesnoneMatch on declared dimensions
fromnumber or DatenoneLower bound on the window, inclusive
tonumber or DatenoneUpper bound, exclusive
completebooleantrueExclude the window still filling
rollup'none' or 'sum''none'Merge every window of a series into one row
groupByarray of dimension namesevery dimensionCollapse to these
orderBycolumn namenoneSort before limit applies
direction'asc' or 'desc''desc'Sort direction
limitnumbernoneTake this many, after sorting
onlyarray of namesevery metricHouse only
strictbooleanfalseReject an only naming a metric the house does not hold. House only

Snapshot options covers what each metric type does with them, and how the row type follows.

Validation that runs at startup ​

These all throw when the module is first imported, not at the first write.

CheckMessage
Empty metric namecounter: name must be a non-empty string
A colon or whitespace in a metric namename "e:checkout" may not contain a colon or whitespace
Half of a surrogate pair in a metric namename "a\ud800" holds half of a surrogate pair
A metric named __proto__a metric cannot be named "__proto__"
No write functionsold: write must be a function that stores the rows, got undefined
A oneOf whose members print the sameoneOf: "2" prints the same as another member
A json() default JSON cannot holddefault for json(): json() needs a value JSON can hold
json() as a dimensiondim "x" declares json(), which cannot be encoded into a series key
Resolution does not divide flushsold: resolution 7s does not divide flush 1m evenly
A zero flush, defaults.flush or batch.maxAgeflush must be longer than zero, got "0s"
A flush, defaults.flush or batch.maxAge past 2147483647msflush is 30d, longer than 2147483647ms (just under 25 days)
Invalid durationparseDuration: "1.5m"
Bad default valuedefault for int(): expected a safe integer, got "five"
Unknown gauge aggregateunknown aggregate "avg"
A collect that is not a functionq: collect must be a function, got string
A collectLead of zeroq: collectLead must be longer than zero, got "0s"
A collectLead as long as resolution or longerq: collectLead is 1m, and it must be shorter than the resolution, 1m
A collectLead that is not a durationq: collectLead: parseDuration: "1.5s"
An unknown collectScopeq: collectScope must be 'fleet' or 'process', got "cluster"
collectLead or collectScope without collectq: collectLead is set, but collect is not, so nothing would run
A gauge aggregate named twiceaggregate names ["sum","sum"], and each one may appear once
A dim named after a column the metric writesdim "id" is a reserved column
A dim or field named like a whole numbera dim cannot be named "2024"
A dim or field named bucket_open or bucket_elapsed_msa dim cannot be named "bucket_open"
An unknown event stage, null includedstage must be 'driver' or 'local', got "memory"
A batch.maxStaged that is not a positive whole numberbatch.maxStaged must be a positive integer, got 0
A batch.maxStaged below batch.maxSizebatch.maxStaged (9) must be at least batch.maxSize (10)
An unknown event durabilitydurability must be 'relaxed' or 'durable', got "always"
durability: 'durable' with stage: 'local'durability 'durable' needs stage 'driver'
durability: 'durable' with sampledurability 'durable' cannot sample
A sample that is neither a number nor a functionsample must be a rate between 0 and 1 or a function returning one
Reserved event field namefield "ts" is a reserved column
Duplicate levellevel "info" is declared twice
minLevel not in levelsminLevel "trace" is not one of the declared levels
A level shadowing a method, or named thenlevel "flush" would shadow an existing property on the logger
A timer dimension named duration_msdim "duration_ms" is reserved
A timer dimension named after a column its record event writesdim "ts" is reserved, because a record event writes a column of that name on every row
Two metrics with the same namecreateHouse: two metrics are both named "x"
A metric registered with a second housealready bound to a house
No cadence anywhereno flush cadence. Declare flush on the counter, or defaults.flush on the house
An unknown deliverycreateHouse: delivery must be 'staged', 'immediate' or 'auto', got "immedate"

Released under the MIT License.