From 58063e71b684a2eae682ad79e81bf9e2ffa25696 Mon Sep 17 00:00:00 2001 From: jack-berg <34418638+jack-berg@users.noreply.github.com> Date: Mon, 27 Mar 2023 09:12:16 -0500 Subject: [PATCH] Ensure logs bridge API doesn't contain implementation details (#3275) Contributes to #3268. @MrAlias did some good work in the metrics API / SDK recently in #3171 and #3067 to ensure that the metrics API spec doesn't contain SDK implementation details. This PR adopts similar language in the Logs Bridge API / SDK documents, which includes breaking out a `noop.md` document. --- CHANGELOG.md | 2 + specification/logs/bridge-api.md | 114 +++++++++++++++---------------- specification/logs/event-api.md | 4 +- specification/logs/noop.md | 57 ++++++++++++++++ specification/logs/sdk.md | 61 ++++++++++++----- 5 files changed, 161 insertions(+), 77 deletions(-) create mode 100644 specification/logs/noop.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fb4679f105..3a8d26f94c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,8 @@ release. ([#3334](https://github.com/open-telemetry/opentelemetry-specification/pull/3334)) - Break out compatibility document on recording trace context in non-OTLP Log Format ([#3331](https://github.com/open-telemetry/opentelemetry-specification/pull/3331)) +- Ensure Logs Bridge API doesn't contain SDK implementation details + ([#3275](https://github.com/open-telemetry/opentelemetry-specification/pull/3275)) ### Resource diff --git a/specification/logs/bridge-api.md b/specification/logs/bridge-api.md index dbbdaaf9791..869714420b3 100644 --- a/specification/logs/bridge-api.md +++ b/specification/logs/bridge-api.md @@ -16,6 +16,8 @@ * [Logger operations](#logger-operations) + [Emit LogRecord](#emit-logrecord) - [LogRecord](#logrecord) +- [Optional and required parameters](#optional-and-required-parameters) +- [Concurrency requirements](#concurrency-requirements) - [Usage](#usage) * [How to Create Log4J Style Appender](#how-to-create-log4j-style-appender) * [Implicit Context Injection](#implicit-context-injection) @@ -53,13 +55,6 @@ Normally, the `LoggerProvider` is expected to be accessed from a central place. Thus, the API SHOULD provide a way to set/register and access a global default `LoggerProvider`. -Notwithstanding any global `LoggerProvider`, some applications may want to or have -to use multiple `LoggerProvider` instances, e.g. to have different configuration -(like [LogRecordProcessors](sdk.md#logrecordprocessor)) for each (and -consequently for the `Logger`s obtained from them), or because it's easier with -dependency injection frameworks. Thus, implementations of `LoggerProvider` SHOULD -allow creating an arbitrary number of instances. - ### LoggerProvider operations The `LoggerProvider` MUST provide the following functions: @@ -70,57 +65,39 @@ The `LoggerProvider` MUST provide the following functions: This API MUST accept the following parameters: -- `name` (required): This name SHOULD uniquely identify the [instrumentation scope](../glossary.md#instrumentation-scope), -such as the [instrumentation library](../glossary.md#instrumentation-library) -(e.g. `io.opentelemetry.contrib.mongodb`), package, module or class name. -If an application or library has built-in OpenTelemetry instrumentation, both -[Instrumented library](../glossary.md#instrumented-library) and -[Instrumentation library](../glossary.md#instrumentation-library) may refer to -the same library. In that scenario, the `name` denotes a module name or component -name within that library or application. In case an invalid name -(null or empty string) is specified, a working Logger implementation MUST be -returned as a fallback rather than returning null or throwing an exception, its -`name` property SHOULD be set to an empty string, and a message reporting that -the specified value is invalid SHOULD be logged. A library implementing the -OpenTelemetry API may also ignore this name and return a default instance for -all calls, if it does not support "named" functionality (e.g. an implementation -which is not even observability-related). A `LoggerProvider` could also return a -no-op `Logger` here if application owners configure the SDK to suppress telemetry -produced by this library. -- `version` (optional): Specifies the version of the instrumentation scope if -the scope has a version (e.g. a library version). Example value: 1.0.0. -- `schema_url` (optional): Specifies the Schema URL that should be recorded in -the emitted telemetry. -- `include_trace_context` (optional): Specifies whether the Trace Context should - automatically be passed on to the `LogRecord`s emitted by the `Logger`. This - SHOULD be true by default. -- `attributes` (optional): Specifies the instrumentation scope attributes to -associate with emitted telemetry. +* `name`: This name uniquely identifies the [instrumentation scope](../glossary.md#instrumentation-scope), + such as the [instrumentation library](../glossary.md#instrumentation-library) + (e.g. `io.opentelemetry.contrib.mongodb`), package, module or class name. + If an application or library has built-in OpenTelemetry instrumentation, both + [Instrumented library](../glossary.md#instrumented-library) and + [Instrumentation library](../glossary.md#instrumentation-library) may refer to + the same library. In that scenario, the `name` denotes a module name or component + name within that library or application. + +* `version` (optional): Specifies the version of the instrumentation scope if + the scope has a version (e.g. a library version). Example value: 1.0.0. + +* `schema_url` (optional): Specifies the Schema URL that should be recorded in + the emitted telemetry. + +* `attributes` (optional): Specifies the instrumentation scope attributes to + associate with emitted telemetry. This API MUST be structured to accept a + variable number of attributes, including none. + +* `include_trace_context` (optional): Specifies whether the Trace Context should + automatically be passed on to the `LogRecord`s emitted by the `Logger`. + If `include_trace_context` is not specified, it SHOULD be `true` by default. `Logger`s are identified by `name`, `version`, and `schema_url` fields. When more than one `Logger` of the same `name`, `version`, and `schema_url` is created, it is unspecified whether or under which conditions the same or different `Logger` instances are returned. It is a user error to create Loggers with different -attributes but the same identity. +`include_trace_context` or `attributes` but the same identity. The term *identical* applied to `Logger`s describes instances where all identifying fields are equal. The term *distinct* applied to `Logger`s describes instances where at least one identifying field has a different value. -Implementations MUST NOT require users to repeatedly obtain a `Logger` again with -the same name+version+schema_url+include_trace_context+attributes -to pick up configuration changes. This can be achieved either by allowing to -work with an outdated configuration or by ensuring that new configuration -applies also to previously returned `Logger`s. - -Note: This could, for example, be implemented by storing any mutable -configuration in the `LoggerProvider` and having `Logger` implementation objects -have a reference to the `LoggerProvider` from which they were obtained. -If configuration must be stored per-Logger (such as disabling a certain `Logger`), -the `Logger` could, for example, do a look-up with its name+version+schema_url+include_trace_context+attributes -in a map in the `LoggerProvider`, or the `LoggerProvider` could maintain a registry -of all returned `Logger`s and actively update their configuration if it changes. - The effect of associating a Schema URL with a `Logger` MUST be that the telemetry emitted using the `Logger` will be associated with the Schema URL, provided that the emitted data format is capable of representing such association. @@ -129,9 +106,6 @@ the emitted data format is capable of representing such association. The `Logger` is responsible for emitting `LogRecord`s. -Note that `Logger`s should not be responsible for configuration. This should be -the responsibility of the `LoggerProvider` instead. - ### Logger operations The `Logger` MUST provide functions to: @@ -151,7 +125,7 @@ This function MAY be named `logRecord`. The API emits [LogRecords](#emit-logrecord) using the `LogRecord` [data model](data-model.md). A function receiving this as an argument MUST be able to set the following -fields: +parameters: - [Timestamp](./data-model.md#field-timestamp) - [Observed Timestamp](./data-model.md#field-observedtimestamp) @@ -162,20 +136,42 @@ fields: - [Body](./data-model.md#field-body) - [Attributes](./data-model.md#field-attributes) +All parameters are optional. + +## Optional and required parameters + +The operations defined include various parameters, some of which are marked +optional. Parameters not marked optional are required. + +For each optional parameter, the API MUST be structured to accept it, but MUST +NOT obligate a user to provide it. + +For each required parameter, the API MUST be structured to obligate a user to +provide it. + +## Concurrency requirements + +For languages which support concurrent execution the Logs Bridge APIs provide +specific guarantees and safeties. + +**LoggerProvider** - all methods are safe to be called concurrently. + +**Logger** - all methods are safe to be called concurrently. + ## Usage ### How to Create Log4J Style Appender -An Appender implementation can be used to allow emitting logs via +An Appender implementation can be used to bridge logs into the [Log SDK](./sdk.md) OpenTelemetry [LogRecordExporters](sdk.md#logrecordexporter). This approach is typically used for applications which are fine with changing the log transport and is [one of the supported](README.md#direct-to-collector) log collection approaches. The Appender implementation will typically acquire a [Logger](#logger) from the -global [LoggerProvider](#loggerprovider) at startup time, then construct -`LogRecord`s for each log received from the application, and then call -[Emit LogRecord](#emit-logrecord). +global [LoggerProvider](#loggerprovider) at startup time, then +call [Emit LogRecord](#emit-logrecord) for `LogRecord`s received from the +application. [Implicit Context Injection](#implicit-context-injection) and [Explicit Context Injection](#explicit-context-injection) describe how an @@ -198,9 +194,9 @@ popular logging library. ### Implicit Context Injection -When Context is implicitly available (e.g. in Java) the log library extension -can rely on automatic context propagation -by [obtaining a Logger](#get-a-logger) with `include_trace_context=true`. +When Context is implicitly available (e.g. in Java) the Appender can rely on +automatic context propagation by [obtaining a Logger](#get-a-logger) +with `include_trace_context=true`. Some log libraries have mechanisms specifically tailored for injecting contextual information into logs, such as MDC in Log4j. When available such mechanisms may diff --git a/specification/logs/event-api.md b/specification/logs/event-api.md index b2e8f687904..f9075d16104 100644 --- a/specification/logs/event-api.md +++ b/specification/logs/event-api.md @@ -50,7 +50,7 @@ instrumentation authors are encouraged to call this API directly. ## EventLogger The `EventLogger` is the entrypoint of the Event API, and is responsible for -emitting `Events` as `LogRecords`. +emitting `Events` as `LogRecord`s. ### EventLogger Operations @@ -64,7 +64,7 @@ on `EventLogger`. **Parameters:** * `logger` - the delegate [Logger](./bridge-api.md#logger) used to emit `Events` - as `LogRecords`. + as `LogRecord`s. * `event_domain` - the domain of emitted events, used to set the `event.domain` attribute. diff --git a/specification/logs/noop.md b/specification/logs/noop.md new file mode 100644 index 00000000000..896e11721f3 --- /dev/null +++ b/specification/logs/noop.md @@ -0,0 +1,57 @@ + + +# Logs Bridge API No-Op Implementation + +**Status**: [Experimental](../document-status.md) + +
+ Table of Contents + + + +- [LoggerProvider](#loggerprovider) + * [Logger Creation](#logger-creation) +- [Logger](#logger) + * [Emit LogRecord](#emit-logrecord) + + + +
+ +Users of OpenTelemetry need a way to disable the API from actually +performing any operations. The No-Op OpenTelemetry API implementation +(henceforth referred to as the No-Op) provides users with this +functionally. It implements the [OpenTelemetry Logs Bridge API](./bridge-api.md) +so that no telemetry is produced and computation resources are minimized. + +All language implementations of OpenTelemetry MUST provide a No-Op. + +The [Logs Bridge API](./bridge-api.md) defines classes with various operations. +All No-Op classes MUST NOT hold configuration or operational state. All No-op +operations MUST accept all defined parameters, MUST NOT validate any arguments +received, and MUST NOT return any non-empty error or log any message. + +## LoggerProvider + +The No-Op MUST allow the creation of multiple `LoggerProviders`s. + +Since all `LoggerProviders`s hold the same empty state, a No-Op MAY +provide the same `LoggerProvider` instances to all creation requests. + +### Logger Creation + +New `Logger` instances are always created with a [LoggerProvider](./bridge-api.md#loggerprovider). +Therefore, `LoggerProvider` MUST allow for the creation of `Logger`s. +All `Logger`s created MUST be an instance of the [No-Op Logger](#logger). + +Since all `Logger`s will hold the same empty state, a `LoggerProvider` MAY +return the same `Logger` instances to all creation requests. + +## Logger + +### Emit LogRecord + +The No-Op `Logger` MUST allow +for [emitting LogRecords](./bridge-api.md#emit-logrecord). diff --git a/specification/logs/sdk.md b/specification/logs/sdk.md index 9480efdd6ff..05d82d68e47 100644 --- a/specification/logs/sdk.md +++ b/specification/logs/sdk.md @@ -8,9 +8,11 @@ - [LoggerProvider](#loggerprovider) + * [LoggerProvider Creation](#loggerprovider-creation) * [Logger Creation](#logger-creation) * [Shutdown](#shutdown) * [ForceFlush](#forceflush) +- [Logger](#logger) - [Additional LogRecord interfaces](#additional-logrecord-interfaces) * [ReadableLogRecord](#readablelogrecord) * [ReadWriteLogRecord](#readwritelogrecord) @@ -33,20 +35,37 @@ +Users of OpenTelemetry need a way for instrumentation interactions with the +OpenTelemetry API to actually produce telemetry. The OpenTelemetry Logging SDK +(henceforth referred to as the SDK) is an implementation of the OpenTelemetry +API that provides users with this functionally. + +All language implementations of OpenTelemetry MUST provide an SDK. + ## LoggerProvider A `LoggerProvider` MUST provide a way to allow a [Resource](../resource/sdk.md) to be specified. If a `Resource` is specified, it SHOULD be associated with all -the `LogRecords` produced by any `Logger` from the `LoggerProvider`. +the `LogRecord`s produced by any `Logger` from the `LoggerProvider`. + +### LoggerProvider Creation + +The SDK SHOULD allow the creation of multiple independent `LoggerProviders`s. ### Logger Creation New `Logger` instances are always created through a `LoggerProvider` -(see [Bridge API](bridge-api.md)). The `name`, `version` (optional), and `attributes` (optional) -supplied to the `LoggerProvider` must be used to create +(see [Bridge API](bridge-api.md)). The `name`, `version` (optional), +`schema_url` (optional), and `attributes` (optional) supplied to +the `LoggerProvider` must be used to create an [`InstrumentationScope`](../glossary.md#instrumentation-scope) instance which is stored on the created `Logger`. +In the case where an invalid `name` (null or empty string) is specified, a +working `Logger` MUST be returned as a fallback rather than returning null or +throwing an exception, its `name` SHOULD keep the original invalid value, and a +message reporting that the specified value is invalid SHOULD be logged. + Configuration (i.e. [LogRecordProcessors](#logrecordprocessor)) MUST be managed solely by the `LoggerProvider` and the SDK MUST provide some way to configure all options that are implemented by the SDK. This MAY be done at the time @@ -54,7 +73,7 @@ of `LoggerProvider` creation if appropriate. The `LoggerProvider` MAY provide methods to update the configuration. If configuration is updated (e.g., adding a `LogRecordProcessor`), the updated -configuration MUST also apply to all already returned `Loggers` (i.e. it MUST +configuration MUST also apply to all already returned `Logger`s (i.e. it MUST NOT matter whether a `Logger` was obtained from the `LoggerProvider` before or after the configuration change). Note: Implementation-wise, this could mean that `Logger` instances have a reference to their `LoggerProvider` and access @@ -87,7 +106,7 @@ registered [LogRecordProcessors](#logrecordprocessor) to immediately export all `ForceFlush` SHOULD provide a way to let the caller know whether it succeeded, failed or timed out. `ForceFlush` SHOULD return some **ERROR** status if there -is an error condition; and if there is no error condition, it should return +is an error condition; and if there is no error condition, it SHOULD return some **NO ERROR** status, language implementations MAY decide how to model **ERROR** and **NO ERROR**. @@ -99,6 +118,15 @@ decide if they want to make the flush timeout configurable. `ForceFlush` MUST invoke `ForceFlush` on all registered [LogRecordProcessors](#logrecordprocessor). +## Logger + +If a `Logger` was obtained with `include_trace_context=true`, the `LogRecord`s +it [emits](./bridge-api.md#emit-logrecord) MUST automatically include the Trace +Context from the active Context, if Context has not been explicitly set. + +Note that `Logger`s should not be responsible for configuration. This should be +the responsibility of the `LoggerProvider` instead. + ## Additional LogRecord interfaces In addition to the [API-level definition for LogRecord](bridge-api.md#logrecord), the @@ -130,7 +158,8 @@ that was added to the `LogRecord` (as with ## LogRecord Limits -`LogRecord` attributes MUST adhere to the [common rules of attribute limits](../common/README.md#attribute-limits). +`LogRecord` attributes MUST adhere to +the [common rules of attribute limits](../common/README.md#attribute-limits). If the SDK implements attribute limits it MUST provide a way to change these limits, via a configuration to the `LoggerProvider`, by allowing users to @@ -161,7 +190,7 @@ To prevent excessive logging, the message MUST be printed at most once per `LogRecordProcessor` is an interface which allows hooks for `LogRecord` emitting. -Built-in processors are responsible for batching and conversion of `LogRecords` +Built-in processors are responsible for batching and conversion of `LogRecord`s to exportable representation and passing batches to exporters. `LogRecordProcessors` can be registered directly on SDK `LoggerProvider` and @@ -173,7 +202,7 @@ MUST allow each pipeline to end with an individual exporter. The SDK MUST allow users to implement and configure custom processors and decorate built-in processors for advanced scenarios such as enriching with -attributes or filtering. +attributes. The following diagram shows `LogRecordProcessor`'s relationship to other components in the SDK: @@ -237,13 +266,13 @@ to make the shutdown timeout configurable. #### ForceFlush -This is a hint to ensure that any tasks associated with `LogRecords` for which +This is a hint to ensure that any tasks associated with `LogRecord`s for which the `LogRecordProcessor` had already received events prior to the call to `ForceFlush` SHOULD be completed as soon as possible, preferably before returning from this method. In particular, if any `LogRecordProcessor` has any associated exporter, it -SHOULD try to call the exporter's `Export` with all `LogRecords` for which this +SHOULD try to call the exporter's `Export` with all `LogRecord`s for which this was not already done and then invoke `ForceFlush` on it. The [built-in LogRecordProcessors](#built-in-processors) MUST do so. If a timeout is specified (see below), the `LogRecordProcessor` MUST prioritize @@ -257,7 +286,7 @@ failed or timed out. `ForceFlush` SHOULD only be called in cases where it is absolutely necessary, such as when using some FaaS providers that may suspend the process after an invocation, but before the `LogRecordProcessor` exports the -completed `LogRecords`. +emitted `LogRecord`s. `ForceFlush` SHOULD complete or abort within some timeout. `ForceFlush` can be implemented as a blocking API or an asynchronous API which notifies the caller @@ -267,7 +296,7 @@ make the flush timeout configurable. ### Built-in processors The standard OpenTelemetry SDK MUST implement both simple and batch processors, -as described below. Other common processing scenarios should be first considered +as described below. Other common processing scenarios SHOULD be first considered for implementation out-of-process in [OpenTelemetry Collector](../overview.md#collector). @@ -280,17 +309,17 @@ finished. **Configurable parameters:** -* `exporter` - the exporter where the `LogRecords` are pushed. +* `exporter` - the exporter where the `LogRecord`s are pushed. #### Batching processor This is an implementation of the `LogRecordProcessor` which create batches -of `LogRecords` and passes the export-friendly `ReadableLogRecord` +of `LogRecord`s and passes the export-friendly `ReadableLogRecord` representations to the configured `LogRecordExporter`. **Configurable parameters:** -* `exporter` - the exporter where the `LogRecords` are pushed. +* `exporter` - the exporter where the `LogRecord`s are pushed. * `maxQueueSize` - the maximum queue size. After the size is reached logs are dropped. The default value is `2048`. * `scheduledDelayMillis` - the delay interval in milliseconds between two @@ -385,7 +414,7 @@ Shuts down the exporter. Called when SDK is shut down. This is an opportunity for exporter to do any cleanup required. Shutdown SHOULD be called only once for each `LogRecordExporter` instance. After -the call to `Shutdown` subsequent calls to `Export` are not allowed and should +the call to `Shutdown` subsequent calls to `Export` are not allowed and SHOULD return a Failure result. `Shutdown` SHOULD NOT block indefinitely (e.g. if it attempts to flush the data