From 0f64b806826deee4ce30d2b9c44b362a64ceb405 Mon Sep 17 00:00:00 2001 From: Caleb Schoepp Date: Thu, 5 Sep 2024 13:59:20 -0600 Subject: [PATCH 1/2] feat(wit): Propose WIT for tracing Signed-off-by: Caleb Schoepp --- .github/workflows/main.yml | 2 +- imports.md | 356 +++++++++++++++++++++++++--- justfile | 2 +- wit/deps.lock | 10 + wit/deps.toml | 1 + wit/deps/clocks/monotonic-clock.wit | 50 ++++ wit/deps/clocks/timezone.wit | 55 +++++ wit/deps/clocks/wall-clock.wit | 46 ++++ wit/deps/clocks/world.wit | 11 + wit/deps/io/error.wit | 34 +++ wit/deps/io/poll.wit | 47 ++++ wit/deps/io/streams.wit | 286 ++++++++++++++++++++++ wit/deps/io/world.wit | 10 + wit/observe.wit | 25 -- wit/traces.wit | 153 ++++++++++++ wit/world.wit | 2 +- 16 files changed, 1027 insertions(+), 63 deletions(-) create mode 100644 wit/deps/clocks/monotonic-clock.wit create mode 100644 wit/deps/clocks/timezone.wit create mode 100644 wit/deps/clocks/wall-clock.wit create mode 100644 wit/deps/clocks/world.wit create mode 100644 wit/deps/io/error.wit create mode 100644 wit/deps/io/poll.wit create mode 100644 wit/deps/io/streams.wit create mode 100644 wit/deps/io/world.wit delete mode 100644 wit/observe.wit create mode 100644 wit/traces.wit diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0df64e9..b7c499d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -11,4 +11,4 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: WebAssembly/wit-abi-up-to-date@v17 + - uses: WebAssembly/wit-abi-up-to-date@v22 diff --git a/imports.md b/imports.md index 857e5ce..4fc02fd 100644 --- a/imports.md +++ b/imports.md @@ -1,52 +1,53 @@ -

World imports

+

World imports

-

Import interface wasi:logging/logging

+

Import interface wasi:logging/logging

WASI Logging is a logging API intended to let users emit log messages with simple priority levels and context values.


Types

-

enum level

+

enum level

A log level, describing a kind of message.

Enum Cases

Functions

-

log: func

+

log: func

Emit a log message.

A log message has a level describing what kind of message is being sent, a context, which is an uninterpreted string meant to help @@ -54,42 +55,327 @@ consumers group similar messages, and a string containing the message text.

Params
-

Import interface wasi:observe/observe

+

Import interface wasi:clocks/wall-clock@0.2.1

+

WASI Wall Clock is a clock API intended to let users query the current +time. The name "wall" makes an analogy to a "clock on the wall", which +is not necessarily monotonic as it may be reset.

+

It is intended to be portable at least between Unix-family platforms and +Windows.

+

A wall clock is a clock which measures the date and time according to +some external reference.

+

External references may be reset, so this clock is not necessarily +monotonic, making it unsuitable for measuring elapsed time.

+

It is intended for reporting the current date and time for humans.


Types

-

enum metric-format

-
Enum Cases
+

record datetime

+

A time and date in seconds plus nanoseconds.

+
Record Fields
+ +
+

Functions

+

now: func

+

Read the current value of the clock.

+

This clock is not monotonic, therefore calling this function repeatedly +will not necessarily produce a sequence of non-decreasing values.

+

The returned timestamps represent the number of seconds since +1970-01-01T00:00:00Z, also known as POSIX's Seconds Since the Epoch, +also known as Unix Time.

+

The nanoseconds field of the output is always less than 1000000000.

+
Return values
+ +

resolution: func

+

Query the resolution of the clock.

+

The nanoseconds field of the output is always less than 1000000000.

+
Return values
+

Import interface wasi:observe/traces


+

Types

+

type datetime

+

datetime

+

+#### `resource span` +

Represents a unit of work or operation.

+

enum span-kind

+

Describes the relationship between the Span, its parents, and its children in a trace.

+
Enum Cases
+ +

variant status

+

The status of a span.

+
Variant Cases
+ +

type key

+

string

+

The key part of attribute `key-value` pairs. +

variant value

+

The value part of attribute key-value pairs.

+
Variant Cases
+ +

record key-value

+

A key-value pair describing an attribute.

+
Record Fields
+ +

tuple trace-id

+

The trace that this span-context belongs to.

+
Tuple Fields
+ +

type span-id

+

u64

+

The id of this `span-context`. +

flags trace-flags

+

Flags that can be set on a span-context.

+
Flags members
+ +

record span-context

+

Identifying trace information about a span that can be serialized and propagated.

+
Record Fields
+ +

record link

+

Describes a relationship to another span.

+
Record Fields
+ +

record start-options

+

Configuration for starting a span.

+
Record Fields
+ +

variant span-parent

+

Determines how the parent of a span should be set.

+
Variant Cases
+ +

type trace-state

+

option<list<(string, string)>> +Carries system-specific configuration data, represented as a list of key-value pairs. trace-state allows multiple tracing systems to participate in the same trace.

Functions

-

metric: func

-

Emit a metric.

-

A metric has a format describing the format of the name parameter.

+

[static]span.start: func

+

Starts a new span with the given name, parent, and options.

+
Params
+ +
Return values
+ +

[method]span.span-context: func

+

Get the span-context for this span.

+
Params
+ +
Return values
+ +

[method]span.is-recording: func

+

Returns true when the data provided to this span is captured in some form. If it returns false then any data provided is discarded.

+
Params
+ +
Return values
+ +

[method]span.set-attributes: func

+

Set attributes of this span.

+

If a key already exists for an attribute of the Span it will be overwritten with the corresponding new value.

+
Params
+ +

[method]span.add-event: func

+

Adds an event with the provided name at the curent timestamp.

+

Optionally an alternative timestamp may be provided. You may also provide attributes of this event.

+
Params
+ +

[method]span.add-link: func

+

Associates this span with another.

+
Params
+ +

[method]span.set-status: func

+

Override the default span status, which is unset.

Params
-

span-enter: func

-

Enter a named span.

-

Communicate to the o11y host that the guest is moving into a new span.

+

[method]span.update-name: func

+

Updates the span name.

Params
-

span-tags: func

-

Add tags to the current span.

-

Add a comma-delimited list of tags to the active span.

+

[method]span.end: func

+

Signals that the operation described by this span has now ended.

+

If a timestamp is not provided then it is treated equivalent to passing the current time.

Params
-

span-exit: func

-

Exit the current span.

-

Communicate to the o11y host that the span is complete.

diff --git a/justfile b/justfile index 75f93a8..8992f2f 100644 --- a/justfile +++ b/justfile @@ -13,7 +13,7 @@ _prepare: fi if ! &>/dev/null which wit-bindgen; then - cargo install wit-bindgen-cli@0.16.0 + cargo install wit-bindgen-cli@0.30.0 fi build: diff --git a/wit/deps.lock b/wit/deps.lock index 1e39064..4cd2bb0 100644 --- a/wit/deps.lock +++ b/wit/deps.lock @@ -1,3 +1,13 @@ +[clocks] +url = "https://github.com/WebAssembly/wasi-clocks/archive/main.tar.gz" +sha256 = "ea9d69ee803bc176e23e5268f5e24a2ac485dd1f62a0ab4c748e9d3f901f576f" +sha512 = "5efc22927c46cd56c41e5549ec775561c7fac2ea0d365abc0b55396d9475a7c9f984077a81f84a44a726f1c008fd2fadbffffa4fa53ecd5fbfd05afd379ab428" +deps = ["io"] + +[io] +sha256 = "2a74bd811adc46b5a0f19827ddbde89870e52b17615f4d0873f06fd977250caf" +sha512 = "94624f00c66e66203592cee820f80b1ba91ecdb71f682c154f25eaf71f8d8954197dcb64503bc21e72ed5e812af7eae876df47b7eb727b02db3a74a7ce0aefca" + [logging] url = "https://github.com/WebAssembly/wasi-logging/archive/main.tar.gz" sha256 = "9676b482485bb0fd2751a390374c1108865a096b7037f4b5dbe524f066bfb06e" diff --git a/wit/deps.toml b/wit/deps.toml index 0ccf93a..2bdfe8b 100644 --- a/wit/deps.toml +++ b/wit/deps.toml @@ -1 +1,2 @@ logging = "https://github.com/WebAssembly/wasi-logging/archive/main.tar.gz" +clocks = "https://github.com/WebAssembly/wasi-clocks/archive/main.tar.gz" diff --git a/wit/deps/clocks/monotonic-clock.wit b/wit/deps/clocks/monotonic-clock.wit new file mode 100644 index 0000000..3c24840 --- /dev/null +++ b/wit/deps/clocks/monotonic-clock.wit @@ -0,0 +1,50 @@ +package wasi:clocks@0.2.1; +/// WASI Monotonic Clock is a clock API intended to let users measure elapsed +/// time. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +/// +/// A monotonic clock is a clock which has an unspecified initial value, and +/// successive reads of the clock will produce non-decreasing values. +@since(version = 0.2.0) +interface monotonic-clock { + @since(version = 0.2.0) + use wasi:io/poll@0.2.1.{pollable}; + + /// An instant in time, in nanoseconds. An instant is relative to an + /// unspecified initial value, and can only be compared to instances from + /// the same monotonic-clock. + @since(version = 0.2.0) + type instant = u64; + + /// A duration of time, in nanoseconds. + @since(version = 0.2.0) + type duration = u64; + + /// Read the current value of the clock. + /// + /// The clock is monotonic, therefore calling this function repeatedly will + /// produce a sequence of non-decreasing values. + @since(version = 0.2.0) + now: func() -> instant; + + /// Query the resolution of the clock. Returns the duration of time + /// corresponding to a clock tick. + @since(version = 0.2.0) + resolution: func() -> duration; + + /// Create a `pollable` which will resolve once the specified instant + /// has occurred. + @since(version = 0.2.0) + subscribe-instant: func( + when: instant, + ) -> pollable; + + /// Create a `pollable` that will resolve after the specified duration has + /// elapsed from the time this function is invoked. + @since(version = 0.2.0) + subscribe-duration: func( + when: duration, + ) -> pollable; +} diff --git a/wit/deps/clocks/timezone.wit b/wit/deps/clocks/timezone.wit new file mode 100644 index 0000000..212da66 --- /dev/null +++ b/wit/deps/clocks/timezone.wit @@ -0,0 +1,55 @@ +package wasi:clocks@0.2.1; + +@unstable(feature = clocks-timezone) +interface timezone { + @unstable(feature = clocks-timezone) + use wall-clock.{datetime}; + + /// Return information needed to display the given `datetime`. This includes + /// the UTC offset, the time zone name, and a flag indicating whether + /// daylight saving time is active. + /// + /// If the timezone cannot be determined for the given `datetime`, return a + /// `timezone-display` for `UTC` with a `utc-offset` of 0 and no daylight + /// saving time. + @unstable(feature = clocks-timezone) + display: func(when: datetime) -> timezone-display; + + /// The same as `display`, but only return the UTC offset. + @unstable(feature = clocks-timezone) + utc-offset: func(when: datetime) -> s32; + + /// Information useful for displaying the timezone of a specific `datetime`. + /// + /// This information may vary within a single `timezone` to reflect daylight + /// saving time adjustments. + @unstable(feature = clocks-timezone) + record timezone-display { + /// The number of seconds difference between UTC time and the local + /// time of the timezone. + /// + /// The returned value will always be less than 86400 which is the + /// number of seconds in a day (24*60*60). + /// + /// In implementations that do not expose an actual time zone, this + /// should return 0. + utc-offset: s32, + + /// The abbreviated name of the timezone to display to a user. The name + /// `UTC` indicates Coordinated Universal Time. Otherwise, this should + /// reference local standards for the name of the time zone. + /// + /// In implementations that do not expose an actual time zone, this + /// should be the string `UTC`. + /// + /// In time zones that do not have an applicable name, a formatted + /// representation of the UTC offset may be returned, such as `-04:00`. + name: string, + + /// Whether daylight saving time is active. + /// + /// In implementations that do not expose an actual time zone, this + /// should return false. + in-daylight-saving-time: bool, + } +} diff --git a/wit/deps/clocks/wall-clock.wit b/wit/deps/clocks/wall-clock.wit new file mode 100644 index 0000000..6be069a --- /dev/null +++ b/wit/deps/clocks/wall-clock.wit @@ -0,0 +1,46 @@ +package wasi:clocks@0.2.1; +/// WASI Wall Clock is a clock API intended to let users query the current +/// time. The name "wall" makes an analogy to a "clock on the wall", which +/// is not necessarily monotonic as it may be reset. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +/// +/// A wall clock is a clock which measures the date and time according to +/// some external reference. +/// +/// External references may be reset, so this clock is not necessarily +/// monotonic, making it unsuitable for measuring elapsed time. +/// +/// It is intended for reporting the current date and time for humans. +@since(version = 0.2.0) +interface wall-clock { + /// A time and date in seconds plus nanoseconds. + @since(version = 0.2.0) + record datetime { + seconds: u64, + nanoseconds: u32, + } + + /// Read the current value of the clock. + /// + /// This clock is not monotonic, therefore calling this function repeatedly + /// will not necessarily produce a sequence of non-decreasing values. + /// + /// The returned timestamps represent the number of seconds since + /// 1970-01-01T00:00:00Z, also known as [POSIX's Seconds Since the Epoch], + /// also known as [Unix Time]. + /// + /// The nanoseconds field of the output is always less than 1000000000. + /// + /// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16 + /// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time + @since(version = 0.2.0) + now: func() -> datetime; + + /// Query the resolution of the clock. + /// + /// The nanoseconds field of the output is always less than 1000000000. + @since(version = 0.2.0) + resolution: func() -> datetime; +} diff --git a/wit/deps/clocks/world.wit b/wit/deps/clocks/world.wit new file mode 100644 index 0000000..9251ac6 --- /dev/null +++ b/wit/deps/clocks/world.wit @@ -0,0 +1,11 @@ +package wasi:clocks@0.2.1; + +@since(version = 0.2.0) +world imports { + @since(version = 0.2.0) + import monotonic-clock; + @since(version = 0.2.0) + import wall-clock; + @unstable(feature = clocks-timezone) + import timezone; +} diff --git a/wit/deps/io/error.wit b/wit/deps/io/error.wit new file mode 100644 index 0000000..4ea29c4 --- /dev/null +++ b/wit/deps/io/error.wit @@ -0,0 +1,34 @@ +package wasi:io@0.2.1; + +@since(version = 0.2.0) +interface error { + /// A resource which represents some error information. + /// + /// The only method provided by this resource is `to-debug-string`, + /// which provides some human-readable information about the error. + /// + /// In the `wasi:io` package, this resource is returned through the + /// `wasi:io/streams/stream-error` type. + /// + /// To provide more specific error information, other interfaces may + /// offer functions to "downcast" this error into more specific types. For example, + /// errors returned from streams derived from filesystem types can be described using + /// the filesystem's own error-code type. This is done using the function + /// `wasi:filesystem/types/filesystem-error-code`, which takes a `borrow` + /// parameter and returns an `option`. + /// + /// The set of functions which can "downcast" an `error` into a more + /// concrete type is open. + @since(version = 0.2.0) + resource error { + /// Returns a string that is suitable to assist humans in debugging + /// this error. + /// + /// WARNING: The returned string should not be consumed mechanically! + /// It may change across platforms, hosts, or other implementation + /// details. Parsing this string is a major platform-compatibility + /// hazard. + @since(version = 0.2.0) + to-debug-string: func() -> string; + } +} diff --git a/wit/deps/io/poll.wit b/wit/deps/io/poll.wit new file mode 100644 index 0000000..b25ac72 --- /dev/null +++ b/wit/deps/io/poll.wit @@ -0,0 +1,47 @@ +package wasi:io@0.2.1; + +/// A poll API intended to let users wait for I/O events on multiple handles +/// at once. +@since(version = 0.2.0) +interface poll { + /// `pollable` represents a single I/O event which may be ready, or not. + @since(version = 0.2.0) + resource pollable { + + /// Return the readiness of a pollable. This function never blocks. + /// + /// Returns `true` when the pollable is ready, and `false` otherwise. + @since(version = 0.2.0) + ready: func() -> bool; + + /// `block` returns immediately if the pollable is ready, and otherwise + /// blocks until ready. + /// + /// This function is equivalent to calling `poll.poll` on a list + /// containing only this pollable. + @since(version = 0.2.0) + block: func(); + } + + /// Poll for completion on a set of pollables. + /// + /// This function takes a list of pollables, which identify I/O sources of + /// interest, and waits until one or more of the events is ready for I/O. + /// + /// The result `list` contains one or more indices of handles in the + /// argument list that is ready for I/O. + /// + /// This function traps if either: + /// - the list is empty, or: + /// - the list contains more elements than can be indexed with a `u32` value. + /// + /// A timeout can be implemented by adding a pollable from the + /// wasi-clocks API to the list. + /// + /// This function does not return a `result`; polling in itself does not + /// do any I/O so it doesn't fail. If any of the I/O sources identified by + /// the pollables has an error, it is indicated by marking the source as + /// being ready for I/O. + @since(version = 0.2.0) + poll: func(in: list>) -> list; +} diff --git a/wit/deps/io/streams.wit b/wit/deps/io/streams.wit new file mode 100644 index 0000000..b697e24 --- /dev/null +++ b/wit/deps/io/streams.wit @@ -0,0 +1,286 @@ +package wasi:io@0.2.1; + +/// WASI I/O is an I/O abstraction API which is currently focused on providing +/// stream types. +/// +/// In the future, the component model is expected to add built-in stream types; +/// when it does, they are expected to subsume this API. +@since(version = 0.2.0) +interface streams { + @since(version = 0.2.0) + use error.{error}; + @since(version = 0.2.0) + use poll.{pollable}; + + /// An error for input-stream and output-stream operations. + @since(version = 0.2.0) + variant stream-error { + /// The last operation (a write or flush) failed before completion. + /// + /// More information is available in the `error` payload. + last-operation-failed(error), + /// The stream is closed: no more input will be accepted by the + /// stream. A closed output-stream will return this error on all + /// future operations. + closed + } + + /// An input bytestream. + /// + /// `input-stream`s are *non-blocking* to the extent practical on underlying + /// platforms. I/O operations always return promptly; if fewer bytes are + /// promptly available than requested, they return the number of bytes promptly + /// available, which could even be zero. To wait for data to be available, + /// use the `subscribe` function to obtain a `pollable` which can be polled + /// for using `wasi:io/poll`. + @since(version = 0.2.0) + resource input-stream { + /// Perform a non-blocking read from the stream. + /// + /// When the source of a `read` is binary data, the bytes from the source + /// are returned verbatim. When the source of a `read` is known to the + /// implementation to be text, bytes containing the UTF-8 encoding of the + /// text are returned. + /// + /// This function returns a list of bytes containing the read data, + /// when successful. The returned list will contain up to `len` bytes; + /// it may return fewer than requested, but not more. The list is + /// empty when no bytes are available for reading at this time. The + /// pollable given by `subscribe` will be ready when more bytes are + /// available. + /// + /// This function fails with a `stream-error` when the operation + /// encounters an error, giving `last-operation-failed`, or when the + /// stream is closed, giving `closed`. + /// + /// When the caller gives a `len` of 0, it represents a request to + /// read 0 bytes. If the stream is still open, this call should + /// succeed and return an empty list, or otherwise fail with `closed`. + /// + /// The `len` parameter is a `u64`, which could represent a list of u8 which + /// is not possible to allocate in wasm32, or not desirable to allocate as + /// as a return value by the callee. The callee may return a list of bytes + /// less than `len` in size while more bytes are available for reading. + @since(version = 0.2.0) + read: func( + /// The maximum number of bytes to read + len: u64 + ) -> result, stream-error>; + + /// Read bytes from a stream, after blocking until at least one byte can + /// be read. Except for blocking, behavior is identical to `read`. + @since(version = 0.2.0) + blocking-read: func( + /// The maximum number of bytes to read + len: u64 + ) -> result, stream-error>; + + /// Skip bytes from a stream. Returns number of bytes skipped. + /// + /// Behaves identical to `read`, except instead of returning a list + /// of bytes, returns the number of bytes consumed from the stream. + @since(version = 0.2.0) + skip: func( + /// The maximum number of bytes to skip. + len: u64, + ) -> result; + + /// Skip bytes from a stream, after blocking until at least one byte + /// can be skipped. Except for blocking behavior, identical to `skip`. + @since(version = 0.2.0) + blocking-skip: func( + /// The maximum number of bytes to skip. + len: u64, + ) -> result; + + /// Create a `pollable` which will resolve once either the specified stream + /// has bytes available to read or the other end of the stream has been + /// closed. + /// The created `pollable` is a child resource of the `input-stream`. + /// Implementations may trap if the `input-stream` is dropped before + /// all derived `pollable`s created with this function are dropped. + @since(version = 0.2.0) + subscribe: func() -> pollable; + } + + + /// An output bytestream. + /// + /// `output-stream`s are *non-blocking* to the extent practical on + /// underlying platforms. Except where specified otherwise, I/O operations also + /// always return promptly, after the number of bytes that can be written + /// promptly, which could even be zero. To wait for the stream to be ready to + /// accept data, the `subscribe` function to obtain a `pollable` which can be + /// polled for using `wasi:io/poll`. + /// + /// Dropping an `output-stream` while there's still an active write in + /// progress may result in the data being lost. Before dropping the stream, + /// be sure to fully flush your writes. + @since(version = 0.2.0) + resource output-stream { + /// Check readiness for writing. This function never blocks. + /// + /// Returns the number of bytes permitted for the next call to `write`, + /// or an error. Calling `write` with more bytes than this function has + /// permitted will trap. + /// + /// When this function returns 0 bytes, the `subscribe` pollable will + /// become ready when this function will report at least 1 byte, or an + /// error. + @since(version = 0.2.0) + check-write: func() -> result; + + /// Perform a write. This function never blocks. + /// + /// When the destination of a `write` is binary data, the bytes from + /// `contents` are written verbatim. When the destination of a `write` is + /// known to the implementation to be text, the bytes of `contents` are + /// transcoded from UTF-8 into the encoding of the destination and then + /// written. + /// + /// Precondition: check-write gave permit of Ok(n) and contents has a + /// length of less than or equal to n. Otherwise, this function will trap. + /// + /// returns Err(closed) without writing if the stream has closed since + /// the last call to check-write provided a permit. + @since(version = 0.2.0) + write: func( + contents: list + ) -> result<_, stream-error>; + + /// Perform a write of up to 4096 bytes, and then flush the stream. Block + /// until all of these operations are complete, or an error occurs. + /// + /// This is a convenience wrapper around the use of `check-write`, + /// `subscribe`, `write`, and `flush`, and is implemented with the + /// following pseudo-code: + /// + /// ```text + /// let pollable = this.subscribe(); + /// while !contents.is_empty() { + /// // Wait for the stream to become writable + /// pollable.block(); + /// let Ok(n) = this.check-write(); // eliding error handling + /// let len = min(n, contents.len()); + /// let (chunk, rest) = contents.split_at(len); + /// this.write(chunk ); // eliding error handling + /// contents = rest; + /// } + /// this.flush(); + /// // Wait for completion of `flush` + /// pollable.block(); + /// // Check for any errors that arose during `flush` + /// let _ = this.check-write(); // eliding error handling + /// ``` + @since(version = 0.2.0) + blocking-write-and-flush: func( + contents: list + ) -> result<_, stream-error>; + + /// Request to flush buffered output. This function never blocks. + /// + /// This tells the output-stream that the caller intends any buffered + /// output to be flushed. the output which is expected to be flushed + /// is all that has been passed to `write` prior to this call. + /// + /// Upon calling this function, the `output-stream` will not accept any + /// writes (`check-write` will return `ok(0)`) until the flush has + /// completed. The `subscribe` pollable will become ready when the + /// flush has completed and the stream can accept more writes. + @since(version = 0.2.0) + flush: func() -> result<_, stream-error>; + + /// Request to flush buffered output, and block until flush completes + /// and stream is ready for writing again. + @since(version = 0.2.0) + blocking-flush: func() -> result<_, stream-error>; + + /// Create a `pollable` which will resolve once the output-stream + /// is ready for more writing, or an error has occurred. When this + /// pollable is ready, `check-write` will return `ok(n)` with n>0, or an + /// error. + /// + /// If the stream is closed, this pollable is always ready immediately. + /// + /// The created `pollable` is a child resource of the `output-stream`. + /// Implementations may trap if the `output-stream` is dropped before + /// all derived `pollable`s created with this function are dropped. + subscribe: func() -> pollable; + + /// Write zeroes to a stream. + /// + /// This should be used precisely like `write` with the exact same + /// preconditions (must use check-write first), but instead of + /// passing a list of bytes, you simply pass the number of zero-bytes + /// that should be written. + @since(version = 0.2.0) + write-zeroes: func( + /// The number of zero-bytes to write + len: u64 + ) -> result<_, stream-error>; + + /// Perform a write of up to 4096 zeroes, and then flush the stream. + /// Block until all of these operations are complete, or an error + /// occurs. + /// + /// This is a convenience wrapper around the use of `check-write`, + /// `subscribe`, `write-zeroes`, and `flush`, and is implemented with + /// the following pseudo-code: + /// + /// ```text + /// let pollable = this.subscribe(); + /// while num_zeroes != 0 { + /// // Wait for the stream to become writable + /// pollable.block(); + /// let Ok(n) = this.check-write(); // eliding error handling + /// let len = min(n, num_zeroes); + /// this.write-zeroes(len); // eliding error handling + /// num_zeroes -= len; + /// } + /// this.flush(); + /// // Wait for completion of `flush` + /// pollable.block(); + /// // Check for any errors that arose during `flush` + /// let _ = this.check-write(); // eliding error handling + /// ``` + @since(version = 0.2.0) + blocking-write-zeroes-and-flush: func( + /// The number of zero-bytes to write + len: u64 + ) -> result<_, stream-error>; + + /// Read from one stream and write to another. + /// + /// The behavior of splice is equivalent to: + /// 1. calling `check-write` on the `output-stream` + /// 2. calling `read` on the `input-stream` with the smaller of the + /// `check-write` permitted length and the `len` provided to `splice` + /// 3. calling `write` on the `output-stream` with that read data. + /// + /// Any error reported by the call to `check-write`, `read`, or + /// `write` ends the splice and reports that error. + /// + /// This function returns the number of bytes transferred; it may be less + /// than `len`. + @since(version = 0.2.0) + splice: func( + /// The stream to read from + src: borrow, + /// The number of bytes to splice + len: u64, + ) -> result; + + /// Read from one stream and write to another, with blocking. + /// + /// This is similar to `splice`, except that it blocks until the + /// `output-stream` is ready for writing, and the `input-stream` + /// is ready for reading, before performing the `splice`. + @since(version = 0.2.0) + blocking-splice: func( + /// The stream to read from + src: borrow, + /// The number of bytes to splice + len: u64, + ) -> result; + } +} diff --git a/wit/deps/io/world.wit b/wit/deps/io/world.wit new file mode 100644 index 0000000..6405a4e --- /dev/null +++ b/wit/deps/io/world.wit @@ -0,0 +1,10 @@ +package wasi:io@0.2.1; + +@since(version = 0.2.0) +world imports { + @since(version = 0.2.0) + import streams; + + @since(version = 0.2.0) + import poll; +} diff --git a/wit/observe.wit b/wit/observe.wit deleted file mode 100644 index 066b6fe..0000000 --- a/wit/observe.wit +++ /dev/null @@ -1,25 +0,0 @@ -interface observe { - enum metric-format { - statsd, - } - - /// Emit a metric. - /// - /// A metric has a `format` describing the format of the `name` parameter. - metric: func(format: metric-format, name: string); - - /// Enter a named span. - /// - /// Communicate to the o11y host that the guest is moving into a new span. - span-enter: func(name: string); - - /// Add tags to the current span. - /// - /// Add a comma-delimited list of tags to the active span. - span-tags: func(tags: string); - - /// Exit the current span. - /// - /// Communicate to the o11y host that the span is complete. - span-exit: func(); -} diff --git a/wit/traces.wit b/wit/traces.wit new file mode 100644 index 0000000..cd2b42a --- /dev/null +++ b/wit/traces.wit @@ -0,0 +1,153 @@ +interface traces { + use wasi:clocks/wall-clock@0.2.1.{datetime}; + + /// Represents a unit of work or operation. + resource span { + /// Starts a new span with the given name, parent, and options. + start: static func(name: string, parent: span-parent, options: option) -> span; + + /// Get the `span-context` for this `span`. + span-context: func() -> span-context; + + /// Returns true when the data provided to this `span` is captured in some form. If it returns false then any data provided is discarded. + is-recording: func() -> bool; + + /// Set attributes of this span. + /// + /// If a key already exists for an attribute of the Span it will be overwritten with the corresponding new value. + set-attributes: func(attributes: list); + + /// Adds an event with the provided name at the curent timestamp. + /// + /// Optionally an alternative timestamp may be provided. You may also provide attributes of this event. + add-event: func(name: string, timestamp: option, attributes: option>); + + /// Associates this `span` with another. + add-link: func(link: link); + + /// Override the default `span` status, which is unset. + set-status: func(status: status); + + /// Updates the `span` name. + update-name: func(name: string); + + /// Signals that the operation described by this span has now ended. + /// + /// If a timestamp is not provided then it is treated equivalent to passing the current time. + end: func(timestamp: option); + } + + /// Configuration for starting a `span`. + record start-options { + /// `span-kind` for the new `span`. + span-kind: option, + /// Attributes for the new `span`. + attributes: option>, + /// `link`'s for the new `span`. + links: option>, + /// When the `span` should begin. If this is not provided it defaults to the current time. + timestamp: option, + } + + /// Describes a relationship to another `span`. + record link { + /// Denotes which `span` to link to. + span-context: span-context, + /// Attributes describing the link. + attributes: list, + } + + /// Describes the relationship between the Span, its parents, and its children in a trace. + enum span-kind { + /// Indicates that the span describes a request to some remote service. This span is usually the parent of a remote server span and does not end until the response is received. + client, + /// Indicates that the span covers server-side handling of a synchronous RPC or other remote request. This span is often the child of a remote client span that was expected to wait for a response. + server, + /// Indicates that the span describes the initiators of an asynchronous request. This parent span will often end before the corresponding child consumer span, possibly even before the child span starts. In messaging scenarios with batching, tracing individual messages requires a new producer span per message to be created. + producer, + /// Indicates that the span describes a child of an asynchronous consumer request. + consumer, + /// Default value. Indicates that the span represents an internal operation within an application, as opposed to an operations with remote parents or children. + internal + } + + /// The `status` of a `span`. + variant status { + /// The default status. + unset, + /// The operation has been validated by an Application developer or Operator to have completed successfully. + ok, + /// The operation contains an error with a description. + error(string), + } + + /// Determines how the parent of a `span` should be set. + variant span-parent { + /// The `span`'s parent is the current active span. The current active span is the most recently created and non-closed span. If no spans have been started in the guest this may be a span in the host. + implicit, + /// The `span` is a root span and should have no parent. + is-root, + /// The `span`'s parent is identified by the given `span-context`. + span-context(span-context), + } + + /// A key-value pair describing an attribute. + record key-value { + /// The attribute name. + key: key, + /// The attribute value. + value: value, + } + + /// The key part of attribute `key-value` pairs. + type key = string; + + /// The value part of attribute `key-value` pairs. + variant value { + /// A string value. + %string(string), + /// A boolean value. + %bool(bool), + /// A double precision floating point value. + %float64(float64), + /// A signed 64 bit integer value. + %s64(s64), + /// A homogeneous array of string values. + string-array(list), + /// A homogeneous array of boolean values. + bool-array(list), + /// A homogeneous array of double precision floating point values. + float64-array(list), + /// A homogeneous array of 64 bit integer values. + s64-array(list), + } + + /// Identifying trace information about a span that can be serialized and propagated. + record span-context { + /// The `trace-id` for this `span-context`. + trace-id: trace-id, + /// The `span-id` for this `span-context`. + span-id: span-id, + /// The `trace-flags` for this `span-context`. + trace-flags: trace-flags, + /// Whether this `span-context` was propagated from a remote parent. + is-remote: bool, + /// The `trace-state` for this `span-context`. + trace-state: string, + } + + /// The trace that this `span-context` belongs to. + type trace-id = tuple; + + /// The id of this `span-context`. + type span-id = u64; + + /// Flags that can be set on a `span-context`. + flags trace-flags { + /// Whether the `span` should be sampled or not. + sampled, + } + + /// Carries system-specific configuration data, represented as a list of key-value pairs. `trace-state` allows multiple tracing systems to participate in the same trace. + type trace-state = option>>; +} diff --git a/wit/world.wit b/wit/world.wit index 9d39d7b..c87c4fb 100644 --- a/wit/world.wit +++ b/wit/world.wit @@ -2,5 +2,5 @@ package wasi:observe; world imports { import wasi:logging/logging; - import observe; + import traces; } From bf25635297b8d535ed90b61adaccbbd4aec0de86 Mon Sep 17 00:00:00 2001 From: Caleb Schoepp Date: Thu, 19 Sep 2024 09:06:02 -0600 Subject: [PATCH 2/2] Small changes - Rename traces interface to tracer - Change some types - Move start to the interface rather than being a constructor - Remove abillity to specify arbitrary span context as parent Signed-off-by: Caleb Schoepp --- imports.md | 62 +++++++++++++--------------------- wit/{traces.wit => tracer.wit} | 36 ++++++++++---------- wit/world.wit | 2 +- 3 files changed, 42 insertions(+), 58 deletions(-) rename wit/{traces.wit => tracer.wit} (86%) diff --git a/imports.md b/imports.md index 4fc02fd..8b000fd 100644 --- a/imports.md +++ b/imports.md @@ -4,7 +4,7 @@ @@ -100,7 +100,7 @@ also known as Unix Time.
  • datetime
  • -

    Import interface wasi:observe/traces

    +

    Import interface wasi:observe/tracer


    Types

    type datetime

    @@ -203,16 +203,14 @@ also known as Unix Time.The attribute value. -

    tuple trace-id

    -

    The trace that this span-context belongs to.

    -
    Tuple Fields
    -
      -
    • 0: u64
    • -
    • 1: u64
    • -
    +

    type trace-id

    +

    string

    +

    The trace that this `span-context` belongs to. +

    16 bytes encoded as a hexadecimal string.

    type span-id

    -

    u64

    +

    string

    The id of this `span-context`. +

    8 bytes encoded as a hexadecimal string.

    flags trace-flags

    Flags that can be set on a span-context.

    Flags members
    @@ -220,6 +218,10 @@ also known as Unix Time.sampled:

    Whether the `span` should be sampled or not. +

    type trace-state

    +

    trace-state

    +

    Carries system-specific configuration data, represented as a list of key-value pairs. `trace-state` allows multiple tracing systems to participate in the same trace. +

    If any invalid keys or values are provided then the trace-state will be treated as an empty list.

    record span-context

    Identifying trace information about a span that can be serialized and propagated.

    Record Fields
    @@ -241,7 +243,7 @@ also known as Unix Time.Whether this `span-context` was propagated from a remote parent.
  • -

    trace-state: string

    +

    trace-state: trace-state

    The `trace-state` for this `span-context`.

  • @@ -263,6 +265,10 @@ also known as Unix Time.Record Fields
    • +

      new-root: bool

      +

      Whether this span should act as the root of a new trace. +

    • +
    • span-kind: option<span-kind>

      `span-kind` for the new `span`.

    • @@ -279,38 +285,18 @@ also known as Unix Time.When the `span` should begin. If this is not provided it defaults to the current time.
    -

    variant span-parent

    -

    Determines how the parent of a span should be set.

    -
    Variant Cases
    -
      -
    • -

      implicit

      -

      The `span`'s parent is the current active span. The current active span is the most recently created and non-closed span. If no spans have been started in the guest this may be a span in the host. -

    • -
    • -

      is-root

      -

      The `span` is a root span and should have no parent. -

    • -
    • -

      span-context: span-context

      -

      The `span`'s parent is identified by the given `span-context`. -

    • -
    -

    type trace-state

    -

    option<list<(string, string)>> -Carries system-specific configuration data, represented as a list of key-value pairs. trace-state allows multiple tracing systems to participate in the same trace.

    +

    Functions

    -

    [static]span.start: func

    -

    Starts a new span with the given name, parent, and options.

    +

    start: func

    +

    Starts a new span with the given name and options.

    Params
    Return values

    [method]span.span-context: func

    Get the span-context for this span.

    @@ -373,7 +359,7 @@ Carries system-specific configuration data, represented as a list of key-value p

    [method]span.end: func

    Signals that the operation described by this span has now ended.

    -

    If a timestamp is not provided then it is treated equivalent to passing the current time.

    +

    If a timestamp is not provided then it is treated equivalent to passing the current time. Dropping this resource is the equivalent of calling end(none).

    Params
    • self: borrow<span>
    • diff --git a/wit/traces.wit b/wit/tracer.wit similarity index 86% rename from wit/traces.wit rename to wit/tracer.wit index cd2b42a..0a75904 100644 --- a/wit/traces.wit +++ b/wit/tracer.wit @@ -1,11 +1,11 @@ -interface traces { +interface tracer { use wasi:clocks/wall-clock@0.2.1.{datetime}; + /// Starts a new `span` with the given name and options. + start: func(name: string, options: option) -> span; + /// Represents a unit of work or operation. resource span { - /// Starts a new span with the given name, parent, and options. - start: static func(name: string, parent: span-parent, options: option) -> span; - /// Get the `span-context` for this `span`. span-context: func() -> span-context; @@ -33,12 +33,14 @@ interface traces { /// Signals that the operation described by this span has now ended. /// - /// If a timestamp is not provided then it is treated equivalent to passing the current time. + /// If a timestamp is not provided then it is treated equivalent to passing the current time. Dropping this resource is the equivalent of calling `end(none)`. end: func(timestamp: option); } /// Configuration for starting a `span`. record start-options { + /// Whether this span should act as the root of a new trace. + new-root: bool, /// `span-kind` for the new `span`. span-kind: option, /// Attributes for the new `span`. @@ -81,16 +83,6 @@ interface traces { error(string), } - /// Determines how the parent of a `span` should be set. - variant span-parent { - /// The `span`'s parent is the current active span. The current active span is the most recently created and non-closed span. If no spans have been started in the guest this may be a span in the host. - implicit, - /// The `span` is a root span and should have no parent. - is-root, - /// The `span`'s parent is identified by the given `span-context`. - span-context(span-context), - } - /// A key-value pair describing an attribute. record key-value { /// The attribute name. @@ -133,14 +125,18 @@ interface traces { /// Whether this `span-context` was propagated from a remote parent. is-remote: bool, /// The `trace-state` for this `span-context`. - trace-state: string, + trace-state: trace-state, } /// The trace that this `span-context` belongs to. - type trace-id = tuple; + /// + /// 16 bytes encoded as a hexadecimal string. + type trace-id = string; /// The id of this `span-context`. - type span-id = u64; + /// + /// 8 bytes encoded as a hexadecimal string. + type span-id = string; /// Flags that can be set on a `span-context`. flags trace-flags { @@ -149,5 +145,7 @@ interface traces { } /// Carries system-specific configuration data, represented as a list of key-value pairs. `trace-state` allows multiple tracing systems to participate in the same trace. - type trace-state = option>>; + /// + /// If any invalid keys or values are provided then the `trace-state` will be treated as an empty list. + type trace-state = list>; } diff --git a/wit/world.wit b/wit/world.wit index c87c4fb..5213851 100644 --- a/wit/world.wit +++ b/wit/world.wit @@ -2,5 +2,5 @@ package wasi:observe; world imports { import wasi:logging/logging; - import traces; + import tracer; }