blob: b88aa9f51d5815b7c9af527a097461d77745296d [file] [log] [blame] [view] [edit]
# Encoding structured records
Fuchsia uses a wire format for structured logs inspired by its [trace format]. This transmission
format allows consumption and propagation of arbitrary data structures as log records.
## Validation
The size of a log record is capped at 32kb.
Writers that send oversize or invalid records to the diagnostics service will have their streams
closed and an error recorded by the diagnostics service.
## Primitives
Integers have little endian encoding. Signed integers are twos-complement.
Timestamps are signed 64-bit integers measured in nanoseconds, as recorded by
[`zx_clock_get_monotonic`].
Strings are denoted by a 16-bit "string ref". If the most significant bit (MSB) is 0, the string is
empty. If the MSB is 1, the remaining bits of the ref indicate the length of the subsequent UTF-8
byte stream. All string refs with an MSB of 0 other than the empty string are reserved for future
extensions. Strings are padded with zeroes until 8-byte aligned.
Records consist of a number of 8-byte words and are 8-byte aligned.
### Header
The required metadata for a log record are a record type, the overall length of the record, and a
timestamp. These are encoded in the first 16 bytes of a record:
```mermaid
---
title: "Header"
---
packet-beta
0-3: "type (9)"
4-15: "size words"
16-31: "reserved (0)"
32-53: "reserved (0) (cont.)"
54-63: "severity"
64-95: "timestamp"
96-127: "timestamp (cont)"
```
Notes:
- The "size words" field includes the header word.
- The timestamp is in nanoseconds.
Currently all records are expected to have type=9. This was chosen to mirror the [trace format] but
may require a change before these records can be processed by tracing tools.
Values for severity are defined in
[/sdk/fidl/fuchsia.diagnostics/severity.fidl](https://cs.opensource.google/fuchsia/fuchsia/+/main:sdk/fidl/fuchsia.diagnostics/severity.fidl)
## Arguments
Log record data is conveyed via a list of typed key-value pairs. The keys are always non-empty
strings, which supports different types of arguments, and the values can have several types.
### Argument header
Each argument has an 8-byte header, followed by the argument name, followed by the argument's value.
The name is padded with zeroes to 8-byte alignment before the argument's content is written.
```mermaid
---
title: "Argument header"
---
packet-beta
0-3: "type"
4-15: "size words"
16-29: "name ref"
30-31: "varies"
32-63: "varies (cont.)"
64-95: "name"
96-127: "name"
128-159: "name (cont.) (1+ words)"
```
Notes:
- The "size words" field includes the header word.
- The "name ref" is a string ref for the argument name.
- The "varies" section varies by argument type and must be 0 if unused.
- The "name length" is "64 * nameref".
- The "name" is the name of the argument, the total length (not in diagram) is
`namelen` and is padded to 8-byte alignment.
The first 4 bits of the argument header determine which type the argument has:
T (type) | name
---------|--------------------------
`3` | signed 64-bit integers
`4` | unsigned 64-bit integers
`5` | double-precision floats
`6` | UTF-8 strings
`9` | booleans
### Signed 64-bit integer arguments
Signed integers are appended after the argument name is terminated.
```mermaid
---
title: "Signed integer argument"
---
packet-beta
0-3: "type (3)"
4-15: "size words"
16-29: "name ref"
30-31: "reserved"
32-63: "reserved"
64-95: "name"
96-127: "name (cont.) (1+ words)"
128-159: "value"
160-191: "value (cont.)"
```
### Unsigned 64-bit integer arguments
Unsigned integers are appended after the argument name is terminated.
```mermaid
---
title: "Unsigned integer argument"
---
packet-beta
0-3: "type (4)"
4-15: "size words"
16-29: "name ref"
30-31: "reserved"
32-63: "reserved"
64-95: "name"
96-127: "name (cont.) (1+ words)"
128-159: "value"
160-191: "value (cont.)"
```
### 64-bit floating point arguments
Floats are appended after the argument name is terminated.
```mermaid
---
title: "Floating point argument"
---
packet-beta
0-3: "type (5)"
4-15: "size words"
16-29: "name ref"
30-31: "reserved"
32-63: "reserved"
64-95: "name"
96-127: "name (cont.) (1+ words)"
128-159: "value"
160-191: "value (cont.)"
```
### String arguments
Strings are encoded in UTF-8, padded with zeroes until 8-byte aligned, and appended after the
argument name.
```mermaid
---
title: "String argument"
---
packet-beta
0-3: "type (6)"
4-15: "size words"
16-29: "name ref"
30-31: "value ref"
32-45: "value ref (cont.)"
46-63: "reserved"
64-95: "name"
96-127: "name cont. (1+ words)"
128-159: "value"
160-191: "value cont. (1+ words)"
```
### Boolean arguments
Booleans are appended after the `NameRef` field in the argument header.
```mermaid
---
title: "Floating point argument"
---
packet-beta
0-3: "type (5)"
4-15: "size words"
16-29: "name ref"
30-31: "value"
32-63: "reserved"
64-95: "name"
96-127: "name (cont.) (1+ words)"
```
# Encoding "legacy" format messages
Components that call [`LogSink.Connect`] are expected to pass a socket in "datagram" mode (as
opposed to "streaming") and to write the ["legacy" wire format] into it. This uses little endian
integers and a mix of length-prefixed and null-terminated UTF-8 strings.
At the moment, this is only used by the [Go logger] implementation.
[Go logger]: /src/lib/syslog/go/logger.go
[`zx_clock_get_monotonic`]: /reference/syscalls/clock_get_monotonic.md
[`LogSink.Connect`]: https://fuchsia.dev/reference/fidl/fuchsia.logger#Connect
["legacy" wire format]: /zircon/system/ulib/syslog/include/lib/syslog/wire_format.h
[trace format]: /docs/reference/tracing/trace-format.md