summaryrefslogtreecommitdiff
path: root/vendor/go.opentelemetry.io/otel/sdk
diff options
context:
space:
mode:
authorLibravatar Daenney <daenney@noreply.codeberg.org>2025-05-05 16:22:45 +0000
committerLibravatar tobi <kipvandenbos@noreply.codeberg.org>2025-05-05 16:22:45 +0000
commitecbdc4227ba49eca622812b7413aa877318fd7a0 (patch)
tree37f441534f0f7673f72b8b1fa83ddc12db8ec95c /vendor/go.opentelemetry.io/otel/sdk
parent[chore] Update goreleaser (#4133) (diff)
downloadgotosocial-ecbdc4227ba49eca622812b7413aa877318fd7a0.tar.xz
[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>
Diffstat (limited to 'vendor/go.opentelemetry.io/otel/sdk')
-rw-r--r--vendor/go.opentelemetry.io/otel/sdk/log/DESIGN.md176
-rw-r--r--vendor/go.opentelemetry.io/otel/sdk/log/LICENSE201
-rw-r--r--vendor/go.opentelemetry.io/otel/sdk/log/README.md3
-rw-r--r--vendor/go.opentelemetry.io/otel/sdk/log/batch.go477
-rw-r--r--vendor/go.opentelemetry.io/otel/sdk/log/doc.go36
-rw-r--r--vendor/go.opentelemetry.io/otel/sdk/log/exporter.go321
-rw-r--r--vendor/go.opentelemetry.io/otel/sdk/log/filter_processor.go62
-rw-r--r--vendor/go.opentelemetry.io/otel/sdk/log/logger.go110
-rw-r--r--vendor/go.opentelemetry.io/otel/sdk/log/processor.go56
-rw-r--r--vendor/go.opentelemetry.io/otel/sdk/log/provider.go256
-rw-r--r--vendor/go.opentelemetry.io/otel/sdk/log/record.go518
-rw-r--r--vendor/go.opentelemetry.io/otel/sdk/log/ring.go82
-rw-r--r--vendor/go.opentelemetry.io/otel/sdk/log/setting.go119
-rw-r--r--vendor/go.opentelemetry.io/otel/sdk/log/simple.go82
-rw-r--r--vendor/go.opentelemetry.io/otel/sdk/trace/tracetest/README.md3
-rw-r--r--vendor/go.opentelemetry.io/otel/sdk/trace/tracetest/exporter.go74
-rw-r--r--vendor/go.opentelemetry.io/otel/sdk/trace/tracetest/recorder.go94
-rw-r--r--vendor/go.opentelemetry.io/otel/sdk/trace/tracetest/span.go166
18 files changed, 2836 insertions, 0 deletions
diff --git a/vendor/go.opentelemetry.io/otel/sdk/log/DESIGN.md b/vendor/go.opentelemetry.io/otel/sdk/log/DESIGN.md
new file mode 100644
index 000000000..2e0fb15e2
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/sdk/log/DESIGN.md
@@ -0,0 +1,176 @@
+# Logs SDK
+
+## Abstract
+
+`go.opentelemetry.io/otel/sdk/log` provides Logs SDK compliant with the
+[specification](https://opentelemetry.io/docs/specs/otel/logs/sdk/).
+
+The prototype was created in
+[#4955](https://github.com/open-telemetry/opentelemetry-go/pull/4955).
+
+## Background
+
+The goal is to design the exported API of the SDK would have low performance
+overhead. Most importantly, have a design that reduces the amount of heap
+allocations and even make it possible to have a zero-allocation implementation.
+Eliminating the amount of heap allocations reduces the GC pressure which can
+produce some of the largest improvements in performance.[^1]
+
+The main and recommended use case is to configure the SDK to use an OTLP
+exporter with a batch processor.[^2] Therefore, the implementation aims to be
+high-performant in this scenario. Some users that require high throughput may
+also want to use e.g. an [user_events](https://docs.kernel.org/trace/user_events.html),
+[LLTng](https://lttng.org/docs/v2.13/#doc-tracing-your-own-user-application)
+or [ETW](https://learn.microsoft.com/en-us/windows/win32/etw/about-event-tracing)
+exporter with a simple processor. Users may also want to use
+[OTLP File](https://opentelemetry.io/docs/specs/otel/protocol/file-exporter/)
+or [Standard Output](https://opentelemetry.io/docs/specs/otel/logs/sdk_exporters/stdout/)
+exporter in order to emit logs to standard output/error or files.
+
+## Modules structure
+
+The SDK is published as a single `go.opentelemetry.io/otel/sdk/log` Go module.
+
+The exporters are going to be published as following Go modules:
+
+- `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc`
+- `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`
+- `go.opentelemetry.io/otel/exporters/stdout/stdoutlog`
+
+## LoggerProvider
+
+The [LoggerProvider](https://opentelemetry.io/docs/specs/otel/logs/sdk/#loggerprovider)
+is implemented as `LoggerProvider` struct in [provider.go](provider.go).
+
+## LogRecord limits
+
+The [LogRecord limits](https://opentelemetry.io/docs/specs/otel/logs/sdk/#logrecord-limits)
+can be configured using following options:
+
+```go
+func WithAttributeCountLimit(limit int) LoggerProviderOption
+func WithAttributeValueLengthLimit(limit int) LoggerProviderOption
+```
+
+The limits can be also configured using the `OTEL_LOGRECORD_*` environment variables as
+[defined by the specification](https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/#logrecord-limits).
+
+### Processor
+
+The [LogRecordProcessor](https://opentelemetry.io/docs/specs/otel/logs/sdk/#logrecordprocessor)
+is defined as `Processor` interface in [processor.go](processor.go).
+
+The user set processors for the `LoggerProvider` using
+`func WithProcessor(processor Processor) LoggerProviderOption`.
+
+The user can configure custom processors and decorate built-in processors.
+
+The specification may add new operations to the
+[LogRecordProcessor](https://opentelemetry.io/docs/specs/otel/logs/sdk/#logrecordprocessor).
+If it happens, [CONTRIBUTING.md](../../CONTRIBUTING.md#how-to-change-other-interfaces)
+describes how the SDK can be extended in a backwards-compatible way.
+
+### SimpleProcessor
+
+The [Simple processor](https://opentelemetry.io/docs/specs/otel/logs/sdk/#simple-processor)
+is implemented as `SimpleProcessor` struct in [simple.go](simple.go).
+
+### BatchProcessor
+
+The [Batching processor](https://opentelemetry.io/docs/specs/otel/logs/sdk/#batching-processor)
+is implemented as `BatchProcessor` struct in [batch.go](batch.go).
+
+The `Batcher` can be also configured using the `OTEL_BLRP_*` environment variables as
+[defined by the specification](https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/#batch-logrecord-processor).
+
+### Exporter
+
+The [LogRecordExporter](https://opentelemetry.io/docs/specs/otel/logs/sdk/#logrecordexporter)
+is defined as `Exporter` interface in [exporter.go](exporter.go).
+
+The slice passed to `Export` must not be retained by the implementation
+(like e.g. [`io.Writer`](https://pkg.go.dev/io#Writer))
+so that the caller can reuse the passed slice
+(e.g. using [`sync.Pool`](https://pkg.go.dev/sync#Pool))
+to avoid heap allocations on each call.
+
+The specification may add new operations to the
+[LogRecordExporter](https://opentelemetry.io/docs/specs/otel/logs/sdk/#logrecordexporter).
+If it happens, [CONTRIBUTING.md](../../CONTRIBUTING.md#how-to-change-other-interfaces)
+describes how the SDK can be extended in a backwards-compatible way.
+
+### Record
+
+The [ReadWriteLogRecord](https://opentelemetry.io/docs/specs/otel/logs/sdk/#readwritelogrecord)
+is defined as `Record` struct in [record.go](record.go).
+
+The `Record` is designed similarly to [`log.Record`](https://pkg.go.dev/go.opentelemetry.io/otel/log#Record)
+in order to reduce the number of heap allocations when processing attributes.
+
+The SDK does not have have an additional definition of
+[ReadableLogRecord](https://opentelemetry.io/docs/specs/otel/logs/sdk/#readablelogrecord)
+as the specification does not say that the exporters must not be able to modify
+the log records. It simply requires them to be able to read the log records.
+Having less abstractions reduces the API surface and makes the design simpler.
+
+## Benchmarking
+
+The benchmarks are supposed to test end-to-end scenarios
+and avoid I/O that could affect the stability of the results.
+
+The benchmark results can be found in [the prototype](https://github.com/open-telemetry/opentelemetry-go/pull/4955).
+
+## Rejected alternatives
+
+### Represent both LogRecordProcessor and LogRecordExporter as Exporter
+
+Because the [LogRecordProcessor](https://opentelemetry.io/docs/specs/otel/logs/sdk/#logrecordprocessor)
+and the [LogRecordProcessor](https://opentelemetry.io/docs/specs/otel/logs/sdk/#logrecordexporter)
+abstractions are so similar, there was a proposal to unify them under
+single `Exporter` interface.[^3]
+
+However, introducing a `Processor` interface makes it easier
+to create custom processor decorators[^4]
+and makes the design more aligned with the specification.
+
+### Embed log.Record
+
+Because [`Record`](#record) and [`log.Record`](https://pkg.go.dev/go.opentelemetry.io/otel/log#Record)
+are very similar, there was a proposal to embed `log.Record` in `Record` definition.
+
+[`log.Record`](https://pkg.go.dev/go.opentelemetry.io/otel/log#Record)
+supports only adding attributes.
+In the SDK, we also need to be able to modify the attributes (e.g. removal)
+provided via API.
+
+Moreover it is safer to have these abstraction decoupled.
+E.g. there can be a need for some fields that can be set via API and cannot be modified by the processors.
+
+### Processor.OnEmit to accept Record values
+
+There was a proposal to make the [Processor](#processor)'s `OnEmit`
+to accept a [Record](#record) value instead of a pointer to reduce allocations
+as well as to have design similar to [`slog.Handler`](https://pkg.go.dev/log/slog#Handler).
+
+There have been long discussions within the OpenTelemetry Specification SIG[^5]
+about whether such a design would comply with the specification. The summary
+was that the current processor design flaws are present in other languages as
+well. Therefore, it would be favorable to introduce new processing concepts
+(e.g. chaining processors) in the specification that would coexist with the
+current "mutable" processor design.
+
+The performance disadvantages caused by using a pointer (which at the time of
+writing causes an additional heap allocation) may be mitigated by future
+versions of the Go compiler, thanks to improved escape analysis and
+profile-guided optimization (PGO)[^6].
+
+On the other hand, [Processor](#processor)'s `Enabled` is fine to accept
+a [Record](#record) value as the processors should not mutate the passed
+parameters.
+
+[^1]: [A Guide to the Go Garbage Collector](https://tip.golang.org/doc/gc-guide)
+[^2]: [OpenTelemetry Logging](https://opentelemetry.io/docs/specs/otel/logs)
+[^3]: [Conversation on representing LogRecordProcessor and LogRecordExporter via a single Exporter interface](https://github.com/open-telemetry/opentelemetry-go/pull/4954#discussion_r1515050480)
+[^4]: [Introduce Processor](https://github.com/pellared/opentelemetry-go/pull/9)
+[^5]: [Log record mutations do not have to be visible in next registered processors](https://github.com/open-telemetry/opentelemetry-specification/pull/4067)
+[^6]: [Profile-guided optimization](https://go.dev/doc/pgo)
diff --git a/vendor/go.opentelemetry.io/otel/sdk/log/LICENSE b/vendor/go.opentelemetry.io/otel/sdk/log/LICENSE
new file mode 100644
index 000000000..261eeb9e9
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/sdk/log/LICENSE
@@ -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.
diff --git a/vendor/go.opentelemetry.io/otel/sdk/log/README.md b/vendor/go.opentelemetry.io/otel/sdk/log/README.md
new file mode 100644
index 000000000..729aca091
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/sdk/log/README.md
@@ -0,0 +1,3 @@
+# Log SDK
+
+[![PkgGoDev](https://pkg.go.dev/badge/go.opentelemetry.io/otel/sdk/log)](https://pkg.go.dev/go.opentelemetry.io/otel/sdk/log)
diff --git a/vendor/go.opentelemetry.io/otel/sdk/log/batch.go b/vendor/go.opentelemetry.io/otel/sdk/log/batch.go
new file mode 100644
index 000000000..28c969262
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/sdk/log/batch.go
@@ -0,0 +1,477 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+package log // import "go.opentelemetry.io/otel/sdk/log"
+
+import (
+ "context"
+ "errors"
+ "slices"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "go.opentelemetry.io/otel/internal/global"
+)
+
+const (
+ dfltMaxQSize = 2048
+ dfltExpInterval = time.Second
+ dfltExpTimeout = 30 * time.Second
+ dfltExpMaxBatchSize = 512
+ dfltExpBufferSize = 1
+
+ envarMaxQSize = "OTEL_BLRP_MAX_QUEUE_SIZE"
+ envarExpInterval = "OTEL_BLRP_SCHEDULE_DELAY"
+ envarExpTimeout = "OTEL_BLRP_EXPORT_TIMEOUT"
+ envarExpMaxBatchSize = "OTEL_BLRP_MAX_EXPORT_BATCH_SIZE"
+)
+
+// Compile-time check BatchProcessor implements Processor.
+var _ Processor = (*BatchProcessor)(nil)
+
+// BatchProcessor is a processor that exports batches of log records.
+//
+// Use [NewBatchProcessor] to create a BatchProcessor. An empty BatchProcessor
+// is shut down by default, no records will be batched or exported.
+type BatchProcessor struct {
+ // The BatchProcessor is designed to provide the highest throughput of
+ // log records possible while being compatible with OpenTelemetry. The
+ // entry point of log records is the OnEmit method. This method is designed
+ // to receive records as fast as possible while still honoring shutdown
+ // commands. All records received are enqueued to queue.
+ //
+ // In order to block OnEmit as little as possible, a separate "poll"
+ // goroutine is spawned at the creation of a BatchProcessor. This
+ // goroutine is responsible for batching the queue at regular polled
+ // intervals, or when it is directly signaled to.
+ //
+ // To keep the polling goroutine from backing up, all batches it makes are
+ // exported with a bufferedExporter. This exporter allows the poll
+ // goroutine to enqueue an export payload that will be handled in a
+ // separate goroutine dedicated to the export. This asynchronous behavior
+ // allows the poll goroutine to maintain accurate interval polling.
+ //
+ // ___BatchProcessor____ __Poll Goroutine__ __Export Goroutine__
+ // || || || || || ||
+ // || ********** || || || || ********** ||
+ // || Records=>* OnEmit * || || | - ticker || || * export * ||
+ // || ********** || || | - trigger || || ********** ||
+ // || || || || | || || || ||
+ // || || || || | || || || ||
+ // || __________\/___ || || |*********** || || ______/\_______ ||
+ // || (____queue______)>=||=||===|* batch *===||=||=>[_export_buffer_] ||
+ // || || || |*********** || || ||
+ // ||_____________________|| ||__________________|| ||____________________||
+ //
+ //
+ // The "release valve" in this processing is the record queue. This queue
+ // is a ring buffer. It will overwrite the oldest records first when writes
+ // to OnEmit are made faster than the queue can be flushed. If batches
+ // cannot be flushed to the export buffer, the records will remain in the
+ // queue.
+
+ // exporter is the bufferedExporter all batches are exported with.
+ exporter *bufferExporter
+
+ // q is the active queue of records that have not yet been exported.
+ q *queue
+ // batchSize is the minimum number of records needed before an export is
+ // triggered (unless the interval expires).
+ batchSize int
+
+ // pollTrigger triggers the poll goroutine to flush a batch from the queue.
+ // This is sent to when it is known that the queue contains at least one
+ // complete batch.
+ //
+ // When a send is made to the channel, the poll loop will be reset after
+ // the flush. If there is still enough records in the queue for another
+ // batch the reset of the poll loop will automatically re-trigger itself.
+ // There is no need for the original sender to monitor and resend.
+ pollTrigger chan struct{}
+ // pollKill kills the poll goroutine. This is only expected to be closed
+ // once by the Shutdown method.
+ pollKill chan struct{}
+ // pollDone signals the poll goroutine has completed.
+ pollDone chan struct{}
+
+ // stopped holds the stopped state of the BatchProcessor.
+ stopped atomic.Bool
+
+ noCmp [0]func() //nolint: unused // This is indeed used.
+}
+
+// NewBatchProcessor decorates the provided exporter
+// so that the log records are batched before exporting.
+//
+// All of the exporter's methods are called synchronously.
+func NewBatchProcessor(exporter Exporter, opts ...BatchProcessorOption) *BatchProcessor {
+ cfg := newBatchConfig(opts)
+ if exporter == nil {
+ // Do not panic on nil export.
+ exporter = defaultNoopExporter
+ }
+ // Order is important here. Wrap the timeoutExporter with the chunkExporter
+ // to ensure each export completes in timeout (instead of all chunked
+ // exports).
+ exporter = newTimeoutExporter(exporter, cfg.expTimeout.Value)
+ // Use a chunkExporter to ensure ForceFlush and Shutdown calls are batched
+ // appropriately on export.
+ exporter = newChunkExporter(exporter, cfg.expMaxBatchSize.Value)
+
+ b := &BatchProcessor{
+ exporter: newBufferExporter(exporter, cfg.expBufferSize.Value),
+
+ q: newQueue(cfg.maxQSize.Value),
+ batchSize: cfg.expMaxBatchSize.Value,
+ pollTrigger: make(chan struct{}, 1),
+ pollKill: make(chan struct{}),
+ }
+ b.pollDone = b.poll(cfg.expInterval.Value)
+ return b
+}
+
+// poll spawns a goroutine to handle interval polling and batch exporting. The
+// returned done chan is closed when the spawned goroutine completes.
+func (b *BatchProcessor) poll(interval time.Duration) (done chan struct{}) {
+ done = make(chan struct{})
+
+ ticker := time.NewTicker(interval)
+ // TODO: investigate using a sync.Pool instead of cloning.
+ buf := make([]Record, b.batchSize)
+ go func() {
+ defer close(done)
+ defer ticker.Stop()
+
+ for {
+ select {
+ case <-ticker.C:
+ case <-b.pollTrigger:
+ ticker.Reset(interval)
+ case <-b.pollKill:
+ return
+ }
+
+ if d := b.q.Dropped(); d > 0 {
+ global.Warn("dropped log records", "dropped", d)
+ }
+
+ qLen := b.q.TryDequeue(buf, func(r []Record) bool {
+ ok := b.exporter.EnqueueExport(r)
+ if ok {
+ buf = slices.Clone(buf)
+ }
+ return ok
+ })
+ if qLen >= b.batchSize {
+ // There is another full batch ready. Immediately trigger
+ // another export attempt.
+ select {
+ case b.pollTrigger <- struct{}{}:
+ default:
+ // Another flush signal already received.
+ }
+ }
+ }
+ }()
+ return done
+}
+
+// OnEmit batches provided log record.
+func (b *BatchProcessor) OnEmit(_ context.Context, r *Record) error {
+ if b.stopped.Load() || b.q == nil {
+ return nil
+ }
+ // The record is cloned so that changes done by subsequent processors
+ // are not going to lead to a data race.
+ if n := b.q.Enqueue(r.Clone()); n >= b.batchSize {
+ select {
+ case b.pollTrigger <- struct{}{}:
+ default:
+ // Flush chan full. The poll goroutine will handle this by
+ // re-sending any trigger until the queue has less than batchSize
+ // records.
+ }
+ }
+ return nil
+}
+
+// Shutdown flushes queued log records and shuts down the decorated exporter.
+func (b *BatchProcessor) Shutdown(ctx context.Context) error {
+ if b.stopped.Swap(true) || b.q == nil {
+ return nil
+ }
+
+ // Stop the poll goroutine.
+ close(b.pollKill)
+ select {
+ case <-b.pollDone:
+ case <-ctx.Done():
+ // Out of time.
+ return errors.Join(ctx.Err(), b.exporter.Shutdown(ctx))
+ }
+
+ // Flush remaining queued before exporter shutdown.
+ err := b.exporter.Export(ctx, b.q.Flush())
+ return errors.Join(err, b.exporter.Shutdown(ctx))
+}
+
+var errPartialFlush = errors.New("partial flush: export buffer full")
+
+// Used for testing.
+var ctxErr = func(ctx context.Context) error {
+ return ctx.Err()
+}
+
+// ForceFlush flushes queued log records and flushes the decorated exporter.
+func (b *BatchProcessor) ForceFlush(ctx context.Context) error {
+ if b.stopped.Load() || b.q == nil {
+ return nil
+ }
+
+ buf := make([]Record, b.q.cap)
+ notFlushed := func() bool {
+ var flushed bool
+ _ = b.q.TryDequeue(buf, func(r []Record) bool {
+ flushed = b.exporter.EnqueueExport(r)
+ return flushed
+ })
+ return !flushed
+ }
+ var err error
+ // For as long as ctx allows, try to make a single flush of the queue.
+ for notFlushed() {
+ // Use ctxErr instead of calling ctx.Err directly so we can test
+ // the partial error return.
+ if e := ctxErr(ctx); e != nil {
+ err = errors.Join(e, errPartialFlush)
+ break
+ }
+ }
+ return errors.Join(err, b.exporter.ForceFlush(ctx))
+}
+
+// queue holds a queue of logging records.
+//
+// When the queue becomes full, the oldest records in the queue are
+// overwritten.
+type queue struct {
+ sync.Mutex
+
+ dropped atomic.Uint64
+ cap, len int
+ read, write *ring
+}
+
+func newQueue(size int) *queue {
+ r := newRing(size)
+ return &queue{
+ cap: size,
+ read: r,
+ write: r,
+ }
+}
+
+// Dropped returns the number of Records dropped during enqueueing since the
+// last time Dropped was called.
+func (q *queue) Dropped() uint64 {
+ return q.dropped.Swap(0)
+}
+
+// Enqueue adds r to the queue. The queue size, including the addition of r, is
+// returned.
+//
+// If enqueueing r will exceed the capacity of q, the oldest Record held in q
+// will be dropped and r retained.
+func (q *queue) Enqueue(r Record) int {
+ q.Lock()
+ defer q.Unlock()
+
+ q.write.Value = r
+ q.write = q.write.Next()
+
+ q.len++
+ if q.len > q.cap {
+ // Overflow. Advance read to be the new "oldest".
+ q.len = q.cap
+ q.read = q.read.Next()
+ q.dropped.Add(1)
+ }
+ return q.len
+}
+
+// TryDequeue attempts to dequeue up to len(buf) Records. The available Records
+// will be assigned into buf and passed to write. If write fails, returning
+// false, the Records will not be removed from the queue. If write succeeds,
+// returning true, the dequeued Records are removed from the queue. The number
+// of Records remaining in the queue are returned.
+//
+// When write is called the lock of q is held. The write function must not call
+// other methods of this q that acquire the lock.
+func (q *queue) TryDequeue(buf []Record, write func([]Record) bool) int {
+ q.Lock()
+ defer q.Unlock()
+
+ origRead := q.read
+
+ n := min(len(buf), q.len)
+ for i := 0; i < n; i++ {
+ buf[i] = q.read.Value
+ q.read = q.read.Next()
+ }
+
+ if write(buf[:n]) {
+ q.len -= n
+ } else {
+ q.read = origRead
+ }
+ return q.len
+}
+
+// Flush returns all the Records held in the queue and resets it to be
+// empty.
+func (q *queue) Flush() []Record {
+ q.Lock()
+ defer q.Unlock()
+
+ out := make([]Record, q.len)
+ for i := range out {
+ out[i] = q.read.Value
+ q.read = q.read.Next()
+ }
+ q.len = 0
+
+ return out
+}
+
+type batchConfig struct {
+ maxQSize setting[int]
+ expInterval setting[time.Duration]
+ expTimeout setting[time.Duration]
+ expMaxBatchSize setting[int]
+ expBufferSize setting[int]
+}
+
+func newBatchConfig(options []BatchProcessorOption) batchConfig {
+ var c batchConfig
+ for _, o := range options {
+ c = o.apply(c)
+ }
+
+ c.maxQSize = c.maxQSize.Resolve(
+ clearLessThanOne[int](),
+ getenv[int](envarMaxQSize),
+ clearLessThanOne[int](),
+ fallback[int](dfltMaxQSize),
+ )
+ c.expInterval = c.expInterval.Resolve(
+ clearLessThanOne[time.Duration](),
+ getenv[time.Duration](envarExpInterval),
+ clearLessThanOne[time.Duration](),
+ fallback[time.Duration](dfltExpInterval),
+ )
+ c.expTimeout = c.expTimeout.Resolve(
+ clearLessThanOne[time.Duration](),
+ getenv[time.Duration](envarExpTimeout),
+ clearLessThanOne[time.Duration](),
+ fallback[time.Duration](dfltExpTimeout),
+ )
+ c.expMaxBatchSize = c.expMaxBatchSize.Resolve(
+ clearLessThanOne[int](),
+ getenv[int](envarExpMaxBatchSize),
+ clearLessThanOne[int](),
+ clampMax[int](c.maxQSize.Value),
+ fallback[int](dfltExpMaxBatchSize),
+ )
+ c.expBufferSize = c.expBufferSize.Resolve(
+ clearLessThanOne[int](),
+ fallback[int](dfltExpBufferSize),
+ )
+
+ return c
+}
+
+// BatchProcessorOption applies a configuration to a [BatchProcessor].
+type BatchProcessorOption interface {
+ apply(batchConfig) batchConfig
+}
+
+type batchOptionFunc func(batchConfig) batchConfig
+
+func (fn batchOptionFunc) apply(c batchConfig) batchConfig {
+ return fn(c)
+}
+
+// WithMaxQueueSize sets the maximum queue size used by the Batcher.
+// After the size is reached log records are dropped.
+//
+// If the OTEL_BLRP_MAX_QUEUE_SIZE environment variable is set,
+// and this option is not passed, that variable value will be used.
+//
+// By default, if an environment variable is not set, and this option is not
+// passed, 2048 will be used.
+// The default value is also used when the provided value is less than one.
+func WithMaxQueueSize(size int) BatchProcessorOption {
+ return batchOptionFunc(func(cfg batchConfig) batchConfig {
+ cfg.maxQSize = newSetting(size)
+ return cfg
+ })
+}
+
+// WithExportInterval sets the maximum duration between batched exports.
+//
+// If the OTEL_BLRP_SCHEDULE_DELAY environment variable is set,
+// and this option is not passed, that variable value will be used.
+//
+// By default, if an environment variable is not set, and this option is not
+// passed, 1s will be used.
+// The default value is also used when the provided value is less than one.
+func WithExportInterval(d time.Duration) BatchProcessorOption {
+ return batchOptionFunc(func(cfg batchConfig) batchConfig {
+ cfg.expInterval = newSetting(d)
+ return cfg
+ })
+}
+
+// WithExportTimeout sets the duration after which a batched export is canceled.
+//
+// If the OTEL_BLRP_EXPORT_TIMEOUT environment variable is set,
+// and this option is not passed, that variable value will be used.
+//
+// By default, if an environment variable is not set, and this option is not
+// passed, 30s will be used.
+// The default value is also used when the provided value is less than one.
+func WithExportTimeout(d time.Duration) BatchProcessorOption {
+ return batchOptionFunc(func(cfg batchConfig) batchConfig {
+ cfg.expTimeout = newSetting(d)
+ return cfg
+ })
+}
+
+// WithExportMaxBatchSize sets the maximum batch size of every export.
+// A batch will be split into multiple exports to not exceed this size.
+//
+// If the OTEL_BLRP_MAX_EXPORT_BATCH_SIZE environment variable is set,
+// and this option is not passed, that variable value will be used.
+//
+// By default, if an environment variable is not set, and this option is not
+// passed, 512 will be used.
+// The default value is also used when the provided value is less than one.
+func WithExportMaxBatchSize(size int) BatchProcessorOption {
+ return batchOptionFunc(func(cfg batchConfig) batchConfig {
+ cfg.expMaxBatchSize = newSetting(size)
+ return cfg
+ })
+}
+
+// WithExportBufferSize sets the batch buffer size.
+// Batches will be temporarily kept in a memory buffer until they are exported.
+//
+// By default, a value of 1 will be used.
+// The default value is also used when the provided value is less than one.
+func WithExportBufferSize(size int) BatchProcessorOption {
+ return batchOptionFunc(func(cfg batchConfig) batchConfig {
+ cfg.expBufferSize = newSetting(size)
+ return cfg
+ })
+}
diff --git a/vendor/go.opentelemetry.io/otel/sdk/log/doc.go b/vendor/go.opentelemetry.io/otel/sdk/log/doc.go
new file mode 100644
index 000000000..6a1f1b0e9
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/sdk/log/doc.go
@@ -0,0 +1,36 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+/*
+Package log provides the OpenTelemetry Logs SDK.
+
+See https://opentelemetry.io/docs/concepts/signals/logs/ for information
+about the concept of OpenTelemetry Logs and
+https://opentelemetry.io/docs/concepts/components/ for more information
+about OpenTelemetry SDKs.
+
+The entry point for the log package is [NewLoggerProvider].
+[LoggerProvider] is the object that all Bridge API calls use to create
+Loggers, and ultimately emit log records.
+Also, it is an object that should be used to
+control the life-cycle (start, flush, and shutdown) of the Logs SDK.
+
+A LoggerProvider needs to be configured to process the log records, this is
+done by configuring it with a [Processor] implementation using [WithProcessor].
+The log package provides the [BatchProcessor] and [SimpleProcessor]
+that are configured with an [Exporter] implementation which
+exports the log records to given destination. See
+[go.opentelemetry.io/otel/exporters] for exporters that can be used with these
+Processors.
+
+The data generated by a LoggerProvider needs to include information about its
+origin. A LoggerProvider needs to be configured with a Resource, by using
+[WithResource], to include this information. This Resource
+should be used to describe the unique runtime environment instrumented code
+is being run on. That way when multiple instances of the code are collected
+at a single endpoint their origin is decipherable.
+
+See [go.opentelemetry.io/otel/log] for more information about
+the OpenTelemetry Logs Bridge API.
+*/
+package log // import "go.opentelemetry.io/otel/sdk/log"
diff --git a/vendor/go.opentelemetry.io/otel/sdk/log/exporter.go b/vendor/go.opentelemetry.io/otel/sdk/log/exporter.go
new file mode 100644
index 000000000..e4e3c5402
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/sdk/log/exporter.go
@@ -0,0 +1,321 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+package log // import "go.opentelemetry.io/otel/sdk/log"
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "go.opentelemetry.io/otel"
+)
+
+// Exporter handles the delivery of log records to external receivers.
+type Exporter interface {
+ // Export transmits log records to a receiver.
+ //
+ // The deadline or cancellation of the passed context must be honored. An
+ // appropriate error should be returned in these situations.
+ //
+ // All retry logic must be contained in this function. The SDK does not
+ // implement any retry logic. All errors returned by this function are
+ // considered unrecoverable and will be reported to a configured error
+ // Handler.
+ //
+ // Implementations must not retain the records slice.
+ //
+ // Before modifying a Record, the implementation must use Record.Clone
+ // to create a copy that shares no state with the original.
+ //
+ // Export should never be called concurrently with other Export calls.
+ // However, it may be called concurrently with other methods.
+ Export(ctx context.Context, records []Record) error
+
+ // Shutdown is called when the SDK shuts down. Any cleanup or release of
+ // resources held by the exporter should be done in this call.
+ //
+ // The deadline or cancellation of the passed context must be honored. An
+ // appropriate error should be returned in these situations.
+ //
+ // After Shutdown is called, calls to Export, Shutdown, or ForceFlush
+ // should perform no operation and return nil error.
+ //
+ // Shutdown may be called concurrently with itself or with other methods.
+ Shutdown(ctx context.Context) error
+
+ // ForceFlush exports log records to the configured Exporter that have not yet
+ // been exported.
+ //
+ // The deadline or cancellation of the passed context must be honored. An
+ // appropriate error should be returned in these situations.
+ //
+ // ForceFlush may be called concurrently with itself or with other methods.
+ ForceFlush(ctx context.Context) error
+}
+
+var defaultNoopExporter = &noopExporter{}
+
+type noopExporter struct{}
+
+func (noopExporter) Export(context.Context, []Record) error { return nil }
+
+func (noopExporter) Shutdown(context.Context) error { return nil }
+
+func (noopExporter) ForceFlush(context.Context) error { return nil }
+
+// chunkExporter wraps an Exporter's Export method so it is called with
+// appropriately sized export payloads. Any payload larger than a defined size
+// is chunked into smaller payloads and exported sequentially.
+type chunkExporter struct {
+ Exporter
+
+ // size is the maximum batch size exported.
+ size int
+}
+
+// newChunkExporter wraps exporter. Calls to the Export will have their records
+// payload chunked so they do not exceed size. If size is less than or equal
+// to 0, exporter is returned directly.
+func newChunkExporter(exporter Exporter, size int) Exporter {
+ if size <= 0 {
+ return exporter
+ }
+ return &chunkExporter{Exporter: exporter, size: size}
+}
+
+// Export exports records in chunks no larger than c.size.
+func (c chunkExporter) Export(ctx context.Context, records []Record) error {
+ n := len(records)
+ for i, j := 0, min(c.size, n); i < n; i, j = i+c.size, min(j+c.size, n) {
+ if err := c.Exporter.Export(ctx, records[i:j]); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// timeoutExporter wraps an Exporter and ensures any call to Export will have a
+// timeout for the context.
+type timeoutExporter struct {
+ Exporter
+
+ // timeout is the maximum time an export is attempted.
+ timeout time.Duration
+}
+
+// newTimeoutExporter wraps exporter with an Exporter that limits the context
+// lifetime passed to Export to be timeout. If timeout is less than or equal to
+// zero, exporter will be returned directly.
+func newTimeoutExporter(exp Exporter, timeout time.Duration) Exporter {
+ if timeout <= 0 {
+ return exp
+ }
+ return &timeoutExporter{Exporter: exp, timeout: timeout}
+}
+
+// Export sets the timeout of ctx before calling the Exporter e wraps.
+func (e *timeoutExporter) Export(ctx context.Context, records []Record) error {
+ ctx, cancel := context.WithTimeout(ctx, e.timeout)
+ defer cancel()
+ return e.Exporter.Export(ctx, records)
+}
+
+// exportSync exports all data from input using exporter in a spawned
+// goroutine. The returned chan will be closed when the spawned goroutine
+// completes.
+func exportSync(input <-chan exportData, exporter Exporter) (done chan struct{}) {
+ done = make(chan struct{})
+ go func() {
+ defer close(done)
+ for data := range input {
+ data.DoExport(exporter.Export)
+ }
+ }()
+ return done
+}
+
+// exportData is data related to an export.
+type exportData struct {
+ ctx context.Context
+ records []Record
+
+ // respCh is the channel any error returned from the export will be sent
+ // on. If this is nil, and the export error is non-nil, the error will
+ // passed to the OTel error handler.
+ respCh chan<- error
+}
+
+// DoExport calls exportFn with the data contained in e. The error response
+// will be returned on e's respCh if not nil. The error will be handled by the
+// default OTel error handle if it is not nil and respCh is nil or full.
+func (e exportData) DoExport(exportFn func(context.Context, []Record) error) {
+ if len(e.records) == 0 {
+ e.respond(nil)
+ return
+ }
+
+ e.respond(exportFn(e.ctx, e.records))
+}
+
+func (e exportData) respond(err error) {
+ select {
+ case e.respCh <- err:
+ default:
+ // e.respCh is nil or busy, default to otel.Handler.
+ if err != nil {
+ otel.Handle(err)
+ }
+ }
+}
+
+// bufferExporter provides asynchronous and synchronous export functionality by
+// buffering export requests.
+type bufferExporter struct {
+ Exporter
+
+ input chan exportData
+ inputMu sync.Mutex
+
+ done chan struct{}
+ stopped atomic.Bool
+}
+
+// newBufferExporter returns a new bufferExporter that wraps exporter. The
+// returned bufferExporter will buffer at most size number of export requests.
+// If size is less than zero, zero will be used (i.e. only synchronous
+// exporting will be supported).
+func newBufferExporter(exporter Exporter, size int) *bufferExporter {
+ if size < 0 {
+ size = 0
+ }
+ input := make(chan exportData, size)
+ return &bufferExporter{
+ Exporter: exporter,
+
+ input: input,
+ done: exportSync(input, exporter),
+ }
+}
+
+var errStopped = errors.New("exporter stopped")
+
+func (e *bufferExporter) enqueue(ctx context.Context, records []Record, rCh chan<- error) error {
+ data := exportData{ctx, records, rCh}
+
+ e.inputMu.Lock()
+ defer e.inputMu.Unlock()
+
+ // Check stopped before enqueueing now that e.inputMu is held. This
+ // prevents sends on a closed chan when Shutdown is called concurrently.
+ if e.stopped.Load() {
+ return errStopped
+ }
+
+ select {
+ case e.input <- data:
+ case <-ctx.Done():
+ return ctx.Err()
+ }
+ return nil
+}
+
+// EnqueueExport enqueues an export of records in the context of ctx to be
+// performed asynchronously. This will return true if the records are
+// successfully enqueued (or the bufferExporter is shut down), false otherwise.
+//
+// The passed records are held after this call returns.
+func (e *bufferExporter) EnqueueExport(records []Record) bool {
+ if len(records) == 0 {
+ // Nothing to enqueue, do not waste input space.
+ return true
+ }
+
+ data := exportData{ctx: context.Background(), records: records}
+
+ e.inputMu.Lock()
+ defer e.inputMu.Unlock()
+
+ // Check stopped before enqueueing now that e.inputMu is held. This
+ // prevents sends on a closed chan when Shutdown is called concurrently.
+ if e.stopped.Load() {
+ return true
+ }
+
+ select {
+ case e.input <- data:
+ return true
+ default:
+ return false
+ }
+}
+
+// Export synchronously exports records in the context of ctx. This will not
+// return until the export has been completed.
+func (e *bufferExporter) Export(ctx context.Context, records []Record) error {
+ if len(records) == 0 {
+ return nil
+ }
+
+ resp := make(chan error, 1)
+ err := e.enqueue(ctx, records, resp)
+ if err != nil {
+ if errors.Is(err, errStopped) {
+ return nil
+ }
+ return fmt.Errorf("%w: dropping %d records", err, len(records))
+ }
+
+ select {
+ case err := <-resp:
+ return err
+ case <-ctx.Done():
+ return ctx.Err()
+ }
+}
+
+// ForceFlush flushes buffered exports. Any existing exports that is buffered
+// is flushed before this returns.
+func (e *bufferExporter) ForceFlush(ctx context.Context) error {
+ resp := make(chan error, 1)
+ err := e.enqueue(ctx, nil, resp)
+ if err != nil {
+ if errors.Is(err, errStopped) {
+ return nil
+ }
+ return err
+ }
+
+ select {
+ case <-resp:
+ case <-ctx.Done():
+ return ctx.Err()
+ }
+ return e.Exporter.ForceFlush(ctx)
+}
+
+// Shutdown shuts down e.
+//
+// Any buffered exports are flushed before this returns.
+//
+// All calls to EnqueueExport or Exporter will return nil without any export
+// after this is called.
+func (e *bufferExporter) Shutdown(ctx context.Context) error {
+ if e.stopped.Swap(true) {
+ return nil
+ }
+ e.inputMu.Lock()
+ defer e.inputMu.Unlock()
+
+ // No more sends will be made.
+ close(e.input)
+ select {
+ case <-e.done:
+ case <-ctx.Done():
+ return errors.Join(ctx.Err(), e.Exporter.Shutdown(ctx))
+ }
+ return e.Exporter.Shutdown(ctx)
+}
diff --git a/vendor/go.opentelemetry.io/otel/sdk/log/filter_processor.go b/vendor/go.opentelemetry.io/otel/sdk/log/filter_processor.go
new file mode 100644
index 000000000..5b99a4a99
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/sdk/log/filter_processor.go
@@ -0,0 +1,62 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+package log // import "go.opentelemetry.io/otel/sdk/log"
+
+import (
+ "context"
+
+ "go.opentelemetry.io/otel/log"
+ "go.opentelemetry.io/otel/sdk/instrumentation"
+ "go.opentelemetry.io/otel/sdk/resource"
+)
+
+// FilterProcessor is a [Processor] that knows, and can identify, what [Record]
+// it will process or drop when it is passed to [Processor.OnEmit].
+//
+// This is useful for users that want to know if a [log.Record]
+// will be processed or dropped before they perform complex operations to
+// construct the [log.Record].
+//
+// The SDK's Logger.Enabled returns false
+// if all the registered Processors implement FilterProcessor
+// and they all return false.
+//
+// Processor implementations that choose to support this by satisfying this
+// interface are expected to re-evaluate the [Record] passed to [Processor.OnEmit],
+// it is not expected that the caller to OnEmit will use the functionality
+// from this interface prior to calling OnEmit.
+//
+// See the [go.opentelemetry.io/contrib/processors/minsev] for an example use-case.
+// It provides a Processor used to filter out [Record]
+// that has a [log.Severity] below a threshold.
+type FilterProcessor interface {
+ // Enabled returns whether the Processor will process for the given context
+ // and param.
+ //
+ // The passed param is likely to be a partial record information being
+ // provided (e.g a param with only the Severity set).
+ // If a Processor 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 Processor will process 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
+}
+
+// EnabledParameters represents payload for [FilterProcessor]'s Enabled method.
+type EnabledParameters struct {
+ Resource resource.Resource
+ InstrumentationScope instrumentation.Scope
+ Severity log.Severity
+}
diff --git a/vendor/go.opentelemetry.io/otel/sdk/log/logger.go b/vendor/go.opentelemetry.io/otel/sdk/log/logger.go
new file mode 100644
index 000000000..6211d5d92
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/sdk/log/logger.go
@@ -0,0 +1,110 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+package log // import "go.opentelemetry.io/otel/sdk/log"
+
+import (
+ "context"
+ "time"
+
+ "go.opentelemetry.io/otel"
+ "go.opentelemetry.io/otel/log"
+ "go.opentelemetry.io/otel/log/embedded"
+ "go.opentelemetry.io/otel/sdk/instrumentation"
+ "go.opentelemetry.io/otel/trace"
+)
+
+var now = time.Now
+
+// Compile-time check logger implements log.Logger.
+var _ log.Logger = (*logger)(nil)
+
+type logger struct {
+ embedded.Logger
+
+ provider *LoggerProvider
+ instrumentationScope instrumentation.Scope
+}
+
+func newLogger(p *LoggerProvider, scope instrumentation.Scope) *logger {
+ return &logger{
+ provider: p,
+ instrumentationScope: scope,
+ }
+}
+
+func (l *logger) Emit(ctx context.Context, r log.Record) {
+ newRecord := l.newRecord(ctx, r)
+ for _, p := range l.provider.processors {
+ if err := p.OnEmit(ctx, &newRecord); err != nil {
+ otel.Handle(err)
+ }
+ }
+}
+
+// Enabled returns true if at least one Processor held by the LoggerProvider
+// that created the logger will process param for the provided context and param.
+//
+// If it is not possible to definitively determine the param will be
+// processed, true will be returned by default. A value of false will only be
+// returned if it can be positively verified that no Processor will process.
+func (l *logger) Enabled(ctx context.Context, param log.EnabledParameters) bool {
+ p := EnabledParameters{
+ Resource: *l.provider.resource,
+ InstrumentationScope: l.instrumentationScope,
+ Severity: param.Severity,
+ }
+
+ // If there are more Processors than FilterProcessors,
+ // which means not all Processors are FilterProcessors,
+ // we cannot be sure that all Processors will drop the record.
+ // Therefore, return true.
+ //
+ // If all Processors are FilterProcessors, check if any is enabled.
+ return len(l.provider.processors) > len(l.provider.fltrProcessors) || anyEnabled(ctx, p, l.provider.fltrProcessors)
+}
+
+func anyEnabled(ctx context.Context, param EnabledParameters, fltrs []FilterProcessor) bool {
+ for _, f := range fltrs {
+ if f.Enabled(ctx, param) {
+ // At least one Processor will process the Record.
+ return true
+ }
+ }
+ // No Processor will process the record
+ return false
+}
+
+func (l *logger) newRecord(ctx context.Context, r log.Record) Record {
+ sc := trace.SpanContextFromContext(ctx)
+
+ newRecord := Record{
+ eventName: r.EventName(),
+ timestamp: r.Timestamp(),
+ observedTimestamp: r.ObservedTimestamp(),
+ severity: r.Severity(),
+ severityText: r.SeverityText(),
+ body: r.Body(),
+
+ traceID: sc.TraceID(),
+ spanID: sc.SpanID(),
+ traceFlags: sc.TraceFlags(),
+
+ resource: l.provider.resource,
+ scope: &l.instrumentationScope,
+ attributeValueLengthLimit: l.provider.attributeValueLengthLimit,
+ attributeCountLimit: l.provider.attributeCountLimit,
+ }
+
+ // This field SHOULD be set once the event is observed by OpenTelemetry.
+ if newRecord.observedTimestamp.IsZero() {
+ newRecord.observedTimestamp = now()
+ }
+
+ r.WalkAttributes(func(kv log.KeyValue) bool {
+ newRecord.AddAttributes(kv)
+ return true
+ })
+
+ return newRecord
+}
diff --git a/vendor/go.opentelemetry.io/otel/sdk/log/processor.go b/vendor/go.opentelemetry.io/otel/sdk/log/processor.go
new file mode 100644
index 000000000..c9b306f23
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/sdk/log/processor.go
@@ -0,0 +1,56 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+package log // import "go.opentelemetry.io/otel/sdk/log"
+
+import (
+ "context"
+)
+
+// Processor handles the processing of log records.
+//
+// Any of the Processor's methods may be called concurrently with itself
+// or with other methods. It is the responsibility of the Processor to manage
+// this concurrency.
+//
+// See [FilterProcessor] for information about how a Processor can support filtering.
+type Processor interface {
+ // OnEmit is called when a Record is emitted.
+ //
+ // OnEmit will be called independent of Enabled. Implementations need to
+ // validate the arguments themselves before processing.
+ //
+ // Implementation should not interrupt the record processing
+ // if the context is canceled.
+ //
+ // All retry logic must be contained in this function. The SDK does not
+ // implement any retry logic. All errors returned by this function are
+ // considered unrecoverable and will be reported to a configured error
+ // Handler.
+ //
+ // The SDK invokes the processors sequentially in the same order as
+ // they were registered using WithProcessor.
+ // Implementations may synchronously modify the record so that the changes
+ // are visible in the next registered processor.
+ // Notice that Record is not concurrent safe. Therefore, asynchronous
+ // processing may cause race conditions. Use Record.Clone
+ // to create a copy that shares no state with the original.
+ OnEmit(ctx context.Context, record *Record) error
+
+ // Shutdown is called when the SDK shuts down. Any cleanup or release of
+ // resources held by the exporter should be done in this call.
+ //
+ // The deadline or cancellation of the passed context must be honored. An
+ // appropriate error should be returned in these situations.
+ //
+ // After Shutdown is called, calls to Export, Shutdown, or ForceFlush
+ // should perform no operation and return nil error.
+ Shutdown(ctx context.Context) error
+
+ // ForceFlush exports log records to the configured Exporter that have not yet
+ // been exported.
+ //
+ // The deadline or cancellation of the passed context must be honored. An
+ // appropriate error should be returned in these situations.
+ ForceFlush(ctx context.Context) error
+}
diff --git a/vendor/go.opentelemetry.io/otel/sdk/log/provider.go b/vendor/go.opentelemetry.io/otel/sdk/log/provider.go
new file mode 100644
index 000000000..096944ea1
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/sdk/log/provider.go
@@ -0,0 +1,256 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+package log // import "go.opentelemetry.io/otel/sdk/log"
+
+import (
+ "context"
+ "errors"
+ "sync"
+ "sync/atomic"
+
+ "go.opentelemetry.io/otel"
+ "go.opentelemetry.io/otel/internal/global"
+ "go.opentelemetry.io/otel/log"
+ "go.opentelemetry.io/otel/log/embedded"
+ "go.opentelemetry.io/otel/log/noop"
+ "go.opentelemetry.io/otel/sdk/instrumentation"
+ "go.opentelemetry.io/otel/sdk/resource"
+)
+
+const (
+ defaultAttrCntLim = 128
+ defaultAttrValLenLim = -1
+
+ envarAttrCntLim = "OTEL_LOGRECORD_ATTRIBUTE_COUNT_LIMIT"
+ envarAttrValLenLim = "OTEL_LOGRECORD_ATTRIBUTE_VALUE_LENGTH_LIMIT"
+)
+
+type providerConfig struct {
+ resource *resource.Resource
+ processors []Processor
+ fltrProcessors []FilterProcessor
+ attrCntLim setting[int]
+ attrValLenLim setting[int]
+}
+
+func newProviderConfig(opts []LoggerProviderOption) providerConfig {
+ var c providerConfig
+ for _, opt := range opts {
+ c = opt.apply(c)
+ }
+
+ if c.resource == nil {
+ c.resource = resource.Default()
+ }
+
+ c.attrCntLim = c.attrCntLim.Resolve(
+ getenv[int](envarAttrCntLim),
+ fallback[int](defaultAttrCntLim),
+ )
+
+ c.attrValLenLim = c.attrValLenLim.Resolve(
+ getenv[int](envarAttrValLenLim),
+ fallback[int](defaultAttrValLenLim),
+ )
+
+ return c
+}
+
+// LoggerProvider handles the creation and coordination of Loggers. All Loggers
+// created by a LoggerProvider will be associated with the same Resource.
+type LoggerProvider struct {
+ embedded.LoggerProvider
+
+ resource *resource.Resource
+ processors []Processor
+ fltrProcessors []FilterProcessor
+ attributeCountLimit int
+ attributeValueLengthLimit int
+
+ loggersMu sync.Mutex
+ loggers map[instrumentation.Scope]*logger
+
+ stopped atomic.Bool
+
+ noCmp [0]func() //nolint: unused // This is indeed used.
+}
+
+// Compile-time check LoggerProvider implements log.LoggerProvider.
+var _ log.LoggerProvider = (*LoggerProvider)(nil)
+
+// NewLoggerProvider returns a new and configured LoggerProvider.
+//
+// By default, the returned LoggerProvider is configured with the default
+// Resource and no Processors. Processors cannot be added after a LoggerProvider is
+// created. This means the returned LoggerProvider, one created with no
+// Processors, will perform no operations.
+func NewLoggerProvider(opts ...LoggerProviderOption) *LoggerProvider {
+ cfg := newProviderConfig(opts)
+ return &LoggerProvider{
+ resource: cfg.resource,
+ processors: cfg.processors,
+ fltrProcessors: cfg.fltrProcessors,
+ attributeCountLimit: cfg.attrCntLim.Value,
+ attributeValueLengthLimit: cfg.attrValLenLim.Value,
+ }
+}
+
+// Logger returns a new [log.Logger] with the provided name and configuration.
+//
+// If p is shut down, a [noop.Logger] instance is returned.
+//
+// This method can be called concurrently.
+func (p *LoggerProvider) Logger(name string, opts ...log.LoggerOption) log.Logger {
+ if name == "" {
+ global.Warn("Invalid Logger name.", "name", name)
+ }
+
+ if p.stopped.Load() {
+ return noop.NewLoggerProvider().Logger(name, opts...)
+ }
+
+ cfg := log.NewLoggerConfig(opts...)
+ scope := instrumentation.Scope{
+ Name: name,
+ Version: cfg.InstrumentationVersion(),
+ SchemaURL: cfg.SchemaURL(),
+ Attributes: cfg.InstrumentationAttributes(),
+ }
+
+ p.loggersMu.Lock()
+ defer p.loggersMu.Unlock()
+
+ if p.loggers == nil {
+ l := newLogger(p, scope)
+ p.loggers = map[instrumentation.Scope]*logger{scope: l}
+ return l
+ }
+
+ l, ok := p.loggers[scope]
+ if !ok {
+ l = newLogger(p, scope)
+ p.loggers[scope] = l
+ }
+
+ return l
+}
+
+// Shutdown shuts down the provider and all processors.
+//
+// This method can be called concurrently.
+func (p *LoggerProvider) Shutdown(ctx context.Context) error {
+ stopped := p.stopped.Swap(true)
+ if stopped {
+ return nil
+ }
+
+ var err error
+ for _, p := range p.processors {
+ err = errors.Join(err, p.Shutdown(ctx))
+ }
+ return err
+}
+
+// ForceFlush flushes all processors.
+//
+// This method can be called concurrently.
+func (p *LoggerProvider) ForceFlush(ctx context.Context) error {
+ if p.stopped.Load() {
+ return nil
+ }
+
+ var err error
+ for _, p := range p.processors {
+ err = errors.Join(err, p.ForceFlush(ctx))
+ }
+ return err
+}
+
+// LoggerProviderOption applies a configuration option value to a LoggerProvider.
+type LoggerProviderOption interface {
+ apply(providerConfig) providerConfig
+}
+
+type loggerProviderOptionFunc func(providerConfig) providerConfig
+
+func (fn loggerProviderOptionFunc) apply(c providerConfig) providerConfig {
+ return fn(c)
+}
+
+// WithResource associates a Resource with a LoggerProvider. This Resource
+// represents the entity producing telemetry and is associated with all Loggers
+// the LoggerProvider will create.
+//
+// By default, if this Option is not used, the default Resource from the
+// go.opentelemetry.io/otel/sdk/resource package will be used.
+func WithResource(res *resource.Resource) LoggerProviderOption {
+ return loggerProviderOptionFunc(func(cfg providerConfig) providerConfig {
+ var err error
+ cfg.resource, err = resource.Merge(resource.Environment(), res)
+ if err != nil {
+ otel.Handle(err)
+ }
+ return cfg
+ })
+}
+
+// WithProcessor associates Processor with a LoggerProvider.
+//
+// By default, if this option is not used, the LoggerProvider will perform no
+// operations; no data will be exported without a processor.
+//
+// The SDK invokes the processors sequentially in the same order as they were
+// registered.
+//
+// For production, use [NewBatchProcessor] to batch log records before they are exported.
+// For testing and debugging, use [NewSimpleProcessor] to synchronously export log records.
+//
+// See [FilterProcessor] for information about how a Processor can support filtering.
+func WithProcessor(processor Processor) LoggerProviderOption {
+ return loggerProviderOptionFunc(func(cfg providerConfig) providerConfig {
+ cfg.processors = append(cfg.processors, processor)
+ if f, ok := processor.(FilterProcessor); ok {
+ cfg.fltrProcessors = append(cfg.fltrProcessors, f)
+ }
+ return cfg
+ })
+}
+
+// WithAttributeCountLimit sets the maximum allowed log record attribute count.
+// Any attribute added to a log record once this limit is reached will be dropped.
+//
+// Setting this to zero means no attributes will be recorded.
+//
+// Setting this to a negative value means no limit is applied.
+//
+// If the OTEL_LOGRECORD_ATTRIBUTE_COUNT_LIMIT environment variable is set,
+// and this option is not passed, that variable value will be used.
+//
+// By default, if an environment variable is not set, and this option is not
+// passed, 128 will be used.
+func WithAttributeCountLimit(limit int) LoggerProviderOption {
+ return loggerProviderOptionFunc(func(cfg providerConfig) providerConfig {
+ cfg.attrCntLim = newSetting(limit)
+ return cfg
+ })
+}
+
+// AttributeValueLengthLimit sets the maximum allowed attribute value length.
+//
+// This limit only applies to string and string slice attribute values.
+// Any string longer than this value will be truncated to this length.
+//
+// Setting this to a negative value means no limit is applied.
+//
+// If the OTEL_LOGRECORD_ATTRIBUTE_VALUE_LENGTH_LIMIT environment variable is set,
+// and this option is not passed, that variable value will be used.
+//
+// By default, if an environment variable is not set, and this option is not
+// passed, no limit (-1) will be used.
+func WithAttributeValueLengthLimit(limit int) LoggerProviderOption {
+ return loggerProviderOptionFunc(func(cfg providerConfig) providerConfig {
+ cfg.attrValLenLim = newSetting(limit)
+ return cfg
+ })
+}
diff --git a/vendor/go.opentelemetry.io/otel/sdk/log/record.go b/vendor/go.opentelemetry.io/otel/sdk/log/record.go
new file mode 100644
index 000000000..a13fcac7b
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/sdk/log/record.go
@@ -0,0 +1,518 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+package log // import "go.opentelemetry.io/otel/sdk/log"
+
+import (
+ "slices"
+ "strings"
+ "sync"
+ "time"
+ "unicode/utf8"
+
+ "go.opentelemetry.io/otel/internal/global"
+ "go.opentelemetry.io/otel/log"
+ "go.opentelemetry.io/otel/sdk/instrumentation"
+ "go.opentelemetry.io/otel/sdk/resource"
+ "go.opentelemetry.io/otel/trace"
+)
+
+// 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
+
+var logAttrDropped = sync.OnceFunc(func() {
+ global.Warn("limit reached: dropping log Record attributes")
+})
+
+// indexPool is a pool of index maps used for de-duplication.
+var indexPool = sync.Pool{
+ New: func() any { return make(map[string]int) },
+}
+
+func getIndex() map[string]int {
+ return indexPool.Get().(map[string]int)
+}
+
+func putIndex(index map[string]int) {
+ clear(index)
+ indexPool.Put(index)
+}
+
+// Record is a log record emitted by the Logger.
+// A log record with non-empty event name is interpreted as an event record.
+//
+// Do not create instances of Record on your own in production code.
+// You can use [go.opentelemetry.io/otel/sdk/log/logtest.RecordFactory]
+// for testing purposes.
+type Record struct {
+ // Do not embed the log.Record. Attributes need to be overwrite-able and
+ // deep-copying needs to be possible.
+
+ eventName string
+ timestamp time.Time
+ observedTimestamp time.Time
+ severity log.Severity
+ severityText string
+ body log.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]log.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 []log.KeyValue
+
+ // dropped is the count of attributes that have been dropped when limits
+ // were reached.
+ dropped int
+
+ traceID trace.TraceID
+ spanID trace.SpanID
+ traceFlags trace.TraceFlags
+
+ // resource represents the entity that collected the log.
+ resource *resource.Resource
+
+ // scope is the Scope that the Logger was created with.
+ scope *instrumentation.Scope
+
+ attributeValueLengthLimit int
+ attributeCountLimit int
+
+ noCmp [0]func() //nolint: unused // This is indeed used.
+}
+
+func (r *Record) addDropped(n int) {
+ logAttrDropped()
+ r.dropped += n
+}
+
+func (r *Record) setDropped(n int) {
+ logAttrDropped()
+ r.dropped = n
+}
+
+// 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() log.Severity {
+ return r.severity
+}
+
+// SetSeverity sets the severity level of the log record.
+func (r *Record) SetSeverity(level log.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() log.Value {
+ return r.body
+}
+
+// SetBody sets the body of the log record.
+func (r *Record) SetBody(v log.Value) {
+ r.body = v
+}
+
+// WalkAttributes walks all attributes the log record holds by calling f for
+// each on each [log.KeyValue] in the [Record]. Iteration stops if f returns false.
+func (r *Record) WalkAttributes(f func(log.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.
+// Attributes in attrs will overwrite any attribute already added to r with the same key.
+func (r *Record) AddAttributes(attrs ...log.KeyValue) {
+ n := r.AttributesLen()
+ if n == 0 {
+ // Avoid the more complex duplicate map lookups below.
+ var drop int
+ attrs, drop = dedup(attrs)
+ r.setDropped(drop)
+
+ attrs, drop = head(attrs, r.attributeCountLimit)
+ r.addDropped(drop)
+
+ r.addAttrs(attrs)
+ return
+ }
+
+ // Used to find duplicates between attrs and existing attributes in r.
+ rIndex := r.attrIndex()
+ defer putIndex(rIndex)
+
+ // Unique attrs that need to be added to r. This uses the same underlying
+ // array as attrs.
+ //
+ // Note, do not iterate attrs twice by just calling dedup(attrs) here.
+ unique := attrs[:0]
+ // Used to find duplicates within attrs itself. The index value is the
+ // index of the element in unique.
+ uIndex := getIndex()
+ defer putIndex(uIndex)
+
+ // Deduplicate attrs within the scope of all existing attributes.
+ for _, a := range attrs {
+ // Last-value-wins for any duplicates in attrs.
+ idx, found := uIndex[a.Key]
+ if found {
+ r.addDropped(1)
+ unique[idx] = a
+ continue
+ }
+
+ idx, found = rIndex[a.Key]
+ if found {
+ // New attrs overwrite any existing with the same key.
+ r.addDropped(1)
+ if idx < 0 {
+ r.front[-(idx + 1)] = a
+ } else {
+ r.back[idx] = a
+ }
+ } else {
+ // Unique attribute.
+ unique = append(unique, a)
+ uIndex[a.Key] = len(unique) - 1
+ }
+ }
+ attrs = unique
+
+ if r.attributeCountLimit > 0 && n+len(attrs) > r.attributeCountLimit {
+ // Truncate the now unique attributes to comply with limit.
+ //
+ // Do not use head(attrs, r.attributeCountLimit - n) here. If
+ // (r.attributeCountLimit - n) <= 0 attrs needs to be emptied.
+ last := max(0, r.attributeCountLimit-n)
+ r.addDropped(len(attrs) - last)
+ attrs = attrs[:last]
+ }
+
+ r.addAttrs(attrs)
+}
+
+// attrIndex returns an index map for all attributes in the Record r. The index
+// maps the attribute key to location the attribute is stored. If the value is
+// < 0 then -(value + 1) (e.g. -1 -> 0, -2 -> 1, -3 -> 2) represents the index
+// in r.nFront. Otherwise, the index is the exact index of r.back.
+//
+// The returned index is taken from the indexPool. It is the callers
+// responsibility to return the index to that pool (putIndex) when done.
+func (r *Record) attrIndex() map[string]int {
+ index := getIndex()
+ for i := 0; i < r.nFront; i++ {
+ key := r.front[i].Key
+ index[key] = -i - 1 // stored in front: negative index.
+ }
+ for i := 0; i < len(r.back); i++ {
+ key := r.back[i].Key
+ index[key] = i // stored in back: positive index.
+ }
+ return index
+}
+
+// addAttrs adds attrs to the Record r. This does not validate any limits or
+// duplication of attributes, these tasks are left to the caller to handle
+// prior to calling.
+func (r *Record) addAttrs(attrs []log.KeyValue) {
+ var i int
+ for i = 0; i < len(attrs) && r.nFront < len(r.front); i++ {
+ a := attrs[i]
+ r.front[r.nFront] = r.applyAttrLimits(a)
+ r.nFront++
+ }
+
+ for j, a := range attrs[i:] {
+ attrs[i+j] = r.applyAttrLimits(a)
+ }
+ r.back = slices.Grow(r.back, len(attrs[i:]))
+ r.back = append(r.back, attrs[i:]...)
+}
+
+// SetAttributes sets (and overrides) attributes to the log record.
+func (r *Record) SetAttributes(attrs ...log.KeyValue) {
+ var drop int
+ attrs, drop = dedup(attrs)
+ r.setDropped(drop)
+
+ attrs, drop = head(attrs, r.attributeCountLimit)
+ r.addDropped(drop)
+
+ r.nFront = 0
+ var i int
+ for i = 0; i < len(attrs) && r.nFront < len(r.front); i++ {
+ a := attrs[i]
+ r.front[r.nFront] = r.applyAttrLimits(a)
+ r.nFront++
+ }
+
+ r.back = slices.Clone(attrs[i:])
+ for i, a := range r.back {
+ r.back[i] = r.applyAttrLimits(a)
+ }
+}
+
+// head returns the first n values of kvs along with the number of elements
+// dropped. If n is less than or equal to zero, kvs is returned with 0.
+func head(kvs []log.KeyValue, n int) (out []log.KeyValue, dropped int) {
+ if n > 0 && len(kvs) > n {
+ return kvs[:n], len(kvs) - n
+ }
+ return kvs, 0
+}
+
+// dedup deduplicates kvs front-to-back with the last value saved.
+func dedup(kvs []log.KeyValue) (unique []log.KeyValue, dropped int) {
+ index := getIndex()
+ defer putIndex(index)
+
+ unique = kvs[:0] // Use the same underlying array as kvs.
+ for _, a := range kvs {
+ idx, found := index[a.Key]
+ if found {
+ dropped++
+ unique[idx] = a
+ } else {
+ unique = append(unique, a)
+ index[a.Key] = len(unique) - 1
+ }
+ }
+ return unique, dropped
+}
+
+// AttributesLen returns the number of attributes in the log record.
+func (r *Record) AttributesLen() int {
+ return r.nFront + len(r.back)
+}
+
+// DroppedAttributes returns the number of attributes dropped due to limits
+// being reached.
+func (r *Record) DroppedAttributes() int {
+ return r.dropped
+}
+
+// TraceID returns the trace ID or empty array.
+func (r *Record) TraceID() trace.TraceID {
+ return r.traceID
+}
+
+// SetTraceID sets the trace ID.
+func (r *Record) SetTraceID(id trace.TraceID) {
+ r.traceID = id
+}
+
+// SpanID returns the span ID or empty array.
+func (r *Record) SpanID() trace.SpanID {
+ return r.spanID
+}
+
+// SetSpanID sets the span ID.
+func (r *Record) SetSpanID(id trace.SpanID) {
+ r.spanID = id
+}
+
+// TraceFlags returns the trace flags.
+func (r *Record) TraceFlags() trace.TraceFlags {
+ return r.traceFlags
+}
+
+// SetTraceFlags sets the trace flags.
+func (r *Record) SetTraceFlags(flags trace.TraceFlags) {
+ r.traceFlags = flags
+}
+
+// Resource returns the entity that collected the log.
+func (r *Record) Resource() resource.Resource {
+ if r.resource == nil {
+ return *resource.Empty()
+ }
+ return *r.resource
+}
+
+// InstrumentationScope returns the scope that the Logger was created with.
+func (r *Record) InstrumentationScope() instrumentation.Scope {
+ if r.scope == nil {
+ return instrumentation.Scope{}
+ }
+ return *r.scope
+}
+
+// Clone returns a copy of the record with no shared state. The original record
+// and the clone can both be modified without interfering with each other.
+func (r *Record) Clone() Record {
+ res := *r
+ res.back = slices.Clone(r.back)
+ return res
+}
+
+func (r *Record) applyAttrLimits(attr log.KeyValue) log.KeyValue {
+ attr.Value = r.applyValueLimits(attr.Value)
+ return attr
+}
+
+func (r *Record) applyValueLimits(val log.Value) log.Value {
+ switch val.Kind() {
+ case log.KindString:
+ s := val.AsString()
+ if len(s) > r.attributeValueLengthLimit {
+ val = log.StringValue(truncate(r.attributeValueLengthLimit, s))
+ }
+ case log.KindSlice:
+ sl := val.AsSlice()
+ for i := range sl {
+ sl[i] = r.applyValueLimits(sl[i])
+ }
+ val = log.SliceValue(sl...)
+ case log.KindMap:
+ // Deduplicate then truncate. Do not do at the same time to avoid
+ // wasted truncation operations.
+ kvs, dropped := dedup(val.AsMap())
+ r.addDropped(dropped)
+ for i := range kvs {
+ kvs[i] = r.applyAttrLimits(kvs[i])
+ }
+ val = log.MapValue(kvs...)
+ }
+ return val
+}
+
+// truncate returns a truncated version of s such that it contains less than
+// the limit number of characters. Truncation is applied by returning the limit
+// number of valid characters contained in s.
+//
+// If limit is negative, it returns the original string.
+//
+// UTF-8 is supported. When truncating, all invalid characters are dropped
+// before applying truncation.
+//
+// If s already contains less than the limit number of bytes, it is returned
+// unchanged. No invalid characters are removed.
+func truncate(limit int, s string) string {
+ // This prioritize performance in the following order based on the most
+ // common expected use-cases.
+ //
+ // - Short values less than the default limit (128).
+ // - Strings with valid encodings that exceed the limit.
+ // - No limit.
+ // - Strings with invalid encodings that exceed the limit.
+ if limit < 0 || len(s) <= limit {
+ return s
+ }
+
+ // Optimistically, assume all valid UTF-8.
+ var b strings.Builder
+ count := 0
+ for i, c := range s {
+ if c != utf8.RuneError {
+ count++
+ if count > limit {
+ return s[:i]
+ }
+ continue
+ }
+
+ _, size := utf8.DecodeRuneInString(s[i:])
+ if size == 1 {
+ // Invalid encoding.
+ b.Grow(len(s) - 1)
+ _, _ = b.WriteString(s[:i])
+ s = s[i:]
+ break
+ }
+ }
+
+ // Fast-path, no invalid input.
+ if b.Cap() == 0 {
+ return s
+ }
+
+ // Truncate while validating UTF-8.
+ for i := 0; i < len(s) && count < limit; {
+ c := s[i]
+ if c < utf8.RuneSelf {
+ // Optimization for single byte runes (common case).
+ _ = b.WriteByte(c)
+ i++
+ count++
+ continue
+ }
+
+ _, size := utf8.DecodeRuneInString(s[i:])
+ if size == 1 {
+ // We checked for all 1-byte runes above, this is a RuneError.
+ i++
+ continue
+ }
+
+ _, _ = b.WriteString(s[i : i+size])
+ i += size
+ count++
+ }
+
+ return b.String()
+}
diff --git a/vendor/go.opentelemetry.io/otel/sdk/log/ring.go b/vendor/go.opentelemetry.io/otel/sdk/log/ring.go
new file mode 100644
index 000000000..5e84cb164
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/sdk/log/ring.go
@@ -0,0 +1,82 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package log // import "go.opentelemetry.io/otel/sdk/log"
+
+// A ring is an element of a circular list, or ring. Rings do not have a
+// beginning or end; a pointer to any ring element serves as reference to the
+// entire ring. Empty rings are represented as nil ring pointers. The zero
+// value for a ring is a one-element ring with a nil Value.
+//
+// This is copied from the "container/ring" package. It uses a Record type for
+// Value instead of any to avoid allocations.
+type ring struct {
+ next, prev *ring
+ Value Record
+}
+
+func (r *ring) init() *ring {
+ r.next = r
+ r.prev = r
+ return r
+}
+
+// Next returns the next ring element. r must not be empty.
+func (r *ring) Next() *ring {
+ if r.next == nil {
+ return r.init()
+ }
+ return r.next
+}
+
+// Prev returns the previous ring element. r must not be empty.
+func (r *ring) Prev() *ring {
+ if r.next == nil {
+ return r.init()
+ }
+ return r.prev
+}
+
+// newRing creates a ring of n elements.
+func newRing(n int) *ring {
+ if n <= 0 {
+ return nil
+ }
+ r := new(ring)
+ p := r
+ for i := 1; i < n; i++ {
+ p.next = &ring{prev: p}
+ p = p.next
+ }
+ p.next = r
+ r.prev = p
+ return r
+}
+
+// Len computes the number of elements in ring r. It executes in time
+// proportional to the number of elements.
+func (r *ring) Len() int {
+ n := 0
+ if r != nil {
+ n = 1
+ for p := r.Next(); p != r; p = p.next {
+ n++
+ }
+ }
+ return n
+}
+
+// Do calls function f on each element of the ring, in forward order. The
+// behavior of Do is undefined if f changes *r.
+func (r *ring) Do(f func(Record)) {
+ if r != nil {
+ f(r.Value)
+ for p := r.Next(); p != r; p = p.next {
+ f(p.Value)
+ }
+ }
+}
diff --git a/vendor/go.opentelemetry.io/otel/sdk/log/setting.go b/vendor/go.opentelemetry.io/otel/sdk/log/setting.go
new file mode 100644
index 000000000..72107a9e5
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/sdk/log/setting.go
@@ -0,0 +1,119 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+package log // import "go.opentelemetry.io/otel/sdk/log"
+
+import (
+ "fmt"
+ "os"
+ "strconv"
+ "time"
+
+ "go.opentelemetry.io/otel"
+)
+
+// setting is a configuration setting value.
+type setting[T any] struct {
+ Value T
+ Set bool
+}
+
+// newSetting returns a new [setting] with the value set.
+func newSetting[T any](value T) setting[T] {
+ return setting[T]{Value: value, Set: true}
+}
+
+// resolver returns an updated setting after applying an resolution operation.
+type resolver[T any] func(setting[T]) setting[T]
+
+// Resolve returns a resolved version of s.
+//
+// It will apply all the passed fn in the order provided, chaining together the
+// return setting to the next input. The setting s is used as the initial
+// argument to the first fn.
+//
+// Each fn needs to validate if it should apply given the Set state of the
+// setting. This will not perform any checks on the set state when chaining
+// function.
+func (s setting[T]) Resolve(fn ...resolver[T]) setting[T] {
+ for _, f := range fn {
+ s = f(s)
+ }
+ return s
+}
+
+// clampMax returns a resolver that will ensure a setting value is no greater
+// than n. If it is, the value is set to n.
+func clampMax[T ~int | ~int64](n T) resolver[T] {
+ return func(s setting[T]) setting[T] {
+ if s.Value > n {
+ s.Value = n
+ }
+ return s
+ }
+}
+
+// clearLessThanOne returns a resolver that will clear a setting value and
+// change its set state to false if its value is less than 1.
+func clearLessThanOne[T ~int | ~int64]() resolver[T] {
+ return func(s setting[T]) setting[T] {
+ if s.Value < 1 {
+ s.Value = 0
+ s.Set = false
+ }
+ return s
+ }
+}
+
+// getenv returns a resolver that will apply an integer environment variable
+// value associated with key to a setting value.
+//
+// If the input setting to the resolver is set, the environment variable will
+// not be applied.
+//
+// If the environment variable value associated with key is not an integer, an
+// error will be sent to the OTel error handler and the setting will not be
+// updated.
+//
+// If the setting value is a [time.Duration] type, the environment variable
+// will be interpreted as a duration of milliseconds.
+func getenv[T ~int | ~int64](key string) resolver[T] {
+ return func(s setting[T]) setting[T] {
+ if s.Set {
+ // Passed, valid, options have precedence.
+ return s
+ }
+
+ if v := os.Getenv(key); v != "" {
+ n, err := strconv.Atoi(v)
+ if err != nil {
+ otel.Handle(fmt.Errorf("invalid %s value %s: %w", key, v, err))
+ } else {
+ switch any(s.Value).(type) {
+ case time.Duration:
+ // OTel duration envar are in millisecond.
+ s.Value = T(time.Duration(n) * time.Millisecond)
+ default:
+ s.Value = T(n)
+ }
+ s.Set = true
+ }
+ }
+ return s
+ }
+}
+
+// fallback returns a resolve that will set a setting value to val if it is not
+// already set.
+//
+// This is usually passed at the end of a resolver chain to ensure a default is
+// applied if the setting has not already been set.
+func fallback[T any](val T) resolver[T] {
+ return func(s setting[T]) setting[T] {
+ if !s.Set {
+ s.Value = val
+ s.Set = true
+ }
+ return s
+ }
+}
diff --git a/vendor/go.opentelemetry.io/otel/sdk/log/simple.go b/vendor/go.opentelemetry.io/otel/sdk/log/simple.go
new file mode 100644
index 000000000..002e52cae
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/sdk/log/simple.go
@@ -0,0 +1,82 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+package log // import "go.opentelemetry.io/otel/sdk/log"
+
+import (
+ "context"
+ "sync"
+)
+
+// Compile-time check SimpleProcessor implements Processor.
+var _ Processor = (*SimpleProcessor)(nil)
+
+// SimpleProcessor is an processor that synchronously exports log records.
+//
+// Use [NewSimpleProcessor] to create a SimpleProcessor.
+type SimpleProcessor struct {
+ mu sync.Mutex
+ exporter Exporter
+
+ noCmp [0]func() //nolint: unused // This is indeed used.
+}
+
+// NewSimpleProcessor is a simple Processor adapter.
+//
+// This Processor is not recommended for production use due to its synchronous
+// nature, which makes it suitable for testing, debugging, or demonstrating
+// other features, but can lead to slow performance and high computational
+// overhead. For production environments, it is recommended to use
+// [NewBatchProcessor] instead. However, there may be exceptions where certain
+// [Exporter] implementations perform better with this Processor.
+func NewSimpleProcessor(exporter Exporter, _ ...SimpleProcessorOption) *SimpleProcessor {
+ return &SimpleProcessor{exporter: exporter}
+}
+
+var simpleProcRecordsPool = sync.Pool{
+ New: func() any {
+ records := make([]Record, 1)
+ return &records
+ },
+}
+
+// OnEmit batches provided log record.
+func (s *SimpleProcessor) OnEmit(ctx context.Context, r *Record) error {
+ if s.exporter == nil {
+ return nil
+ }
+
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ records := simpleProcRecordsPool.Get().(*[]Record)
+ (*records)[0] = *r
+ defer func() {
+ simpleProcRecordsPool.Put(records)
+ }()
+
+ return s.exporter.Export(ctx, *records)
+}
+
+// Shutdown shuts down the exporter.
+func (s *SimpleProcessor) Shutdown(ctx context.Context) error {
+ if s.exporter == nil {
+ return nil
+ }
+
+ return s.exporter.Shutdown(ctx)
+}
+
+// ForceFlush flushes the exporter.
+func (s *SimpleProcessor) ForceFlush(ctx context.Context) error {
+ if s.exporter == nil {
+ return nil
+ }
+
+ return s.exporter.ForceFlush(ctx)
+}
+
+// SimpleProcessorOption applies a configuration to a [SimpleProcessor].
+type SimpleProcessorOption interface {
+ apply()
+}
diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/tracetest/README.md b/vendor/go.opentelemetry.io/otel/sdk/trace/tracetest/README.md
new file mode 100644
index 000000000..0678d6564
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/sdk/trace/tracetest/README.md
@@ -0,0 +1,3 @@
+# SDK Trace test
+
+[![PkgGoDev](https://pkg.go.dev/badge/go.opentelemetry.io/otel/sdk/trace/tracetest)](https://pkg.go.dev/go.opentelemetry.io/otel/sdk/trace/tracetest)
diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/tracetest/exporter.go b/vendor/go.opentelemetry.io/otel/sdk/trace/tracetest/exporter.go
new file mode 100644
index 000000000..07117495a
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/sdk/trace/tracetest/exporter.go
@@ -0,0 +1,74 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+// Package tracetest is a testing helper package for the SDK. User can
+// configure no-op or in-memory exporters to verify different SDK behaviors or
+// custom instrumentation.
+package tracetest // import "go.opentelemetry.io/otel/sdk/trace/tracetest"
+
+import (
+ "context"
+ "sync"
+
+ "go.opentelemetry.io/otel/sdk/trace"
+)
+
+var _ trace.SpanExporter = (*NoopExporter)(nil)
+
+// NewNoopExporter returns a new no-op exporter.
+func NewNoopExporter() *NoopExporter {
+ return new(NoopExporter)
+}
+
+// NoopExporter is an exporter that drops all received spans and performs no
+// action.
+type NoopExporter struct{}
+
+// ExportSpans handles export of spans by dropping them.
+func (nsb *NoopExporter) ExportSpans(context.Context, []trace.ReadOnlySpan) error { return nil }
+
+// Shutdown stops the exporter by doing nothing.
+func (nsb *NoopExporter) Shutdown(context.Context) error { return nil }
+
+var _ trace.SpanExporter = (*InMemoryExporter)(nil)
+
+// NewInMemoryExporter returns a new InMemoryExporter.
+func NewInMemoryExporter() *InMemoryExporter {
+ return new(InMemoryExporter)
+}
+
+// InMemoryExporter is an exporter that stores all received spans in-memory.
+type InMemoryExporter struct {
+ mu sync.Mutex
+ ss SpanStubs
+}
+
+// ExportSpans handles export of spans by storing them in memory.
+func (imsb *InMemoryExporter) ExportSpans(_ context.Context, spans []trace.ReadOnlySpan) error {
+ imsb.mu.Lock()
+ defer imsb.mu.Unlock()
+ imsb.ss = append(imsb.ss, SpanStubsFromReadOnlySpans(spans)...)
+ return nil
+}
+
+// Shutdown stops the exporter by clearing spans held in memory.
+func (imsb *InMemoryExporter) Shutdown(context.Context) error {
+ imsb.Reset()
+ return nil
+}
+
+// Reset the current in-memory storage.
+func (imsb *InMemoryExporter) Reset() {
+ imsb.mu.Lock()
+ defer imsb.mu.Unlock()
+ imsb.ss = nil
+}
+
+// GetSpans returns the current in-memory stored spans.
+func (imsb *InMemoryExporter) GetSpans() SpanStubs {
+ imsb.mu.Lock()
+ defer imsb.mu.Unlock()
+ ret := make(SpanStubs, len(imsb.ss))
+ copy(ret, imsb.ss)
+ return ret
+}
diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/tracetest/recorder.go b/vendor/go.opentelemetry.io/otel/sdk/trace/tracetest/recorder.go
new file mode 100644
index 000000000..732669a17
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/sdk/trace/tracetest/recorder.go
@@ -0,0 +1,94 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+package tracetest // import "go.opentelemetry.io/otel/sdk/trace/tracetest"
+
+import (
+ "context"
+ "sync"
+
+ sdktrace "go.opentelemetry.io/otel/sdk/trace"
+)
+
+// SpanRecorder records started and ended spans.
+type SpanRecorder struct {
+ startedMu sync.RWMutex
+ started []sdktrace.ReadWriteSpan
+
+ endedMu sync.RWMutex
+ ended []sdktrace.ReadOnlySpan
+}
+
+var _ sdktrace.SpanProcessor = (*SpanRecorder)(nil)
+
+// NewSpanRecorder returns a new initialized SpanRecorder.
+func NewSpanRecorder() *SpanRecorder {
+ return new(SpanRecorder)
+}
+
+// OnStart records started spans.
+//
+// This method is safe to be called concurrently.
+func (sr *SpanRecorder) OnStart(_ context.Context, s sdktrace.ReadWriteSpan) {
+ sr.startedMu.Lock()
+ defer sr.startedMu.Unlock()
+ sr.started = append(sr.started, s)
+}
+
+// OnEnd records completed spans.
+//
+// This method is safe to be called concurrently.
+func (sr *SpanRecorder) OnEnd(s sdktrace.ReadOnlySpan) {
+ sr.endedMu.Lock()
+ defer sr.endedMu.Unlock()
+ sr.ended = append(sr.ended, s)
+}
+
+// Shutdown does nothing.
+//
+// This method is safe to be called concurrently.
+func (sr *SpanRecorder) Shutdown(context.Context) error {
+ return nil
+}
+
+// ForceFlush does nothing.
+//
+// This method is safe to be called concurrently.
+func (sr *SpanRecorder) ForceFlush(context.Context) error {
+ return nil
+}
+
+// Started returns a copy of all started spans that have been recorded.
+//
+// This method is safe to be called concurrently.
+func (sr *SpanRecorder) Started() []sdktrace.ReadWriteSpan {
+ sr.startedMu.RLock()
+ defer sr.startedMu.RUnlock()
+ dst := make([]sdktrace.ReadWriteSpan, len(sr.started))
+ copy(dst, sr.started)
+ return dst
+}
+
+// Reset clears the recorded spans.
+//
+// This method is safe to be called concurrently.
+func (sr *SpanRecorder) Reset() {
+ sr.startedMu.Lock()
+ sr.endedMu.Lock()
+ defer sr.startedMu.Unlock()
+ defer sr.endedMu.Unlock()
+
+ sr.started = nil
+ sr.ended = nil
+}
+
+// Ended returns a copy of all ended spans that have been recorded.
+//
+// This method is safe to be called concurrently.
+func (sr *SpanRecorder) Ended() []sdktrace.ReadOnlySpan {
+ sr.endedMu.RLock()
+ defer sr.endedMu.RUnlock()
+ dst := make([]sdktrace.ReadOnlySpan, len(sr.ended))
+ copy(dst, sr.ended)
+ return dst
+}
diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/tracetest/span.go b/vendor/go.opentelemetry.io/otel/sdk/trace/tracetest/span.go
new file mode 100644
index 000000000..cd2cc30ca
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/sdk/trace/tracetest/span.go
@@ -0,0 +1,166 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+package tracetest // import "go.opentelemetry.io/otel/sdk/trace/tracetest"
+
+import (
+ "time"
+
+ "go.opentelemetry.io/otel/attribute"
+ "go.opentelemetry.io/otel/sdk/instrumentation"
+ "go.opentelemetry.io/otel/sdk/resource"
+ tracesdk "go.opentelemetry.io/otel/sdk/trace"
+ "go.opentelemetry.io/otel/trace"
+)
+
+// SpanStubs is a slice of SpanStub use for testing an SDK.
+type SpanStubs []SpanStub
+
+// SpanStubsFromReadOnlySpans returns SpanStubs populated from ro.
+func SpanStubsFromReadOnlySpans(ro []tracesdk.ReadOnlySpan) SpanStubs {
+ if len(ro) == 0 {
+ return nil
+ }
+
+ s := make(SpanStubs, 0, len(ro))
+ for _, r := range ro {
+ s = append(s, SpanStubFromReadOnlySpan(r))
+ }
+
+ return s
+}
+
+// Snapshots returns s as a slice of ReadOnlySpans.
+func (s SpanStubs) Snapshots() []tracesdk.ReadOnlySpan {
+ if len(s) == 0 {
+ return nil
+ }
+
+ ro := make([]tracesdk.ReadOnlySpan, len(s))
+ for i := 0; i < len(s); i++ {
+ ro[i] = s[i].Snapshot()
+ }
+ return ro
+}
+
+// SpanStub is a stand-in for a Span.
+type SpanStub struct {
+ Name string
+ SpanContext trace.SpanContext
+ Parent trace.SpanContext
+ SpanKind trace.SpanKind
+ StartTime time.Time
+ EndTime time.Time
+ Attributes []attribute.KeyValue
+ Events []tracesdk.Event
+ Links []tracesdk.Link
+ Status tracesdk.Status
+ DroppedAttributes int
+ DroppedEvents int
+ DroppedLinks int
+ ChildSpanCount int
+ Resource *resource.Resource
+ InstrumentationScope instrumentation.Scope
+
+ // Deprecated: use InstrumentationScope instead.
+ InstrumentationLibrary instrumentation.Library //nolint:staticcheck // This method needs to be define for backwards compatibility
+}
+
+// SpanStubFromReadOnlySpan returns a SpanStub populated from ro.
+func SpanStubFromReadOnlySpan(ro tracesdk.ReadOnlySpan) SpanStub {
+ if ro == nil {
+ return SpanStub{}
+ }
+
+ return SpanStub{
+ Name: ro.Name(),
+ SpanContext: ro.SpanContext(),
+ Parent: ro.Parent(),
+ SpanKind: ro.SpanKind(),
+ StartTime: ro.StartTime(),
+ EndTime: ro.EndTime(),
+ Attributes: ro.Attributes(),
+ Events: ro.Events(),
+ Links: ro.Links(),
+ Status: ro.Status(),
+ DroppedAttributes: ro.DroppedAttributes(),
+ DroppedEvents: ro.DroppedEvents(),
+ DroppedLinks: ro.DroppedLinks(),
+ ChildSpanCount: ro.ChildSpanCount(),
+ Resource: ro.Resource(),
+ InstrumentationScope: ro.InstrumentationScope(),
+ InstrumentationLibrary: ro.InstrumentationScope(),
+ }
+}
+
+// Snapshot returns a read-only copy of the SpanStub.
+func (s SpanStub) Snapshot() tracesdk.ReadOnlySpan {
+ scopeOrLibrary := s.InstrumentationScope
+ if scopeOrLibrary.Name == "" && scopeOrLibrary.Version == "" && scopeOrLibrary.SchemaURL == "" {
+ scopeOrLibrary = s.InstrumentationLibrary
+ }
+
+ return spanSnapshot{
+ name: s.Name,
+ spanContext: s.SpanContext,
+ parent: s.Parent,
+ spanKind: s.SpanKind,
+ startTime: s.StartTime,
+ endTime: s.EndTime,
+ attributes: s.Attributes,
+ events: s.Events,
+ links: s.Links,
+ status: s.Status,
+ droppedAttributes: s.DroppedAttributes,
+ droppedEvents: s.DroppedEvents,
+ droppedLinks: s.DroppedLinks,
+ childSpanCount: s.ChildSpanCount,
+ resource: s.Resource,
+ instrumentationScope: scopeOrLibrary,
+ }
+}
+
+type spanSnapshot struct {
+ // Embed the interface to implement the private method.
+ tracesdk.ReadOnlySpan
+
+ name string
+ spanContext trace.SpanContext
+ parent trace.SpanContext
+ spanKind trace.SpanKind
+ startTime time.Time
+ endTime time.Time
+ attributes []attribute.KeyValue
+ events []tracesdk.Event
+ links []tracesdk.Link
+ status tracesdk.Status
+ droppedAttributes int
+ droppedEvents int
+ droppedLinks int
+ childSpanCount int
+ resource *resource.Resource
+ instrumentationScope instrumentation.Scope
+}
+
+func (s spanSnapshot) Name() string { return s.name }
+func (s spanSnapshot) SpanContext() trace.SpanContext { return s.spanContext }
+func (s spanSnapshot) Parent() trace.SpanContext { return s.parent }
+func (s spanSnapshot) SpanKind() trace.SpanKind { return s.spanKind }
+func (s spanSnapshot) StartTime() time.Time { return s.startTime }
+func (s spanSnapshot) EndTime() time.Time { return s.endTime }
+func (s spanSnapshot) Attributes() []attribute.KeyValue { return s.attributes }
+func (s spanSnapshot) Links() []tracesdk.Link { return s.links }
+func (s spanSnapshot) Events() []tracesdk.Event { return s.events }
+func (s spanSnapshot) Status() tracesdk.Status { return s.status }
+func (s spanSnapshot) DroppedAttributes() int { return s.droppedAttributes }
+func (s spanSnapshot) DroppedLinks() int { return s.droppedLinks }
+func (s spanSnapshot) DroppedEvents() int { return s.droppedEvents }
+func (s spanSnapshot) ChildSpanCount() int { return s.childSpanCount }
+func (s spanSnapshot) Resource() *resource.Resource { return s.resource }
+func (s spanSnapshot) InstrumentationScope() instrumentation.Scope {
+ return s.instrumentationScope
+}
+
+func (s spanSnapshot) InstrumentationLibrary() instrumentation.Library { //nolint:staticcheck // This method needs to be define for backwards compatibility
+ return s.instrumentationScope
+}