About custom events
Your team may want to record events Vinaya does not know about, such as a deploy. You declare them in vinaya. under logs.events. You write no code and add no new top-level key.
A declared event is written as one custom · recorded line, with the same header, destination and redaction as every other event. An event that does not match its declaration writes no custom line; the refusal is recorded instead as an operation event.
How it works
- 1
Declare the event
Add it under
logs.events, with a type for each field. Vinaya checks the declarations when it loads the config. - 2
Emit it
Call
vinaya log emit <event>from a script, a CI step or another tool, or emit it from Vinaya code. Every declared field must be given. - 3
Vinaya checks the values
A matching event is written to your Log destination. A wrong one is refused, with the reason and the field names, and the refusal is logged.
Declare an event
{
"logs": {
"events": {
"acme.deploy": {
"fields": { "env": ["prod", "staging"], "count": "number", "note": "text", "ok": "boolean" }
}
}
}
}A field is "text", "number", "boolean", or a list of allowed words (like env above).
Declaration rules
| Rule | Limit |
|---|---|
| Name | <namespace>., matching ^[a- |
| Name length | At most 64 characters |
| Reserved namespace | vinaya. cannot be declared |
| Fields per event | At most 20 |
| Field name | ^[a-, at most 64 characters |
| Field type | "text", "number", "boolean", or a list of words |
| Word list | 1 to 50 words, no duplicates |
| Word | ^[a-, at most 64 characters |
| Shape | Only fields next to the name; no nested values and no lists as values |
A broken declaration is refused with a message that names the entry, and the field when one is at fault: logs..
Emit an event
Pass the fields as one JSON object, with --json or on stdin.
vinaya log emit acme.deploy --json '{"env":"prod","count":3,"note":"weekly","ok":true}'vinaya log emit resolves the destination as a live event would: an unattended caller is held to the default-branch rule, but the command does not force itself unattended. It waits for the event to be delivered before it exits.
| Exit code | When | Prints |
|---|---|---|
0 | The event is declared and every field is valid | The event name and the destination kind: folder, server or none |
1 | Refused, for one of the five reasons below | The reason and the field names, never a value |
2 | No event name, unreadable JSON, or JSON that is not a flat object | A usage message; nothing is recorded |
Plug in a process that is not Vinaya
- 1
Declare the event
Add it under
logs.events. - 2
Say what the work is
Set
VINAYA_WORK_REF(the unit of work, any opaque value) andVINAYA_FLOW(your way of working). You do not needVINAYA_ROLEorVINAYA_TASK. - 3
Emit it
printf '{"env":"prod","service":"checkout-api"}' | vinaya log emit acme. deploy_ started
Refusals
Every declared field is required. Vinaya checks in this order. The first reason that applies wins, and it names every field with that problem, sorted.
| Reason | Meaning |
|---|---|
undeclared | The name is not in logs.events |
missing_field | A declared field was not given |
extra_field | A field was given that is not declared |
wrong_type | A value is not its declared type, including nested values and lists |
too_long | A text value is over 500 characters |
Configuration
| Key or variable | Effect |
|---|---|
logs.events | Your declarations |
VINAYA_WORK_REF, VINAYA_FLOW, VINAYA_, VINAYA_RUNTIME, VINAYA_SOURCE | Fill in the header for an emitter that is not Vinaya |
What gets logged
A valid event writes one custom · recorded line:
| Field | Value |
|---|---|
name | The declared name |
fields | A flat record of at most 20 entries: text up to 500 characters, a finite number, or a boolean |
kind and event are fixed, and a custom event never sets its own header. It is valid only under a schema 3 header. Like every line, its text, fields included, is redacted before it is written; see Redaction and trust.
A refusal writes exactly one operation · completed line:
| Field | Value |
|---|---|
operation | log.emit |
result | refused |
error_class | The reason |
field_names | The fields with that problem, never their values |
target | The event name if it is shaped like a name, else null |
An extra field whose name is not shaped like a field name is recorded as <unrecordable>.