Daemon
Point selection: selectors
The rule set's overall semantics — ordered list, first match wins, implicit deny — are described in the architecture. This page defines how a rule selects points.
match: simplified selectors
A rule's match supports four keys:
| Selector | Matches | Example |
|---|---|---|
class |
The point's Brick class, including subclasses | brick:Temperature_Sensor |
equipment |
The equipment the point belongs to | ex:AHU_01 |
location |
The point's location (zone, room, floor, building): the location it is a point of, or the location of its equipment, and everything those are part of or located in | ex:Room_1_17 |
point |
One specific point by its URI | ex:TT_1_17 |
- Subclasses:
classfollows Brick's class hierarchy, sobrick:Temperature_Sensoralso hits e.g.brick:Zone_Air_Temperature_Sensor. An exact match is obtained by giving a leaf class. - Transitivity:
equipmentandlocationmatch through the graph's part hierarchies:location: ex:Floor_1also hits points in rooms on the floor (isPartOfchains), andequipment: ex:AHU_01also hits points on the equipment's sub-components (hasPart). For locations the chain also follows where a location is located (hasLocation,rec:locatedIn), and Brick's and RealEstateCore's relations count alike. - A point's location: a location is a Brick
Locationor a RealEstateCore space —rec:Building,rec:Level,rec:Roomand its kinds,rec:HVACZone. Brick models a zone sensor as a point of the zone or room (isPointOf), and a point on a piece of equipment has no location of its own. A point's location is therefore the location it is a point of, or the location of its equipment and of anything that equipment is part of, and from there everything the location is part of or located in — an AHU's supply air temperature is in the plant room the AHU is in, on that room's floor and in the building. - Zones and rooms: a zone within a room is
rec:isPartOfthe room, and a room may hold several zones; a zone that spans rooms has them as parts,rec:hasPart. A zone's points count in the zone, in every room the zone has as a part, and in everything those rooms and the zone are part of, solocation: ex:Room_1_02reaches the zone's temperature and CO2 either way. Only zones are expanded into their parts: a building's own points are not in every room of the building. - AND: If several keys appear in the same
match, all must be satisfied. - OR: A list as value matches if just one element matches:
location: [ex:Room_1_17, ex:Room_1_18].
match_regex: regular expressions
As an alternative to match, a rule can use match_regex with the same four
keys, where the values are regular expressions:
- name: All rooms on floor 1
match_regex: { location: "ex:Room_1_.*" }
action: accept
method: poll
interval: 10m
- The expression must match the whole URI in prefixed form (e.g.
ex:Room_1_17) — the same form written in the configuration, using the model's own prefixes and Brick's. classinmatch_regexis a pure string match against the classes the model asserts, without the subclass hierarchy;equipmentandlocationmatch through the part hierarchies as inmatch.matchandmatch_regexare mutually exclusive: a rule uses one or the other. To combine the two forms, use SPARQL.
SPARQL as an escape hatch
When the selectors are not enough, a rule can instead give a raw SPARQL query
that selects the points. That covers arbitrarily complex selections — boolean
logic, relations across the graph, and combinations of exact match and regex.
The query's first selected variable is the point. The model's prefixes and
Brick's are declared for the query already, so PREFIX lines are needed only
for others.
A rule that cannot be evaluated as written — a value with an unknown prefix, a query that fails — is skipped with a warning in status, and the rules after it are evaluated as if it were absent. A regular expression that does not compile is caught earlier, by validation.
The query runs against the
working graph with the ontology and
the OWL-RL-inferred triples: ?p a brick:Temperature_Sensor hits all
subclasses, and relations can be followed in both directions. Transitive part
hierarchies are written explicitly with property paths, e.g.
brick:isPartOf*.
The full rule schema — action, interval and the other keys — is defined
in the configuration document.
Model findings
A finding is what the model lacks, as against a warning, which is what the
daemon has run into. It is read from the graph whenever it is asked for,
holds for as long as that version is active, and is therefore never withdrawn,
counted or cleared: the way to end a finding is to correct the model and
activate it anew. Findings travel with the
entity document, which is what the
model explorer draws and model tree prints, and
each of them is a filter there.
| Code | On | Meaning |
|---|---|---|
no_owner |
A point | It is a point of no equipment and of no location, and is located nowhere: nothing in the building answers for it |
no_reference |
A point | It has no external reference, so no source can address it. On a point a rule accepts, the daemon raises the warning of the same name, and the explorer and model tree then show it once |
no_location |
Equipment | Neither it nor anything it is part of has a location, so its points inherit none either |
no_relations |
A point, equipment, a location or a system | It stands alone in the model, tied to nothing else |
deprecated_class |
Anything | One of its asserted classes is marked deprecated in the bundled Brick — the spatial classes that moved to RealEstateCore, among others |
A deprecated class is read from Brick's own owl:deprecated, not from the
warnings of the validation that ran at upload: the ontology marks every
deprecated class, while Brick's shape reports only those that name a
replacement. A model with deprecated classes is valid and logs perfectly
well; the finding is there so a model can be modernised deliberately.
Status
Everything in status comes from runtime state and the status channels of the plugins. Status never asks a plugin synchronously — the same principle as for metadata. It is machine-readable JSON with stable field names; the CLI renders tables and can emit the JSON unchanged, which is what gives the web interface its parity. The endpoints are listed in the API reference.
Health
Two endpoints, for two kinds of monitor:
| Endpoint | Purpose | Result |
|---|---|---|
/health/live |
Is the process alive | Always 200 while the daemon runs |
/health |
Is the logger doing its job | 200 for ok and idle, 503 for degraded |
ok: collecting, nothing failed.idle: no model, or every source instance stopped by the operator — a healthy daemon that is deliberately not collecting.degraded: collecting, but a source or destination instance is failed, or a destination's spool has dropped data and has not been drained since.
The split exists so that a restart-on-failure probe uses /health/live and an
alerting probe uses /health; a dead database never causes a daemon restart.
The status tree
- Daemon: version, uptime, state, config and data directories, the active model version with upload and activation times, counts of points accepted, assigned and active, and every configured instance with its role, type and state, so the summary alone says whether they are all running. When the daily update check has found newer releases of Bricklogger or its plugins, they stand here too, with the time of the check.
- Sources, per instance: state (
starting,running,failed,stopped), type, resources, last error, restart count and next retry. Per device: reachable or not, last success, error counters and skipped poll rounds. Per outcome: how many points areactive,unsupportedandrejected. An instance whose plugin could not be loaded, or whose type no installed plugin provides, source or destination, isfailedwith the error and has no next retry: it runs when the plugin loads at a later start. - Destinations, per instance: state, whether it stores metadata, spool depth as observations, bytes and age of the oldest entry, dropped count, last successful write, last error.
- Warnings: one flat list. Each entry has a code and its kind, a subject — a point URI, an instance or a device — a message, first seen, last seen and a count. The per-point and per-instance views filter the same list.
A warning is a condition that holds now. Every code has a defined end,
and the daemon withdraws the warning when it sees that end, so a warning that
stands is true; one that stands after its condition is over is a bug in
Bricklogger, not something to live with. The operator may also clear a
warning by hand — status warnings clear, or the buttons on the overview —
as an acknowledgement; a cleared warning returns the next time its condition
is asserted. Counts and the two times survive a restart with the rest of the
runtime state, except where the end is the restart itself.
| Code | Kind | Meaning | Withdrawn when |
|---|---|---|---|
unclaimed |
model |
An accepted point that no source instance claims | The next plan no longer finds it |
unknown_reference |
model |
None of the point's references is understood by an installed source | The next plan no longer finds it |
no_reference |
model |
An accepted point without an external reference | The next plan no longer finds it |
no_fallback |
model |
A device cannot do the requested method, and the rule has no fallback |
The next plan, or the point's outcome is no longer unsupported |
rejected |
model |
A source rejected an assigned point; the message carries its reason | The point gets an outcome other than rejected |
rule_skipped |
model |
A rule could not be evaluated as written and was skipped | The next plan no longer finds it |
unit_conflict |
model |
The graph's unit and the protocol's unit disagree | The next plan, or metadata in which the two agree |
read_error |
operation |
The device answers a read with an error | The point delivers a value |
poll_overrun |
operation |
A device keeps missing its poll interval, and rounds are skipped | The device reports a round without a skip |
rate_limited |
operation |
A source's request budget makes its rounds outlast their interval; the message carries the cadence it can keep | The source's rounds keep their interval again |
future_timestamp |
operation |
Observations rejected for a timestamp in the future | The point delivers an observation with an acceptable timestamp |
spool_drop |
operation |
A destination's spool has dropped observations | The spool has been drained again; the dropped total stays in the destination's status |
instance_failed |
operation |
A source or destination instance is failed, including one whose plugin could not be loaded or is not installed | The instance runs again |
instance_stopped |
operation |
An instance stopped by the operator | The instance is started |
stop_timeout |
operation |
An instance did not stop within stop_timeout and was abandoned |
The daemon starts anew — the abandoned instance died with the old process — or the instance stops cleanly |
notify_failed |
operation |
A notification mail could not be sent; the message carries the server's answer | A mail goes through again |
notifications_dormant |
operation |
Notifications are switched on, but no model is active, so nothing can be sent | A model is active again |
The kind says what a warning is about. operation means the logger is not
doing its job right now; model means the model or the rule set leaves
something unresolved, which stands until one of them changes. Status, the
points view and the manual clear treat the two alike. The difference decides
which of them notifications
mail at once and which wait for the daily summary — and the last two codes,
being about the mail itself, never mail at all.
Points
A separate, paged and filterable view, because a site has thousands of
points: per point its URI, name and Brick class, source instance, method and
whether the fallback is in effect, outcome, time of the last observation, last
valid value with its time, and its warnings. Filters on instance, outcome,
warning code and Brick class. The web interface shows the class as well as
filtering on it, because a name says what an engineer called the point and the
class says what Bricklogger reads it as, and the two part company more often
than one would like: Luftmængde Indblæsning is a
brick:Supply_Air_Flow_Sensor.
The instance and the outcome are chosen from what exists and must match in
full. The warning code and the Brick class are searched: the text given
need only appear somewhere in the value, without regard to case, so unit
finds unit_conflict and Temperature finds every temperature sensor
whatever its Brick class is called. The class is still compared against the
class the model asserts for the point, in prefixed form, and a full IRI is
shortened before the comparison; subclasses are a
rule selector's business, not this view's.
Logging
The daemon logs its own life — start and stop, reloads and model activations,
instance state changes, and every warning the first time it appears — never
the observations, whose volume belongs in the time series. The settings live
in daemon.yaml:
- Level.
log.levelisdebug,info,warningorerror; the default isinfo.debugadds the plugins' protocol traffic and is meant for troubleshooting, not for running. - Format.
log.formatistextfor people orjsonfor log shippers: one object per line with time, level, logger and message, plus the fields the message carries, such as the instance or point concerned. - Destination.
daemon runlogs to stdout only, because systemd and Docker collect it themselves.daemon startwrites tolog.file, by defaultbricklogger.login the data directory, rotated by size: when the file reacheslog.max_size(default10MB) it is renamed and a new one started, and thelog.keep(default5) newest old files are kept.
Runtime state
The daemon's operational state lives in an SQLite database in the data directory, as the architecture describes. It holds:
- per point: the outcome and its reason, the method in effect and whether the fallback is active, the time of the last observation, and the last valid value with its time — which is also what the value overlay of the working graph is rebuilt from,
- per device: reachability, last success, error counters and skipped poll rounds,
- per instance: state, last error, restart count and the operator's stop intent,
- the warning list with first and last seen times and counts,
- the activation history of the model versions.
A data directory it cannot write
The daemon cannot run without its state, so a data directory it cannot write is a start that fails — but it fails with one line that names the cause:
error: the data directory /var/lib/bricklogger cannot be used: [Errno 13]
Permission denied. It belongs to uid 0, and the daemon runs as uid 999
The directory, the error, its owner and the user the daemon runs as, and exit code 1 — never a Python traceback. The owner and the running user are named because the two together are the answer nine times out of ten: a directory created by another user, a service that changed user, or a container with a bind mount. The same line is what a restart policy repeats, so a container that comes back again and again says why each time.
The tables are internal and may change between versions of Bricklogger;
status, the API and the SPARQL endpoint are the interfaces to this state. The
active model version is recorded beside the version files, so the runtime
state can be deleted while the daemon is stopped: counters, last values,
warnings and the operator's stops are lost, the daemon starts on the active
model with every instance running, and the working graph is rebuilt; a
history source, which resumes from the last values, fetches its backfill
again.