Skip to content

Commit

Permalink
Ensure logs bridge API doesn't contain implementation details
Browse files Browse the repository at this point in the history
  • Loading branch information
jack-berg committed Mar 2, 2023
1 parent ec84e5d commit a31221e
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 92 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
108 changes: 45 additions & 63 deletions specification/logs/bridge-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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:
Expand Down Expand Up @@ -167,24 +145,28 @@ 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
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)

Expand All @@ -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
Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions specification/logs/event-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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.

Expand Down
85 changes: 85 additions & 0 deletions specification/logs/noop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<!--- Hugo front matter used to generate the website version of this page:
linkTitle: noop
--->

# Logs Bridge API No-Op Implementation

**Status**: [Experimental](../document-status.md)

<details>
<summary> Table of Contents </summary>

<!-- toc -->

- [LoggerProvider](#loggerprovider)
* [Logger Creation](#logger-creation)
- [Logger](#logger)
* [Emit LogRecord](#emit-logrecord)

<!-- tocstop -->

</details>

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.
Loading

0 comments on commit a31221e

Please sign in to comment.