A colour a terminal understands. Alarm is white on red, which is what a fatal line is worth. Plain writes no escape at all, so a value that never reached a terminal is unchanged.
API reference
Everything the library exports. The root package is grouped by what a
name does rather than alphabetically: a reader looking for a name
usually knows which part of the pipeline it belongs to. The six
subpackages follow, each one a section of its own. Times are nanoseconds since the epoch
and durations the same; a text that is not a String is a
StringView. Every value type derives Eq and
Debug, so equal, not_equal and
to_repr exist wherever a value does; they are not listed
one by one. This page is generated from the interfaces.
Levels
The escape that starts this colour, empty for Plain.
The name used in configuration, filters and vector files.
A colour by name, gray included, which is how American spelling arrives.
How severe a record is. A level is a value rather than an enumeration so an application can define its own — Level::{ name: "notice", value: 35, colour: Blue } — and have it work in filters, calls and layouts alike. Only value counts when levels are compared; name is what filters and labels read. Implements Compare, Eq, Show.
Whether this level passes a minimum.
Its colour in a terminal.
How a profile's logger writes this level. A pattern preset uses its own logger's vocabulary; every other layout uses the profile in force. Python's WARNING and CRITICAL, and slog's numeric names for the two levels it has no word for, are the only differences — which is the point of a profile.
The number Logstash writes, 5000 to 40000.
Its lower-case name, as filters, labels and configuration files write it.
The same level under another name, which is how a profile's vocabulary reaches a JSON document.
The severity number of the OpenTelemetry log model.
A level by name or by number, in any capitalisation. warning is warn, critical is fatal, and 0 to 255 is a level by value, so a level defined by an application is still spelled the same way once it reaches a filter.
The syslog severity, for GELF and anything else shaped like syslog.
Its number.
The six built-in levels, quietest first.
Diagnostics for developers.
A failure that needs attention.
A failure the program cannot carry on from.
Normal operation.
The escape that ends every colour.
Step-by-step detail.
Something unexpected that the program survived.
Values and records
Where in the source a record was made. Every part may be unknown; a layout writes ? for the ones that are, which is what a call site without debug information looks like.
A caller with no part known; set the parts that are.
The process and the deployment it is one instance of. This is the only place where the library can learn who is writing: it is passed in, never probed, so a test writes the identity it wants to check. offset answers the seconds east of UTC at an instant, which is how a local timestamp is written without the library owning a time zone database.
An identity that names nobody: the deployment a library has not been told about.
An identity with the parts a deployment knows.
One log event, as every sink sees it. There is no clock in here either: time is what was handed in, in nanoseconds since the epoch, and 0 means nobody said.
The same record at another level.
A copy, with the arrays copied too, so an edit cannot be seen elsewhere.
The last field called key.
Whether any field is called key.
The same record carrying a source file and line.
The same record numbered seq.
A record with everything but its level, target and message left empty.
The same record carrying a thread name.
The same record with another message.
The same record with key set: an earlier field of that name takes the new value where it stands, and a new name is appended, so the order a reader sees is scope, then logger, then the call.
The same record with every pair set in turn.
The same record at an instant, in nanoseconds since the epoch.
The same record carrying one more tag.
Whether the record carries tag.
The same record with key gone.
A field's value, typed. Fields are carried as values rather than as text so a layout decides how they read: 7 and "7" are the same three characters on a line and different things in a JSON document.
The value as JSON.
A Bool.
An error with its chain of causes, the way error travels as a field.
A Float.
An Int, widened to 64 bits.
A List of values.
An Int from a 64-bit integer.
A Map, its pairs kept in the order given.
Null.
Anything else, shown as text.
A Str.
The text form: bare strings, [a, b] for a list, {k=v} for a map, and an error with its causes after colons.
Writes the value as JSON into out.
Errors
A value the library will not take, and why. at is the offset in the input, counted in characters; got is what was there and want names what would have been taken. Parsing answers this rather than a default: a configuration file with a typo in it should say so, not run at a verbosity nobody asked for.
The refusal as one line, for a terminal.
Filters
Which records pass, by target, level and optionally message. Comma-separated directives: a bare level for the default, app=debug for a target, app for everything a target writes, off for nothing, * inside a target, and a /regex at the end that the message must match. A target matches itself and everything under it, split at ::, ., : or /, and the most specific directive wins.
Whether the message passes the /regex part, when there is one.
Whether a record at level from target passes, before its message is read.
A filter that lets min and above through for every target.
Whether there is a /regex part.
The lowest level any target lets through, or None when everything is off.
A filter that lets nothing through.
Reads the directives described on Filter; a malformed one is refused with its offset.
The spec this filter reads back as.
Steps
Drops a record that repeats one kept within the window; the next record kept carries repeated, the number dropped in between. Implements Processor.
Drops repeats kept within within nanoseconds of each other.
As a Processor: The record, changed or as it was, or None to drop it.
What a step may know about the moment it runs: the instant, in nanoseconds since the epoch, handed in by whoever owns the clock.
The environment at now, in nanoseconds since the epoch.
Drops a record when every condition holds. Implements Processor.
Requires a field whose text is value.
Requires the level to be at least min.
No conditions yet; a step with none drops every record, so add the ones that pick what to drop.
As a Processor: The record, changed or as it was, or None to drop it.
Requires the record to carry tag.
Adds the same fields to every record. Implements Processor.
No fields yet; add them with set.
As a Processor: The record, changed or as it was, or None to drop it.
Adds key with value to every record, replacing a field of that name where it stands.
Conditions on tags, fields and level; every one given must hold. As a step, keeps the record only when they do — the match of the specification. Implements Processor.
Requires a field whose text is value.
Whether every condition holds for record.
Requires min and above.
No conditions yet, so every record passes until one is added.
As a Processor: The record, changed or as it was, or None to drop it.
Requires a tag.
A token bucket: rate records a second, with bursts of up to burst. Implements Processor.
rate records a second on average, and up to burst at once; a burst below one is raised to one.
As a Processor: The record, changed or as it was, or None to drop it.
A step in a processor chain: it may change the record, or drop it. A step that only decides returns the record it was given, untouched, so the other slots still see what was written. The first step that changes anything returns a copy.
Masks fields by name at any depth or at a dotted path, and parts of the message that match a pattern. Implements Processor.
Masks these names wherever they appear.
What a masked value becomes; *** unless set.
A step that masks nothing yet.
Masks the field at a dotted path, such as req.headers.authorization.
Replaces whatever matches in the message.
As a Processor: The record, changed or as it was, or None to drop it.
Keeps the first records of each level and message every period, then one in so many after that. Implements Processor.
Keeps the first first records per per nanoseconds, then one in every.
As a Processor: The record, changed or as it was, or None to drop it.
Escapes line breaks, tabs and other control characters, so a message cannot forge a second line. Implements Processor.
The step; it has nothing to configure.
As a Processor: The record, changed or as it was, or None to drop it.
Layouts
How an instant is written. The library holds no clock, so every instant here arrives as nanoseconds since the epoch and every local time is the instant shifted by an offset that was handed in. A Custom spec is strftime, the subset below.
A clock spec, as a {time:…} hole spells it.
Writes time in this clock's form; offset is seconds east of UTC.
One record as a JSON object in a schema, with any key renamed. Implements Layout.
Writes one record as a JSON object.
Writes levels by name or by number, whatever the schema does by default.
Puts the fields under one key instead of at the top.
Lines in schema, with its own key names and level spelling.
Writes key as to, such as msg as message.
As a Layout: The record as one line of text, without the line break.
How a record becomes one line. A layout knows nothing about where the line goes; a sink is handed the line its slot's layout made of the record.
How levels are written, when a schema is told to write them another way.
name or number: how a JSON line writes the level.
One key=value line per record: time=… level=… target=… msg=… key=value. Values are quoted the logfmt way, so a message with a space in it cannot be mistaken for two fields. This is the shape a shell pipeline reads with cut and awk. Implements Layout.
The layout; it has nothing to configure.
As a Layout: The record as one line of text, without the line break.
A line template: literal text with {token} or {token:options} holes, and {{ and }} for literal braces. Every token but time may end with <N, >N or ^N to pad to N columns, then .M to keep at most M characters. ^ puts the extra space on the right, which is what centred columns do in the loggers this reproduces. Implements Layout.
A pattern, or the name of a preset standing for one.
As a Layout: The record as one line of text, without the line break.
The text it was parsed from.
A first line with the time, level, target and message, then one aligned key = value line per field. What a person reads in a terminal when the fields matter more than the bytes. Implements Layout.
The layout; it has nothing to configure.
As a Layout: The record as one line of text, without the line break.
A JSON shape other tools already read.
The name used in configuration files and vector files.
A schema by name: serlog, pino, bunyan, zap, slog, structlog, logstash, ecs, gelf or otel.
Sinks
A sink that drops everything, for an application that configures logging off and a test that would rather not have output. Implements Sink.
A sink that accepts every entry and keeps none.
As a Sink: Writes one entry; Err says why it could not.
What a sink is handed: the record, and the line its slot's layout made of it. The line is made once, whether or not the sink reads it.
A sink that writes each line through a function. This is the seam: Lines::new(fn(line) { println(line) }) prints, and Lines::new(fn(line) { held.push(line) }) is how a test reads what was written without a terminal in the way. Implements Sink.
A sink that hands each line to write.
As a Sink: Writes one entry; Err says why it could not.
A sink that keeps the newest entries in memory, for a test or a status page. Implements Sink.
Forgets everything it kept.
The entries whose line contains text.
The lines it kept, oldest first.
Keeps the newest size entries; 0 keeps everything.
The records it kept, oldest first.
As a Sink: Writes one entry; Err says why it could not.
Where entries go. Only write is required. Queues, retries, health and alarms belong to the slot the sink is registered in, so a sink delivers and says what went wrong and nothing more. Err is a failure worth telling someone about; a sink that cannot fail answers Ok.
Slots and health
A slot's health: up, down, or one write testing whether it works again. It knows no I/O and reads no clock — every method takes the instant — which is what makes the machine checkable against vectors instead of against a stopwatch.
Whether a write may go ahead at now: turns a due Down into Probing, and counts a skip otherwise.
The latest error.
When the current failure began, which is what an incident calls since; since itself moves when the sink recovers.
A write failed; Opened or Reminder when that is to be told.
Where the sink stands now: Up, Down, or Probing while a trial write is due.
A healthy machine that first probes probe nanoseconds after a failure.
A write went through; Resolved when the failure had been told.
Whether a sink is skipped at now without counting anything, for choosing alarm targets.
When the current health began.
Records skipped while down, over the slot's life.
Records skipped since the current failure began.
How long until the next probe.
What went wrong on the way to a sink, and whether the failure is one the alarm policy has been told about.
What a full queue does with a record that arrives.
Whether a sink is working.
The name used in configuration, status and the shared vectors.
What a slot does when its sink fails.
A buffer in front of a sink, written in batches. Without a runtime to hand, the buffer is drained by the hub: when it holds batch records, when wait has passed since the first one, or at flush. That is the policy a background task would follow, with the clock still in the caller's hands.
The same queue, written batch records at a time.
The same queue, with full deciding what an arriving record meets when it is full.
Holds size records, written batch_default at a time or wait_default after the first.
The same queue, flushed at most wait nanoseconds after its first record.
What a delivery did: the failure it met, if any, and the phase an alarm is owed, if any. A recovery is a phase too, which is why this is not a Result.
One sink with the settings around it: name, filter, processors, layout, colour, queue, retries, health and the policy for failure. A slot is a value an application builds and hands to a hub. Nothing about it is process-wide, so two hubs can hold two slots writing the same file in two formats and neither knows about the other.
Keeps it off the log route, for alarms only.
Takes only min and above.
Whether this slot's lines are coloured.
When the current failure began, for the incident an alarm carries.
Takes only what filter lets through.
Names the kind of sink this is, for a status page: memory, file, loki, or whatever a sink of your own calls itself. custom until it is said.
Lays entries out this way instead of the profile's pattern.
The slot's name.
A slot named name writing to sink, with the library's own pattern as its layout until one is given.
What happens when the sink fails.
First wait before probing a failed sink; it doubles up to five minutes.
Adds a step that runs for this slot only.
Runs the sink behind queue.
Tries a failed write this many more times, without pausing.
Where it stands.
A slot's state, for a status page.
How a failure is told.
How many records a queue writes at once when nothing says otherwise. Chosen here rather than taken from a case file: a thousand lines is one sensible write, and a queue that is told otherwise says so.
Five minutes: the longest a failed sink waits between probes. The cap the health cases stop at: the wait doubles up to this and no further, however long the outage lasts.
Five seconds: the first wait before a failed sink is probed again. The interval the health cases are written with, so a slot built with the defaults is the slot those cases describe.
One second: how long a queue waits for its batch to fill. Chosen here: long enough to gather a burst, short enough that a quiet service still sees its last line promptly.
Alarms
Chooses where an incident goes. The runtime walks the tiers, skips the failing sink and any that are resting, and stops at the first tier where one sink took the record — so an implementation only has to say which sinks to try.
What the alarm walk needs from the sinks around it.
One failed delivery, on the way to an alarm.
What an alarm does when no tier took the incident.
A sink's failure, from the first error to its recovery. A phase is a fresh incident each time: Opened when the sink first failed, Reminder while it is still down, Resolved when it writes again. Each carries what is known at that moment, which is why the type is a value and not a handle.
The record an incident becomes: error when something failed, info when something works again, with the failures on one field.
A fresh incident: sink, a sink of kind, failed with error at now, in phase.
How an alarm walk ended.
Which report of an incident this is.
The name used in a record's phase field and in the shared vectors.
Fixed tiers of sink names: [["loki"], ["oncall"], ["stderr"]]. Implements Alarm.
As a Alarm: What happens when no tier took the incident.
Tiers of names, tried in order.
The default alarm: standard error alone, one hour between reminders.
As a Alarm: How long a sink may stay down before it is reported again, in nanoseconds.
As a Alarm: The tiers of sink names to try for this incident, in order.
The tiers themselves.
The same tiers, with fallback deciding what happens when no tier took the incident.
How long a sink may stay down before it is reported again.
One hour: how long an alarm stays quiet about a sink that is still down. The interval the alarm cases are written with, so a tier built with the defaults reports a long outage at the same moments they describe.
Delivers incident through alarm's tiers, adding every failed delivery to it. The failing sink is never asked. * stands for every sink on the log route. A sink already tried is not tried again, and a resting one is left out. One success ends the walk; when no tier takes it, nothing more happens here.
Hub and logger
The whole of a running logger: a global filter, a chain of steps every record goes through, a table of slots, and the identity and clock that the application handed in. There is no process-wide hub. An application builds one and passes the Logger values that write to it to the libraries it uses; a library that was handed none writes nothing. Implements Desk.
Registers a slot and answers the name it took. A second slot with the same name is refused, and so is the name *, which would make an alarm tier ambiguous.
As a Desk: Writes record to the named sink at once, bypassing its queue. Err is what the sink said, or that no sink has that name.
Sends record everywhere it belongs: through the global chain, then to each slot's own filter and steps, then to its sink. A record made while a sink is already running is dropped and counted: a sink that logs into the hub it belongs to would otherwise never finish.
Whether a record at level could pass, before its target or message is known. Worth asking before building a message that costs something.
The filter now in force.
Writes what every queue holds, up to now.
The identity records are stamped with.
A logger writing to this hub under target, which is usually a module path.
As a Desk: The sinks on the log route, which * stands for.
A hub at profile, writing what the filter lets through. clock answers nanoseconds since the epoch. Without one every record carries 0, which is honest — this library never reads a clock — and every test says what instant it means.
The instant the hub is working at, from the clock it was given.
Adds a step to the global chain, which runs before any slot sees the record.
The profile in force.
Takes a slot out by name, after writing what its queue holds.
As a Desk: Whether a sink is down and not yet due for a probe.
The scene a line is written in: this hub's vocabulary, this hub's identity.
Replaces the global filter; takes effect at once.
Lets through min and above for every target.
Every registered slot, in the order they were added.
How every slot stands.
Records made while a sink was running, dropped so a sink that logs cannot feed itself.
Writes a record at level from target.
A target and the fields bound to it: what a module or a request holds and writes through. A logger is a value. It is passed in, it can be copied, and a library that was handed none writes nothing — there is no default one to inherit.
The same logger with key bound, so every line it writes carries it.
Writes at debug.
Whether a message at level would be written. Worth asking before assembling a message that costs something: a logger cannot skip work its caller has already done.
Writes at error.
Writes at fatal. It does not abort: what to do after the last line is the caller's to decide, and a library that ends the process takes that decision away.
Writes at info.
Writes a message, with fields if there are any.
A logger writing to hub under target.
The target it writes under.
Writes at trace.
The same logger under another target, which is what a submodule is.
Writes at warn.
How a record is shown: whose vocabulary the level is written in, whether a terminal takes colour, and who is writing.
The same scene at another profile.
The same scene with colour turned on or off.
A plain scene: no colour, the default vocabulary, an identity that names nobody.
Profiles
Another logger whose defaults an application can take on. A profile changes four things at once — the default line, the stream a console writes to, the level it starts at, and the variables a filter is read from — because those four are what a deployment already expects when it moves from one logger to another.
The variables this profile reads a filter from, first one set wins.
The level this profile starts at.
The name used in configuration files.
A profile by its lower-case name: serlog, tracing, env_logger, python, logback (or log4j), slog or pino.
The pattern this profile's logger writes by default.
Where this profile's console writes.
Where a console writes.
The name used in configuration files.
stdout, stderr or auto.
moonlog/stdio
A console on standard output, standard error, or both.
A sink that writes each entry as one line on the console. Auto sends a record to standard error when its level reaches from and to standard output otherwise, which is what a pipeline wants: the log stream can be redirected while failures still reach the terminal. Whether the line carries colour is settled once, when the console is built, and the slot has to be told the same answer: Console::slot does both. Implements Sink.
Whether this console's lines carry escapes.
The level at which Auto turns to standard error.
A console on stream. colour decides whether the slot writes escapes: leave it out and the console asks the environment and the terminal (see decide). from is the level at which Auto starts using standard error; every level from error up unless it is given.
A slot around this console, already coloured the way the console decided.
The stream this console was built for.
The stream a record at level is written to.
Whether colour is wanted, given what the terminal said and what the environment said. FORCE_COLOR is the last word — someone who set it meant it — then NO_COLOR, then the terminal.
FORCE_COLOR, when it says something: 0 and false turn colour off, any other value turns it on, and an unset variable says nothing.
Whether NO_COLOR is set to anything but the empty string, which is what the convention at no-color.org asks for.
Whether a slot writing to stream should carry escapes. Auto asks for both streams, so a run whose log is piped into a file gets plain lines even though its failures still reach the terminal.
moonlog/file
A file that rotates itself.
A sink that appends each entry to a file, rotating it when it grows too large or too old. The file is opened on the first write, so a sink that cannot open its file is a failing slot rather than a failure at startup: the health machine puts the sink down, the alarm says why, and a probe retries. reopen picks up a file that an outside tool moved away. Segments are named <file>.<20261101T090000>, or .gz too when compress is on, and are pruned by Limits after every cut. Names are UTC, so a directory listing reads as a history. Implements Sink.
Writes what the buffer holds, then gives the file back to the system. A sink that is buffered should be closed, or its last lines are lost.
Writes what the buffer holds.
A file sink on path. append keeps what is there (the default) and false starts the file empty. perms is the mode a new file gets, in the usual octal form. mkdir creates the parent directories. buffer gathers that many bytes before writing, which trades a lost tail on a crash for fewer calls; 0 writes every line as it arrives. clock answers nanoseconds since the epoch. A file sink needs one for segment names and time-based rotation, so it reads the system clock unless it is given another.
The file this sink writes.
Closes the file and opens it again, which is what an outside rotate wants: the old descriptor keeps writing to a file that was moved, this one writes to the path again.
Cuts the file now, whether or not a limit was reached: the current file becomes a segment and a fresh one is started.
The segments of this file, oldest first. A segment is a file this sink cut and left behind: what a status page lists, and what keep, max_age and max_total count.
A slot around this sink.
How many bytes the current file holds.
What becomes of the files rotation leaves behind. All three are ceilings: zero keeps everything. max_age and max_total are only applied to segments, never to the file being written.
The same limits, keeping at most keep segments.
The same limits, dropping segments older than max_age nanoseconds.
The same limits, dropping the oldest segments until the rest fit in max_total bytes.
Keep every segment, for ever.
When a file is rotated. size is a limit in bytes and every a span in nanoseconds; zero turns either one off. A file that has been written to rotates before the line that would cross the limit, so a line never straddles two files.
The same rotation, with a time limit.
No rotation: the file grows until something else deals with it.
The same rotation, with a size limit.
moonlog/loki
Entries pushed to Loki.
How much a Loki sink holds while it waits for its next push, and how much goes in one request. capacity is a ceiling on entries waiting to be sent; when the queue is full the oldest goes, because a shipper that stops the program it logs for is worse than one that loses the start of an outage. timeout is in seconds and applies to one request.
The same limits, pushng a different number of entries at a time.
The same limits, holding a different number of entries.
Eight thousand entries, which is a few seconds of a busy service, and one request's worth of them.
The same limits, waiting a different number of seconds for an answer.
A sink that ships entries to Loki. write only queues: a log call must not wait for a network. push does the sending, from an async context the application already runs, so the retry that a busy Loki asks for lives in the caller's loop — and the slot's own queue, retries and health machine still govern how the sink is treated when a push keeps failing. Each level gets a stream of its own, so a query can select on it without parsing a line, and the entry's fields travel as structured metadata. Implements Sink.
The push body the pending entries would make, without taking them. What a check before a deployment prints, and what a test reads instead of standing up a server.
How many were dropped because the queue was full.
The push body: one stream per level, each with its own labels.
How many pushes came back with something to retry.
A sink pushing to url, which is the Loki base address: /loki/api/v1/push is appended unless the address already ends with it. labels are the stream's own, such as the job and the host; the level is added per stream. auth is a user and password for basic authentication, and tenant is sent as X-Scope-OrgID for a multi-tenant Loki.
How many entries are waiting.
How many Loki refused for good.
How many entries Loki has taken.
A slot around this sink.
The address entries are pushed to.
What one push did. A batch either lands, is refused for good, or is worth trying again: Loki answers 400 for an entry it will never take — a timestamp outside the tenant's window is the usual one — and 429 or 5xx when it is merely busy. Sending the first kind again would spin for ever, so those entries are dropped and counted, and only the second kind comes back to the queue.
Nothing was pending, so nothing was tried.
Whether the push asked to be repeated.
What to do with a batch that came back as status.
How a status is read: 2xx landed, 400 and 404 will never land, everything else — 408, 429, 5xx — is worth another try.
moonlog/mail
Records mailed as one letter.
A sink that mails the records a deployment cannot afford to lose. It is the mail handler a service already knows: a level of its own, a window in which a burst becomes one mail, and a ceiling on how many records one mail carries. write only queues — a log call never waits for SMTP — and send does the talking, from the application's own async loop. When the server is down the mail stays queued, and the slot's retries, health machine and alarm take over from there. Implements Sink.
How many records one mail carries.
The mail the pending records would become, without sending it. What a check before a deployment prints, and what a test reads instead of standing up a server.
How many records were dropped because the queue was full.
Whether a batch is ready at now: the oldest record has waited out the window, or there are enough of them to fill a mail.
How many sends failed and left their records queued.
The HTML body: the same lines in a table, with the fields that came with them, so an inbox shows the same thing a console does.
The mail a batch becomes, ready to encode.
The floor this sink mails at.
A sink mailing through host. from is the envelope sender and to the recipients, both as addresses. subject is a template: {count} (a number), {records} (the number and the noun that goes with it), {level} and {target} are filled in from the batch. level is the floor this sink is interested in — it mails what a slot would already have let through, but a sink built beside a console slot gets the whole stream, and this is what keeps a queued warning out of the on-call inbox. security decides how the connection is protected; StartTls and Tls need tls, a client with its own trust store, because which roots to trust is the application's decision and not this sink's.
How many records are waiting.
How many records have been mailed.
A slot around this sink.
The subject a batch is mailed under.
The plain text body: one line per record, as the slots wrote them.
How long a batch is gathered.
How the connection to the mail server is protected.
The port a security setting usually means: 25 for the clear, 587 for submission with STARTTLS, 465 for implicit TLS. The submission ports are what a relay in front of a service listens on when it is not the local one.
How many records one mail carries; a burst larger than this is sent in several mails rather than one too big to read.
How long a batch of records is gathered before it is mailed.
moonlog/config
A document read, checked and written back.
An alarm as a document writes it: which sinks to try, in which tiers, how often to remind, and what to do when every tier is spent.
The alarm as the hub wants it.
A logging configuration, read from a document and checked against the settings the shipped sinks read. Reading a document does not build anything: it says what the deployment asked for, and the application — or moonlog/global — turns that into sinks. That split is what lets the same document be checked, dumped and reloaded without a file system in the way.
The alarm the document describes, if any.
Writes the configuration as a document, which is what dump is for: a deployment can be read back and checked without the file that produced it.
The filter the document asks for, as written.
Reads a configuration file, taking the format from its extension.
What a configuration with no document says: the default profile, the default filter, and nothing to write to.
Reads a document. ${NAME} in any string is replaced by the environment variable of that name; a name the environment does not have is refused, because a configuration that silently logs to an empty URL is worse than one that will not start.
The profile the document asks for.
Writes the configuration to path in the format its extension asks for.
The JSON Schema a document of this shape answers to. It is built from the same tables the parser checks against, so the schema an editor or a pipeline validates with cannot drift from the parser: a key the schema allows is a key the parser reads, and a value of the wrong shape is refused in both places. Publish it next to a deployment for the editor to pick up; moonlog/config never needs it to read a file.
The slot called name, if there is one.
The slots the document describes, in document order.
The document this configuration is written as.
Starts watching path.
The three shapes a configuration file is written in. All three read into the same tree, so the same document means the same configuration whichever one it is written in.
The name this format is written as.
The format a file name asks for, by its extension.
A format by name.
Reads a document into the tree every reader in this family produces.
Writes the tree back out in this format.
A queue as a document writes it.
What went wrong in a configuration file, said in the terms a reader can act on: where in the document, what was there, and what belongs there. at is a key path such as slots.file.max_size, or empty for the document as a whole.
A refusal with the key path it happened at.
One sink as the document describes it. settings holds the keys that belong to the kind; text, flag, size and the rest read them back with the shape the parser already checked.
A setting as a whole number.
A setting as a yes or no.
The keys this slot's kind reads, which is what a deployment file is checked against.
A setting as a table of strings, in document order where the format keeps one.
A setting as a byte count, 10mb and a bare number alike.
A setting as a number of nanoseconds.
A setting as a string. The parser has already checked that anything that is there has the shape the key asks for, so a None here only means the document left the key out.
A setting as a list of strings.
A configuration file watched for changes. There is no background task here: poll reads the file and answers it when the bytes differ from the last document it saw, which keeps the reload on the same thread as everything else and makes it something a test can step through. Comparing content — rather than the modification time — means a rewrite is never missed because two edits landed inside one timestamp tick, which is the usual way a fast test (or a coarse filesystem clock) fools an mtime check. A configuration document is small, so reading it on each poll is cheap; parsing happens only when the content actually moved.
The file being watched.
The newest configuration, when the file has changed since the last answer. Ok(None) means the bytes are unchanged. An Err means the file changed and the new one will not do — the caller keeps what it has and decides whether to say so.
A span in the shortest form that reads back as the same number of nanoseconds.
How many bytes a size names: 512, 4kb, 10MiB, 1.5gb. A bare number is bytes. k, m, g and t are powers of a thousand, ki, mi, gi and ti powers of 1024, with or without a b.
How many nanoseconds a span names: 200ms, 5s, 1m30s, 2h. A span always says its unit — a bare number is refused, because a configuration that means seconds in one program and nanoseconds in another is a configuration that is wrong silently.
One second, the reminder an alarm gets when the document does not say.
moonlog/global
The process-wide hub.