This folder contains YAML schemas that declaratively define each log source and its events. A generator turns these schemas into Sorbet-typed Ruby T::Structs under lib/log_struct/log/*, plus supporting helpers.
- Inputs: YAML files in
schemas/log_sources/*.ymland the meta JSON Schemas inschemas/meta/*. - Enums: Human-friendly field names are defined in
lib/log_struct/enums/log_field.rb(e.g.,Database,DurationMs). Each enum value serializes to a compact JSON key (e.g.,:db,:duration_ms). - Validation: The meta schema
schemas/meta/log-source-schema.jsonvalidates every source schema. It importsschemas/meta/log-fields.json, an auto-generated enum of allowedLogFieldnames for field keys. - Codegen: Run
ruby scripts/generate_structs.rbto:- Generate Ruby structs for every source/event
- Rebuild
schemas/meta/log-fields.jsonfromLogFieldso schema validation stays in sync
- schemas/log_sources/*.yml: One file per source. Declares base fields and events.
- schemas/meta/log-source-schema.json: JSON Schema for validating the shape and field names of all source YAMLs.
- schemas/meta/log-fields.json: Generated from
LogField; lists the only allowed field key names. - scripts/generate_structs.rb: Main codegen entry; generates Ruby + TS and log-fields.
- LogField meta schema is generated as part of
scripts/generate_structs.rb(from theLogFieldenum).
- source: Required. CamelCase source name, e.g.,
ActiveJob,SQL. - snake_case: Optional snake-case filename override; normally inferred.
- consts.source: Optional override for the
Sourceenum; omit unless it differs from source name. - additional_data: Boolean. If true, generated structs include an
additional_dataHash merged at top-level on serialization. - add_request_fields: Boolean. Adds request-related accessors and merges them during serialization.
- Preferred form:
- base.optional: Optional boolean. When true, base fields are optional by default; use per-field
required: trueto force required. When omitted/false, fields are required by default.- base.fields: Map of
LogFieldnames to types.
- base.fields: Map of
- Legacy form:
base_fields:is still supported (treated as optional-by-default). Preferbase:going forward.
-
events: Map of event name → object with keys:
- optional: Optional boolean. When true, fields are optional by default; use per-field
required: trueto force required. When omitted/false, fields are required by default. - fields: Map of
LogFieldnames to types. Field keys must be validLogFieldenum names (validated bylog-fields.json).
- optional: Optional boolean. When true, fields are optional by default; use per-field
-
You may also declare an event with no fields by using a bare event key or
null:Delivery:orDelivery: null→ generates an event with no additional fields.
You can write fields in two styles:
-
Shorthand (recommended):
FieldName: Type- Required determined by
default_required(event/base). - Example:
DurationMs: Float
- Required determined by
-
Object form (conditional required):
FieldName: type: Type required: false- Use when some fields must remain optional even if
default_required: true.
- Use the exact
LogFieldconstant name for keys (e.g.,RequestId,Database,DurationMs,WaitMs). - The generator maps these to short JSON keys via
LogField(e.g.,RequestId→:request_id).
- Durations: Prefer explicit units. Use
DurationMsfor durations in milliseconds. UseWaitMsfor wait times in milliseconds. - Optional data: Use
additional_data: trueonly when needed; the generator flattens the hash on serialization. - Source override: Omit
consts.sourceunless the source enum differs from the source name.
- Single-event sources generate a single top-level struct (e.g.,
LogStruct::Log::Request). - Multi-event sources generate:
- A parent file
lib/log_struct/log/<source>.rbthat requires event files and may defineBaseFields. - One file per event (e.g.,
lib/log_struct/log/<source>/<event>.rb).
- A parent file
- All generated classes:
- Include shared common fields (source, event, timestamp, level)
- Serialize using
LogField::<Name>.serializefor every key - Serialize
Timefields as ISO-8601 strings - Respect
default_requiredandrequiredfor field nilability
- Use
default_required: falsewhen all fields in an event (or base) are optional — this removes per-fieldrequired: falsenoise. - Do NOT add
default_required: falsewhen you have a mix of required and optional fields — declare required fields in shorthand and optional fields withrequired: false.
Examples:
-
All optional fields:
default_required: falsefields: Filename: String MimeType: String
-
Mixed required/optional fields:
- Omit
optional fields: Message: String AhoyEvent: type: String required: false
- Omit
- Validate schemas:
bundle exec ruby -Itest test/schemas/schema_validation_test.rb - Generate structs + log-fields:
ruby scripts/generate_structs.rb
- Using snake_case field keys. Always use
LogFieldnames (e.g.,RequestId, notrequest_id). - Adding
default_required: falseto events that have a mix of required and optional fields — this turns everything optional. Instead, omit it and mark optional fields explicitly. - Hardcoding JSON keys. Generated code always uses
LogField::<Name>.serializefor keys.