From a31221e8ecbed918e23374f985e528a217274e84 Mon Sep 17 00:00:00 2001 From: Jack Berg Date: Thu, 2 Mar 2023 14:09:48 -0600 Subject: [PATCH] Ensure logs bridge API doesn't contain implementation details --- CHANGELOG.md | 2 + specification/logs/bridge-api.md | 108 +++++++++++++------------------ specification/logs/event-api.md | 4 +- specification/logs/noop.md | 85 ++++++++++++++++++++++++ specification/logs/sdk.md | 64 +++++++++++++----- specification/metrics/api.md | 3 - specification/metrics/noop.md | 6 +- specification/metrics/sdk.md | 9 ++- 8 files changed, 189 insertions(+), 92 deletions(-) create mode 100644 specification/logs/noop.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 58e5986ba9b..0dc0cc34b61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,8 @@ release. ([#3197](https://github.com/open-telemetry/opentelemetry-specification/pull/3197)) - Move event language from log README to event-api. ([#3252](https://github.com/open-telemetry/opentelemetry-specification/pull/3252)) +- 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 320eb3ac458..bfe32614311 100644 --- a/specification/logs/bridge-api.md +++ b/specification/logs/bridge-api.md @@ -57,13 +57,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 Loggers 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: @@ -74,68 +67,53 @@ 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 LogRecords 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 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. +* `version`: Specifies the version of the instrumentation scope if + the scope has a version (e.g. a library version). Example value: 1.0.0. + + Users can provide a `version`, but it is up to their discretion. Therefore, + this API needs to be structured to accept a `version`, but MUST NOT obligate + a user to provide one. +* `schema_url`: Specifies the Schema URL that should be recorded in + the emitted telemetry. + + Users can provide a `schema_url`, but it is up to their discretion. + Therefore, this API needs to be structured to accept a `schema_url`, but MUST + NOT obligate a user to provide one. +* `attributes`: Specifies the instrumentation scope attributes to + associate with emitted telemetry. + + Users can provide attributes to associate with the instrumentation scope, but + it is up to their discretion. Therefore, this API MUST be structured to + accept a variable number of attributes, including none. +* `include_trace_context`: Specifies whether the Trace Context should + automatically be passed on to the `LogRecord`s emitted by the `Logger`. + + Users can provide `include_trace_context`, but it is up to their discretion. + Therefore, this API needs to be structured to accept a `include_trace_context`, but MUST + NOT obligate a user to provide one. Loggers 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 Loggers describes instances where all identifying fields are equal. The term *distinct* applied to Loggers 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 Loggers. - -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. - ## Logger 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: @@ -167,11 +145,15 @@ fields: - [Body](./data-model.md#field-body) - [Attributes](./data-model.md#field-attributes) +User can provide each of these fields, but it is up to their discretion. +Therefore, this API needs to be structured to accept these fields, but MUST NOT +obligate a user to provide them. + ## 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 @@ -179,12 +161,12 @@ approaches. The Appender implementation will typically acquire a [Logger](#logger) from the global [LoggerProvider](#loggerprovider) at startup time, then -call [Emit LogRecord](#emit-logrecord) for `LogRecords` received from the +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 -Appender injects `TraceContext` into `LogRecords`. +Appender injects `TraceContext` into `LogRecord`s. ![Appender](img/appender.png) @@ -203,9 +185,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 mechanism specifically tailored for injecting contextual information into logs, such as MDC in Log4j. When available such mechanisms may @@ -221,13 +203,13 @@ log appender are executed on different threads. In languages where the Context must be provided explicitly (e.g. Go) the end user must capture the context and explicitly pass it to the logging subsystem in -order for `TraceContext` to be recorded in `LogRecords` +order for `TraceContext` to be recorded in `LogRecord`s Support for OpenTelemetry for logging libraries in these languages typically can be implemented in the form of logger wrappers that can capture the context once, when the span is created and then use the wrapped logger to execute log statements in a normal way. The wrapper will be responsible for injecting the -captured context in the `LogRecords`. +captured context in the `LogRecord`s. This specification does not define how exactly it is achieved since the actual mechanism depends on the language and the particular logging library used. In diff --git a/specification/logs/event-api.md b/specification/logs/event-api.md index 972aae938b4..07963271a4c 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..f51f14e896e --- /dev/null +++ b/specification/logs/noop.md @@ -0,0 +1,85 @@ + + +# 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. + +## LoggerProvider + +The No-Op MUST allow the creation of multiple `LoggerProviders`s. + +The `LoggerProviders`s created by the No-Op needs to hold as small a memory +footprint as possible. Therefore, all `LoggerProviders`s created MUST NOT +hold configuration or operational state. + +Since all `LoggerProviders`s hold the same empty state, a No-Op MAY +provide the same `LoggerProvider` instances to all creation requests. + +The No-Op is used by OpenTelemetry users to disable OpenTelemetry +computation overhead and eliminate OpenTelemetry related output. For +this reason, the `LoggerProvider` MUST NOT return a non-empty error or log +any message for any operations it performs. + +All operations a `LoggerProvider` provides MUST be safe to be run +concurrently. + +### 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. + +[The API specifies multiple parameters](./bridge-api.md#loggerprovider) that +need to be accepted by the creation operation. The `LoggerProvider` MUST +accept these parameters. However, the `LoggerProvider` MUST NOT validate +any argument it receives. + +## Logger + +A `Logger` is always created by a `LoggerProvider`. The No-Op MUST NOT provide +a way for a user to create a `Logger` other than by a No-Op `LoggerProvider`. + +The `Logger`s created by the No-Op need to hold as small a memory +footprint as possible. Therefore, all `Logger`s created MUST NOT hold +configuration or operational state. + +The `Logger` MUST NOT return a non-empty error or log any message for any +operations it performs. + +All operations a `Logger` provides MUST be safe to be run concurrently. + +### Emit LogRecord + +The No-Op `Logger` MUST allow +for [emitting LogRecords](./bridge-api.md#emit-logrecord). + +[The API specifies multiple parameters](./bridge-api.md#emit-logrecord) that +need to be accepted by `emit`. The `Logger` MUST accept these parameters. +However, the `Logger` MUST NOT validate any argument it receives. diff --git a/specification/logs/sdk.md b/specification/logs/sdk.md index 9480efdd6ff..25d4925513d 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,40 @@ +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. + +If the case where `include_trace_context` is not specified, its default value +SHOULD be true by default. + 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 +76,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 +109,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 +121,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 +161,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 +193,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 +205,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 +269,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 +289,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 +299,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 +312,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 +417,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 diff --git a/specification/metrics/api.md b/specification/metrics/api.md index 5bac1968ec0..6e1a37e6a44 100644 --- a/specification/metrics/api.md +++ b/specification/metrics/api.md @@ -156,9 +156,6 @@ at least one identifying field has a different value. The meter is responsible for creating [Instruments](#instrument). -Note: `Meter` SHOULD NOT be responsible for the configuration. This should be -the responsibility of the `MeterProvider` instead. - ### Meter operations The `Meter` MUST provide functions to create new [Instruments](#instrument): diff --git a/specification/metrics/noop.md b/specification/metrics/noop.md index 94e72b70325..212dd0d7c12 100644 --- a/specification/metrics/noop.md +++ b/specification/metrics/noop.md @@ -2,7 +2,7 @@ linkTitle: noop ---> -# Metrics No-Op API Implementation +# Metrics API No-Op Implementation **Status**: [Experimental](../document-status.md) @@ -41,8 +41,8 @@ linkTitle: noop 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 API so that no telemetry -is produced and computation resources are minimized. +functionally. It implements the [OpenTelemetry Metrics API](./api.md) so that no +telemetry is produced and computation resources are minimized. All language implementations of OpenTelemetry MUST provide a No-Op. diff --git a/specification/metrics/sdk.md b/specification/metrics/sdk.md index c3776142538..593d5d03b3c 100644 --- a/specification/metrics/sdk.md +++ b/specification/metrics/sdk.md @@ -70,7 +70,7 @@ linkTitle: SDK Users of OpenTelemetry need a way for instrumentation interactions with the -OpenTelemetry API to actually produce telemetry. The OpenTelemetry SDK +OpenTelemetry API to actually produce telemetry. The OpenTelemetry Metrics SDK (henceforth referred to as the SDK) is an implementation of the OpenTelemetry API that provides users with this functionally. @@ -104,10 +104,6 @@ working Meter 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. -When a Schema URL is passed as an argument when creating a `Meter` the emitted -telemetry for that `Meter` MUST be associated with the Schema URL, provided -that the emitted data format is capable of representing such association. - Configuration (i.e., [MetricExporters](#metricexporter), [MetricReaders](#metricreader) and [Views](#view)) MUST be managed solely by the `MeterProvider` and the SDK MUST provide a way to configure all options that are @@ -586,6 +582,9 @@ given instrument before starting a subsequent round of collection. Distinct meters MUST be treated as separate namespaces for the purposes of detecting [duplicate instrument registrations](#duplicate-instrument-registration). +Note: `Meter` SHOULD NOT be responsible for the configuration. This should be +the responsibility of the `MeterProvider` instead. + ### Duplicate instrument registration When more than one Instrument of the same `name` is created for identical