mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-12-02 19:18:07 -06:00
[chore] Simplify the OTEL setup (#4110)
# Description This simplifies our OTEL setup by: * Getting rid of some deprecated things. * Using `autoexport` and letting things get configured by the `OTEL_` environment variables. * Removing all the unnecessary config options. ## Checklist Please put an x inside each checkbox to indicate that you've read and followed it: `[ ]` -> `[x]` If this is a documentation change, only the first checkbox must be filled (you can delete the others if you want). - [x] I/we have read the [GoToSocial contribution guidelines](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/CONTRIBUTING.md). - [x] I/we have discussed the proposed changes already, either in an issue on the repository, or in the Matrix chat. - [x] I/we have not leveraged AI to create the proposed changes. - [x] I/we have performed a self-review of added code. - [x] I/we have written code that is legible and maintainable by others. - [ ] I/we have commented the added code, particularly in hard-to-understand areas. - [x] I/we have made any necessary changes to documentation. - [ ] I/we have added tests that cover new code. - [x] I/we have run tests and they pass locally with the changes. - [x] I/we have run `go fmt ./...` and `golangci-lint run`. Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4110 Reviewed-by: tobi <kipvandenbos@noreply.codeberg.org> Co-authored-by: Daenney <daenney@noreply.codeberg.org> Co-committed-by: Daenney <daenney@noreply.codeberg.org>
This commit is contained in:
parent
ad71066973
commit
ecbdc4227b
145 changed files with 21740 additions and 1319 deletions
634
vendor/go.opentelemetry.io/otel/log/DESIGN.md
generated
vendored
Normal file
634
vendor/go.opentelemetry.io/otel/log/DESIGN.md
generated
vendored
Normal file
|
|
@ -0,0 +1,634 @@
|
|||
# Logs API
|
||||
|
||||
## Abstract
|
||||
|
||||
`go.opentelemetry.io/otel/log` provides
|
||||
[Logs API](https://opentelemetry.io/docs/specs/otel/logs/api/).
|
||||
|
||||
The prototype was created in
|
||||
[#4725](https://github.com/open-telemetry/opentelemetry-go/pull/4725).
|
||||
|
||||
## Background
|
||||
|
||||
The key challenge is to create a performant API compliant with the [specification](https://opentelemetry.io/docs/specs/otel/logs/api/)
|
||||
with an intuitive and user friendly design.
|
||||
Performance is seen as one of the most important characteristics of logging libraries in Go.
|
||||
|
||||
## Design
|
||||
|
||||
This proposed design aims to:
|
||||
|
||||
- be specification compliant,
|
||||
- be similar to Trace and Metrics API,
|
||||
- take advantage of both OpenTelemetry and `slog` experience to achieve acceptable performance.
|
||||
|
||||
### Module structure
|
||||
|
||||
The API is published as a single `go.opentelemetry.io/otel/log` Go module.
|
||||
|
||||
The package structure is similar to Trace API and Metrics API.
|
||||
The Go module consists of the following packages:
|
||||
|
||||
- `go.opentelemetry.io/otel/log`
|
||||
- `go.opentelemetry.io/otel/log/embedded`
|
||||
- `go.opentelemetry.io/otel/log/logtest`
|
||||
- `go.opentelemetry.io/otel/log/noop`
|
||||
|
||||
Rejected alternative:
|
||||
|
||||
- [Reuse slog](#reuse-slog)
|
||||
|
||||
### LoggerProvider
|
||||
|
||||
The [`LoggerProvider` abstraction](https://opentelemetry.io/docs/specs/otel/logs/api/#loggerprovider)
|
||||
is defined as `LoggerProvider` interface in [provider.go](provider.go).
|
||||
|
||||
The specification may add new operations to `LoggerProvider`.
|
||||
The interface may have methods added without a package major version bump.
|
||||
This embeds `embedded.LoggerProvider` to help inform an API implementation
|
||||
author about this non-standard API evolution.
|
||||
This approach is already used in Trace API and Metrics API.
|
||||
|
||||
#### LoggerProvider.Logger
|
||||
|
||||
The `Logger` method implements the [`Get a Logger` operation](https://opentelemetry.io/docs/specs/otel/logs/api/#get-a-logger).
|
||||
|
||||
The required `name` parameter is accepted as a `string` method argument.
|
||||
|
||||
The `LoggerOption` options are defined to support optional parameters.
|
||||
|
||||
Implementation requirements:
|
||||
|
||||
- The [specification requires](https://opentelemetry.io/docs/specs/otel/logs/api/#concurrency-requirements)
|
||||
the method to be safe to be called concurrently.
|
||||
|
||||
- The method should use some default name if the passed name is empty
|
||||
in order to meet the [specification's SDK requirement](https://opentelemetry.io/docs/specs/otel/logs/sdk/#logger-creation)
|
||||
to return a working logger when an invalid name is passed
|
||||
as well as to resemble the behavior of getting tracers and meters.
|
||||
|
||||
`Logger` can be extended by adding new `LoggerOption` options
|
||||
and adding new exported fields to the `LoggerConfig` struct.
|
||||
This design is already used in Trace API for getting tracers
|
||||
and in Metrics API for getting meters.
|
||||
|
||||
Rejected alternative:
|
||||
|
||||
- [Passing struct as parameter to LoggerProvider.Logger](#passing-struct-as-parameter-to-loggerproviderlogger).
|
||||
|
||||
### Logger
|
||||
|
||||
The [`Logger` abstraction](https://opentelemetry.io/docs/specs/otel/logs/api/#logger)
|
||||
is defined as `Logger` interface in [logger.go](logger.go).
|
||||
|
||||
The specification may add new operations to `Logger`.
|
||||
The interface may have methods added without a package major version bump.
|
||||
This embeds `embedded.Logger` to help inform an API implementation
|
||||
author about this non-standard API evolution.
|
||||
This approach is already used in Trace API and Metrics API.
|
||||
|
||||
### Logger.Emit
|
||||
|
||||
The `Emit` method implements the [`Emit a LogRecord` operation](https://opentelemetry.io/docs/specs/otel/logs/api/#emit-a-logrecord).
|
||||
|
||||
[`Context` associated with the `LogRecord`](https://opentelemetry.io/docs/specs/otel/context/)
|
||||
is accepted as a `context.Context` method argument.
|
||||
|
||||
Calls to `Emit` are supposed to be on the hot path.
|
||||
Therefore, in order to reduce the number of heap allocations,
|
||||
the [`LogRecord` abstraction](https://opentelemetry.io/docs/specs/otel/logs/api/#emit-a-logrecord),
|
||||
is defined as `Record` struct in [record.go](record.go).
|
||||
|
||||
[`Timestamp`](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-timestamp)
|
||||
is accessed using following methods:
|
||||
|
||||
```go
|
||||
func (r *Record) Timestamp() time.Time
|
||||
func (r *Record) SetTimestamp(t time.Time)
|
||||
```
|
||||
|
||||
[`ObservedTimestamp`](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-observedtimestamp)
|
||||
is accessed using following methods:
|
||||
|
||||
```go
|
||||
func (r *Record) ObservedTimestamp() time.Time
|
||||
func (r *Record) SetObservedTimestamp(t time.Time)
|
||||
```
|
||||
|
||||
[`EventName`](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-eventname)
|
||||
is accessed using following methods:
|
||||
|
||||
```go
|
||||
func (r *Record) EventName() string
|
||||
func (r *Record) SetEventName(s string)
|
||||
```
|
||||
|
||||
[`SeverityNumber`](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-severitynumber)
|
||||
is accessed using following methods:
|
||||
|
||||
```go
|
||||
func (r *Record) Severity() Severity
|
||||
func (r *Record) SetSeverity(s Severity)
|
||||
```
|
||||
|
||||
`Severity` type is defined in [severity.go](severity.go).
|
||||
The constants are are based on
|
||||
[Displaying Severity recommendation](https://opentelemetry.io/docs/specs/otel/logs/data-model/#displaying-severity).
|
||||
Additionally, `Severity[Level]` constants are defined to make the API more readable and user friendly.
|
||||
|
||||
[`SeverityText`](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-severitytext)
|
||||
is accessed using following methods:
|
||||
|
||||
```go
|
||||
func (r *Record) SeverityText() string
|
||||
func (r *Record) SetSeverityText(s string)
|
||||
```
|
||||
|
||||
[`Body`](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-body)
|
||||
is accessed using following methods:
|
||||
|
||||
```go
|
||||
func (r *Record) Body() Value
|
||||
func (r *Record) SetBody(v Value)
|
||||
```
|
||||
|
||||
[Log record attributes](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-attributes)
|
||||
are accessed using following methods:
|
||||
|
||||
```go
|
||||
func (r *Record) WalkAttributes(f func(KeyValue) bool)
|
||||
func (r *Record) AddAttributes(attrs ...KeyValue)
|
||||
```
|
||||
|
||||
`Record` has a `AttributesLen` method that returns
|
||||
the number of attributes to allow slice preallocation
|
||||
when converting records to a different representation:
|
||||
|
||||
```go
|
||||
func (r *Record) AttributesLen() int
|
||||
```
|
||||
|
||||
The records attributes design and implementation is based on
|
||||
[`slog.Record`](https://pkg.go.dev/log/slog#Record).
|
||||
It allows achieving high-performance access and manipulation of the attributes
|
||||
while keeping the API user friendly.
|
||||
It relieves the user from making his own improvements
|
||||
for reducing the number of allocations when passing attributes.
|
||||
|
||||
The abstractions described in
|
||||
[the specification](https://opentelemetry.io/docs/specs/otel/logs/#new-first-party-application-logs)
|
||||
are defined in [keyvalue.go](keyvalue.go).
|
||||
|
||||
`Value` is representing `any`.
|
||||
`KeyValue` is representing a key(string)-value(`any`) pair.
|
||||
|
||||
`Kind` is an enumeration used for specifying the underlying value type.
|
||||
`KindEmpty` is used for an empty (zero) value.
|
||||
`KindBool` is used for boolean value.
|
||||
`KindFloat64` is used for a double precision floating point (IEEE 754-1985) value.
|
||||
`KindInt64` is used for a signed integer value.
|
||||
`KindString` is used for a string value.
|
||||
`KindBytes` is used for a slice of bytes (in spec: A byte array).
|
||||
`KindSlice` is used for a slice of values (in spec: an array (a list) of any values).
|
||||
`KindMap` is used for a slice of key-value pairs (in spec: `map<string, any>`).
|
||||
|
||||
These types are defined in `go.opentelemetry.io/otel/log` package
|
||||
as they are tightly coupled with the API and different from common attributes.
|
||||
|
||||
The internal implementation of `Value` is based on
|
||||
[`slog.Value`](https://pkg.go.dev/log/slog#Value)
|
||||
and the API is mostly inspired by
|
||||
[`attribute.Value`](https://pkg.go.dev/go.opentelemetry.io/otel/attribute#Value).
|
||||
The benchmarks[^1] show that the implementation is more performant than
|
||||
[`attribute.Value`](https://pkg.go.dev/go.opentelemetry.io/otel/attribute#Value).
|
||||
|
||||
The value accessors (`func (v Value) As[Kind]` methods) must not panic,
|
||||
as it would violate the [specification](https://opentelemetry.io/docs/specs/otel/error-handling/):
|
||||
|
||||
> API methods MUST NOT throw unhandled exceptions when used incorrectly by end
|
||||
> users. The API and SDK SHOULD provide safe defaults for missing or invalid
|
||||
> arguments. [...] Whenever the library suppresses an error that would otherwise
|
||||
> have been exposed to the user, the library SHOULD log the error using
|
||||
> language-specific conventions.
|
||||
|
||||
Therefore, the value accessors should return a zero value
|
||||
and log an error when a bad accessor is called.
|
||||
|
||||
The `Severity`, `Kind`, `Value`, `KeyValue` may implement
|
||||
the [`fmt.Stringer`](https://pkg.go.dev/fmt#Stringer) interface.
|
||||
However, it is not needed for the first stable release
|
||||
and the `String` methods can be added later.
|
||||
|
||||
The caller must not subsequently mutate the record passed to `Emit`.
|
||||
This would allow the implementation to not clone the record,
|
||||
but simply retain, modify or discard it.
|
||||
The implementation may still choose to clone the record or copy its attributes
|
||||
if it needs to retain or modify it,
|
||||
e.g. in case of asynchronous processing to eliminate the possibility of data races,
|
||||
because the user can technically reuse the record and add new attributes
|
||||
after the call (even when the documentation says that the caller must not do it).
|
||||
|
||||
Implementation requirements:
|
||||
|
||||
- The [specification requires](https://opentelemetry.io/docs/specs/otel/logs/api/#concurrency-requirements)
|
||||
the method to be safe to be called concurrently.
|
||||
|
||||
- The method must not interrupt the record processing if the context is canceled
|
||||
per ["ignoring context cancellation" guideline](../CONTRIBUTING.md#ignoring-context-cancellation).
|
||||
|
||||
- The [specification requires](https://opentelemetry.io/docs/specs/otel/logs/api/#emit-a-logrecord)
|
||||
use the current time as observed timestamp if the passed is empty.
|
||||
|
||||
- The method should handle the trace context passed via `ctx` argument in order to meet the
|
||||
[specification's SDK requirement](https://opentelemetry.io/docs/specs/otel/logs/sdk/#readablelogrecord)
|
||||
to populate the trace context fields from the resolved context.
|
||||
|
||||
`Emit` can be extended by adding new exported fields to the `Record` struct.
|
||||
|
||||
Rejected alternatives:
|
||||
|
||||
- [Record as interface](#record-as-interface)
|
||||
- [Options as parameter to Logger.Emit](#options-as-parameter-to-loggeremit)
|
||||
- [Passing record as pointer to Logger.Emit](#passing-record-as-pointer-to-loggeremit)
|
||||
- [Logger.WithAttributes](#loggerwithattributes)
|
||||
- [Record attributes as slice](#record-attributes-as-slice)
|
||||
- [Use any instead of defining Value](#use-any-instead-of-defining-value)
|
||||
- [Severity type encapsulating number and text](#severity-type-encapsulating-number-and-text)
|
||||
- [Reuse attribute package](#reuse-attribute-package)
|
||||
- [Mix receiver types for Record](#mix-receiver-types-for-record)
|
||||
- [Add XYZ method to Logger](#add-xyz-method-to-logger)
|
||||
- [Rename KeyValue to Attr](#rename-keyvalue-to-attr)
|
||||
|
||||
### Logger.Enabled
|
||||
|
||||
The `Enabled` method implements the [`Enabled` operation](https://opentelemetry.io/docs/specs/otel/logs/api/#enabled).
|
||||
|
||||
[`Context` associated with the `LogRecord`](https://opentelemetry.io/docs/specs/otel/context/)
|
||||
is accepted as a `context.Context` method argument.
|
||||
|
||||
Calls to `Enabled` are supposed to be on the hot path and the list of arguments
|
||||
can be extendend in future. Therefore, in order to reduce the number of heap
|
||||
allocations and make it possible to handle new arguments, `Enabled` accepts
|
||||
a `EnabledParameters` struct, defined in [logger.go](logger.go), as the second
|
||||
method argument.
|
||||
|
||||
The `EnabledParameters` uses fields, instead of getters and setters, to allow
|
||||
simpler usage which allows configuring the `EnabledParameters` in the same line
|
||||
where `Enabled` is called.
|
||||
|
||||
### noop package
|
||||
|
||||
The `go.opentelemetry.io/otel/log/noop` package provides
|
||||
[Logs API No-Op Implementation](https://opentelemetry.io/docs/specs/otel/logs/noop/).
|
||||
|
||||
### Trace context correlation
|
||||
|
||||
The bridge implementation should do its best to pass
|
||||
the `ctx` containing the trace context from the caller
|
||||
so it can later be passed via `Logger.Emit`.
|
||||
|
||||
It is not expected that users (caller or bridge implementation) reconstruct
|
||||
a `context.Context`. Reconstructing a `context.Context` with
|
||||
[`trace.ContextWithSpanContext`](https://pkg.go.dev/go.opentelemetry.io/otel/trace#ContextWithSpanContext)
|
||||
and [`trace.NewSpanContext`](https://pkg.go.dev/go.opentelemetry.io/otel/trace#NewSpanContext)
|
||||
would usually involve more memory allocations.
|
||||
|
||||
The logging libraries which have recording methods that accepts `context.Context`,
|
||||
such us [`slog`](https://pkg.go.dev/log/slog),
|
||||
[`logrus`](https://pkg.go.dev/github.com/sirupsen/logrus),
|
||||
[`zerolog`](https://pkg.go.dev/github.com/rs/zerolog),
|
||||
makes passing the trace context trivial.
|
||||
|
||||
However, some libraries do not accept a `context.Context` in their recording methods.
|
||||
Structured logging libraries,
|
||||
such as [`logr`](https://pkg.go.dev/github.com/go-logr/logr)
|
||||
and [`zap`](https://pkg.go.dev/go.uber.org/zap),
|
||||
offer passing `any` type as a log attribute/field.
|
||||
Therefore, their bridge implementations can define a "special" log attributes/field
|
||||
that will be used to capture the trace context.
|
||||
|
||||
[The prototype](https://github.com/open-telemetry/opentelemetry-go/pull/4725)
|
||||
has bridge implementations that handle trace context correlation efficiently.
|
||||
|
||||
## Benchmarking
|
||||
|
||||
The benchmarks take inspiration from [`slog`](https://pkg.go.dev/log/slog),
|
||||
because for the Go team it was also critical to create API that would be fast
|
||||
and interoperable with existing logging packages.[^2][^3]
|
||||
|
||||
The benchmark results can be found in [the prototype](https://github.com/open-telemetry/opentelemetry-go/pull/4725).
|
||||
|
||||
## Rejected alternatives
|
||||
|
||||
### Reuse slog
|
||||
|
||||
The API must not be coupled to [`slog`](https://pkg.go.dev/log/slog),
|
||||
nor any other logging library.
|
||||
|
||||
The API needs to evolve orthogonally to `slog`.
|
||||
|
||||
`slog` is not compliant with the [Logs API](https://opentelemetry.io/docs/specs/otel/logs/api/).
|
||||
and we cannot expect the Go team to make `slog` compliant with it.
|
||||
|
||||
The interoperability can be achieved using [a log bridge](https://opentelemetry.io/docs/specs/otel/glossary/#log-appender--bridge).
|
||||
|
||||
You can read more about OpenTelemetry Logs design on [opentelemetry.io](https://opentelemetry.io/docs/concepts/signals/logs/).
|
||||
|
||||
### Record as interface
|
||||
|
||||
`Record` is defined as a `struct` because of the following reasons.
|
||||
|
||||
Log record is a value object without any behavior.
|
||||
It is used as data input for Logger methods.
|
||||
|
||||
The log record resembles the instrument config structs like [metric.Float64CounterConfig](https://pkg.go.dev/go.opentelemetry.io/otel/metric#Float64CounterConfig).
|
||||
|
||||
Using `struct` instead of `interface` improves the performance as e.g.
|
||||
indirect calls are less optimized,
|
||||
usage of interfaces tend to increase heap allocations.[^3]
|
||||
|
||||
### Options as parameter to Logger.Emit
|
||||
|
||||
One of the initial ideas was to have:
|
||||
|
||||
```go
|
||||
type Logger interface{
|
||||
embedded.Logger
|
||||
Emit(ctx context.Context, options ...RecordOption)
|
||||
}
|
||||
```
|
||||
|
||||
The main reason was that design would be similar
|
||||
to the [Meter API](https://pkg.go.dev/go.opentelemetry.io/otel/metric#Meter)
|
||||
for creating instruments.
|
||||
|
||||
However, passing `Record` directly, instead of using options,
|
||||
is more performant as it reduces heap allocations.[^4]
|
||||
|
||||
Another advantage of passing `Record` is that API would not have functions like `NewRecord(options...)`,
|
||||
which would be used by the SDK and not by the users.
|
||||
|
||||
Finally, the definition would be similar to [`slog.Handler.Handle`](https://pkg.go.dev/log/slog#Handler)
|
||||
that was designed to provide optimization opportunities.[^2]
|
||||
|
||||
### Passing record as pointer to Logger.Emit
|
||||
|
||||
So far the benchmarks do not show differences that would
|
||||
favor passing the record via pointer (and vice versa).
|
||||
|
||||
Passing via value feels safer because of the following reasons.
|
||||
|
||||
The user would not be able to pass `nil`.
|
||||
Therefore, it reduces the possibility to have a nil pointer dereference.
|
||||
|
||||
It should reduce the possibility of a heap allocation.
|
||||
|
||||
It follows the design of [`slog.Handler`](https://pkg.go.dev/log/slog#Handler).
|
||||
|
||||
If follows one of Google's Go Style Decisions
|
||||
to prefer [passing values](https://google.github.io/styleguide/go/decisions#pass-values).
|
||||
|
||||
### Passing struct as parameter to LoggerProvider.Logger
|
||||
|
||||
Similarly to `Logger.Emit`, we could have something like:
|
||||
|
||||
```go
|
||||
type LoggerProvider interface{
|
||||
embedded.LoggerProvider
|
||||
Logger(name string, config LoggerConfig)
|
||||
}
|
||||
```
|
||||
|
||||
The drawback of this idea would be that this would be
|
||||
a different design from Trace and Metrics API.
|
||||
|
||||
The performance of acquiring a logger is not as critical
|
||||
as the performance of emitting a log record. While a single
|
||||
HTTP/RPC handler could write hundreds of logs, it should not
|
||||
create a new logger for each log entry.
|
||||
The bridge implementation should reuse loggers whenever possible.
|
||||
|
||||
### Logger.WithAttributes
|
||||
|
||||
We could add `WithAttributes` to the `Logger` interface.
|
||||
Then `Record` could be a simple struct with only exported fields.
|
||||
The idea was that the SDK would implement the performance improvements
|
||||
instead of doing it in the API.
|
||||
This would allow having different optimization strategies.
|
||||
|
||||
During the analysis[^5], it occurred that the main problem of this proposal
|
||||
is that the variadic slice passed to an interface method is always heap allocated.
|
||||
|
||||
Moreover, the logger returned by `WithAttribute` was allocated on the heap.
|
||||
|
||||
Lastly, the proposal was not specification compliant.
|
||||
|
||||
### Record attributes as slice
|
||||
|
||||
One of the proposals[^6] was to have `Record` as a simple struct:
|
||||
|
||||
```go
|
||||
type Record struct {
|
||||
Timestamp time.Time
|
||||
ObservedTimestamp time.Time
|
||||
EventName string
|
||||
Severity Severity
|
||||
SeverityText string
|
||||
Body Value
|
||||
Attributes []KeyValue
|
||||
}
|
||||
```
|
||||
|
||||
The bridge implementations could use [`sync.Pool`](https://pkg.go.dev/sync#Pool)
|
||||
for reducing the number of allocations when passing attributes.
|
||||
|
||||
The benchmarks results were better.
|
||||
|
||||
In such a design, most bridges would have a `sync.Pool`
|
||||
to reduce the number of heap allocations.
|
||||
However, the `sync.Pool` will not work correctly with API implementations
|
||||
that would take ownership of the record
|
||||
(e.g. implementations that do not copy records for asynchronous processing).
|
||||
The current design, even in case of improper API implementation,
|
||||
has lower chances of encountering a bug as most bridges would
|
||||
create a record, pass it, and forget about it.
|
||||
|
||||
For reference, here is the reason why `slog` does not use `sync.Pool`[^3]
|
||||
as well:
|
||||
|
||||
> We can use a sync pool for records though we decided not to.
|
||||
You can but it's a bad idea for us. Why?
|
||||
Because users have control of Records.
|
||||
Handler writers can get their hands on a record
|
||||
and we'd have to ask them to free it
|
||||
or try to free it magically at some some point.
|
||||
But either way, they could get themselves in trouble by freeing it twice
|
||||
or holding on to one after they free it.
|
||||
That's a use after free bug and that's why `zerolog` was problematic for us.
|
||||
`zerolog` as as part of its speed exposes a pool allocated value to users
|
||||
if you use `zerolog` the normal way, that you'll see in all the examples,
|
||||
you will never encounter a problem.
|
||||
But if you do something a little out of the ordinary you can get
|
||||
use after free bugs and we just didn't want to put that in the standard library.
|
||||
|
||||
Therefore, we decided to not follow the proposal as it is
|
||||
less user friendly (users and bridges would use e.g. a `sync.Pool` to reduce
|
||||
the number of heap allocation), less safe (more prone to use after free bugs
|
||||
and race conditions), and the benchmark differences were not significant.
|
||||
|
||||
### Use any instead of defining Value
|
||||
|
||||
[Logs Data Model](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-body)
|
||||
defines Body to be `any`.
|
||||
One could propose to define `Body` (and attribute values) as `any`
|
||||
instead of a defining a new type (`Value`).
|
||||
|
||||
First of all, [`any` type defined in the specification](https://opentelemetry.io/docs/specs/otel/logs/data-model/#type-any)
|
||||
is not the same as `any` (`interface{}`) in Go.
|
||||
|
||||
Moreover, using `any` as a field would decrease the performance.[^7]
|
||||
|
||||
Notice it will be still possible to add following kind and factories
|
||||
in a backwards compatible way:
|
||||
|
||||
```go
|
||||
const KindMap Kind
|
||||
|
||||
func AnyValue(value any) KeyValue
|
||||
|
||||
func Any(key string, value any) KeyValue
|
||||
```
|
||||
|
||||
However, currently, it would not be specification compliant.
|
||||
|
||||
### Severity type encapsulating number and text
|
||||
|
||||
We could combine severity into a single field defining a type:
|
||||
|
||||
```go
|
||||
type Severity struct {
|
||||
Number SeverityNumber
|
||||
Text string
|
||||
}
|
||||
```
|
||||
|
||||
However, the [Logs Data Model](https://opentelemetry.io/docs/specs/otel/logs/data-model/#log-and-event-record-definition)
|
||||
define it as independent fields.
|
||||
It should be more user friendly to have them separated.
|
||||
Especially when having getter and setter methods, setting one value
|
||||
when the other is already set would be unpleasant.
|
||||
|
||||
### Reuse attribute package
|
||||
|
||||
It was tempting to reuse the existing
|
||||
[https://pkg.go.dev/go.opentelemetry.io/otel/attribute] package
|
||||
for defining log attributes and body.
|
||||
|
||||
However, this would be wrong because [the log attribute definition](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-attributes)
|
||||
is different from [the common attribute definition](https://opentelemetry.io/docs/specs/otel/common/#attribute).
|
||||
|
||||
Moreover, it there is nothing telling that [the body definition](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-body)
|
||||
has anything in common with a common attribute value.
|
||||
|
||||
Therefore, we define new types representing the abstract types defined
|
||||
in the [Logs Data Model](https://opentelemetry.io/docs/specs/otel/logs/data-model/#definitions-used-in-this-document).
|
||||
|
||||
### Mix receiver types for Record
|
||||
|
||||
Methods of [`slog.Record`](https://pkg.go.dev/log/slog#Record)
|
||||
have different receiver types.
|
||||
|
||||
In `log/slog` GitHub issue we can only find that the reason is:[^8]
|
||||
|
||||
>> some receiver of Record struct is by value
|
||||
> Passing Records by value means they incur no heap allocation.
|
||||
> That improves performance overall, even though they are copied.
|
||||
|
||||
However, the benchmarks do not show any noticeable differences.[^9]
|
||||
|
||||
The compiler is smart-enough to not make a heap allocation for any of these methods.
|
||||
The use of a pointer receiver does not cause any heap allocation.
|
||||
From Go FAQ:[^10]
|
||||
|
||||
> In the current compilers, if a variable has its address taken,
|
||||
> that variable is a candidate for allocation on the heap.
|
||||
> However, a basic escape analysis recognizes some cases
|
||||
> when such variables will not live past the return from the function
|
||||
> and can reside on the stack.
|
||||
|
||||
The [Understanding Allocations: the Stack and the Heap](https://www.youtube.com/watch?v=ZMZpH4yT7M0)
|
||||
presentation by Jacob Walker describes the escape analysis with details.
|
||||
|
||||
Moreover, also from Go FAQ:[^10]
|
||||
|
||||
> Also, if a local variable is very large,
|
||||
> it might make more sense to store it on the heap rather than the stack.
|
||||
|
||||
Therefore, even if we use a value receiver and the value is very large
|
||||
it may be heap allocated.
|
||||
|
||||
Both [Go Code Review Comments](https://go.dev/wiki/CodeReviewComments#receiver-type)
|
||||
and [Google's Go Style Decisions](https://google.github.io/styleguide/go/decisions#receiver-type)
|
||||
highly recommend making the methods for a type either all pointer methods
|
||||
or all value methods. Google's Go Style Decisions even goes further and says:
|
||||
|
||||
> There is a lot of misinformation about whether passing a value or a pointer
|
||||
> to a function can affect performance.
|
||||
> The compiler can choose to pass pointers to values on the stack
|
||||
> as well as copying values on the stack,
|
||||
> but these considerations should not outweigh the readability
|
||||
> and correctness of the code in most circumstances.
|
||||
> When the performance does matter, it is important to profile both approaches
|
||||
> with a realistic benchmark before deciding that one approach outperforms the other.
|
||||
|
||||
Because, the benchmarks[^9] do not proof any performance difference
|
||||
and the general recommendation is to not mix receiver types,
|
||||
we decided to use pointer receivers for all `Record` methods.
|
||||
|
||||
### Add XYZ method to Logger
|
||||
|
||||
The `Logger` does not have methods like `SetSeverity`, etc.
|
||||
as the Logs API needs to follow (be compliant with)
|
||||
the [specification](https://opentelemetry.io/docs/specs/otel/logs/api/)
|
||||
|
||||
### Rename KeyValue to Attr
|
||||
|
||||
There was a proposal to rename `KeyValue` to `Attr` (or `Attribute`).[^11]
|
||||
New developers may not intuitively know that `log.KeyValue` is an attribute in
|
||||
the OpenTelemetry parlance.
|
||||
|
||||
During the discussion we agreed to keep the `KeyValue` name.
|
||||
|
||||
The type is used in multiple semantics:
|
||||
|
||||
- as a log attribute,
|
||||
- as a map item,
|
||||
- as a log record Body.
|
||||
|
||||
As for map item semantics, this type is a key-value pair, not an attribute.
|
||||
Naming the type as `Attr` would convey semantical meaning
|
||||
that would not be correct for a map.
|
||||
|
||||
We expect that most of the Logs API users will be OpenTelemetry contributors.
|
||||
We plan to implement bridges for the most popular logging libraries ourselves.
|
||||
Given we will all have the context needed to disambiguate these overlapping
|
||||
names, developers' confusion should not be an issue.
|
||||
|
||||
For bridges not developed by us,
|
||||
developers will likely look at our existing bridges for inspiration.
|
||||
Our correct use of these types will be a reference to them.
|
||||
|
||||
At last, we provide `ValueFromAttribute` and `KeyValueFromAttribute`
|
||||
to offer reuse of `attribute.Value` and `attribute.KeyValue`.
|
||||
|
||||
[^1]: [Handle structured body and attributes](https://github.com/pellared/opentelemetry-go/pull/7)
|
||||
[^2]: Jonathan Amsterdam, [The Go Blog: Structured Logging with slog](https://go.dev/blog/slog)
|
||||
[^3]: Jonathan Amsterdam, [GopherCon Europe 2023: A Fast Structured Logging Package](https://www.youtube.com/watch?v=tC4Jt3i62ns)
|
||||
[^4]: [Emit definition discussion with benchmarks](https://github.com/open-telemetry/opentelemetry-go/pull/4725#discussion_r1400869566)
|
||||
[^5]: [Logger.WithAttributes analysis](https://github.com/pellared/opentelemetry-go/pull/3)
|
||||
[^6]: [Record attributes as field and use sync.Pool for reducing allocations](https://github.com/pellared/opentelemetry-go/pull/4) and [Record attributes based on slog.Record](https://github.com/pellared/opentelemetry-go/pull/6)
|
||||
[^7]: [Record.Body as any](https://github.com/pellared/opentelemetry-go/pull/5)
|
||||
[^8]: [log/slog: structured, leveled logging](https://github.com/golang/go/issues/56345#issuecomment-1302563756)
|
||||
[^9]: [Record with pointer receivers only](https://github.com/pellared/opentelemetry-go/pull/8)
|
||||
[^10]: [Go FAQ: Stack or heap](https://go.dev/doc/faq#stack_or_heap)
|
||||
[^11]: [Rename KeyValue to Attr discussion](https://github.com/open-telemetry/opentelemetry-go/pull/4809#discussion_r1476080093)
|
||||
201
vendor/go.opentelemetry.io/otel/log/LICENSE
generated
vendored
Normal file
201
vendor/go.opentelemetry.io/otel/log/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
3
vendor/go.opentelemetry.io/otel/log/README.md
generated
vendored
Normal file
3
vendor/go.opentelemetry.io/otel/log/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Log API
|
||||
|
||||
[](https://pkg.go.dev/go.opentelemetry.io/otel/log)
|
||||
76
vendor/go.opentelemetry.io/otel/log/doc.go
generated
vendored
Normal file
76
vendor/go.opentelemetry.io/otel/log/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/*
|
||||
Package log provides the OpenTelemetry Logs API.
|
||||
|
||||
This package is intended to be used by bridges between existing logging
|
||||
libraries and OpenTelemetry. Users should not directly use this package as a
|
||||
logging library. Instead, install one of the bridges listed in the
|
||||
[registry], and use the associated logging library.
|
||||
|
||||
# API Implementations
|
||||
|
||||
This package does not conform to the standard Go versioning policy, all of its
|
||||
interfaces may have methods added to them without a package major version bump.
|
||||
This non-standard API evolution could surprise an uninformed implementation
|
||||
author. They could unknowingly build their implementation in a way that would
|
||||
result in a runtime panic for their users that update to the new API.
|
||||
|
||||
The API is designed to help inform an instrumentation author about this
|
||||
non-standard API evolution. It requires them to choose a default behavior for
|
||||
unimplemented interface methods. There are three behavior choices they can
|
||||
make:
|
||||
|
||||
- Compilation failure
|
||||
- Panic
|
||||
- Default to another implementation
|
||||
|
||||
All interfaces in this API embed a corresponding interface from
|
||||
[go.opentelemetry.io/otel/log/embedded]. If an author wants the default
|
||||
behavior of their implementations to be a compilation failure, signaling to
|
||||
their users they need to update to the latest version of that implementation,
|
||||
they need to embed the corresponding interface from
|
||||
[go.opentelemetry.io/otel/log/embedded] in their implementation. For example,
|
||||
|
||||
import "go.opentelemetry.io/otel/log/embedded"
|
||||
|
||||
type LoggerProvider struct {
|
||||
embedded.LoggerProvider
|
||||
// ...
|
||||
}
|
||||
|
||||
If an author wants the default behavior of their implementations to a panic,
|
||||
they need to embed the API interface directly.
|
||||
|
||||
import "go.opentelemetry.io/otel/log"
|
||||
|
||||
type LoggerProvider struct {
|
||||
log.LoggerProvider
|
||||
// ...
|
||||
}
|
||||
|
||||
This is not a recommended behavior as it could lead to publishing packages that
|
||||
contain runtime panics when users update other package that use newer versions
|
||||
of [go.opentelemetry.io/otel/log].
|
||||
|
||||
Finally, an author can embed another implementation in theirs. The embedded
|
||||
implementation will be used for methods not defined by the author. For example,
|
||||
an author who wants to default to silently dropping the call can use
|
||||
[go.opentelemetry.io/otel/log/noop]:
|
||||
|
||||
import "go.opentelemetry.io/otel/log/noop"
|
||||
|
||||
type LoggerProvider struct {
|
||||
noop.LoggerProvider
|
||||
// ...
|
||||
}
|
||||
|
||||
It is strongly recommended that authors only embed
|
||||
go.opentelemetry.io/otel/log/noop if they choose this default behavior. That
|
||||
implementation is the only one OpenTelemetry authors can guarantee will fully
|
||||
implement all the API interfaces when a user updates their API.
|
||||
|
||||
[registry]: https://opentelemetry.io/ecosystem/registry/?language=go&component=log-bridge
|
||||
*/
|
||||
package log // import "go.opentelemetry.io/otel/log"
|
||||
3
vendor/go.opentelemetry.io/otel/log/embedded/README.md
generated
vendored
Normal file
3
vendor/go.opentelemetry.io/otel/log/embedded/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Log Embedded
|
||||
|
||||
[](https://pkg.go.dev/go.opentelemetry.io/otel/log/embedded)
|
||||
36
vendor/go.opentelemetry.io/otel/log/embedded/embedded.go
generated
vendored
Normal file
36
vendor/go.opentelemetry.io/otel/log/embedded/embedded.go
generated
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package embedded provides interfaces embedded within the [OpenTelemetry Logs
|
||||
// Bridge API].
|
||||
//
|
||||
// Implementers of the [OpenTelemetry Logs Bridge API] can embed the relevant
|
||||
// type from this package into their implementation directly. Doing so will
|
||||
// result in a compilation error for users when the [OpenTelemetry Logs Bridge
|
||||
// API] is extended (which is something that can happen without a major version
|
||||
// bump of the API package).
|
||||
//
|
||||
// [OpenTelemetry Logs Bridge API]: https://pkg.go.dev/go.opentelemetry.io/otel/log
|
||||
package embedded // import "go.opentelemetry.io/otel/log/embedded"
|
||||
|
||||
// LoggerProvider is embedded in the [Logs Bridge API LoggerProvider].
|
||||
//
|
||||
// Embed this interface in your implementation of the [Logs Bridge API
|
||||
// LoggerProvider] if you want users to experience a compilation error,
|
||||
// signaling they need to update to your latest implementation, when the [Logs
|
||||
// Bridge API LoggerProvider] interface is extended (which is something that
|
||||
// can happen without a major version bump of the API package).
|
||||
//
|
||||
// [Logs Bridge API LoggerProvider]: https://pkg.go.dev/go.opentelemetry.io/otel/log#LoggerProvider
|
||||
type LoggerProvider interface{ loggerProvider() }
|
||||
|
||||
// Logger is embedded in [Logs Bridge API Logger].
|
||||
//
|
||||
// Embed this interface in your implementation of the [Logs Bridge API Logger]
|
||||
// if you want users to experience a compilation error, signaling they need to
|
||||
// update to your latest implementation, when the [Logs Bridge API Logger]
|
||||
// interface is extended (which is something that can happen without a major
|
||||
// version bump of the API package).
|
||||
//
|
||||
// [Logs Bridge API Logger]: https://pkg.go.dev/go.opentelemetry.io/otel/log#Logger
|
||||
type Logger interface{ logger() }
|
||||
443
vendor/go.opentelemetry.io/otel/log/keyvalue.go
generated
vendored
Normal file
443
vendor/go.opentelemetry.io/otel/log/keyvalue.go
generated
vendored
Normal file
|
|
@ -0,0 +1,443 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//go:generate stringer -type=Kind -trimprefix=Kind
|
||||
|
||||
package log // import "go.opentelemetry.io/otel/log"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"cmp"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"slices"
|
||||
"strconv"
|
||||
"unsafe"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
)
|
||||
|
||||
// errKind is logged when a Value is decoded to an incompatible type.
|
||||
var errKind = errors.New("invalid Kind")
|
||||
|
||||
// Kind is the kind of a [Value].
|
||||
type Kind int
|
||||
|
||||
// Kind values.
|
||||
const (
|
||||
KindEmpty Kind = iota
|
||||
KindBool
|
||||
KindFloat64
|
||||
KindInt64
|
||||
KindString
|
||||
KindBytes
|
||||
KindSlice
|
||||
KindMap
|
||||
)
|
||||
|
||||
// A Value represents a structured log value.
|
||||
// A zero value is valid and represents an empty value.
|
||||
type Value struct {
|
||||
// Ensure forward compatibility by explicitly making this not comparable.
|
||||
noCmp [0]func() //nolint: unused // This is indeed used.
|
||||
|
||||
// num holds the value for Int64, Float64, and Bool. It holds the length
|
||||
// for String, Bytes, Slice, Map.
|
||||
num uint64
|
||||
// any holds either the KindBool, KindInt64, KindFloat64, stringptr,
|
||||
// bytesptr, sliceptr, or mapptr. If KindBool, KindInt64, or KindFloat64
|
||||
// then the value of Value is in num as described above. Otherwise, it
|
||||
// contains the value wrapped in the appropriate type.
|
||||
any any
|
||||
}
|
||||
|
||||
type (
|
||||
// sliceptr represents a value in Value.any for KindString Values.
|
||||
stringptr *byte
|
||||
// bytesptr represents a value in Value.any for KindBytes Values.
|
||||
bytesptr *byte
|
||||
// sliceptr represents a value in Value.any for KindSlice Values.
|
||||
sliceptr *Value
|
||||
// mapptr represents a value in Value.any for KindMap Values.
|
||||
mapptr *KeyValue
|
||||
)
|
||||
|
||||
// StringValue returns a new [Value] for a string.
|
||||
func StringValue(v string) Value {
|
||||
return Value{
|
||||
num: uint64(len(v)),
|
||||
any: stringptr(unsafe.StringData(v)),
|
||||
}
|
||||
}
|
||||
|
||||
// IntValue returns a [Value] for an int.
|
||||
func IntValue(v int) Value { return Int64Value(int64(v)) }
|
||||
|
||||
// Int64Value returns a [Value] for an int64.
|
||||
func Int64Value(v int64) Value {
|
||||
// This can be later converted back to int64 (overflow not checked).
|
||||
return Value{num: uint64(v), any: KindInt64} // nolint:gosec
|
||||
}
|
||||
|
||||
// Float64Value returns a [Value] for a float64.
|
||||
func Float64Value(v float64) Value {
|
||||
return Value{num: math.Float64bits(v), any: KindFloat64}
|
||||
}
|
||||
|
||||
// BoolValue returns a [Value] for a bool.
|
||||
func BoolValue(v bool) Value { //nolint:revive // Not a control flag.
|
||||
var n uint64
|
||||
if v {
|
||||
n = 1
|
||||
}
|
||||
return Value{num: n, any: KindBool}
|
||||
}
|
||||
|
||||
// BytesValue returns a [Value] for a byte slice. The passed slice must not be
|
||||
// changed after it is passed.
|
||||
func BytesValue(v []byte) Value {
|
||||
return Value{
|
||||
num: uint64(len(v)),
|
||||
any: bytesptr(unsafe.SliceData(v)),
|
||||
}
|
||||
}
|
||||
|
||||
// SliceValue returns a [Value] for a slice of [Value]. The passed slice must
|
||||
// not be changed after it is passed.
|
||||
func SliceValue(vs ...Value) Value {
|
||||
return Value{
|
||||
num: uint64(len(vs)),
|
||||
any: sliceptr(unsafe.SliceData(vs)),
|
||||
}
|
||||
}
|
||||
|
||||
// MapValue returns a new [Value] for a slice of key-value pairs. The passed
|
||||
// slice must not be changed after it is passed.
|
||||
func MapValue(kvs ...KeyValue) Value {
|
||||
return Value{
|
||||
num: uint64(len(kvs)),
|
||||
any: mapptr(unsafe.SliceData(kvs)),
|
||||
}
|
||||
}
|
||||
|
||||
// AsString returns the value held by v as a string.
|
||||
func (v Value) AsString() string {
|
||||
if sp, ok := v.any.(stringptr); ok {
|
||||
return unsafe.String(sp, v.num)
|
||||
}
|
||||
global.Error(errKind, "AsString", "Kind", v.Kind())
|
||||
return ""
|
||||
}
|
||||
|
||||
// asString returns the value held by v as a string. It will panic if the Value
|
||||
// is not KindString.
|
||||
func (v Value) asString() string {
|
||||
return unsafe.String(v.any.(stringptr), v.num)
|
||||
}
|
||||
|
||||
// AsInt64 returns the value held by v as an int64.
|
||||
func (v Value) AsInt64() int64 {
|
||||
if v.Kind() != KindInt64 {
|
||||
global.Error(errKind, "AsInt64", "Kind", v.Kind())
|
||||
return 0
|
||||
}
|
||||
return v.asInt64()
|
||||
}
|
||||
|
||||
// asInt64 returns the value held by v as an int64. If v is not of KindInt64,
|
||||
// this will return garbage.
|
||||
func (v Value) asInt64() int64 {
|
||||
// Assumes v.num was a valid int64 (overflow not checked).
|
||||
return int64(v.num) // nolint: gosec
|
||||
}
|
||||
|
||||
// AsBool returns the value held by v as a bool.
|
||||
func (v Value) AsBool() bool {
|
||||
if v.Kind() != KindBool {
|
||||
global.Error(errKind, "AsBool", "Kind", v.Kind())
|
||||
return false
|
||||
}
|
||||
return v.asBool()
|
||||
}
|
||||
|
||||
// asBool returns the value held by v as a bool. If v is not of KindBool, this
|
||||
// will return garbage.
|
||||
func (v Value) asBool() bool { return v.num == 1 }
|
||||
|
||||
// AsFloat64 returns the value held by v as a float64.
|
||||
func (v Value) AsFloat64() float64 {
|
||||
if v.Kind() != KindFloat64 {
|
||||
global.Error(errKind, "AsFloat64", "Kind", v.Kind())
|
||||
return 0
|
||||
}
|
||||
return v.asFloat64()
|
||||
}
|
||||
|
||||
// asFloat64 returns the value held by v as a float64. If v is not of
|
||||
// KindFloat64, this will return garbage.
|
||||
func (v Value) asFloat64() float64 { return math.Float64frombits(v.num) }
|
||||
|
||||
// AsBytes returns the value held by v as a []byte.
|
||||
func (v Value) AsBytes() []byte {
|
||||
if sp, ok := v.any.(bytesptr); ok {
|
||||
return unsafe.Slice((*byte)(sp), v.num)
|
||||
}
|
||||
global.Error(errKind, "AsBytes", "Kind", v.Kind())
|
||||
return nil
|
||||
}
|
||||
|
||||
// asBytes returns the value held by v as a []byte. It will panic if the Value
|
||||
// is not KindBytes.
|
||||
func (v Value) asBytes() []byte {
|
||||
return unsafe.Slice((*byte)(v.any.(bytesptr)), v.num)
|
||||
}
|
||||
|
||||
// AsSlice returns the value held by v as a []Value.
|
||||
func (v Value) AsSlice() []Value {
|
||||
if sp, ok := v.any.(sliceptr); ok {
|
||||
return unsafe.Slice((*Value)(sp), v.num)
|
||||
}
|
||||
global.Error(errKind, "AsSlice", "Kind", v.Kind())
|
||||
return nil
|
||||
}
|
||||
|
||||
// asSlice returns the value held by v as a []Value. It will panic if the Value
|
||||
// is not KindSlice.
|
||||
func (v Value) asSlice() []Value {
|
||||
return unsafe.Slice((*Value)(v.any.(sliceptr)), v.num)
|
||||
}
|
||||
|
||||
// AsMap returns the value held by v as a []KeyValue.
|
||||
func (v Value) AsMap() []KeyValue {
|
||||
if sp, ok := v.any.(mapptr); ok {
|
||||
return unsafe.Slice((*KeyValue)(sp), v.num)
|
||||
}
|
||||
global.Error(errKind, "AsMap", "Kind", v.Kind())
|
||||
return nil
|
||||
}
|
||||
|
||||
// asMap returns the value held by v as a []KeyValue. It will panic if the
|
||||
// Value is not KindMap.
|
||||
func (v Value) asMap() []KeyValue {
|
||||
return unsafe.Slice((*KeyValue)(v.any.(mapptr)), v.num)
|
||||
}
|
||||
|
||||
// Kind returns the Kind of v.
|
||||
func (v Value) Kind() Kind {
|
||||
switch x := v.any.(type) {
|
||||
case Kind:
|
||||
return x
|
||||
case stringptr:
|
||||
return KindString
|
||||
case bytesptr:
|
||||
return KindBytes
|
||||
case sliceptr:
|
||||
return KindSlice
|
||||
case mapptr:
|
||||
return KindMap
|
||||
default:
|
||||
return KindEmpty
|
||||
}
|
||||
}
|
||||
|
||||
// Empty returns if v does not hold any value.
|
||||
func (v Value) Empty() bool { return v.Kind() == KindEmpty }
|
||||
|
||||
// Equal returns if v is equal to w.
|
||||
func (v Value) Equal(w Value) bool {
|
||||
k1 := v.Kind()
|
||||
k2 := w.Kind()
|
||||
if k1 != k2 {
|
||||
return false
|
||||
}
|
||||
switch k1 {
|
||||
case KindInt64, KindBool:
|
||||
return v.num == w.num
|
||||
case KindString:
|
||||
return v.asString() == w.asString()
|
||||
case KindFloat64:
|
||||
return v.asFloat64() == w.asFloat64()
|
||||
case KindSlice:
|
||||
return slices.EqualFunc(v.asSlice(), w.asSlice(), Value.Equal)
|
||||
case KindMap:
|
||||
sv := sortMap(v.asMap())
|
||||
sw := sortMap(w.asMap())
|
||||
return slices.EqualFunc(sv, sw, KeyValue.Equal)
|
||||
case KindBytes:
|
||||
return bytes.Equal(v.asBytes(), w.asBytes())
|
||||
case KindEmpty:
|
||||
return true
|
||||
default:
|
||||
global.Error(errKind, "Equal", "Kind", k1)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func sortMap(m []KeyValue) []KeyValue {
|
||||
sm := make([]KeyValue, len(m))
|
||||
copy(sm, m)
|
||||
slices.SortFunc(sm, func(a, b KeyValue) int {
|
||||
return cmp.Compare(a.Key, b.Key)
|
||||
})
|
||||
|
||||
return sm
|
||||
}
|
||||
|
||||
// String returns Value's value as a string, formatted like [fmt.Sprint].
|
||||
//
|
||||
// The returned string is meant for debugging;
|
||||
// the string representation is not stable.
|
||||
func (v Value) String() string {
|
||||
switch v.Kind() {
|
||||
case KindString:
|
||||
return v.asString()
|
||||
case KindInt64:
|
||||
// Assumes v.num was a valid int64 (overflow not checked).
|
||||
return strconv.FormatInt(int64(v.num), 10) // nolint: gosec
|
||||
case KindFloat64:
|
||||
return strconv.FormatFloat(v.asFloat64(), 'g', -1, 64)
|
||||
case KindBool:
|
||||
return strconv.FormatBool(v.asBool())
|
||||
case KindBytes:
|
||||
return fmt.Sprint(v.asBytes())
|
||||
case KindMap:
|
||||
return fmt.Sprint(v.asMap())
|
||||
case KindSlice:
|
||||
return fmt.Sprint(v.asSlice())
|
||||
case KindEmpty:
|
||||
return "<nil>"
|
||||
default:
|
||||
// Try to handle this as gracefully as possible.
|
||||
//
|
||||
// Don't panic here. The goal here is to have developers find this
|
||||
// first if a slog.Kind is is not handled. It is
|
||||
// preferable to have user's open issue asking why their attributes
|
||||
// have a "unhandled: " prefix than say that their code is panicking.
|
||||
return fmt.Sprintf("<unhandled log.Kind: %s>", v.Kind())
|
||||
}
|
||||
}
|
||||
|
||||
// A KeyValue is a key-value pair used to represent a log attribute (a
|
||||
// superset of [go.opentelemetry.io/otel/attribute.KeyValue]) and map item.
|
||||
type KeyValue struct {
|
||||
Key string
|
||||
Value Value
|
||||
}
|
||||
|
||||
// Equal returns if a is equal to b.
|
||||
func (a KeyValue) Equal(b KeyValue) bool {
|
||||
return a.Key == b.Key && a.Value.Equal(b.Value)
|
||||
}
|
||||
|
||||
// String returns a KeyValue for a string value.
|
||||
func String(key, value string) KeyValue {
|
||||
return KeyValue{key, StringValue(value)}
|
||||
}
|
||||
|
||||
// Int64 returns a KeyValue for an int64 value.
|
||||
func Int64(key string, value int64) KeyValue {
|
||||
return KeyValue{key, Int64Value(value)}
|
||||
}
|
||||
|
||||
// Int returns a KeyValue for an int value.
|
||||
func Int(key string, value int) KeyValue {
|
||||
return KeyValue{key, IntValue(value)}
|
||||
}
|
||||
|
||||
// Float64 returns a KeyValue for a float64 value.
|
||||
func Float64(key string, value float64) KeyValue {
|
||||
return KeyValue{key, Float64Value(value)}
|
||||
}
|
||||
|
||||
// Bool returns a KeyValue for a bool value.
|
||||
func Bool(key string, value bool) KeyValue {
|
||||
return KeyValue{key, BoolValue(value)}
|
||||
}
|
||||
|
||||
// Bytes returns a KeyValue for a []byte value.
|
||||
// The passed slice must not be changed after it is passed.
|
||||
func Bytes(key string, value []byte) KeyValue {
|
||||
return KeyValue{key, BytesValue(value)}
|
||||
}
|
||||
|
||||
// Slice returns a KeyValue for a []Value value.
|
||||
// The passed slice must not be changed after it is passed.
|
||||
func Slice(key string, value ...Value) KeyValue {
|
||||
return KeyValue{key, SliceValue(value...)}
|
||||
}
|
||||
|
||||
// Map returns a KeyValue for a map value.
|
||||
// The passed slice must not be changed after it is passed.
|
||||
func Map(key string, value ...KeyValue) KeyValue {
|
||||
return KeyValue{key, MapValue(value...)}
|
||||
}
|
||||
|
||||
// Empty returns a KeyValue with an empty value.
|
||||
func Empty(key string) KeyValue {
|
||||
return KeyValue{key, Value{}}
|
||||
}
|
||||
|
||||
// String returns key-value pair as a string, formatted like "key:value".
|
||||
//
|
||||
// The returned string is meant for debugging;
|
||||
// the string representation is not stable.
|
||||
func (a KeyValue) String() string {
|
||||
return fmt.Sprintf("%s:%s", a.Key, a.Value)
|
||||
}
|
||||
|
||||
// ValueFromAttribute converts [attribute.Value] to [Value].
|
||||
func ValueFromAttribute(value attribute.Value) Value {
|
||||
switch value.Type() {
|
||||
case attribute.INVALID:
|
||||
return Value{}
|
||||
case attribute.BOOL:
|
||||
return BoolValue(value.AsBool())
|
||||
case attribute.BOOLSLICE:
|
||||
val := value.AsBoolSlice()
|
||||
res := make([]Value, 0, len(val))
|
||||
for _, v := range val {
|
||||
res = append(res, BoolValue(v))
|
||||
}
|
||||
return SliceValue(res...)
|
||||
case attribute.INT64:
|
||||
return Int64Value(value.AsInt64())
|
||||
case attribute.INT64SLICE:
|
||||
val := value.AsInt64Slice()
|
||||
res := make([]Value, 0, len(val))
|
||||
for _, v := range val {
|
||||
res = append(res, Int64Value(v))
|
||||
}
|
||||
return SliceValue(res...)
|
||||
case attribute.FLOAT64:
|
||||
return Float64Value(value.AsFloat64())
|
||||
case attribute.FLOAT64SLICE:
|
||||
val := value.AsFloat64Slice()
|
||||
res := make([]Value, 0, len(val))
|
||||
for _, v := range val {
|
||||
res = append(res, Float64Value(v))
|
||||
}
|
||||
return SliceValue(res...)
|
||||
case attribute.STRING:
|
||||
return StringValue(value.AsString())
|
||||
case attribute.STRINGSLICE:
|
||||
val := value.AsStringSlice()
|
||||
res := make([]Value, 0, len(val))
|
||||
for _, v := range val {
|
||||
res = append(res, StringValue(v))
|
||||
}
|
||||
return SliceValue(res...)
|
||||
}
|
||||
// This code should never be reached
|
||||
// as log attributes are a superset of standard attributes.
|
||||
panic("unknown attribute type")
|
||||
}
|
||||
|
||||
// KeyValueFromAttribute converts [attribute.KeyValue] to [KeyValue].
|
||||
func KeyValueFromAttribute(kv attribute.KeyValue) KeyValue {
|
||||
return KeyValue{
|
||||
Key: string(kv.Key),
|
||||
Value: ValueFromAttribute(kv.Value),
|
||||
}
|
||||
}
|
||||
30
vendor/go.opentelemetry.io/otel/log/kind_string.go
generated
vendored
Normal file
30
vendor/go.opentelemetry.io/otel/log/kind_string.go
generated
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
// Code generated by "stringer -type=Kind -trimprefix=Kind"; DO NOT EDIT.
|
||||
|
||||
package log
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[KindEmpty-0]
|
||||
_ = x[KindBool-1]
|
||||
_ = x[KindFloat64-2]
|
||||
_ = x[KindInt64-3]
|
||||
_ = x[KindString-4]
|
||||
_ = x[KindBytes-5]
|
||||
_ = x[KindSlice-6]
|
||||
_ = x[KindMap-7]
|
||||
}
|
||||
|
||||
const _Kind_name = "EmptyBoolFloat64Int64StringBytesSliceMap"
|
||||
|
||||
var _Kind_index = [...]uint8{0, 5, 9, 16, 21, 27, 32, 37, 40}
|
||||
|
||||
func (i Kind) String() string {
|
||||
if i < 0 || i >= Kind(len(_Kind_index)-1) {
|
||||
return "Kind(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _Kind_name[_Kind_index[i]:_Kind_index[i+1]]
|
||||
}
|
||||
140
vendor/go.opentelemetry.io/otel/log/logger.go
generated
vendored
Normal file
140
vendor/go.opentelemetry.io/otel/log/logger.go
generated
vendored
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package log // import "go.opentelemetry.io/otel/log"
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/log/embedded"
|
||||
)
|
||||
|
||||
// Logger emits log records.
|
||||
//
|
||||
// Warning: Methods may be added to this interface in minor releases. See
|
||||
// package documentation on API implementation for information on how to set
|
||||
// default behavior for unimplemented methods.
|
||||
type Logger interface {
|
||||
// Users of the interface can ignore this. This embedded type is only used
|
||||
// by implementations of this interface. See the "API Implementations"
|
||||
// section of the package documentation for more information.
|
||||
embedded.Logger
|
||||
|
||||
// Emit emits a log record.
|
||||
//
|
||||
// The record may be held by the implementation. Callers should not mutate
|
||||
// the record after passed.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Emit(ctx context.Context, record Record)
|
||||
|
||||
// Enabled returns whether the Logger emits for the given context and
|
||||
// param.
|
||||
//
|
||||
// This is useful for users that want to know if a [Record]
|
||||
// will be processed or dropped before they perform complex operations to
|
||||
// construct the [Record].
|
||||
//
|
||||
// The passed param is likely to be a partial record information being
|
||||
// provided (e.g a param with only the Severity set).
|
||||
// If a Logger needs more information than is provided, it
|
||||
// is said to be in an indeterminate state (see below).
|
||||
//
|
||||
// The returned value will be true when the Logger will emit for the
|
||||
// provided context and param, and will be false if the Logger will not
|
||||
// emit. The returned value may be true or false in an indeterminate state.
|
||||
// An implementation should default to returning true for an indeterminate
|
||||
// state, but may return false if valid reasons in particular circumstances
|
||||
// exist (e.g. performance, correctness).
|
||||
//
|
||||
// The param should not be held by the implementation. A copy should be
|
||||
// made if the param needs to be held after the call returns.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Enabled(ctx context.Context, param EnabledParameters) bool
|
||||
}
|
||||
|
||||
// LoggerOption applies configuration options to a [Logger].
|
||||
type LoggerOption interface {
|
||||
// applyLogger is used to set a LoggerOption value of a LoggerConfig.
|
||||
applyLogger(LoggerConfig) LoggerConfig
|
||||
}
|
||||
|
||||
// LoggerConfig contains options for a [Logger].
|
||||
type LoggerConfig struct {
|
||||
// Ensure forward compatibility by explicitly making this not comparable.
|
||||
noCmp [0]func() //nolint: unused // This is indeed used.
|
||||
|
||||
version string
|
||||
schemaURL string
|
||||
attrs attribute.Set
|
||||
}
|
||||
|
||||
// NewLoggerConfig returns a new [LoggerConfig] with all the options applied.
|
||||
func NewLoggerConfig(options ...LoggerOption) LoggerConfig {
|
||||
var c LoggerConfig
|
||||
for _, opt := range options {
|
||||
c = opt.applyLogger(c)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// InstrumentationVersion returns the version of the library providing
|
||||
// instrumentation.
|
||||
func (cfg LoggerConfig) InstrumentationVersion() string {
|
||||
return cfg.version
|
||||
}
|
||||
|
||||
// InstrumentationAttributes returns the attributes associated with the library
|
||||
// providing instrumentation.
|
||||
func (cfg LoggerConfig) InstrumentationAttributes() attribute.Set {
|
||||
return cfg.attrs
|
||||
}
|
||||
|
||||
// SchemaURL returns the schema URL of the library providing instrumentation.
|
||||
func (cfg LoggerConfig) SchemaURL() string {
|
||||
return cfg.schemaURL
|
||||
}
|
||||
|
||||
type loggerOptionFunc func(LoggerConfig) LoggerConfig
|
||||
|
||||
func (fn loggerOptionFunc) applyLogger(cfg LoggerConfig) LoggerConfig {
|
||||
return fn(cfg)
|
||||
}
|
||||
|
||||
// WithInstrumentationVersion returns a [LoggerOption] that sets the
|
||||
// instrumentation version of a [Logger].
|
||||
func WithInstrumentationVersion(version string) LoggerOption {
|
||||
return loggerOptionFunc(func(config LoggerConfig) LoggerConfig {
|
||||
config.version = version
|
||||
return config
|
||||
})
|
||||
}
|
||||
|
||||
// WithInstrumentationAttributes returns a [LoggerOption] that sets the
|
||||
// instrumentation attributes of a [Logger].
|
||||
//
|
||||
// The passed attributes will be de-duplicated.
|
||||
func WithInstrumentationAttributes(attr ...attribute.KeyValue) LoggerOption {
|
||||
return loggerOptionFunc(func(config LoggerConfig) LoggerConfig {
|
||||
config.attrs = attribute.NewSet(attr...)
|
||||
return config
|
||||
})
|
||||
}
|
||||
|
||||
// WithSchemaURL returns a [LoggerOption] that sets the schema URL for a
|
||||
// [Logger].
|
||||
func WithSchemaURL(schemaURL string) LoggerOption {
|
||||
return loggerOptionFunc(func(config LoggerConfig) LoggerConfig {
|
||||
config.schemaURL = schemaURL
|
||||
return config
|
||||
})
|
||||
}
|
||||
|
||||
// EnabledParameters represents payload for [Logger]'s Enabled method.
|
||||
type EnabledParameters struct {
|
||||
Severity Severity
|
||||
}
|
||||
3
vendor/go.opentelemetry.io/otel/log/noop/README.md
generated
vendored
Normal file
3
vendor/go.opentelemetry.io/otel/log/noop/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Log Noop
|
||||
|
||||
[](https://pkg.go.dev/go.opentelemetry.io/otel/log/noop)
|
||||
50
vendor/go.opentelemetry.io/otel/log/noop/noop.go
generated
vendored
Normal file
50
vendor/go.opentelemetry.io/otel/log/noop/noop.go
generated
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package noop provides an implementation of the [OpenTelemetry Logs Bridge
|
||||
// API] that produces no telemetry and minimizes used computation resources.
|
||||
//
|
||||
// Using this package to implement the [OpenTelemetry Logs Bridge API] will
|
||||
// effectively disable OpenTelemetry.
|
||||
//
|
||||
// This implementation can be embedded in other implementations of the
|
||||
// [OpenTelemetry Logs Bridge API]. Doing so will mean the implementation
|
||||
// defaults to no operation for methods it does not implement.
|
||||
//
|
||||
// [OpenTelemetry Logs Bridge API]: https://pkg.go.dev/go.opentelemetry.io/otel/log
|
||||
package noop // import "go.opentelemetry.io/otel/log/noop"
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.opentelemetry.io/otel/log"
|
||||
"go.opentelemetry.io/otel/log/embedded"
|
||||
)
|
||||
|
||||
var (
|
||||
// Compile-time check this implements the OpenTelemetry API.
|
||||
_ log.LoggerProvider = LoggerProvider{}
|
||||
_ log.Logger = Logger{}
|
||||
)
|
||||
|
||||
// LoggerProvider is an OpenTelemetry No-Op LoggerProvider.
|
||||
type LoggerProvider struct{ embedded.LoggerProvider }
|
||||
|
||||
// NewLoggerProvider returns a LoggerProvider that does not record any telemetry.
|
||||
func NewLoggerProvider() LoggerProvider {
|
||||
return LoggerProvider{}
|
||||
}
|
||||
|
||||
// Logger returns an OpenTelemetry Logger that does not record any telemetry.
|
||||
func (LoggerProvider) Logger(string, ...log.LoggerOption) log.Logger {
|
||||
return Logger{}
|
||||
}
|
||||
|
||||
// Logger is an OpenTelemetry No-Op Logger.
|
||||
type Logger struct{ embedded.Logger }
|
||||
|
||||
// Emit does nothing.
|
||||
func (Logger) Emit(context.Context, log.Record) {}
|
||||
|
||||
// Enabled returns false. No log records are ever emitted.
|
||||
func (Logger) Enabled(context.Context, log.EnabledParameters) bool { return false }
|
||||
37
vendor/go.opentelemetry.io/otel/log/provider.go
generated
vendored
Normal file
37
vendor/go.opentelemetry.io/otel/log/provider.go
generated
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package log // import "go.opentelemetry.io/otel/log"
|
||||
|
||||
import "go.opentelemetry.io/otel/log/embedded"
|
||||
|
||||
// LoggerProvider provides access to [Logger].
|
||||
//
|
||||
// Warning: Methods may be added to this interface in minor releases. See
|
||||
// package documentation on API implementation for information on how to set
|
||||
// default behavior for unimplemented methods.
|
||||
type LoggerProvider interface {
|
||||
// Users of the interface can ignore this. This embedded type is only used
|
||||
// by implementations of this interface. See the "API Implementations"
|
||||
// section of the package documentation for more information.
|
||||
embedded.LoggerProvider
|
||||
|
||||
// Logger returns a new [Logger] with the provided name and configuration.
|
||||
//
|
||||
// The name needs to uniquely identify the source of logged code. It is
|
||||
// recommended that name is the Go package name of the library using a log
|
||||
// bridge (note: this is not the name of the bridge package). Most
|
||||
// commonly, this means a bridge will need to accept this value from its
|
||||
// users.
|
||||
//
|
||||
// If name is empty, implementations need to provide a default name.
|
||||
//
|
||||
// The version of the packages using a bridge can be critical information
|
||||
// to include when logging. The bridge should accept this version
|
||||
// information and use the [WithInstrumentationVersion] option to configure
|
||||
// the Logger appropriately.
|
||||
//
|
||||
// Implementations of this method need to be safe for a user to call
|
||||
// concurrently.
|
||||
Logger(name string, options ...LoggerOption) Logger
|
||||
}
|
||||
144
vendor/go.opentelemetry.io/otel/log/record.go
generated
vendored
Normal file
144
vendor/go.opentelemetry.io/otel/log/record.go
generated
vendored
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package log // import "go.opentelemetry.io/otel/log"
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"time"
|
||||
)
|
||||
|
||||
// attributesInlineCount is the number of attributes that are efficiently
|
||||
// stored in an array within a Record. This value is borrowed from slog which
|
||||
// performed a quantitative survey of log library use and found this value to
|
||||
// cover 95% of all use-cases (https://go.dev/blog/slog#performance).
|
||||
const attributesInlineCount = 5
|
||||
|
||||
// Record represents a log record.
|
||||
// A log record with non-empty event name is interpreted as an event record.
|
||||
type Record struct {
|
||||
// Ensure forward compatibility by explicitly making this not comparable.
|
||||
noCmp [0]func() //nolint: unused // This is indeed used.
|
||||
|
||||
eventName string
|
||||
timestamp time.Time
|
||||
observedTimestamp time.Time
|
||||
severity Severity
|
||||
severityText string
|
||||
body Value
|
||||
|
||||
// The fields below are for optimizing the implementation of Attributes and
|
||||
// AddAttributes. This design is borrowed from the slog Record type:
|
||||
// https://cs.opensource.google/go/go/+/refs/tags/go1.22.0:src/log/slog/record.go;l=20
|
||||
|
||||
// Allocation optimization: an inline array sized to hold
|
||||
// the majority of log calls (based on examination of open-source
|
||||
// code). It holds the start of the list of attributes.
|
||||
front [attributesInlineCount]KeyValue
|
||||
|
||||
// The number of attributes in front.
|
||||
nFront int
|
||||
|
||||
// The list of attributes except for those in front.
|
||||
// Invariants:
|
||||
// - len(back) > 0 if nFront == len(front)
|
||||
// - Unused array elements are zero-ed. Used to detect mistakes.
|
||||
back []KeyValue
|
||||
}
|
||||
|
||||
// EventName returns the event name.
|
||||
// A log record with non-empty event name is interpreted as an event record.
|
||||
func (r *Record) EventName() string {
|
||||
return r.eventName
|
||||
}
|
||||
|
||||
// SetEventName sets the event name.
|
||||
// A log record with non-empty event name is interpreted as an event record.
|
||||
func (r *Record) SetEventName(s string) {
|
||||
r.eventName = s
|
||||
}
|
||||
|
||||
// Timestamp returns the time when the log record occurred.
|
||||
func (r *Record) Timestamp() time.Time {
|
||||
return r.timestamp
|
||||
}
|
||||
|
||||
// SetTimestamp sets the time when the log record occurred.
|
||||
func (r *Record) SetTimestamp(t time.Time) {
|
||||
r.timestamp = t
|
||||
}
|
||||
|
||||
// ObservedTimestamp returns the time when the log record was observed.
|
||||
func (r *Record) ObservedTimestamp() time.Time {
|
||||
return r.observedTimestamp
|
||||
}
|
||||
|
||||
// SetObservedTimestamp sets the time when the log record was observed.
|
||||
func (r *Record) SetObservedTimestamp(t time.Time) {
|
||||
r.observedTimestamp = t
|
||||
}
|
||||
|
||||
// Severity returns the [Severity] of the log record.
|
||||
func (r *Record) Severity() Severity {
|
||||
return r.severity
|
||||
}
|
||||
|
||||
// SetSeverity sets the [Severity] level of the log record.
|
||||
func (r *Record) SetSeverity(level Severity) {
|
||||
r.severity = level
|
||||
}
|
||||
|
||||
// SeverityText returns severity (also known as log level) text. This is the
|
||||
// original string representation of the severity as it is known at the source.
|
||||
func (r *Record) SeverityText() string {
|
||||
return r.severityText
|
||||
}
|
||||
|
||||
// SetSeverityText sets severity (also known as log level) text. This is the
|
||||
// original string representation of the severity as it is known at the source.
|
||||
func (r *Record) SetSeverityText(text string) {
|
||||
r.severityText = text
|
||||
}
|
||||
|
||||
// Body returns the body of the log record.
|
||||
func (r *Record) Body() Value {
|
||||
return r.body
|
||||
}
|
||||
|
||||
// SetBody sets the body of the log record.
|
||||
func (r *Record) SetBody(v Value) {
|
||||
r.body = v
|
||||
}
|
||||
|
||||
// WalkAttributes walks all attributes the log record holds by calling f for
|
||||
// each on each [KeyValue] in the [Record]. Iteration stops if f returns false.
|
||||
func (r *Record) WalkAttributes(f func(KeyValue) bool) {
|
||||
for i := 0; i < r.nFront; i++ {
|
||||
if !f(r.front[i]) {
|
||||
return
|
||||
}
|
||||
}
|
||||
for _, a := range r.back {
|
||||
if !f(a) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AddAttributes adds attributes to the log record.
|
||||
func (r *Record) AddAttributes(attrs ...KeyValue) {
|
||||
var i int
|
||||
for i = 0; i < len(attrs) && r.nFront < len(r.front); i++ {
|
||||
a := attrs[i]
|
||||
r.front[r.nFront] = a
|
||||
r.nFront++
|
||||
}
|
||||
|
||||
r.back = slices.Grow(r.back, len(attrs[i:]))
|
||||
r.back = append(r.back, attrs[i:]...)
|
||||
}
|
||||
|
||||
// AttributesLen returns the number of attributes in the log record.
|
||||
func (r *Record) AttributesLen() int {
|
||||
return r.nFront + len(r.back)
|
||||
}
|
||||
64
vendor/go.opentelemetry.io/otel/log/severity.go
generated
vendored
Normal file
64
vendor/go.opentelemetry.io/otel/log/severity.go
generated
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//go:generate stringer -type=Severity -linecomment
|
||||
|
||||
package log // import "go.opentelemetry.io/otel/log"
|
||||
|
||||
// Severity represents a log record severity (also known as log level). Smaller
|
||||
// numerical values correspond to less severe log records (such as debug
|
||||
// events), larger numerical values correspond to more severe log records (such
|
||||
// as errors and critical events).
|
||||
type Severity int
|
||||
|
||||
// Severity values defined by OpenTelemetry.
|
||||
const (
|
||||
// SeverityUndefined represents an unset Severity.
|
||||
SeverityUndefined Severity = 0 // UNDEFINED
|
||||
|
||||
// A fine-grained debugging log record. Typically disabled in default
|
||||
// configurations.
|
||||
SeverityTrace1 Severity = 1 // TRACE
|
||||
SeverityTrace2 Severity = 2 // TRACE2
|
||||
SeverityTrace3 Severity = 3 // TRACE3
|
||||
SeverityTrace4 Severity = 4 // TRACE4
|
||||
|
||||
// A debugging log record.
|
||||
SeverityDebug1 Severity = 5 // DEBUG
|
||||
SeverityDebug2 Severity = 6 // DEBUG2
|
||||
SeverityDebug3 Severity = 7 // DEBUG3
|
||||
SeverityDebug4 Severity = 8 // DEBUG4
|
||||
|
||||
// An informational log record. Indicates that an event happened.
|
||||
SeverityInfo1 Severity = 9 // INFO
|
||||
SeverityInfo2 Severity = 10 // INFO2
|
||||
SeverityInfo3 Severity = 11 // INFO3
|
||||
SeverityInfo4 Severity = 12 // INFO4
|
||||
|
||||
// A warning log record. Not an error but is likely more important than an
|
||||
// informational event.
|
||||
SeverityWarn1 Severity = 13 // WARN
|
||||
SeverityWarn2 Severity = 14 // WARN2
|
||||
SeverityWarn3 Severity = 15 // WARN3
|
||||
SeverityWarn4 Severity = 16 // WARN4
|
||||
|
||||
// An error log record. Something went wrong.
|
||||
SeverityError1 Severity = 17 // ERROR
|
||||
SeverityError2 Severity = 18 // ERROR2
|
||||
SeverityError3 Severity = 19 // ERROR3
|
||||
SeverityError4 Severity = 20 // ERROR4
|
||||
|
||||
// A fatal log record such as application or system crash.
|
||||
SeverityFatal1 Severity = 21 // FATAL
|
||||
SeverityFatal2 Severity = 22 // FATAL2
|
||||
SeverityFatal3 Severity = 23 // FATAL3
|
||||
SeverityFatal4 Severity = 24 // FATAL4
|
||||
|
||||
// Convenience definitions for the base severity of each level.
|
||||
SeverityTrace = SeverityTrace1
|
||||
SeverityDebug = SeverityDebug1
|
||||
SeverityInfo = SeverityInfo1
|
||||
SeverityWarn = SeverityWarn1
|
||||
SeverityError = SeverityError1
|
||||
SeverityFatal = SeverityFatal1
|
||||
)
|
||||
47
vendor/go.opentelemetry.io/otel/log/severity_string.go
generated
vendored
Normal file
47
vendor/go.opentelemetry.io/otel/log/severity_string.go
generated
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
// Code generated by "stringer -type=Severity -linecomment"; DO NOT EDIT.
|
||||
|
||||
package log
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[SeverityUndefined-0]
|
||||
_ = x[SeverityTrace1-1]
|
||||
_ = x[SeverityTrace2-2]
|
||||
_ = x[SeverityTrace3-3]
|
||||
_ = x[SeverityTrace4-4]
|
||||
_ = x[SeverityDebug1-5]
|
||||
_ = x[SeverityDebug2-6]
|
||||
_ = x[SeverityDebug3-7]
|
||||
_ = x[SeverityDebug4-8]
|
||||
_ = x[SeverityInfo1-9]
|
||||
_ = x[SeverityInfo2-10]
|
||||
_ = x[SeverityInfo3-11]
|
||||
_ = x[SeverityInfo4-12]
|
||||
_ = x[SeverityWarn1-13]
|
||||
_ = x[SeverityWarn2-14]
|
||||
_ = x[SeverityWarn3-15]
|
||||
_ = x[SeverityWarn4-16]
|
||||
_ = x[SeverityError1-17]
|
||||
_ = x[SeverityError2-18]
|
||||
_ = x[SeverityError3-19]
|
||||
_ = x[SeverityError4-20]
|
||||
_ = x[SeverityFatal1-21]
|
||||
_ = x[SeverityFatal2-22]
|
||||
_ = x[SeverityFatal3-23]
|
||||
_ = x[SeverityFatal4-24]
|
||||
}
|
||||
|
||||
const _Severity_name = "UNDEFINEDTRACETRACE2TRACE3TRACE4DEBUGDEBUG2DEBUG3DEBUG4INFOINFO2INFO3INFO4WARNWARN2WARN3WARN4ERRORERROR2ERROR3ERROR4FATALFATAL2FATAL3FATAL4"
|
||||
|
||||
var _Severity_index = [...]uint8{0, 9, 14, 20, 26, 32, 37, 43, 49, 55, 59, 64, 69, 74, 78, 83, 88, 93, 98, 104, 110, 116, 121, 127, 133, 139}
|
||||
|
||||
func (i Severity) String() string {
|
||||
if i < 0 || i >= Severity(len(_Severity_index)-1) {
|
||||
return "Severity(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _Severity_name[_Severity_index[i]:_Severity_index[i+1]]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue