mirror of
https://github.com/superseriousbusiness/gotosocial
synced 2025-06-05 21:59:39 +02:00
[chore] Simplify the OTEL setup (#4110)
# Description This simplifies our OTEL setup by: * Getting rid of some deprecated things. * Using `autoexport` and letting things get configured by the `OTEL_` environment variables. * Removing all the unnecessary config options. ## Checklist Please put an x inside each checkbox to indicate that you've read and followed it: `[ ]` -> `[x]` If this is a documentation change, only the first checkbox must be filled (you can delete the others if you want). - [x] I/we have read the [GoToSocial contribution guidelines](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/CONTRIBUTING.md). - [x] I/we have discussed the proposed changes already, either in an issue on the repository, or in the Matrix chat. - [x] I/we have not leveraged AI to create the proposed changes. - [x] I/we have performed a self-review of added code. - [x] I/we have written code that is legible and maintainable by others. - [ ] I/we have commented the added code, particularly in hard-to-understand areas. - [x] I/we have made any necessary changes to documentation. - [ ] I/we have added tests that cover new code. - [x] I/we have run tests and they pass locally with the changes. - [x] I/we have run `go fmt ./...` and `golangci-lint run`. Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4110 Reviewed-by: tobi <kipvandenbos@noreply.codeberg.org> Co-authored-by: Daenney <daenney@noreply.codeberg.org> Co-committed-by: Daenney <daenney@noreply.codeberg.org>
This commit is contained in:
@@ -184,7 +184,7 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// Initialize tracing (noop if not enabled).
|
||||
if err := observability.InitializeTracing(); err != nil {
|
||||
if err := observability.InitializeTracing(ctx); err != nil {
|
||||
return fmt.Errorf("error initializing tracing: %w", err)
|
||||
}
|
||||
|
||||
@@ -377,7 +377,7 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// Initialize metrics.
|
||||
if err := observability.InitializeMetrics(state.DB); err != nil {
|
||||
if err := observability.InitializeMetrics(ctx, state.DB); err != nil {
|
||||
return fmt.Errorf("error initializing metrics: %w", err)
|
||||
}
|
||||
|
||||
@@ -484,7 +484,6 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||
var (
|
||||
authModule = api.NewAuth(state, process, idp, routerSession, sessionName, cookiePolicy) // auth/oauth paths
|
||||
clientModule = api.NewClient(state, process) // api client endpoints
|
||||
metricsModule = api.NewMetrics() // Metrics endpoints
|
||||
healthModule = api.NewHealth(dbService.Ready) // Health check endpoints
|
||||
fileserverModule = api.NewFileserver(process) // fileserver endpoints
|
||||
robotsModule = api.NewRobots() // robots.txt endpoint
|
||||
@@ -538,7 +537,6 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||
// apply throttling *after* rate limiting
|
||||
authModule.Route(route, clLimit, clThrottle, robotsDisallowAll, gzip)
|
||||
clientModule.Route(route, clLimit, clThrottle, robotsDisallowAll, gzip)
|
||||
metricsModule.Route(route, clLimit, clThrottle, robotsDisallowAIOnly)
|
||||
healthModule.Route(route, clLimit, clThrottle, robotsDisallowAIOnly)
|
||||
fileserverModule.Route(route, fsMainLimit, fsThrottle, robotsDisallowAIOnly)
|
||||
fileserverModule.RouteEmojis(route, instanceAccount.ID, fsEmojiLimit, fsThrottle, robotsDisallowAIOnly)
|
||||
|
@@ -2,10 +2,32 @@
|
||||
|
||||
These settings let you tune and configure certain observability related behaviours.
|
||||
|
||||
GoToSocial uses OpenTelemetry. The metrics and trace exporters can be configured using the standard OpenTelemetry SDK environment variables. For a full reference, see [the OpenTelemetry docs](https://opentelemetry.io/docs/languages/sdk-configuration/).
|
||||
|
||||
## Metrics
|
||||
|
||||
Before enabling metrics, [read the guide](../advanced/metrics.md) and ensure you've taken the appropriate security measures for your setup.
|
||||
|
||||
### Tracing
|
||||
|
||||
To enable tracing, set `tracing-enabled` to `true`.
|
||||
|
||||
Valid values for `OTEL_TRACES_EXPORTER` are [documented here](https://opentelemetry.io/docs/languages/sdk-configuration/general/#otel_traces_exporter).
|
||||
|
||||
### Metrics
|
||||
|
||||
To enable metrics, set `metrics-enabled` to `true`. By default this'll use OTLP to push metrics to an OpenTelemetry receiver.
|
||||
|
||||
Valid values for `OTEL_METRICS_EXPORTER` are [documented here](https://opentelemetry.io/docs/languages/sdk-configuration/general/#otel_metrics_exporter)
|
||||
|
||||
For Prometheus, set `OTEL_METRICS_EXPORTER` to `prometheus`. This will start a **second** HTTP server, bound for `localhost:9464` with the OpenTelemetry metrics. You can change this using:
|
||||
* `OTEL_EXPORTER_PROMETHEUS_HOST`
|
||||
* `OTEL_EXPORTER_PROMETHEUS_PORT`
|
||||
|
||||
### Authentication
|
||||
|
||||
If you want to expose the metrics with authentication, you'll need a reverse proxy. You can configure it to proxy to `http://localhost:9464/metrics` and handle authentication in your reverse proxy however you like.
|
||||
|
||||
## Settings
|
||||
|
||||
```yaml
|
||||
@@ -22,36 +44,7 @@ request-id-header: "X-Request-Id"
|
||||
# Default: false
|
||||
tracing-enabled: false
|
||||
|
||||
# String. Set the transport protocol for the tracing system. Can either be "grpc"
|
||||
# for OTLP gRPC, or "http" for OTLP HTTP.
|
||||
# Options: ["grpc", "http"]
|
||||
# Default: "grpc"
|
||||
tracing-transport: "grpc"
|
||||
|
||||
# String. Endpoint of the trace ingester. When using the gRPC or HTTP based
|
||||
# transports, provide the endpoint as a single address/port combination without a
|
||||
# protocol scheme.
|
||||
# Examples: ["localhost:4317"]
|
||||
# Default: ""
|
||||
tracing-endpoint: ""
|
||||
|
||||
# Bool. Disable TLS for the gRPC and HTTP transport protocols.
|
||||
# Default: false
|
||||
tracing-insecure-transport: false
|
||||
|
||||
# Bool. Enable OpenTelemetry based metrics support.
|
||||
# Default: false
|
||||
metrics-enabled: false
|
||||
|
||||
# Bool. Enable HTTP Basic Authentication for Prometheus metrics endpoint.
|
||||
# Default: false
|
||||
metrics-auth-enabled: false
|
||||
|
||||
# String. Username for Prometheus metrics endpoint.
|
||||
# Default: ""
|
||||
metrics-auth-username: ""
|
||||
|
||||
# String. Password for Prometheus metrics endpoint.
|
||||
# Default: ""
|
||||
metrics-auth-password: ""
|
||||
```
|
@@ -993,38 +993,10 @@ request-id-header: "X-Request-Id"
|
||||
# Default: false
|
||||
tracing-enabled: false
|
||||
|
||||
# String. Set the transport protocol for the tracing system. Can either be "grpc"
|
||||
# for OTLP gRPC, or "http" for OTLP HTTP.
|
||||
# Options: ["grpc", "http"]
|
||||
# Default: "grpc"
|
||||
tracing-transport: "grpc"
|
||||
|
||||
# String. Endpoint of the trace ingester. When using the gRPC or HTTP based transports,
|
||||
# provide the endpoint as a single address/port combination without a protocol scheme.
|
||||
# Examples: ["localhost:4317"]
|
||||
# Default: ""
|
||||
tracing-endpoint: ""
|
||||
|
||||
# Bool. Disable TLS for the gRPC and HTTP transport protocols.
|
||||
# Default: false
|
||||
tracing-insecure-transport: false
|
||||
|
||||
# Bool. Enable OpenTelemetry based metrics support.
|
||||
# Default: false
|
||||
metrics-enabled: false
|
||||
|
||||
# Bool. Enable HTTP Basic Authentication for Prometheus metrics endpoint.
|
||||
# Default: false
|
||||
metrics-auth-enabled: false
|
||||
|
||||
# String. Username for Prometheus metrics endpoint.
|
||||
# Default: ""
|
||||
metrics-auth-username: ""
|
||||
|
||||
# String. Password for Prometheus metrics endpoint.
|
||||
# Default: ""
|
||||
metrics-auth-password: ""
|
||||
|
||||
################################
|
||||
##### HTTP CLIENT SETTINGS #####
|
||||
################################
|
||||
|
19
go.mod
19
go.mod
@@ -56,7 +56,6 @@ require (
|
||||
github.com/ncruces/go-sqlite3 v0.25.1
|
||||
github.com/oklog/ulid v1.3.1
|
||||
github.com/pquerna/otp v1.4.0
|
||||
github.com/prometheus/client_golang v1.22.0
|
||||
github.com/rivo/uniseg v0.4.7
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/spf13/viper v1.20.1
|
||||
@@ -73,10 +72,8 @@ require (
|
||||
github.com/uptrace/bun/extra/bunotel v1.2.11
|
||||
github.com/wagslane/go-password-validator v0.3.0
|
||||
github.com/yuin/goldmark v1.7.11
|
||||
go.opentelemetry.io/contrib/exporters/autoexport v0.60.0
|
||||
go.opentelemetry.io/otel v1.35.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.57.0
|
||||
go.opentelemetry.io/otel/metric v1.35.0
|
||||
go.opentelemetry.io/otel/sdk v1.35.0
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0
|
||||
@@ -183,6 +180,7 @@ require (
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_golang v1.22.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.62.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
@@ -208,7 +206,20 @@ require (
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
go.mongodb.org/mongo-driver v1.17.3 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/bridges/prometheus v0.60.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.11.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.11.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.57.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.11.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/log v0.11.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/log v0.11.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/arch v0.16.0 // indirect
|
||||
|
22
go.sum
generated
22
go.sum
generated
@@ -491,8 +491,20 @@ go.mongodb.org/mongo-driver v1.17.3 h1:TQyXhnsWfWtgAhMtOgtYHMTkZIfBTpMTsMnd9ZBeH
|
||||
go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/bridges/prometheus v0.60.0 h1:x7sPooQCwSg27SjtQee8GyIIRTQcF4s7eSkac6F2+VA=
|
||||
go.opentelemetry.io/contrib/bridges/prometheus v0.60.0/go.mod h1:4K5UXgiHxV484efGs42ejD7E2J/sIlepYgdGoPXe7hE=
|
||||
go.opentelemetry.io/contrib/exporters/autoexport v0.60.0 h1:GuQXpvSXNjpswpweIem84U9BNauqHHi2w1GtNAalvpM=
|
||||
go.opentelemetry.io/contrib/exporters/autoexport v0.60.0/go.mod h1:CkmxekdHco4d7thFJNPQ7Mby4jMBgZUclnrxT4e+ryk=
|
||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.11.0 h1:HMUytBT3uGhPKYY/u/G5MR9itrlSO2SMOsSD3Tk3k7A=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.11.0/go.mod h1:hdDXsiNLmdW/9BF2jQpnHHlhFajpWCEYfM6e5m2OAZg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.11.0 h1:C/Wi2F8wEmbxJ9Kuzw/nhP+Z9XaHYMkyDmXy6yR2cjw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.11.0/go.mod h1:0Lr9vmGKzadCTgsiBydxr6GEZ8SsZ7Ks53LzjWG5Ar4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 h1:QcFwRrZLc82r8wODjvyCbP7Ifp3UANaBSmhDSFjnqSc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0/go.mod h1:CXIWhUomyWBG/oY2/r/kLp6K/cmx9e/7DLpBuuGdLCA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.35.0 h1:0NIXxOCFx+SKbhCVxwl3ETG8ClLPAa0KuKV6p3yhxP8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.35.0/go.mod h1:ChZSJbbfbl/DcRZNc9Gqh6DYGlfjw4PvO1pEOZH1ZsE=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI=
|
||||
@@ -501,10 +513,20 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qH
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk=
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.57.0 h1:AHh/lAP1BHrY5gBwk8ncc25FXWm/gmmY3BX258z5nuk=
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.57.0/go.mod h1:QpFWz1QxqevfjwzYdbMb4Y1NnlJvqSGwyuU0B4iuc9c=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.11.0 h1:k6KdfZk72tVW/QVZf60xlDziDvYAePj5QHwoQvrB2m8=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.11.0/go.mod h1:5Y3ZJLqzi/x/kYtrSrPSx7TFI/SGsL7q2kME027tH6I=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.35.0 h1:PB3Zrjs1sG1GBX51SXyTSoOTqcDglmsk7nT6tkKPb/k=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.35.0/go.mod h1:U2R3XyVPzn0WX7wOIypPuptulsMcPDPs/oiSVOMVnHY=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.35.0 h1:T0Ec2E+3YZf5bgTNQVet8iTDW7oIk03tXHq+wkwIDnE=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.35.0/go.mod h1:30v2gqH+vYGJsesLWFov8u47EpYTcIQcBjKpI6pJThg=
|
||||
go.opentelemetry.io/otel/log v0.11.0 h1:c24Hrlk5WJ8JWcwbQxdBqxZdOK7PcP/LFtOtwpDTe3Y=
|
||||
go.opentelemetry.io/otel/log v0.11.0/go.mod h1:U/sxQ83FPmT29trrifhQg+Zj2lo1/IPN1PF6RTFqdwc=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
|
||||
go.opentelemetry.io/otel/sdk/log v0.11.0 h1:7bAOpjpGglWhdEzP8z0VXc4jObOiDEwr3IYbhBnjk2c=
|
||||
go.opentelemetry.io/otel/sdk/log v0.11.0/go.mod h1:dndLTxZbwBstZoqsJB3kGsRPkpAgaJrWfQg3lhlHFFY=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
|
@@ -1,66 +0,0 @@
|
||||
// GoToSocial
|
||||
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"code.superseriousbusiness.org/gotosocial/internal/api/metrics"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/middleware"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/router"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type Metrics struct {
|
||||
metrics *metrics.Module
|
||||
}
|
||||
|
||||
func (mt *Metrics) Route(r *router.Router, m ...gin.HandlerFunc) {
|
||||
if !config.GetMetricsEnabled() {
|
||||
// Noop: metrics
|
||||
// not enabled.
|
||||
return
|
||||
}
|
||||
|
||||
// Create new group on top level "metrics" prefix.
|
||||
metricsGroup := r.AttachGroup("metrics")
|
||||
metricsGroup.Use(m...)
|
||||
metricsGroup.Use(
|
||||
middleware.CacheControl(middleware.CacheControlConfig{
|
||||
// Never cache metrics responses.
|
||||
Directives: []string{"no-store"},
|
||||
}),
|
||||
)
|
||||
|
||||
// Attach basic auth if enabled.
|
||||
if config.GetMetricsAuthEnabled() {
|
||||
var (
|
||||
username = config.GetMetricsAuthUsername()
|
||||
password = config.GetMetricsAuthPassword()
|
||||
accounts = gin.Accounts{username: password}
|
||||
)
|
||||
metricsGroup.Use(gin.BasicAuth(accounts))
|
||||
}
|
||||
|
||||
mt.metrics.Route(metricsGroup.Handle)
|
||||
}
|
||||
|
||||
func NewMetrics() *Metrics {
|
||||
return &Metrics{
|
||||
metrics: metrics.New(),
|
||||
}
|
||||
}
|
@@ -1,60 +0,0 @@
|
||||
// GoToSocial
|
||||
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//go:build !nootel
|
||||
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
type Module struct {
|
||||
handler http.Handler
|
||||
}
|
||||
|
||||
func New() *Module {
|
||||
// Let prometheus use "identity", ie., no compression,
|
||||
// or "gzip", to match our own gzip compression middleware.
|
||||
opts := promhttp.HandlerOpts{
|
||||
OfferedCompressions: []promhttp.Compression{
|
||||
promhttp.Identity,
|
||||
promhttp.Gzip,
|
||||
},
|
||||
}
|
||||
|
||||
// Instrument handler itself.
|
||||
handler := promhttp.InstrumentMetricHandler(
|
||||
prometheus.DefaultRegisterer,
|
||||
promhttp.HandlerFor(prometheus.DefaultGatherer, opts),
|
||||
)
|
||||
|
||||
return &Module{
|
||||
handler: handler,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
|
||||
attachHandler(http.MethodGet, "", func(c *gin.Context) {
|
||||
// Defer all "/metrics" handling to prom.
|
||||
m.handler.ServeHTTP(c.Writer, c.Request)
|
||||
})
|
||||
}
|
@@ -145,16 +145,8 @@ type Configuration struct {
|
||||
OIDCLinkExisting bool `name:"oidc-link-existing" usage:"link existing user accounts to OIDC logins based on the stored email value"`
|
||||
OIDCAllowedGroups []string `name:"oidc-allowed-groups" usage:"Membership of one of the listed groups allows access to GtS. If this is empty, all groups are allowed."`
|
||||
OIDCAdminGroups []string `name:"oidc-admin-groups" usage:"Membership of one of the listed groups makes someone a GtS admin"`
|
||||
|
||||
TracingEnabled bool `name:"tracing-enabled" usage:"Enable OTLP Tracing"`
|
||||
TracingTransport string `name:"tracing-transport" usage:"grpc or http"`
|
||||
TracingEndpoint string `name:"tracing-endpoint" usage:"Endpoint of your trace collector. Eg., 'localhost:4317' for gRPC, 'localhost:4318' for http"`
|
||||
TracingInsecureTransport bool `name:"tracing-insecure-transport" usage:"Disable TLS for the gRPC or HTTP transport protocol"`
|
||||
|
||||
MetricsEnabled bool `name:"metrics-enabled" usage:"Enable OpenTelemetry based metrics support."`
|
||||
MetricsAuthEnabled bool `name:"metrics-auth-enabled" usage:"Enable HTTP Basic Authentication for Prometheus metrics endpoint"`
|
||||
MetricsAuthUsername string `name:"metrics-auth-username" usage:"Username for Prometheus metrics endpoint"`
|
||||
MetricsAuthPassword string `name:"metrics-auth-password" usage:"Password for Prometheus metrics endpoint"`
|
||||
|
||||
SMTPHost string `name:"smtp-host" usage:"Host of the smtp server. Eg., 'smtp.eu.mailgun.org'"`
|
||||
SMTPPort int `name:"smtp-port" usage:"Port of the smtp server. Eg., 587"`
|
||||
|
@@ -123,12 +123,7 @@ var Defaults = Configuration{
|
||||
SMTPDiscloseRecipients: false,
|
||||
|
||||
TracingEnabled: false,
|
||||
TracingTransport: "grpc",
|
||||
TracingEndpoint: "",
|
||||
TracingInsecureTransport: false,
|
||||
|
||||
MetricsEnabled: false,
|
||||
MetricsAuthEnabled: false,
|
||||
|
||||
SyslogEnabled: false,
|
||||
SyslogProtocol: "udp",
|
||||
|
@@ -2306,81 +2306,6 @@ func GetTracingEnabled() bool { return global.GetTracingEnabled() }
|
||||
// SetTracingEnabled safely sets the value for global configuration 'TracingEnabled' field
|
||||
func SetTracingEnabled(v bool) { global.SetTracingEnabled(v) }
|
||||
|
||||
// GetTracingTransport safely fetches the Configuration value for state's 'TracingTransport' field
|
||||
func (st *ConfigState) GetTracingTransport() (v string) {
|
||||
st.mutex.RLock()
|
||||
v = st.config.TracingTransport
|
||||
st.mutex.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// SetTracingTransport safely sets the Configuration value for state's 'TracingTransport' field
|
||||
func (st *ConfigState) SetTracingTransport(v string) {
|
||||
st.mutex.Lock()
|
||||
defer st.mutex.Unlock()
|
||||
st.config.TracingTransport = v
|
||||
st.reloadToViper()
|
||||
}
|
||||
|
||||
// TracingTransportFlag returns the flag name for the 'TracingTransport' field
|
||||
func TracingTransportFlag() string { return "tracing-transport" }
|
||||
|
||||
// GetTracingTransport safely fetches the value for global configuration 'TracingTransport' field
|
||||
func GetTracingTransport() string { return global.GetTracingTransport() }
|
||||
|
||||
// SetTracingTransport safely sets the value for global configuration 'TracingTransport' field
|
||||
func SetTracingTransport(v string) { global.SetTracingTransport(v) }
|
||||
|
||||
// GetTracingEndpoint safely fetches the Configuration value for state's 'TracingEndpoint' field
|
||||
func (st *ConfigState) GetTracingEndpoint() (v string) {
|
||||
st.mutex.RLock()
|
||||
v = st.config.TracingEndpoint
|
||||
st.mutex.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// SetTracingEndpoint safely sets the Configuration value for state's 'TracingEndpoint' field
|
||||
func (st *ConfigState) SetTracingEndpoint(v string) {
|
||||
st.mutex.Lock()
|
||||
defer st.mutex.Unlock()
|
||||
st.config.TracingEndpoint = v
|
||||
st.reloadToViper()
|
||||
}
|
||||
|
||||
// TracingEndpointFlag returns the flag name for the 'TracingEndpoint' field
|
||||
func TracingEndpointFlag() string { return "tracing-endpoint" }
|
||||
|
||||
// GetTracingEndpoint safely fetches the value for global configuration 'TracingEndpoint' field
|
||||
func GetTracingEndpoint() string { return global.GetTracingEndpoint() }
|
||||
|
||||
// SetTracingEndpoint safely sets the value for global configuration 'TracingEndpoint' field
|
||||
func SetTracingEndpoint(v string) { global.SetTracingEndpoint(v) }
|
||||
|
||||
// GetTracingInsecureTransport safely fetches the Configuration value for state's 'TracingInsecureTransport' field
|
||||
func (st *ConfigState) GetTracingInsecureTransport() (v bool) {
|
||||
st.mutex.RLock()
|
||||
v = st.config.TracingInsecureTransport
|
||||
st.mutex.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// SetTracingInsecureTransport safely sets the Configuration value for state's 'TracingInsecureTransport' field
|
||||
func (st *ConfigState) SetTracingInsecureTransport(v bool) {
|
||||
st.mutex.Lock()
|
||||
defer st.mutex.Unlock()
|
||||
st.config.TracingInsecureTransport = v
|
||||
st.reloadToViper()
|
||||
}
|
||||
|
||||
// TracingInsecureTransportFlag returns the flag name for the 'TracingInsecureTransport' field
|
||||
func TracingInsecureTransportFlag() string { return "tracing-insecure-transport" }
|
||||
|
||||
// GetTracingInsecureTransport safely fetches the value for global configuration 'TracingInsecureTransport' field
|
||||
func GetTracingInsecureTransport() bool { return global.GetTracingInsecureTransport() }
|
||||
|
||||
// SetTracingInsecureTransport safely sets the value for global configuration 'TracingInsecureTransport' field
|
||||
func SetTracingInsecureTransport(v bool) { global.SetTracingInsecureTransport(v) }
|
||||
|
||||
// GetMetricsEnabled safely fetches the Configuration value for state's 'MetricsEnabled' field
|
||||
func (st *ConfigState) GetMetricsEnabled() (v bool) {
|
||||
st.mutex.RLock()
|
||||
@@ -2406,81 +2331,6 @@ func GetMetricsEnabled() bool { return global.GetMetricsEnabled() }
|
||||
// SetMetricsEnabled safely sets the value for global configuration 'MetricsEnabled' field
|
||||
func SetMetricsEnabled(v bool) { global.SetMetricsEnabled(v) }
|
||||
|
||||
// GetMetricsAuthEnabled safely fetches the Configuration value for state's 'MetricsAuthEnabled' field
|
||||
func (st *ConfigState) GetMetricsAuthEnabled() (v bool) {
|
||||
st.mutex.RLock()
|
||||
v = st.config.MetricsAuthEnabled
|
||||
st.mutex.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// SetMetricsAuthEnabled safely sets the Configuration value for state's 'MetricsAuthEnabled' field
|
||||
func (st *ConfigState) SetMetricsAuthEnabled(v bool) {
|
||||
st.mutex.Lock()
|
||||
defer st.mutex.Unlock()
|
||||
st.config.MetricsAuthEnabled = v
|
||||
st.reloadToViper()
|
||||
}
|
||||
|
||||
// MetricsAuthEnabledFlag returns the flag name for the 'MetricsAuthEnabled' field
|
||||
func MetricsAuthEnabledFlag() string { return "metrics-auth-enabled" }
|
||||
|
||||
// GetMetricsAuthEnabled safely fetches the value for global configuration 'MetricsAuthEnabled' field
|
||||
func GetMetricsAuthEnabled() bool { return global.GetMetricsAuthEnabled() }
|
||||
|
||||
// SetMetricsAuthEnabled safely sets the value for global configuration 'MetricsAuthEnabled' field
|
||||
func SetMetricsAuthEnabled(v bool) { global.SetMetricsAuthEnabled(v) }
|
||||
|
||||
// GetMetricsAuthUsername safely fetches the Configuration value for state's 'MetricsAuthUsername' field
|
||||
func (st *ConfigState) GetMetricsAuthUsername() (v string) {
|
||||
st.mutex.RLock()
|
||||
v = st.config.MetricsAuthUsername
|
||||
st.mutex.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// SetMetricsAuthUsername safely sets the Configuration value for state's 'MetricsAuthUsername' field
|
||||
func (st *ConfigState) SetMetricsAuthUsername(v string) {
|
||||
st.mutex.Lock()
|
||||
defer st.mutex.Unlock()
|
||||
st.config.MetricsAuthUsername = v
|
||||
st.reloadToViper()
|
||||
}
|
||||
|
||||
// MetricsAuthUsernameFlag returns the flag name for the 'MetricsAuthUsername' field
|
||||
func MetricsAuthUsernameFlag() string { return "metrics-auth-username" }
|
||||
|
||||
// GetMetricsAuthUsername safely fetches the value for global configuration 'MetricsAuthUsername' field
|
||||
func GetMetricsAuthUsername() string { return global.GetMetricsAuthUsername() }
|
||||
|
||||
// SetMetricsAuthUsername safely sets the value for global configuration 'MetricsAuthUsername' field
|
||||
func SetMetricsAuthUsername(v string) { global.SetMetricsAuthUsername(v) }
|
||||
|
||||
// GetMetricsAuthPassword safely fetches the Configuration value for state's 'MetricsAuthPassword' field
|
||||
func (st *ConfigState) GetMetricsAuthPassword() (v string) {
|
||||
st.mutex.RLock()
|
||||
v = st.config.MetricsAuthPassword
|
||||
st.mutex.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// SetMetricsAuthPassword safely sets the Configuration value for state's 'MetricsAuthPassword' field
|
||||
func (st *ConfigState) SetMetricsAuthPassword(v string) {
|
||||
st.mutex.Lock()
|
||||
defer st.mutex.Unlock()
|
||||
st.config.MetricsAuthPassword = v
|
||||
st.reloadToViper()
|
||||
}
|
||||
|
||||
// MetricsAuthPasswordFlag returns the flag name for the 'MetricsAuthPassword' field
|
||||
func MetricsAuthPasswordFlag() string { return "metrics-auth-password" }
|
||||
|
||||
// GetMetricsAuthPassword safely fetches the value for global configuration 'MetricsAuthPassword' field
|
||||
func GetMetricsAuthPassword() string { return global.GetMetricsAuthPassword() }
|
||||
|
||||
// SetMetricsAuthPassword safely sets the value for global configuration 'MetricsAuthPassword' field
|
||||
func SetMetricsAuthPassword(v string) { global.SetMetricsAuthPassword(v) }
|
||||
|
||||
// GetSMTPHost safely fetches the Configuration value for state's 'SMTPHost' field
|
||||
func (st *ConfigState) GetSMTPHost() (v string) {
|
||||
st.mutex.RLock()
|
||||
|
@@ -21,46 +21,32 @@ package observability
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/db"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/technologize/otel-go-contrib/otelginmetrics"
|
||||
"go.opentelemetry.io/contrib/exporters/autoexport"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/exporters/prometheus"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
sdk "go.opentelemetry.io/otel/sdk/metric"
|
||||
"go.opentelemetry.io/otel/sdk/metric/exemplar"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
|
||||
)
|
||||
|
||||
const (
|
||||
serviceName = "GoToSocial"
|
||||
)
|
||||
|
||||
func InitializeMetrics(db db.DB) error {
|
||||
func InitializeMetrics(ctx context.Context, db db.DB) error {
|
||||
if !config.GetMetricsEnabled() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if config.GetMetricsAuthEnabled() {
|
||||
if config.GetMetricsAuthPassword() == "" || config.GetMetricsAuthUsername() == "" {
|
||||
return errors.New("metrics-auth-username and metrics-auth-password must be set when metrics-auth-enabled is true")
|
||||
}
|
||||
r, err := Resource()
|
||||
if err != nil {
|
||||
// this can happen if semconv versioning is out-of-sync
|
||||
return fmt.Errorf("building tracing resource: %w", err)
|
||||
}
|
||||
|
||||
r, _ := resource.Merge(
|
||||
resource.Default(),
|
||||
resource.NewSchemaless(
|
||||
semconv.ServiceName(serviceName),
|
||||
semconv.ServiceVersion(config.GetSoftwareVersion()),
|
||||
),
|
||||
)
|
||||
|
||||
prometheusExporter, err := prometheus.New()
|
||||
mt, err := autoexport.NewMetricReader(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -68,7 +54,7 @@ func InitializeMetrics(db db.DB) error {
|
||||
meterProvider := sdk.NewMeterProvider(
|
||||
sdk.WithExemplarFilter(exemplar.AlwaysOffFilter),
|
||||
sdk.WithResource(r),
|
||||
sdk.WithReader(prometheusExporter),
|
||||
sdk.WithReader(mt),
|
||||
)
|
||||
|
||||
otel.SetMeterProvider(meterProvider)
|
||||
|
@@ -15,17 +15,26 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//go:build nootel
|
||||
//go:build !nootel
|
||||
|
||||
package metrics
|
||||
package observability
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
|
||||
)
|
||||
|
||||
type Module struct{}
|
||||
const (
|
||||
serviceName = "GoToSocial"
|
||||
)
|
||||
|
||||
func New() *Module { return &Module{} }
|
||||
|
||||
func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
|
||||
func Resource() (*resource.Resource, error) {
|
||||
return resource.Merge(
|
||||
resource.Default(),
|
||||
resource.NewSchemaless(
|
||||
semconv.ServiceName(serviceName),
|
||||
semconv.ServiceVersion(config.GetSoftwareVersion()),
|
||||
),
|
||||
)
|
||||
}
|
@@ -22,19 +22,21 @@ package observability
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"codeberg.org/gruf/go-kv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"go.opentelemetry.io/contrib/exporters/autoexport"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
"go.opentelemetry.io/otel/sdk/trace"
|
||||
"go.opentelemetry.io/otel/semconv/v1.20.0/httpconv"
|
||||
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
|
||||
oteltrace "go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/gtscontext"
|
||||
@@ -46,66 +48,35 @@ const (
|
||||
tracerName = "code.superseriousbusiness.org/gotosocial/internal/observability"
|
||||
)
|
||||
|
||||
func InitializeTracing() error {
|
||||
func InitializeTracing(ctx context.Context) error {
|
||||
if !config.GetTracingEnabled() {
|
||||
return nil
|
||||
}
|
||||
|
||||
insecure := config.GetTracingInsecureTransport()
|
||||
|
||||
var tpo trace.TracerProviderOption
|
||||
switch config.GetTracingTransport() {
|
||||
case "grpc":
|
||||
opts := []otlptracegrpc.Option{
|
||||
otlptracegrpc.WithEndpoint(config.GetTracingEndpoint()),
|
||||
}
|
||||
if insecure {
|
||||
opts = append(opts, otlptracegrpc.WithInsecure())
|
||||
}
|
||||
exp, err := otlptracegrpc.New(context.Background(), opts...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("building tracing exporter: %w", err)
|
||||
}
|
||||
tpo = trace.WithBatcher(exp)
|
||||
case "http":
|
||||
opts := []otlptracehttp.Option{
|
||||
otlptracehttp.WithEndpoint(config.GetTracingEndpoint()),
|
||||
}
|
||||
if insecure {
|
||||
opts = append(opts, otlptracehttp.WithInsecure())
|
||||
}
|
||||
exp, err := otlptracehttp.New(context.Background(), opts...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("building tracing exporter: %w", err)
|
||||
}
|
||||
tpo = trace.WithBatcher(exp)
|
||||
default:
|
||||
return fmt.Errorf("invalid tracing transport: %s", config.GetTracingTransport())
|
||||
}
|
||||
r, err := resource.Merge(
|
||||
resource.Default(),
|
||||
resource.NewSchemaless(
|
||||
semconv.ServiceName("GoToSocial"),
|
||||
semconv.ServiceVersion(config.GetSoftwareVersion()),
|
||||
),
|
||||
)
|
||||
r, err := Resource()
|
||||
if err != nil {
|
||||
// this can happen if semconv versioning is out-of-sync
|
||||
return fmt.Errorf("building tracing resource: %w", err)
|
||||
}
|
||||
|
||||
tp := trace.NewTracerProvider(
|
||||
tpo,
|
||||
trace.WithResource(r),
|
||||
se, err := autoexport.NewSpanExporter(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tp := sdktrace.NewTracerProvider(
|
||||
sdktrace.WithResource(r),
|
||||
sdktrace.WithBatcher(se),
|
||||
)
|
||||
|
||||
otel.SetTracerProvider(tp)
|
||||
propagator := propagation.NewCompositeTextMapPropagator(
|
||||
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
|
||||
propagation.TraceContext{},
|
||||
propagation.Baggage{},
|
||||
)
|
||||
otel.SetTextMapPropagator(propagator)
|
||||
))
|
||||
|
||||
log.Hook(func(ctx context.Context, kvs []kv.Field) []kv.Field {
|
||||
span := oteltrace.SpanFromContext(ctx)
|
||||
span := trace.SpanFromContext(ctx)
|
||||
if span != nil && span.SpanContext().HasTraceID() {
|
||||
return append(kvs, kv.Field{K: "traceID", V: span.SpanContext().TraceID().String()})
|
||||
}
|
||||
@@ -121,7 +92,7 @@ func TracingMiddleware() gin.HandlerFunc {
|
||||
provider := otel.GetTracerProvider()
|
||||
tracer := provider.Tracer(
|
||||
tracerName,
|
||||
oteltrace.WithInstrumentationVersion(config.GetSoftwareVersion()),
|
||||
trace.WithInstrumentationVersion(config.GetSoftwareVersion()),
|
||||
)
|
||||
propagator := otel.GetTextMapPropagator()
|
||||
return func(c *gin.Context) {
|
||||
@@ -140,16 +111,16 @@ func TracingMiddleware() gin.HandlerFunc {
|
||||
c.Request = c.Request.WithContext(savedCtx)
|
||||
}()
|
||||
ctx := propagator.Extract(savedCtx, propagation.HeaderCarrier(c.Request.Header))
|
||||
opts := []oteltrace.SpanStartOption{
|
||||
oteltrace.WithAttributes(httpconv.ServerRequest(config.GetHost(), c.Request)...),
|
||||
oteltrace.WithSpanKind(oteltrace.SpanKindServer),
|
||||
opts := []trace.SpanStartOption{
|
||||
trace.WithAttributes(ServerRequestAttributes(c.Request)...),
|
||||
trace.WithSpanKind(trace.SpanKindServer),
|
||||
}
|
||||
|
||||
rAttr := semconv.HTTPRoute(spanName)
|
||||
opts = append(opts, oteltrace.WithAttributes(rAttr))
|
||||
opts = append(opts, trace.WithAttributes(rAttr))
|
||||
id := gtscontext.RequestID(c.Request.Context())
|
||||
if id != "" {
|
||||
opts = append(opts, oteltrace.WithAttributes(attribute.String("requestID", id)))
|
||||
opts = append(opts, trace.WithAttributes(attribute.String("requestID", id)))
|
||||
}
|
||||
ctx, span := tracer.Start(ctx, spanName, opts...)
|
||||
defer span.End()
|
||||
@@ -161,7 +132,6 @@ func TracingMiddleware() gin.HandlerFunc {
|
||||
c.Next()
|
||||
|
||||
status := c.Writer.Status()
|
||||
span.SetStatus(httpconv.ServerStatus(status))
|
||||
if status > 0 {
|
||||
span.SetAttributes(semconv.HTTPResponseStatusCode(status))
|
||||
}
|
||||
@@ -175,8 +145,52 @@ func InjectRequestID() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
id := gtscontext.RequestID(c.Request.Context())
|
||||
if id != "" {
|
||||
span := oteltrace.SpanFromContext(c.Request.Context())
|
||||
span := trace.SpanFromContext(c.Request.Context())
|
||||
span.SetAttributes(attribute.String("requestID", id))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ServerRequestAttributes(req *http.Request) []attribute.KeyValue {
|
||||
attrs := make([]attribute.KeyValue, 0, 8)
|
||||
attrs = append(attrs, method(req.Method))
|
||||
attrs = append(attrs, semconv.URLFull(req.URL.RequestURI()))
|
||||
attrs = append(attrs, semconv.URLScheme(req.URL.Scheme))
|
||||
attrs = append(attrs, semconv.UserAgentOriginal(req.UserAgent()))
|
||||
attrs = append(attrs, semconv.NetworkProtocolName("http"))
|
||||
attrs = append(attrs, semconv.NetworkProtocolVersion(fmt.Sprintf("%d:%d", req.ProtoMajor, req.ProtoMinor)))
|
||||
|
||||
if ip, port, err := net.SplitHostPort(req.RemoteAddr); err == nil {
|
||||
iport, _ := strconv.Atoi(port)
|
||||
attrs = append(attrs,
|
||||
semconv.NetworkPeerAddress(ip),
|
||||
semconv.NetworkPeerPort(iport),
|
||||
)
|
||||
} else if req.RemoteAddr != "" {
|
||||
attrs = append(attrs,
|
||||
semconv.NetworkPeerAddress(req.RemoteAddr),
|
||||
)
|
||||
}
|
||||
|
||||
return attrs
|
||||
}
|
||||
|
||||
func method(m string) attribute.KeyValue {
|
||||
var methodLookup = map[string]attribute.KeyValue{
|
||||
http.MethodConnect: semconv.HTTPRequestMethodConnect,
|
||||
http.MethodDelete: semconv.HTTPRequestMethodDelete,
|
||||
http.MethodGet: semconv.HTTPRequestMethodGet,
|
||||
http.MethodHead: semconv.HTTPRequestMethodHead,
|
||||
http.MethodOptions: semconv.HTTPRequestMethodOptions,
|
||||
http.MethodPatch: semconv.HTTPRequestMethodPatch,
|
||||
http.MethodPost: semconv.HTTPRequestMethodPost,
|
||||
http.MethodPut: semconv.HTTPRequestMethodPut,
|
||||
http.MethodTrace: semconv.HTTPRequestMethodTrace,
|
||||
}
|
||||
|
||||
if kv, ok := methodLookup[m]; ok {
|
||||
return kv
|
||||
}
|
||||
|
||||
return semconv.HTTPRequestMethodGet
|
||||
}
|
||||
|
@@ -146,9 +146,6 @@ EXPECT=$(cat << "EOF"
|
||||
"media-remote-cache-days": 30,
|
||||
"media-remote-max-size": 420,
|
||||
"media-video-size-hint": 41943040,
|
||||
"metrics-auth-enabled": false,
|
||||
"metrics-auth-password": "",
|
||||
"metrics-auth-username": "",
|
||||
"metrics-enabled": false,
|
||||
"oidc-admin-groups": [
|
||||
"steamy"
|
||||
@@ -199,9 +196,6 @@ EXPECT=$(cat << "EOF"
|
||||
"tls-certificate-chain": "",
|
||||
"tls-certificate-key": "",
|
||||
"tracing-enabled": false,
|
||||
"tracing-endpoint": "localhost:4317",
|
||||
"tracing-insecure-transport": true,
|
||||
"tracing-transport": "grpc",
|
||||
"trusted-proxies": [
|
||||
"127.0.0.1/32",
|
||||
"docker.host.local"
|
||||
@@ -269,7 +263,6 @@ GTS_MEDIA_EMOJI_LOCAL_MAX_SIZE=420 \
|
||||
GTS_MEDIA_EMOJI_REMOTE_MAX_SIZE=420 \
|
||||
GTS_MEDIA_FFMPEG_POOL_SIZE=8 \
|
||||
GTS_MEDIA_VIDEO_SIZE_HINT='40MiB' \
|
||||
GTS_METRICS_AUTH_ENABLED=false \
|
||||
GTS_METRICS_ENABLED=false \
|
||||
GTS_STORAGE_BACKEND='local' \
|
||||
GTS_STORAGE_LOCAL_BASE_PATH='/root/store' \
|
||||
@@ -308,8 +301,6 @@ GTS_SMTP_DISCLOSE_RECIPIENTS=true \
|
||||
GTS_SYSLOG_ENABLED=true \
|
||||
GTS_SYSLOG_PROTOCOL='udp' \
|
||||
GTS_SYSLOG_ADDRESS='127.0.0.1:6969' \
|
||||
GTS_TRACING_ENDPOINT='localhost:4317' \
|
||||
GTS_TRACING_INSECURE_TRANSPORT=true \
|
||||
GTS_ADVANCED_COOKIES_SAMESITE='strict' \
|
||||
GTS_ADVANCED_RATE_LIMIT_EXCEPTIONS="192.0.2.0/24,127.0.0.1/32" \
|
||||
GTS_ADVANCED_RATE_LIMIT_REQUESTS=6969 \
|
||||
|
@@ -155,12 +155,7 @@ func testDefaults() config.Configuration {
|
||||
SMTPDiscloseRecipients: false,
|
||||
|
||||
TracingEnabled: false,
|
||||
TracingEndpoint: "localhost:4317",
|
||||
TracingTransport: "grpc",
|
||||
TracingInsecureTransport: true,
|
||||
|
||||
MetricsEnabled: true,
|
||||
MetricsAuthEnabled: false,
|
||||
MetricsEnabled: false,
|
||||
|
||||
SyslogEnabled: false,
|
||||
SyslogProtocol: "udp",
|
||||
|
24
vendor/go.opentelemetry.io/contrib/bridges/prometheus/BENCHMARKS.md
generated
vendored
Normal file
24
vendor/go.opentelemetry.io/contrib/bridges/prometheus/BENCHMARKS.md
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
## Summary
|
||||
|
||||
Using the Prometheus bridge and the OTLP exporter adds roughly ~50% to the CPU and memory overhead of an application compared to serving a Prometheus HTTP endpoint for metrics.
|
||||
|
||||
However, unless the application has extremely high cardinality for metrics, this is unlikely to represent a significant amount of additional overhead because the base-line memory consumption of client libraries is relatively low. For an application with 30k timeseries (which is a very high number), the additional overhead is about 50MB and about 0.1 CPU cores.
|
||||
|
||||
The bridge is particularly useful if you are exporting to an OpenTelemetry Collector, since the OTLP receiver is much more efficient than the Prometheus receiver. For the same 30k timeseries, the Prometheus receiver uses 3x the amount of memory, and 20x the amount of CPU. In concrete numbers, this is an additional 228 MB of memory, and 0.57 CPU cores.
|
||||
|
||||
For an application using the Prometheus client library, and exporting to an OpenTelemetry collector, the total CPU usage is 55% lower and total memory usage is 45% lower when using the bridge and the OTLP receiver compared to using a Prometheus endpoint and the collector's Prometheus receiver.
|
||||
|
||||
## Methods and Results
|
||||
|
||||
The sample application uses the Prometheus client library, and defines one histogram with the default 12 buckets, one counter, and one gauge. Each metric has a single label with 10k values, which are observed every second. See the [sample application's source](https://github.com/dashpole/client_golang/pull/1).
|
||||
|
||||
The memory usage of the sample application is measured using the `/memory/classes/total:bytes` metric from the go runtime. The CPU usage of the application is measured using `top`. The CPU and memory usage of the collector are measured using `docker stats`. It was built using v0.50.0 of the bridge, v1.25.0 of the OpenTelemetry API and SDK, and v1.19.0 of the Prometheus client.
|
||||
|
||||
The OpenTelemetry Collector is configured with only the OTLP or Prometheus receiver, and the debug (logging) exporter with only the basic output. The benchmark uses the Contrib distribution at v0.97.0.
|
||||
|
||||
| Experiment | Memory Usage (MB) | CPU Usage (millicores) |
|
||||
|---|---|---|
|
||||
| App w/ Prometheus Export | 94 | 220 |
|
||||
| App w/ Bridge + OTLP Export | 140 | 330 |
|
||||
| Collector w/ Prometheus Receiver | 320 | 600 |
|
||||
| Collector w/ OTLP Receiver | 92 | 30 |
|
201
vendor/go.opentelemetry.io/contrib/bridges/prometheus/LICENSE
generated
vendored
Normal file
201
vendor/go.opentelemetry.io/contrib/bridges/prometheus/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
47
vendor/go.opentelemetry.io/contrib/bridges/prometheus/config.go
generated
vendored
Normal file
47
vendor/go.opentelemetry.io/contrib/bridges/prometheus/config.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package prometheus // import "go.opentelemetry.io/contrib/bridges/prometheus"
|
||||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
// config contains options for the producer.
|
||||
type config struct {
|
||||
gatherers []prometheus.Gatherer
|
||||
}
|
||||
|
||||
// newConfig creates a validated config configured with options.
|
||||
func newConfig(opts ...Option) config {
|
||||
cfg := config{}
|
||||
for _, opt := range opts {
|
||||
cfg = opt.apply(cfg)
|
||||
}
|
||||
|
||||
if len(cfg.gatherers) == 0 {
|
||||
cfg.gatherers = []prometheus.Gatherer{prometheus.DefaultGatherer}
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
// Option sets producer option values.
|
||||
type Option interface {
|
||||
apply(config) config
|
||||
}
|
||||
|
||||
type optionFunc func(config) config
|
||||
|
||||
func (fn optionFunc) apply(cfg config) config {
|
||||
return fn(cfg)
|
||||
}
|
||||
|
||||
// WithGatherer configures which prometheus Gatherer the Bridge will gather
|
||||
// from. If no registerer is used the prometheus DefaultGatherer is used.
|
||||
func WithGatherer(gatherer prometheus.Gatherer) Option {
|
||||
return optionFunc(func(cfg config) config {
|
||||
cfg.gatherers = append(cfg.gatherers, gatherer)
|
||||
return cfg
|
||||
})
|
||||
}
|
22
vendor/go.opentelemetry.io/contrib/bridges/prometheus/doc.go
generated
vendored
Normal file
22
vendor/go.opentelemetry.io/contrib/bridges/prometheus/doc.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package prometheus provides a bridge from Prometheus to OpenTelemetry.
|
||||
//
|
||||
// The Prometheus Bridge allows using the [Prometheus Golang client library]
|
||||
// with the OpenTelemetry SDK. This enables prometheus instrumentation libraries
|
||||
// to be used with OpenTelemetry exporters, including OTLP.
|
||||
//
|
||||
// Prometheus histograms are translated to OpenTelemetry exponential histograms
|
||||
// when native histograms are enabled in the Prometheus client. To enable
|
||||
// Prometheus native histograms, set the (currently experimental) NativeHistogram...
|
||||
// options of the prometheus [HistogramOpts] when creating prometheus histograms.
|
||||
//
|
||||
// While the Prometheus Bridge has some overhead, it can significantly reduce the
|
||||
// combined overall CPU and Memory footprint when sending to an OpenTelemetry
|
||||
// Collector. See the [benchmarks] for more details.
|
||||
//
|
||||
// [Prometheus Golang client library]: https://github.com/prometheus/client_golang
|
||||
// [HistogramOpts]: https://pkg.go.dev/github.com/prometheus/client_golang/prometheus#HistogramOpts
|
||||
// [benchmarks]: https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/bridges/prometheus/BENCHMARKS.md
|
||||
package prometheus // import "go.opentelemetry.io/contrib/bridges/prometheus"
|
404
vendor/go.opentelemetry.io/contrib/bridges/prometheus/producer.go
generated
vendored
Normal file
404
vendor/go.opentelemetry.io/contrib/bridges/prometheus/producer.go
generated
vendored
Normal file
@@ -0,0 +1,404 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package prometheus // import "go.opentelemetry.io/contrib/bridges/prometheus"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/sdk/instrumentation"
|
||||
"go.opentelemetry.io/otel/sdk/metric"
|
||||
"go.opentelemetry.io/otel/sdk/metric/metricdata"
|
||||
)
|
||||
|
||||
const (
|
||||
scopeName = "go.opentelemetry.io/contrib/bridges/prometheus"
|
||||
traceIDLabel = "trace_id"
|
||||
spanIDLabel = "span_id"
|
||||
)
|
||||
|
||||
var (
|
||||
errUnsupportedType = errors.New("unsupported metric type")
|
||||
processStartTime = time.Now()
|
||||
)
|
||||
|
||||
type producer struct {
|
||||
gatherers prometheus.Gatherers
|
||||
}
|
||||
|
||||
// NewMetricProducer returns a metric.Producer that fetches metrics from
|
||||
// Prometheus. This can be used to allow Prometheus instrumentation to be
|
||||
// added to an OpenTelemetry export pipeline.
|
||||
func NewMetricProducer(opts ...Option) metric.Producer {
|
||||
cfg := newConfig(opts...)
|
||||
return &producer{
|
||||
gatherers: cfg.gatherers,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *producer) Produce(context.Context) ([]metricdata.ScopeMetrics, error) {
|
||||
now := time.Now()
|
||||
var errs multierr
|
||||
otelMetrics := make([]metricdata.Metrics, 0)
|
||||
for _, gatherer := range p.gatherers {
|
||||
promMetrics, err := gatherer.Gather()
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
m, err := convertPrometheusMetricsInto(promMetrics, now)
|
||||
otelMetrics = append(otelMetrics, m...)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
if errs.errOrNil() != nil {
|
||||
otel.Handle(errs.errOrNil())
|
||||
}
|
||||
if len(otelMetrics) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return []metricdata.ScopeMetrics{{
|
||||
Scope: instrumentation.Scope{
|
||||
Name: scopeName,
|
||||
},
|
||||
Metrics: otelMetrics,
|
||||
}}, nil
|
||||
}
|
||||
|
||||
func convertPrometheusMetricsInto(promMetrics []*dto.MetricFamily, now time.Time) ([]metricdata.Metrics, error) {
|
||||
var errs multierr
|
||||
otelMetrics := make([]metricdata.Metrics, 0)
|
||||
for _, pm := range promMetrics {
|
||||
if len(pm.GetMetric()) == 0 {
|
||||
// This shouldn't ever happen
|
||||
continue
|
||||
}
|
||||
newMetric := metricdata.Metrics{
|
||||
Name: pm.GetName(),
|
||||
Description: pm.GetHelp(),
|
||||
}
|
||||
switch pm.GetType() {
|
||||
case dto.MetricType_GAUGE:
|
||||
newMetric.Data = convertGauge(pm.GetMetric(), now)
|
||||
case dto.MetricType_COUNTER:
|
||||
newMetric.Data = convertCounter(pm.GetMetric(), now)
|
||||
case dto.MetricType_SUMMARY:
|
||||
newMetric.Data = convertSummary(pm.GetMetric(), now)
|
||||
case dto.MetricType_HISTOGRAM:
|
||||
if isExponentialHistogram(pm.GetMetric()[0].GetHistogram()) {
|
||||
newMetric.Data = convertExponentialHistogram(pm.GetMetric(), now)
|
||||
} else {
|
||||
newMetric.Data = convertHistogram(pm.GetMetric(), now)
|
||||
}
|
||||
default:
|
||||
// MetricType_GAUGE_HISTOGRAM, MetricType_UNTYPED
|
||||
errs = append(errs, fmt.Errorf("%w: %v for metric %v", errUnsupportedType, pm.GetType(), pm.GetName()))
|
||||
continue
|
||||
}
|
||||
otelMetrics = append(otelMetrics, newMetric)
|
||||
}
|
||||
return otelMetrics, errs.errOrNil()
|
||||
}
|
||||
|
||||
func isExponentialHistogram(hist *dto.Histogram) bool {
|
||||
// The prometheus go client ensures at least one of these is non-zero
|
||||
// so it can be distinguished from a fixed-bucket histogram.
|
||||
// https://github.com/prometheus/client_golang/blob/7ac90362b02729a65109b33d172bafb65d7dab50/prometheus/histogram.go#L818
|
||||
return hist.GetZeroThreshold() > 0 ||
|
||||
hist.GetZeroCount() > 0 ||
|
||||
len(hist.GetPositiveSpan()) > 0 ||
|
||||
len(hist.GetNegativeSpan()) > 0
|
||||
}
|
||||
|
||||
func convertGauge(metrics []*dto.Metric, now time.Time) metricdata.Gauge[float64] {
|
||||
otelGauge := metricdata.Gauge[float64]{
|
||||
DataPoints: make([]metricdata.DataPoint[float64], len(metrics)),
|
||||
}
|
||||
for i, m := range metrics {
|
||||
dp := metricdata.DataPoint[float64]{
|
||||
Attributes: convertLabels(m.GetLabel()),
|
||||
Time: now,
|
||||
Value: m.GetGauge().GetValue(),
|
||||
}
|
||||
if m.GetTimestampMs() != 0 {
|
||||
dp.Time = time.UnixMilli(m.GetTimestampMs())
|
||||
}
|
||||
otelGauge.DataPoints[i] = dp
|
||||
}
|
||||
return otelGauge
|
||||
}
|
||||
|
||||
func convertCounter(metrics []*dto.Metric, now time.Time) metricdata.Sum[float64] {
|
||||
otelCounter := metricdata.Sum[float64]{
|
||||
DataPoints: make([]metricdata.DataPoint[float64], len(metrics)),
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
IsMonotonic: true,
|
||||
}
|
||||
for i, m := range metrics {
|
||||
dp := metricdata.DataPoint[float64]{
|
||||
Attributes: convertLabels(m.GetLabel()),
|
||||
StartTime: processStartTime,
|
||||
Time: now,
|
||||
Value: m.GetCounter().GetValue(),
|
||||
}
|
||||
if ex := m.GetCounter().GetExemplar(); ex != nil {
|
||||
dp.Exemplars = []metricdata.Exemplar[float64]{convertExemplar(ex)}
|
||||
}
|
||||
createdTs := m.GetCounter().GetCreatedTimestamp()
|
||||
if createdTs.IsValid() {
|
||||
dp.StartTime = createdTs.AsTime()
|
||||
}
|
||||
if m.GetTimestampMs() != 0 {
|
||||
dp.Time = time.UnixMilli(m.GetTimestampMs())
|
||||
}
|
||||
otelCounter.DataPoints[i] = dp
|
||||
}
|
||||
return otelCounter
|
||||
}
|
||||
|
||||
func convertExponentialHistogram(metrics []*dto.Metric, now time.Time) metricdata.ExponentialHistogram[float64] {
|
||||
otelExpHistogram := metricdata.ExponentialHistogram[float64]{
|
||||
DataPoints: make([]metricdata.ExponentialHistogramDataPoint[float64], len(metrics)),
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
}
|
||||
for i, m := range metrics {
|
||||
dp := metricdata.ExponentialHistogramDataPoint[float64]{
|
||||
Attributes: convertLabels(m.GetLabel()),
|
||||
StartTime: processStartTime,
|
||||
Time: now,
|
||||
Count: m.GetHistogram().GetSampleCount(),
|
||||
Sum: m.GetHistogram().GetSampleSum(),
|
||||
Scale: m.GetHistogram().GetSchema(),
|
||||
ZeroCount: m.GetHistogram().GetZeroCount(),
|
||||
ZeroThreshold: m.GetHistogram().GetZeroThreshold(),
|
||||
PositiveBucket: convertExponentialBuckets(
|
||||
m.GetHistogram().GetPositiveSpan(),
|
||||
m.GetHistogram().GetPositiveDelta(),
|
||||
),
|
||||
NegativeBucket: convertExponentialBuckets(
|
||||
m.GetHistogram().GetNegativeSpan(),
|
||||
m.GetHistogram().GetNegativeDelta(),
|
||||
),
|
||||
// TODO: Support exemplars
|
||||
}
|
||||
createdTs := m.GetHistogram().GetCreatedTimestamp()
|
||||
if createdTs.IsValid() {
|
||||
dp.StartTime = createdTs.AsTime()
|
||||
}
|
||||
if t := m.GetTimestampMs(); t != 0 {
|
||||
dp.Time = time.UnixMilli(t)
|
||||
}
|
||||
otelExpHistogram.DataPoints[i] = dp
|
||||
}
|
||||
return otelExpHistogram
|
||||
}
|
||||
|
||||
func convertExponentialBuckets(bucketSpans []*dto.BucketSpan, deltas []int64) metricdata.ExponentialBucket {
|
||||
if len(bucketSpans) == 0 {
|
||||
return metricdata.ExponentialBucket{}
|
||||
}
|
||||
// Prometheus Native Histograms buckets are indexed by upper boundary
|
||||
// while Exponential Histograms are indexed by lower boundary, the result
|
||||
// being that the Offset fields are different-by-one.
|
||||
initialOffset := bucketSpans[0].GetOffset() - 1
|
||||
// We will have one bucket count for each delta, and zeros for the offsets
|
||||
// after the initial offset.
|
||||
lenCounts := len(deltas)
|
||||
for i, bs := range bucketSpans {
|
||||
if i != 0 {
|
||||
lenCounts += int(bs.GetOffset())
|
||||
}
|
||||
}
|
||||
counts := make([]uint64, lenCounts)
|
||||
deltaIndex := 0
|
||||
countIndex := int32(0)
|
||||
count := int64(0)
|
||||
for i, bs := range bucketSpans {
|
||||
// Do not insert zeroes if this is the first bucketSpan, since those
|
||||
// zeroes are accounted for in the Offset field.
|
||||
if i != 0 {
|
||||
// Increase the count index by the Offset to insert Offset zeroes
|
||||
countIndex += bs.GetOffset()
|
||||
}
|
||||
for j := uint32(0); j < bs.GetLength(); j++ {
|
||||
// Convert deltas to the cumulative number of observations
|
||||
count += deltas[deltaIndex]
|
||||
deltaIndex++
|
||||
// count should always be positive after accounting for deltas
|
||||
if count > 0 {
|
||||
counts[countIndex] = uint64(count)
|
||||
}
|
||||
countIndex++
|
||||
}
|
||||
}
|
||||
return metricdata.ExponentialBucket{
|
||||
Offset: initialOffset,
|
||||
Counts: counts,
|
||||
}
|
||||
}
|
||||
|
||||
func convertHistogram(metrics []*dto.Metric, now time.Time) metricdata.Histogram[float64] {
|
||||
otelHistogram := metricdata.Histogram[float64]{
|
||||
DataPoints: make([]metricdata.HistogramDataPoint[float64], len(metrics)),
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
}
|
||||
for i, m := range metrics {
|
||||
bounds, bucketCounts, exemplars := convertBuckets(m.GetHistogram().GetBucket(), m.GetHistogram().GetSampleCount())
|
||||
dp := metricdata.HistogramDataPoint[float64]{
|
||||
Attributes: convertLabels(m.GetLabel()),
|
||||
StartTime: processStartTime,
|
||||
Time: now,
|
||||
Count: m.GetHistogram().GetSampleCount(),
|
||||
Sum: m.GetHistogram().GetSampleSum(),
|
||||
Bounds: bounds,
|
||||
BucketCounts: bucketCounts,
|
||||
Exemplars: exemplars,
|
||||
}
|
||||
createdTs := m.GetHistogram().GetCreatedTimestamp()
|
||||
if createdTs.IsValid() {
|
||||
dp.StartTime = createdTs.AsTime()
|
||||
}
|
||||
if m.GetTimestampMs() != 0 {
|
||||
dp.Time = time.UnixMilli(m.GetTimestampMs())
|
||||
}
|
||||
otelHistogram.DataPoints[i] = dp
|
||||
}
|
||||
return otelHistogram
|
||||
}
|
||||
|
||||
func convertBuckets(buckets []*dto.Bucket, sampleCount uint64) ([]float64, []uint64, []metricdata.Exemplar[float64]) {
|
||||
if len(buckets) == 0 {
|
||||
// This should never happen
|
||||
return nil, nil, nil
|
||||
}
|
||||
// buckets will only include the +Inf bucket if there is an exemplar for it
|
||||
// https://github.com/prometheus/client_golang/blob/d038ab96c0c7b9cd217a39072febd610bcdf1fd8/prometheus/metric.go#L189
|
||||
// we need to handle the case where it is present, or where it is missing.
|
||||
hasInf := math.IsInf(buckets[len(buckets)-1].GetUpperBound(), +1)
|
||||
var bounds []float64
|
||||
var bucketCounts []uint64
|
||||
if hasInf {
|
||||
bounds = make([]float64, len(buckets)-1)
|
||||
bucketCounts = make([]uint64, len(buckets))
|
||||
} else {
|
||||
bounds = make([]float64, len(buckets))
|
||||
bucketCounts = make([]uint64, len(buckets)+1)
|
||||
}
|
||||
exemplars := make([]metricdata.Exemplar[float64], 0)
|
||||
var previousCount uint64
|
||||
for i, bucket := range buckets {
|
||||
// The last bound may be the +Inf bucket, which is implied in OTel, but
|
||||
// is explicit in Prometheus. Skip the last boundary if it is the +Inf
|
||||
// bound.
|
||||
if bound := bucket.GetUpperBound(); !math.IsInf(bound, +1) {
|
||||
bounds[i] = bound
|
||||
}
|
||||
previousCount, bucketCounts[i] = bucket.GetCumulativeCount(), bucket.GetCumulativeCount()-previousCount
|
||||
if ex := bucket.GetExemplar(); ex != nil {
|
||||
exemplars = append(exemplars, convertExemplar(ex))
|
||||
}
|
||||
}
|
||||
if !hasInf {
|
||||
// The Inf bucket was missing, so set the last bucket counts to the
|
||||
// overall count
|
||||
bucketCounts[len(bucketCounts)-1] = sampleCount - previousCount
|
||||
}
|
||||
return bounds, bucketCounts, exemplars
|
||||
}
|
||||
|
||||
func convertSummary(metrics []*dto.Metric, now time.Time) metricdata.Summary {
|
||||
otelSummary := metricdata.Summary{
|
||||
DataPoints: make([]metricdata.SummaryDataPoint, len(metrics)),
|
||||
}
|
||||
for i, m := range metrics {
|
||||
dp := metricdata.SummaryDataPoint{
|
||||
Attributes: convertLabels(m.GetLabel()),
|
||||
StartTime: processStartTime,
|
||||
Time: now,
|
||||
Count: m.GetSummary().GetSampleCount(),
|
||||
Sum: m.GetSummary().GetSampleSum(),
|
||||
QuantileValues: convertQuantiles(m.GetSummary().GetQuantile()),
|
||||
}
|
||||
createdTs := m.GetSummary().GetCreatedTimestamp()
|
||||
if createdTs.IsValid() {
|
||||
dp.StartTime = createdTs.AsTime()
|
||||
}
|
||||
if t := m.GetTimestampMs(); t != 0 {
|
||||
dp.Time = time.UnixMilli(t)
|
||||
}
|
||||
otelSummary.DataPoints[i] = dp
|
||||
}
|
||||
return otelSummary
|
||||
}
|
||||
|
||||
func convertQuantiles(quantiles []*dto.Quantile) []metricdata.QuantileValue {
|
||||
otelQuantiles := make([]metricdata.QuantileValue, len(quantiles))
|
||||
for i, quantile := range quantiles {
|
||||
dp := metricdata.QuantileValue{
|
||||
Quantile: quantile.GetQuantile(),
|
||||
Value: quantile.GetValue(),
|
||||
}
|
||||
otelQuantiles[i] = dp
|
||||
}
|
||||
return otelQuantiles
|
||||
}
|
||||
|
||||
func convertLabels(labels []*dto.LabelPair) attribute.Set {
|
||||
kvs := make([]attribute.KeyValue, len(labels))
|
||||
for i, l := range labels {
|
||||
kvs[i] = attribute.String(l.GetName(), l.GetValue())
|
||||
}
|
||||
return attribute.NewSet(kvs...)
|
||||
}
|
||||
|
||||
func convertExemplar(exemplar *dto.Exemplar) metricdata.Exemplar[float64] {
|
||||
attrs := make([]attribute.KeyValue, 0)
|
||||
var traceID, spanID []byte
|
||||
// find the trace ID and span ID in attributes, if it exists
|
||||
for _, label := range exemplar.GetLabel() {
|
||||
if label.GetName() == traceIDLabel {
|
||||
traceID = []byte(label.GetValue())
|
||||
} else if label.GetName() == spanIDLabel {
|
||||
spanID = []byte(label.GetValue())
|
||||
} else {
|
||||
attrs = append(attrs, attribute.String(label.GetName(), label.GetValue()))
|
||||
}
|
||||
}
|
||||
return metricdata.Exemplar[float64]{
|
||||
Value: exemplar.GetValue(),
|
||||
Time: exemplar.GetTimestamp().AsTime(),
|
||||
TraceID: traceID,
|
||||
SpanID: spanID,
|
||||
FilteredAttributes: attrs,
|
||||
}
|
||||
}
|
||||
|
||||
type multierr []error
|
||||
|
||||
func (e multierr) errOrNil() error {
|
||||
if len(e) == 0 {
|
||||
return nil
|
||||
} else if len(e) == 1 {
|
||||
return e[0]
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func (e multierr) Error() string {
|
||||
es := make([]string, len(e))
|
||||
for i, err := range e {
|
||||
es[i] = fmt.Sprintf("* %s", err)
|
||||
}
|
||||
return strings.Join(es, "\n\t")
|
||||
}
|
201
vendor/go.opentelemetry.io/contrib/exporters/autoexport/LICENSE
generated
vendored
Normal file
201
vendor/go.opentelemetry.io/contrib/exporters/autoexport/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
7
vendor/go.opentelemetry.io/contrib/exporters/autoexport/doc.go
generated
vendored
Normal file
7
vendor/go.opentelemetry.io/contrib/exporters/autoexport/doc.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package autoexport provides OpenTelemetry exporter factory functions
|
||||
// with defaults and environment variable support as defined by the
|
||||
// OpenTelemetry specification.
|
||||
package autoexport // import "go.opentelemetry.io/contrib/exporters/autoexport"
|
93
vendor/go.opentelemetry.io/contrib/exporters/autoexport/logs.go
generated
vendored
Normal file
93
vendor/go.opentelemetry.io/contrib/exporters/autoexport/logs.go
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package autoexport // import "go.opentelemetry.io/contrib/exporters/autoexport"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp"
|
||||
"go.opentelemetry.io/otel/exporters/stdout/stdoutlog"
|
||||
"go.opentelemetry.io/otel/sdk/log"
|
||||
)
|
||||
|
||||
const otelExporterOTLPLogsProtoEnvKey = "OTEL_EXPORTER_OTLP_LOGS_PROTOCOL"
|
||||
|
||||
// LogOption applies an autoexport configuration option.
|
||||
type LogOption = option[log.Exporter]
|
||||
|
||||
var logsSignal = newSignal[log.Exporter]("OTEL_LOGS_EXPORTER")
|
||||
|
||||
// WithFallbackLogExporter sets the fallback exporter to use when no exporter
|
||||
// is configured through the OTEL_LOGS_EXPORTER environment variable.
|
||||
func WithFallbackLogExporter(logExporterFactory func(ctx context.Context) (log.Exporter, error)) LogOption {
|
||||
return withFallbackFactory[log.Exporter](logExporterFactory)
|
||||
}
|
||||
|
||||
// NewLogExporter returns a configured [go.opentelemetry.io/otel/sdk/log.Exporter]
|
||||
// defined using the environment variables described below.
|
||||
//
|
||||
// OTEL_LOGS_EXPORTER defines the logs exporter; supported values:
|
||||
// - "none" - "no operation" exporter
|
||||
// - "otlp" (default) - OTLP exporter; see [go.opentelemetry.io/otel/exporters/otlp/otlplog]
|
||||
// - "console" - Standard output exporter; see [go.opentelemetry.io/otel/exporters/stdout/stdoutlog]
|
||||
//
|
||||
// OTEL_EXPORTER_OTLP_PROTOCOL defines OTLP exporter's transport protocol;
|
||||
// supported values:
|
||||
// - "http/protobuf" (default) - protobuf-encoded data over HTTP connection;
|
||||
// see: [go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp]
|
||||
// - "grpc" - gRPC with protobuf-encoded data over HTTP/2 connection;
|
||||
// see: [go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc]
|
||||
//
|
||||
// OTEL_EXPORTER_OTLP_LOGS_PROTOCOL defines OTLP exporter's transport protocol for the logs signal;
|
||||
// supported values are the same as OTEL_EXPORTER_OTLP_PROTOCOL.
|
||||
//
|
||||
// An error is returned if an environment value is set to an unhandled value.
|
||||
//
|
||||
// Use [RegisterLogExporter] to handle more values of OTEL_LOGS_EXPORTER.
|
||||
//
|
||||
// Use [WithFallbackLogExporter] option to change the returned exporter
|
||||
// when OTEL_LOGS_EXPORTER is unset or empty.
|
||||
//
|
||||
// Use [IsNoneLogExporter] to check if the returned exporter is a "no operation" exporter.
|
||||
func NewLogExporter(ctx context.Context, opts ...LogOption) (log.Exporter, error) {
|
||||
return logsSignal.create(ctx, opts...)
|
||||
}
|
||||
|
||||
// RegisterLogExporter sets the log.Exporter factory to be used when the
|
||||
// OTEL_LOGS_EXPORTER environment variable contains the exporter name.
|
||||
// This will panic if name has already been registered.
|
||||
func RegisterLogExporter(name string, factory func(context.Context) (log.Exporter, error)) {
|
||||
must(logsSignal.registry.store(name, factory))
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterLogExporter("otlp", func(ctx context.Context) (log.Exporter, error) {
|
||||
proto := os.Getenv(otelExporterOTLPLogsProtoEnvKey)
|
||||
if proto == "" {
|
||||
proto = os.Getenv(otelExporterOTLPProtoEnvKey)
|
||||
}
|
||||
|
||||
// Fallback to default, http/protobuf.
|
||||
if proto == "" {
|
||||
proto = "http/protobuf"
|
||||
}
|
||||
|
||||
switch proto {
|
||||
case "grpc":
|
||||
return otlploggrpc.New(ctx)
|
||||
case "http/protobuf":
|
||||
return otlploghttp.New(ctx)
|
||||
default:
|
||||
return nil, errInvalidOTLPProtocol
|
||||
}
|
||||
})
|
||||
RegisterLogExporter("console", func(ctx context.Context) (log.Exporter, error) {
|
||||
return stdoutlog.New()
|
||||
})
|
||||
RegisterLogExporter("none", func(ctx context.Context) (log.Exporter, error) {
|
||||
return noopLogExporter{}, nil
|
||||
})
|
||||
}
|
299
vendor/go.opentelemetry.io/contrib/exporters/autoexport/metrics.go
generated
vendored
Normal file
299
vendor/go.opentelemetry.io/contrib/exporters/autoexport/metrics.go
generated
vendored
Normal file
@@ -0,0 +1,299 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package autoexport // import "go.opentelemetry.io/contrib/exporters/autoexport"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
|
||||
prometheusbridge "go.opentelemetry.io/contrib/bridges/prometheus"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp"
|
||||
promexporter "go.opentelemetry.io/otel/exporters/prometheus"
|
||||
"go.opentelemetry.io/otel/exporters/stdout/stdoutmetric"
|
||||
"go.opentelemetry.io/otel/sdk/metric"
|
||||
)
|
||||
|
||||
const otelExporterOTLPMetricsProtoEnvKey = "OTEL_EXPORTER_OTLP_METRICS_PROTOCOL"
|
||||
|
||||
// MetricOption applies an autoexport configuration option.
|
||||
type MetricOption = option[metric.Reader]
|
||||
|
||||
// WithFallbackMetricReader sets the fallback exporter to use when no exporter
|
||||
// is configured through the OTEL_METRICS_EXPORTER environment variable.
|
||||
func WithFallbackMetricReader(metricReaderFactory func(ctx context.Context) (metric.Reader, error)) MetricOption {
|
||||
return withFallbackFactory[metric.Reader](metricReaderFactory)
|
||||
}
|
||||
|
||||
// NewMetricReader returns a configured [go.opentelemetry.io/otel/sdk/metric.Reader]
|
||||
// defined using the environment variables described below.
|
||||
//
|
||||
// OTEL_METRICS_EXPORTER defines the metrics exporter; supported values:
|
||||
// - "none" - "no operation" exporter
|
||||
// - "otlp" (default) - OTLP exporter; see [go.opentelemetry.io/otel/exporters/otlp/otlpmetric]
|
||||
// - "prometheus" - Prometheus exporter + HTTP server; see [go.opentelemetry.io/otel/exporters/prometheus]
|
||||
// - "console" - Standard output exporter; see [go.opentelemetry.io/otel/exporters/stdout/stdoutmetric]
|
||||
//
|
||||
// OTEL_EXPORTER_OTLP_PROTOCOL defines OTLP exporter's transport protocol;
|
||||
// supported values:
|
||||
// - "grpc" - protobuf-encoded data using gRPC wire format over HTTP/2 connection;
|
||||
// see: [go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc]
|
||||
// - "http/protobuf" (default) - protobuf-encoded data over HTTP connection;
|
||||
// see: [go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp]
|
||||
//
|
||||
// OTEL_EXPORTER_OTLP_METRICS_PROTOCOL defines OTLP exporter's transport protocol for the metrics signal;
|
||||
// supported values are the same as OTEL_EXPORTER_OTLP_PROTOCOL.
|
||||
//
|
||||
// OTEL_EXPORTER_PROMETHEUS_HOST (defaulting to "localhost") and
|
||||
// OTEL_EXPORTER_PROMETHEUS_PORT (defaulting to 9464) define the host and port for the
|
||||
// Prometheus exporter's HTTP server.
|
||||
//
|
||||
// Experimental: OTEL_METRICS_PRODUCERS can be used to configure metric producers.
|
||||
// supported values: prometheus, none. Multiple values can be specified separated by commas.
|
||||
//
|
||||
// An error is returned if an environment value is set to an unhandled value.
|
||||
//
|
||||
// Use [RegisterMetricReader] to handle more values of OTEL_METRICS_EXPORTER.
|
||||
// Use [RegisterMetricProducer] to handle more values of OTEL_METRICS_PRODUCERS.
|
||||
//
|
||||
// Use [WithFallbackMetricReader] option to change the returned exporter
|
||||
// when OTEL_METRICS_EXPORTER is unset or empty.
|
||||
//
|
||||
// Use [IsNoneMetricReader] to check if the returned exporter is a "no operation" exporter.
|
||||
func NewMetricReader(ctx context.Context, opts ...MetricOption) (metric.Reader, error) {
|
||||
return metricsSignal.create(ctx, opts...)
|
||||
}
|
||||
|
||||
// RegisterMetricReader sets the MetricReader factory to be used when the
|
||||
// OTEL_METRICS_EXPORTERS environment variable contains the exporter name. This
|
||||
// will panic if name has already been registered.
|
||||
func RegisterMetricReader(name string, factory func(context.Context) (metric.Reader, error)) {
|
||||
must(metricsSignal.registry.store(name, factory))
|
||||
}
|
||||
|
||||
// RegisterMetricProducer sets the MetricReader factory to be used when the
|
||||
// OTEL_METRICS_PRODUCERS environment variable contains the producer name. This
|
||||
// will panic if name has already been registered.
|
||||
func RegisterMetricProducer(name string, factory func(context.Context) (metric.Producer, error)) {
|
||||
must(metricsProducers.registry.store(name, factory))
|
||||
}
|
||||
|
||||
// WithFallbackMetricProducer sets the fallback producer to use when no producer
|
||||
// is configured through the OTEL_METRICS_PRODUCERS environment variable.
|
||||
func WithFallbackMetricProducer(producerFactory func(ctx context.Context) (metric.Producer, error)) {
|
||||
metricsProducers.fallbackProducer = producerFactory
|
||||
}
|
||||
|
||||
var (
|
||||
metricsSignal = newSignal[metric.Reader]("OTEL_METRICS_EXPORTER")
|
||||
metricsProducers = newProducerRegistry("OTEL_METRICS_PRODUCERS")
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterMetricReader("otlp", func(ctx context.Context) (metric.Reader, error) {
|
||||
producers, err := metricsProducers.create(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
readerOpts := []metric.PeriodicReaderOption{}
|
||||
for _, producer := range producers {
|
||||
readerOpts = append(readerOpts, metric.WithProducer(producer))
|
||||
}
|
||||
|
||||
proto := os.Getenv(otelExporterOTLPMetricsProtoEnvKey)
|
||||
if proto == "" {
|
||||
proto = os.Getenv(otelExporterOTLPProtoEnvKey)
|
||||
}
|
||||
|
||||
// Fallback to default, http/protobuf.
|
||||
if proto == "" {
|
||||
proto = "http/protobuf"
|
||||
}
|
||||
|
||||
switch proto {
|
||||
case "grpc":
|
||||
r, err := otlpmetricgrpc.New(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return metric.NewPeriodicReader(r, readerOpts...), nil
|
||||
case "http/protobuf":
|
||||
r, err := otlpmetrichttp.New(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return metric.NewPeriodicReader(r, readerOpts...), nil
|
||||
default:
|
||||
return nil, errInvalidOTLPProtocol
|
||||
}
|
||||
})
|
||||
RegisterMetricReader("console", func(ctx context.Context) (metric.Reader, error) {
|
||||
producers, err := metricsProducers.create(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
readerOpts := []metric.PeriodicReaderOption{}
|
||||
for _, producer := range producers {
|
||||
readerOpts = append(readerOpts, metric.WithProducer(producer))
|
||||
}
|
||||
|
||||
r, err := stdoutmetric.New()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return metric.NewPeriodicReader(r, readerOpts...), nil
|
||||
})
|
||||
RegisterMetricReader("none", func(ctx context.Context) (metric.Reader, error) {
|
||||
return newNoopMetricReader(), nil
|
||||
})
|
||||
RegisterMetricReader("prometheus", func(ctx context.Context) (metric.Reader, error) {
|
||||
// create an isolated registry instead of using the global registry --
|
||||
// the user might not want to mix OTel with non-OTel metrics.
|
||||
// Those that want to comingle metrics from global registry can use
|
||||
// OTEL_METRICS_PRODUCERS=prometheus
|
||||
reg := prometheus.NewRegistry()
|
||||
|
||||
exporterOpts := []promexporter.Option{promexporter.WithRegisterer(reg)}
|
||||
|
||||
producers, err := metricsProducers.create(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, producer := range producers {
|
||||
exporterOpts = append(exporterOpts, promexporter.WithProducer(producer))
|
||||
}
|
||||
|
||||
reader, err := promexporter.New(exporterOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{Registry: reg}))
|
||||
server := http.Server{
|
||||
// Timeouts are necessary to make a server resilient to attacks, but ListenAndServe doesn't set any.
|
||||
// We use values from this example: https://blog.cloudflare.com/exposing-go-on-the-internet/#:~:text=There%20are%20three%20main%20timeouts
|
||||
ReadTimeout: 5 * time.Second,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
IdleTimeout: 120 * time.Second,
|
||||
Handler: mux,
|
||||
}
|
||||
|
||||
// environment variable names and defaults specified at https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/#prometheus-exporter
|
||||
host := getenv("OTEL_EXPORTER_PROMETHEUS_HOST", "localhost")
|
||||
port := getenv("OTEL_EXPORTER_PROMETHEUS_PORT", "9464")
|
||||
addr := host + ":" + port
|
||||
lis, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, errors.Join(
|
||||
fmt.Errorf("binding address %s for Prometheus exporter: %w", addr, err),
|
||||
reader.Shutdown(ctx),
|
||||
)
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := server.Serve(lis); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
otel.Handle(fmt.Errorf("the Prometheus HTTP server exited unexpectedly: %w", err))
|
||||
}
|
||||
}()
|
||||
|
||||
return readerWithServer{lis.Addr(), reader, &server}, nil
|
||||
})
|
||||
|
||||
RegisterMetricProducer("prometheus", func(ctx context.Context) (metric.Producer, error) {
|
||||
return prometheusbridge.NewMetricProducer(), nil
|
||||
})
|
||||
RegisterMetricProducer("none", func(ctx context.Context) (metric.Producer, error) {
|
||||
return newNoopMetricProducer(), nil
|
||||
})
|
||||
}
|
||||
|
||||
type readerWithServer struct {
|
||||
addr net.Addr
|
||||
metric.Reader
|
||||
server *http.Server
|
||||
}
|
||||
|
||||
func (rws readerWithServer) Shutdown(ctx context.Context) error {
|
||||
return errors.Join(
|
||||
rws.Reader.Shutdown(ctx),
|
||||
rws.server.Shutdown(ctx),
|
||||
)
|
||||
}
|
||||
|
||||
func getenv(key, fallback string) string {
|
||||
result, ok := os.LookupEnv(key)
|
||||
if !ok {
|
||||
return fallback
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
type producerRegistry struct {
|
||||
envKey string
|
||||
fallbackProducer func(context.Context) (metric.Producer, error)
|
||||
registry *registry[metric.Producer]
|
||||
}
|
||||
|
||||
func newProducerRegistry(envKey string) producerRegistry {
|
||||
return producerRegistry{
|
||||
envKey: envKey,
|
||||
registry: ®istry[metric.Producer]{
|
||||
names: make(map[string]func(context.Context) (metric.Producer, error)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (pr producerRegistry) create(ctx context.Context) ([]metric.Producer, error) {
|
||||
expType := os.Getenv(pr.envKey)
|
||||
if expType == "" {
|
||||
if pr.fallbackProducer != nil {
|
||||
producer, err := pr.fallbackProducer(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []metric.Producer{producer}, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
producers := dedupedMetricProducers(expType)
|
||||
metricProducers := make([]metric.Producer, 0, len(producers))
|
||||
for _, producer := range producers {
|
||||
producer, err := pr.registry.load(ctx, producer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
metricProducers = append(metricProducers, producer)
|
||||
}
|
||||
|
||||
return metricProducers, nil
|
||||
}
|
||||
|
||||
func dedupedMetricProducers(envValue string) []string {
|
||||
producers := make(map[string]struct{})
|
||||
for _, producer := range strings.Split(envValue, ",") {
|
||||
producers[producer] = struct{}{}
|
||||
}
|
||||
|
||||
result := make([]string, 0, len(producers))
|
||||
for producer := range producers {
|
||||
result = append(result, producer)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
87
vendor/go.opentelemetry.io/contrib/exporters/autoexport/noop.go
generated
vendored
Normal file
87
vendor/go.opentelemetry.io/contrib/exporters/autoexport/noop.go
generated
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package autoexport // import "go.opentelemetry.io/contrib/exporters/autoexport"
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.opentelemetry.io/otel/sdk/log"
|
||||
"go.opentelemetry.io/otel/sdk/metric"
|
||||
"go.opentelemetry.io/otel/sdk/metric/metricdata"
|
||||
"go.opentelemetry.io/otel/sdk/trace"
|
||||
)
|
||||
|
||||
// noopSpanExporter is an implementation of trace.SpanExporter that performs no operations.
|
||||
type noopSpanExporter struct{}
|
||||
|
||||
var _ trace.SpanExporter = noopSpanExporter{}
|
||||
|
||||
// ExportSpans is part of trace.SpanExporter interface.
|
||||
func (e noopSpanExporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Shutdown is part of trace.SpanExporter interface.
|
||||
func (e noopSpanExporter) Shutdown(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsNoneSpanExporter returns true for the exporter returned by [NewSpanExporter]
|
||||
// when OTEL_TRACES_EXPORTER environment variable is set to "none".
|
||||
func IsNoneSpanExporter(e trace.SpanExporter) bool {
|
||||
_, ok := e.(noopSpanExporter)
|
||||
return ok
|
||||
}
|
||||
|
||||
type noopMetricReader struct {
|
||||
*metric.ManualReader
|
||||
}
|
||||
|
||||
func newNoopMetricReader() noopMetricReader {
|
||||
return noopMetricReader{metric.NewManualReader()}
|
||||
}
|
||||
|
||||
// IsNoneMetricReader returns true for the exporter returned by [NewMetricReader]
|
||||
// when OTEL_METRICS_EXPORTER environment variable is set to "none".
|
||||
func IsNoneMetricReader(e metric.Reader) bool {
|
||||
_, ok := e.(noopMetricReader)
|
||||
return ok
|
||||
}
|
||||
|
||||
type noopMetricProducer struct{}
|
||||
|
||||
func (e noopMetricProducer) Produce(ctx context.Context) ([]metricdata.ScopeMetrics, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func newNoopMetricProducer() noopMetricProducer {
|
||||
return noopMetricProducer{}
|
||||
}
|
||||
|
||||
// noopLogExporter is an implementation of log.SpanExporter that performs no operations.
|
||||
type noopLogExporter struct{}
|
||||
|
||||
var _ log.Exporter = noopLogExporter{}
|
||||
|
||||
// ExportSpans is part of log.Exporter interface.
|
||||
func (e noopLogExporter) Export(ctx context.Context, records []log.Record) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Shutdown is part of log.Exporter interface.
|
||||
func (e noopLogExporter) Shutdown(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ForceFlush is part of log.Exporter interface.
|
||||
func (e noopLogExporter) ForceFlush(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsNoneLogExporter returns true for the exporter returned by [NewLogExporter]
|
||||
// when OTEL_LOGSS_EXPORTER environment variable is set to "none".
|
||||
func IsNoneLogExporter(e log.Exporter) bool {
|
||||
_, ok := e.(noopLogExporter)
|
||||
return ok
|
||||
}
|
67
vendor/go.opentelemetry.io/contrib/exporters/autoexport/registry.go
generated
vendored
Normal file
67
vendor/go.opentelemetry.io/contrib/exporters/autoexport/registry.go
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package autoexport // import "go.opentelemetry.io/contrib/exporters/autoexport"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const otelExporterOTLPProtoEnvKey = "OTEL_EXPORTER_OTLP_PROTOCOL"
|
||||
|
||||
// registry maintains a map of exporter names to exporter factories
|
||||
// func(context.Context) (T, error) that is safe for concurrent use by multiple
|
||||
// goroutines without additional locking or coordination.
|
||||
type registry[T any] struct {
|
||||
mu sync.Mutex
|
||||
names map[string]func(context.Context) (T, error)
|
||||
}
|
||||
|
||||
var (
|
||||
// errUnknownExporterProducer is returned when an unknown exporter name is used in
|
||||
// the OTEL_*_EXPORTER or OTEL_METRICS_PRODUCERS environment variables.
|
||||
errUnknownExporterProducer = errors.New("unknown exporter or metrics producer")
|
||||
|
||||
// errInvalidOTLPProtocol is returned when an invalid protocol is used in
|
||||
// the OTEL_EXPORTER_OTLP_PROTOCOL environment variable.
|
||||
errInvalidOTLPProtocol = errors.New("invalid OTLP protocol - should be one of ['grpc', 'http/protobuf']")
|
||||
|
||||
// errDuplicateRegistration is returned when an duplicate registration is detected.
|
||||
errDuplicateRegistration = errors.New("duplicate registration")
|
||||
)
|
||||
|
||||
// load returns tries to find the exporter factory with the key and
|
||||
// then execute the factory, returning the created SpanExporter.
|
||||
// errUnknownExporterProducer is returned if the registration is missing and the error from
|
||||
// executing the factory if not nil.
|
||||
func (r *registry[T]) load(ctx context.Context, key string) (T, error) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
factory, ok := r.names[key]
|
||||
if !ok {
|
||||
var zero T
|
||||
return zero, errUnknownExporterProducer
|
||||
}
|
||||
return factory(ctx)
|
||||
}
|
||||
|
||||
// store sets the factory for a key if is not already in the registry. errDuplicateRegistration
|
||||
// is returned if the registry already contains key.
|
||||
func (r *registry[T]) store(key string, factory func(context.Context) (T, error)) error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
if _, ok := r.names[key]; ok {
|
||||
return fmt.Errorf("%w: %q", errDuplicateRegistration, key)
|
||||
}
|
||||
r.names[key] = factory
|
||||
return nil
|
||||
}
|
||||
|
||||
func must(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
61
vendor/go.opentelemetry.io/contrib/exporters/autoexport/signal.go
generated
vendored
Normal file
61
vendor/go.opentelemetry.io/contrib/exporters/autoexport/signal.go
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package autoexport // import "go.opentelemetry.io/contrib/exporters/autoexport"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
)
|
||||
|
||||
type signal[T any] struct {
|
||||
envKey string
|
||||
registry *registry[T]
|
||||
}
|
||||
|
||||
func newSignal[T any](envKey string) signal[T] {
|
||||
return signal[T]{
|
||||
envKey: envKey,
|
||||
registry: ®istry[T]{
|
||||
names: make(map[string]func(context.Context) (T, error)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s signal[T]) create(ctx context.Context, opts ...option[T]) (T, error) {
|
||||
var cfg config[T]
|
||||
for _, opt := range opts {
|
||||
opt.apply(&cfg)
|
||||
}
|
||||
|
||||
expType := os.Getenv(s.envKey)
|
||||
if expType == "" {
|
||||
if cfg.fallbackFactory != nil {
|
||||
return cfg.fallbackFactory(ctx)
|
||||
}
|
||||
expType = "otlp"
|
||||
}
|
||||
|
||||
return s.registry.load(ctx, expType)
|
||||
}
|
||||
|
||||
type config[T any] struct {
|
||||
fallbackFactory func(ctx context.Context) (T, error)
|
||||
}
|
||||
|
||||
type option[T any] interface {
|
||||
apply(cfg *config[T])
|
||||
}
|
||||
|
||||
type optionFunc[T any] func(cfg *config[T])
|
||||
|
||||
//lint:ignore U1000 https://github.com/dominikh/go-tools/issues/1440
|
||||
func (fn optionFunc[T]) apply(cfg *config[T]) {
|
||||
fn(cfg)
|
||||
}
|
||||
|
||||
func withFallbackFactory[T any](fallbackFactory func(ctx context.Context) (T, error)) option[T] {
|
||||
return optionFunc[T](func(cfg *config[T]) {
|
||||
cfg.fallbackFactory = fallbackFactory
|
||||
})
|
||||
}
|
98
vendor/go.opentelemetry.io/contrib/exporters/autoexport/spans.go
generated
vendored
Normal file
98
vendor/go.opentelemetry.io/contrib/exporters/autoexport/spans.go
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package autoexport // import "go.opentelemetry.io/contrib/exporters/autoexport"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
|
||||
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
|
||||
"go.opentelemetry.io/otel/sdk/trace"
|
||||
)
|
||||
|
||||
const otelExporterOTLPTracesProtoEnvKey = "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL"
|
||||
|
||||
// SpanOption applies an autoexport configuration option.
|
||||
type SpanOption = option[trace.SpanExporter]
|
||||
|
||||
// Option applies an autoexport configuration option.
|
||||
//
|
||||
// Deprecated: Use SpanOption.
|
||||
type Option = SpanOption
|
||||
|
||||
// WithFallbackSpanExporter sets the fallback exporter to use when no exporter
|
||||
// is configured through the OTEL_TRACES_EXPORTER environment variable.
|
||||
func WithFallbackSpanExporter(spanExporterFactory func(ctx context.Context) (trace.SpanExporter, error)) SpanOption {
|
||||
return withFallbackFactory[trace.SpanExporter](spanExporterFactory)
|
||||
}
|
||||
|
||||
// NewSpanExporter returns a configured [go.opentelemetry.io/otel/sdk/trace.SpanExporter]
|
||||
// defined using the environment variables described below.
|
||||
//
|
||||
// OTEL_TRACES_EXPORTER defines the traces exporter; supported values:
|
||||
// - "none" - "no operation" exporter
|
||||
// - "otlp" (default) - OTLP exporter; see [go.opentelemetry.io/otel/exporters/otlp/otlptrace]
|
||||
// - "console" - Standard output exporter; see [go.opentelemetry.io/otel/exporters/stdout/stdouttrace]
|
||||
//
|
||||
// OTEL_EXPORTER_OTLP_PROTOCOL defines OTLP exporter's transport protocol;
|
||||
// supported values:
|
||||
// - "grpc" - protobuf-encoded data using gRPC wire format over HTTP/2 connection;
|
||||
// see: [go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc]
|
||||
// - "http/protobuf" (default) - protobuf-encoded data over HTTP connection;
|
||||
// see: [go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp]
|
||||
//
|
||||
// OTEL_EXPORTER_OTLP_TRACES_PROTOCOL defines OTLP exporter's transport protocol for the traces signal;
|
||||
// supported values are the same as OTEL_EXPORTER_OTLP_PROTOCOL.
|
||||
//
|
||||
// An error is returned if an environment value is set to an unhandled value.
|
||||
//
|
||||
// Use [RegisterSpanExporter] to handle more values of OTEL_TRACES_EXPORTER.
|
||||
//
|
||||
// Use [WithFallbackSpanExporter] option to change the returned exporter
|
||||
// when OTEL_TRACES_EXPORTER is unset or empty.
|
||||
//
|
||||
// Use [IsNoneSpanExporter] to check if the returned exporter is a "no operation" exporter.
|
||||
func NewSpanExporter(ctx context.Context, opts ...SpanOption) (trace.SpanExporter, error) {
|
||||
return tracesSignal.create(ctx, opts...)
|
||||
}
|
||||
|
||||
// RegisterSpanExporter sets the SpanExporter factory to be used when the
|
||||
// OTEL_TRACES_EXPORTER environment variable contains the exporter name. This
|
||||
// will panic if name has already been registered.
|
||||
func RegisterSpanExporter(name string, factory func(context.Context) (trace.SpanExporter, error)) {
|
||||
must(tracesSignal.registry.store(name, factory))
|
||||
}
|
||||
|
||||
var tracesSignal = newSignal[trace.SpanExporter]("OTEL_TRACES_EXPORTER")
|
||||
|
||||
func init() {
|
||||
RegisterSpanExporter("otlp", func(ctx context.Context) (trace.SpanExporter, error) {
|
||||
proto := os.Getenv(otelExporterOTLPTracesProtoEnvKey)
|
||||
if proto == "" {
|
||||
proto = os.Getenv(otelExporterOTLPProtoEnvKey)
|
||||
}
|
||||
|
||||
// Fallback to default, http/protobuf.
|
||||
if proto == "" {
|
||||
proto = "http/protobuf"
|
||||
}
|
||||
|
||||
switch proto {
|
||||
case "grpc":
|
||||
return otlptracegrpc.New(ctx)
|
||||
case "http/protobuf":
|
||||
return otlptracehttp.New(ctx)
|
||||
default:
|
||||
return nil, errInvalidOTLPProtocol
|
||||
}
|
||||
})
|
||||
RegisterSpanExporter("console", func(ctx context.Context) (trace.SpanExporter, error) {
|
||||
return stdouttrace.New()
|
||||
})
|
||||
RegisterSpanExporter("none", func(ctx context.Context) (trace.SpanExporter, error) {
|
||||
return noopSpanExporter{}, nil
|
||||
})
|
||||
}
|
201
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/LICENSE
generated
vendored
Normal file
201
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
3
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/README.md
generated
vendored
Normal file
3
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/README.md
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# OTLP Log gRPC Exporter
|
||||
|
||||
[](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc)
|
258
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/client.go
generated
vendored
Normal file
258
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/client.go
generated
vendored
Normal file
@@ -0,0 +1,258 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otlploggrpc // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"google.golang.org/genproto/googleapis/rpc/errdetails"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/backoff"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/encoding/gzip"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/internal/retry"
|
||||
collogpb "go.opentelemetry.io/proto/otlp/collector/logs/v1"
|
||||
logpb "go.opentelemetry.io/proto/otlp/logs/v1"
|
||||
)
|
||||
|
||||
// The methods of this type are not expected to be called concurrently.
|
||||
type client struct {
|
||||
metadata metadata.MD
|
||||
exportTimeout time.Duration
|
||||
requestFunc retry.RequestFunc
|
||||
|
||||
// ourConn keeps track of where conn was created: true if created here in
|
||||
// NewClient, or false if passed with an option. This is important on
|
||||
// Shutdown as conn should only be closed if we created it. Otherwise,
|
||||
// it is up to the processes that passed conn to close it.
|
||||
ourConn bool
|
||||
conn *grpc.ClientConn
|
||||
lsc collogpb.LogsServiceClient
|
||||
}
|
||||
|
||||
// Used for testing.
|
||||
var newGRPCClientFn = grpc.NewClient
|
||||
|
||||
// newClient creates a new gRPC log client.
|
||||
func newClient(cfg config) (*client, error) {
|
||||
c := &client{
|
||||
exportTimeout: cfg.timeout.Value,
|
||||
requestFunc: cfg.retryCfg.Value.RequestFunc(retryable),
|
||||
conn: cfg.gRPCConn.Value,
|
||||
}
|
||||
|
||||
if len(cfg.headers.Value) > 0 {
|
||||
c.metadata = metadata.New(cfg.headers.Value)
|
||||
}
|
||||
|
||||
if c.conn == nil {
|
||||
// If the caller did not provide a ClientConn when the client was
|
||||
// created, create one using the configuration they did provide.
|
||||
dialOpts := newGRPCDialOptions(cfg)
|
||||
|
||||
conn, err := newGRPCClientFn(cfg.endpoint.Value, dialOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Keep track that we own the lifecycle of this conn and need to close
|
||||
// it on Shutdown.
|
||||
c.ourConn = true
|
||||
c.conn = conn
|
||||
}
|
||||
|
||||
c.lsc = collogpb.NewLogsServiceClient(c.conn)
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func newGRPCDialOptions(cfg config) []grpc.DialOption {
|
||||
userAgent := "OTel Go OTLP over gRPC logs exporter/" + Version()
|
||||
dialOpts := []grpc.DialOption{grpc.WithUserAgent(userAgent)}
|
||||
dialOpts = append(dialOpts, cfg.dialOptions.Value...)
|
||||
|
||||
// Convert other grpc configs to the dial options.
|
||||
// Service config
|
||||
if cfg.serviceConfig.Value != "" {
|
||||
dialOpts = append(dialOpts, grpc.WithDefaultServiceConfig(cfg.serviceConfig.Value))
|
||||
}
|
||||
// Prioritize GRPCCredentials over Insecure (passing both is an error).
|
||||
if cfg.gRPCCredentials.Value != nil {
|
||||
dialOpts = append(dialOpts, grpc.WithTransportCredentials(cfg.gRPCCredentials.Value))
|
||||
} else if cfg.insecure.Value {
|
||||
dialOpts = append(dialOpts, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
} else {
|
||||
// Default to using the host's root CA.
|
||||
dialOpts = append(dialOpts, grpc.WithTransportCredentials(
|
||||
credentials.NewTLS(nil),
|
||||
))
|
||||
}
|
||||
// Compression
|
||||
if cfg.compression.Value == GzipCompression {
|
||||
dialOpts = append(dialOpts, grpc.WithDefaultCallOptions(grpc.UseCompressor(gzip.Name)))
|
||||
}
|
||||
// Reconnection period
|
||||
if cfg.reconnectionPeriod.Value != 0 {
|
||||
p := grpc.ConnectParams{
|
||||
Backoff: backoff.DefaultConfig,
|
||||
MinConnectTimeout: cfg.reconnectionPeriod.Value,
|
||||
}
|
||||
dialOpts = append(dialOpts, grpc.WithConnectParams(p))
|
||||
}
|
||||
|
||||
return dialOpts
|
||||
}
|
||||
|
||||
// UploadLogs sends proto logs to connected endpoint.
|
||||
//
|
||||
// Retryable errors from the server will be handled according to any
|
||||
// RetryConfig the client was created with.
|
||||
//
|
||||
// The otlplog.Exporter synchronizes access to client methods, and
|
||||
// ensures this is not called after the Exporter is shutdown. Only thing
|
||||
// to do here is send data.
|
||||
func (c *client) UploadLogs(ctx context.Context, rl []*logpb.ResourceLogs) error {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// Do not upload if the context is already expired.
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
ctx, cancel := c.exportContext(ctx)
|
||||
defer cancel()
|
||||
|
||||
return c.requestFunc(ctx, func(ctx context.Context) error {
|
||||
resp, err := c.lsc.Export(ctx, &collogpb.ExportLogsServiceRequest{
|
||||
ResourceLogs: rl,
|
||||
})
|
||||
if resp != nil && resp.PartialSuccess != nil {
|
||||
msg := resp.PartialSuccess.GetErrorMessage()
|
||||
n := resp.PartialSuccess.GetRejectedLogRecords()
|
||||
if n != 0 || msg != "" {
|
||||
err := fmt.Errorf("OTLP partial success: %s (%d log records rejected)", msg, n)
|
||||
otel.Handle(err)
|
||||
}
|
||||
}
|
||||
// nil is converted to OK.
|
||||
if status.Code(err) == codes.OK {
|
||||
// Success.
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// Shutdown shuts down the client, freeing all resources.
|
||||
//
|
||||
// Any active connections to a remote endpoint are closed if they were created
|
||||
// by the client. Any gRPC connection passed during creation using
|
||||
// WithGRPCConn will not be closed. It is the caller's responsibility to
|
||||
// handle cleanup of that resource.
|
||||
//
|
||||
// The otlplog.Exporter synchronizes access to client methods and
|
||||
// ensures this is called only once. The only thing that needs to be done
|
||||
// here is to release any computational resources the client holds.
|
||||
func (c *client) Shutdown(ctx context.Context) error {
|
||||
c.metadata = nil
|
||||
c.requestFunc = nil
|
||||
c.lsc = nil
|
||||
|
||||
// Release the connection if we created it.
|
||||
err := ctx.Err()
|
||||
if c.ourConn {
|
||||
closeErr := c.conn.Close()
|
||||
// A context timeout error takes precedence over this error.
|
||||
if err == nil && closeErr != nil {
|
||||
err = closeErr
|
||||
}
|
||||
}
|
||||
c.conn = nil
|
||||
return err
|
||||
}
|
||||
|
||||
// exportContext returns a copy of parent with an appropriate deadline and
|
||||
// cancellation function based on the clients configured export timeout.
|
||||
//
|
||||
// It is the callers responsibility to cancel the returned context once its
|
||||
// use is complete, via the parent or directly with the returned CancelFunc, to
|
||||
// ensure all resources are correctly released.
|
||||
func (c *client) exportContext(parent context.Context) (context.Context, context.CancelFunc) {
|
||||
var (
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
)
|
||||
|
||||
if c.exportTimeout > 0 {
|
||||
ctx, cancel = context.WithTimeout(parent, c.exportTimeout)
|
||||
} else {
|
||||
ctx, cancel = context.WithCancel(parent)
|
||||
}
|
||||
|
||||
if c.metadata.Len() > 0 {
|
||||
md := c.metadata
|
||||
if outMD, ok := metadata.FromOutgoingContext(ctx); ok {
|
||||
md = metadata.Join(md, outMD)
|
||||
}
|
||||
|
||||
ctx = metadata.NewOutgoingContext(ctx, md)
|
||||
}
|
||||
|
||||
return ctx, cancel
|
||||
}
|
||||
|
||||
type noopClient struct{}
|
||||
|
||||
func newNoopClient() *noopClient {
|
||||
return &noopClient{}
|
||||
}
|
||||
|
||||
func (c *noopClient) UploadLogs(context.Context, []*logpb.ResourceLogs) error { return nil }
|
||||
|
||||
func (c *noopClient) Shutdown(context.Context) error { return nil }
|
||||
|
||||
// retryable returns if err identifies a request that can be retried and a
|
||||
// duration to wait for if an explicit throttle time is included in err.
|
||||
func retryable(err error) (bool, time.Duration) {
|
||||
s := status.Convert(err)
|
||||
return retryableGRPCStatus(s)
|
||||
}
|
||||
|
||||
func retryableGRPCStatus(s *status.Status) (bool, time.Duration) {
|
||||
switch s.Code() {
|
||||
case codes.Canceled,
|
||||
codes.DeadlineExceeded,
|
||||
codes.Aborted,
|
||||
codes.OutOfRange,
|
||||
codes.Unavailable,
|
||||
codes.DataLoss:
|
||||
// Additionally, handle RetryInfo.
|
||||
_, d := throttleDelay(s)
|
||||
return true, d
|
||||
case codes.ResourceExhausted:
|
||||
// Retry only if the server signals that the recovery from resource exhaustion is possible.
|
||||
return throttleDelay(s)
|
||||
}
|
||||
|
||||
// Not a retry-able error.
|
||||
return false, 0
|
||||
}
|
||||
|
||||
// throttleDelay returns if the status is RetryInfo
|
||||
// and the duration to wait for if an explicit throttle time is included.
|
||||
func throttleDelay(s *status.Status) (bool, time.Duration) {
|
||||
for _, detail := range s.Details() {
|
||||
if t, ok := detail.(*errdetails.RetryInfo); ok {
|
||||
return true, t.RetryDelay.AsDuration()
|
||||
}
|
||||
}
|
||||
return false, 0
|
||||
}
|
653
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/config.go
generated
vendored
Normal file
653
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/config.go
generated
vendored
Normal file
@@ -0,0 +1,653 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otlploggrpc // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc"
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/internal/retry"
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
)
|
||||
|
||||
// Default values.
|
||||
var (
|
||||
defaultEndpoint = "localhost:4317"
|
||||
defaultTimeout = 10 * time.Second
|
||||
defaultRetryCfg = retry.DefaultConfig
|
||||
)
|
||||
|
||||
// Environment variable keys.
|
||||
var (
|
||||
envEndpoint = []string{
|
||||
"OTEL_EXPORTER_OTLP_LOGS_ENDPOINT",
|
||||
"OTEL_EXPORTER_OTLP_ENDPOINT",
|
||||
}
|
||||
envInsecure = []string{
|
||||
"OTEL_EXPORTER_OTLP_LOGS_INSECURE",
|
||||
"OTEL_EXPORTER_OTLP_INSECURE",
|
||||
}
|
||||
|
||||
envHeaders = []string{
|
||||
"OTEL_EXPORTER_OTLP_LOGS_HEADERS",
|
||||
"OTEL_EXPORTER_OTLP_HEADERS",
|
||||
}
|
||||
|
||||
envCompression = []string{
|
||||
"OTEL_EXPORTER_OTLP_LOGS_COMPRESSION",
|
||||
"OTEL_EXPORTER_OTLP_COMPRESSION",
|
||||
}
|
||||
|
||||
envTimeout = []string{
|
||||
"OTEL_EXPORTER_OTLP_LOGS_TIMEOUT",
|
||||
"OTEL_EXPORTER_OTLP_TIMEOUT",
|
||||
}
|
||||
|
||||
envTLSCert = []string{
|
||||
"OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE",
|
||||
"OTEL_EXPORTER_OTLP_CERTIFICATE",
|
||||
}
|
||||
envTLSClient = []struct {
|
||||
Certificate string
|
||||
Key string
|
||||
}{
|
||||
{
|
||||
"OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE",
|
||||
"OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY",
|
||||
},
|
||||
{
|
||||
"OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE",
|
||||
"OTEL_EXPORTER_OTLP_CLIENT_KEY",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type fnOpt func(config) config
|
||||
|
||||
func (f fnOpt) applyOption(c config) config { return f(c) }
|
||||
|
||||
// Option applies an option to the Exporter.
|
||||
type Option interface {
|
||||
applyOption(config) config
|
||||
}
|
||||
|
||||
type config struct {
|
||||
endpoint setting[string]
|
||||
insecure setting[bool]
|
||||
tlsCfg setting[*tls.Config]
|
||||
headers setting[map[string]string]
|
||||
compression setting[Compression]
|
||||
timeout setting[time.Duration]
|
||||
retryCfg setting[retry.Config]
|
||||
|
||||
// gRPC configurations
|
||||
gRPCCredentials setting[credentials.TransportCredentials]
|
||||
serviceConfig setting[string]
|
||||
reconnectionPeriod setting[time.Duration]
|
||||
dialOptions setting[[]grpc.DialOption]
|
||||
gRPCConn setting[*grpc.ClientConn]
|
||||
}
|
||||
|
||||
func newConfig(options []Option) config {
|
||||
var c config
|
||||
for _, opt := range options {
|
||||
c = opt.applyOption(c)
|
||||
}
|
||||
|
||||
// Apply environment value and default value
|
||||
c.endpoint = c.endpoint.Resolve(
|
||||
getEnv[string](envEndpoint, convEndpoint),
|
||||
fallback[string](defaultEndpoint),
|
||||
)
|
||||
c.insecure = c.insecure.Resolve(
|
||||
loadInsecureFromEnvEndpoint(envEndpoint),
|
||||
getEnv[bool](envInsecure, convInsecure),
|
||||
)
|
||||
c.tlsCfg = c.tlsCfg.Resolve(
|
||||
loadEnvTLS[*tls.Config](),
|
||||
)
|
||||
c.headers = c.headers.Resolve(
|
||||
getEnv[map[string]string](envHeaders, convHeaders),
|
||||
)
|
||||
c.compression = c.compression.Resolve(
|
||||
getEnv[Compression](envCompression, convCompression),
|
||||
)
|
||||
c.timeout = c.timeout.Resolve(
|
||||
getEnv[time.Duration](envTimeout, convDuration),
|
||||
fallback[time.Duration](defaultTimeout),
|
||||
)
|
||||
c.retryCfg = c.retryCfg.Resolve(
|
||||
fallback[retry.Config](defaultRetryCfg),
|
||||
)
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// RetryConfig defines configuration for retrying the export of log data
|
||||
// that failed.
|
||||
//
|
||||
// This configuration does not define any network retry strategy. That is
|
||||
// entirely handled by the gRPC ClientConn.
|
||||
type RetryConfig retry.Config
|
||||
|
||||
// WithInsecure disables client transport security for the Exporter's gRPC
|
||||
// connection, just like grpc.WithInsecure()
|
||||
// (https://pkg.go.dev/google.golang.org/grpc#WithInsecure) does.
|
||||
//
|
||||
// If the OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_LOGS_ENDPOINT
|
||||
// environment variable is set, and this option is not passed, that variable
|
||||
// value will be used to determine client security. If the endpoint has a
|
||||
// scheme of "http" or "unix" client security will be disabled. If both are
|
||||
// set, OTEL_EXPORTER_OTLP_LOGS_ENDPOINT will take precedence.
|
||||
//
|
||||
// By default, if an environment variable is not set, and this option is not
|
||||
// passed, client security will be used.
|
||||
//
|
||||
// This option has no effect if WithGRPCConn is used.
|
||||
func WithInsecure() Option {
|
||||
return fnOpt(func(c config) config {
|
||||
c.insecure = newSetting(true)
|
||||
return c
|
||||
})
|
||||
}
|
||||
|
||||
// WithEndpoint sets the target endpoint the Exporter will connect to.
|
||||
//
|
||||
// If the OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_LOGS_ENDPOINT
|
||||
// environment variable is set, and this option is not passed, that variable
|
||||
// value will be used. If both are set, OTEL_EXPORTER_OTLP_LOGS_ENDPOINT
|
||||
// will take precedence.
|
||||
//
|
||||
// If both this option and WithEndpointURL are used, the last used option will
|
||||
// take precedence.
|
||||
//
|
||||
// By default, if an environment variable is not set, and this option is not
|
||||
// passed, "localhost:4317" will be used.
|
||||
//
|
||||
// This option has no effect if WithGRPCConn is used.
|
||||
func WithEndpoint(endpoint string) Option {
|
||||
return fnOpt(func(c config) config {
|
||||
c.endpoint = newSetting(endpoint)
|
||||
return c
|
||||
})
|
||||
}
|
||||
|
||||
// WithEndpointURL sets the target endpoint URL the Exporter will connect to.
|
||||
//
|
||||
// If the OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_LOGS_ENDPOINT
|
||||
// environment variable is set, and this option is not passed, that variable
|
||||
// value will be used. If both are set, OTEL_EXPORTER_OTLP_LOGS_ENDPOINT
|
||||
// will take precedence.
|
||||
//
|
||||
// If both this option and WithEndpoint are used, the last used option will
|
||||
// take precedence.
|
||||
//
|
||||
// If an invalid URL is provided, the default value will be kept.
|
||||
//
|
||||
// By default, if an environment variable is not set, and this option is not
|
||||
// passed, "localhost:4317" will be used.
|
||||
//
|
||||
// This option has no effect if WithGRPCConn is used.
|
||||
func WithEndpointURL(rawURL string) Option {
|
||||
u, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
global.Error(err, "otlplog: parse endpoint url", "url", rawURL)
|
||||
return fnOpt(func(c config) config { return c })
|
||||
}
|
||||
return fnOpt(func(c config) config {
|
||||
c.endpoint = newSetting(u.Host)
|
||||
c.insecure = insecureFromScheme(c.insecure, u.Scheme)
|
||||
return c
|
||||
})
|
||||
}
|
||||
|
||||
// WithReconnectionPeriod set the minimum amount of time between connection
|
||||
// attempts to the target endpoint.
|
||||
//
|
||||
// This option has no effect if WithGRPCConn is used.
|
||||
func WithReconnectionPeriod(rp time.Duration) Option {
|
||||
return fnOpt(func(c config) config {
|
||||
c.reconnectionPeriod = newSetting(rp)
|
||||
return c
|
||||
})
|
||||
}
|
||||
|
||||
// Compression describes the compression used for exported payloads.
|
||||
type Compression int
|
||||
|
||||
const (
|
||||
// NoCompression represents that no compression should be used.
|
||||
NoCompression Compression = iota
|
||||
// GzipCompression represents that gzip compression should be used.
|
||||
GzipCompression
|
||||
)
|
||||
|
||||
// WithCompressor sets the compressor the gRPC client uses.
|
||||
// Supported compressor values: "gzip".
|
||||
//
|
||||
// If the OTEL_EXPORTER_OTLP_COMPRESSION or
|
||||
// OTEL_EXPORTER_OTLP_LOGS_COMPRESSION environment variable is set, and
|
||||
// this option is not passed, that variable value will be used. That value can
|
||||
// be either "none" or "gzip". If both are set,
|
||||
// OTEL_EXPORTER_OTLP_LOGS_COMPRESSION will take precedence.
|
||||
//
|
||||
// By default, if an environment variable is not set, and this option is not
|
||||
// passed, no compression strategy will be used.
|
||||
//
|
||||
// This option has no effect if WithGRPCConn is used.
|
||||
func WithCompressor(compressor string) Option {
|
||||
return fnOpt(func(c config) config {
|
||||
c.compression = newSetting(compressorToCompression(compressor))
|
||||
return c
|
||||
})
|
||||
}
|
||||
|
||||
// WithHeaders will send the provided headers with each gRPC requests.
|
||||
//
|
||||
// If the OTEL_EXPORTER_OTLP_HEADERS or OTEL_EXPORTER_OTLP_LOGS_HEADERS
|
||||
// environment variable is set, and this option is not passed, that variable
|
||||
// value will be used. The value will be parsed as a list of key value pairs.
|
||||
// These pairs are expected to be in the W3C Correlation-Context format
|
||||
// without additional semi-colon delimited metadata (i.e. "k1=v1,k2=v2"). If
|
||||
// both are set, OTEL_EXPORTER_OTLP_LOGS_HEADERS will take precedence.
|
||||
//
|
||||
// By default, if an environment variable is not set, and this option is not
|
||||
// passed, no user headers will be set.
|
||||
func WithHeaders(headers map[string]string) Option {
|
||||
return fnOpt(func(c config) config {
|
||||
c.headers = newSetting(headers)
|
||||
return c
|
||||
})
|
||||
}
|
||||
|
||||
// WithTLSCredentials sets the gRPC connection to use creds.
|
||||
//
|
||||
// If the OTEL_EXPORTER_OTLP_CERTIFICATE or
|
||||
// OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE environment variable is set, and
|
||||
// this option is not passed, that variable value will be used. The value will
|
||||
// be parsed the filepath of the TLS certificate chain to use. If both are
|
||||
// set, OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE will take precedence.
|
||||
//
|
||||
// By default, if an environment variable is not set, and this option is not
|
||||
// passed, no TLS credentials will be used.
|
||||
//
|
||||
// This option has no effect if WithGRPCConn is used.
|
||||
func WithTLSCredentials(credential credentials.TransportCredentials) Option {
|
||||
return fnOpt(func(c config) config {
|
||||
c.gRPCCredentials = newSetting(credential)
|
||||
return c
|
||||
})
|
||||
}
|
||||
|
||||
// WithServiceConfig defines the default gRPC service config used.
|
||||
//
|
||||
// This option has no effect if WithGRPCConn is used.
|
||||
func WithServiceConfig(serviceConfig string) Option {
|
||||
return fnOpt(func(c config) config {
|
||||
c.serviceConfig = newSetting(serviceConfig)
|
||||
return c
|
||||
})
|
||||
}
|
||||
|
||||
// WithDialOption sets explicit grpc.DialOptions to use when establishing a
|
||||
// gRPC connection. The options here are appended to the internal grpc.DialOptions
|
||||
// used so they will take precedence over any other internal grpc.DialOptions
|
||||
// they might conflict with.
|
||||
// The [grpc.WithBlock], [grpc.WithTimeout], and [grpc.WithReturnConnectionError]
|
||||
// grpc.DialOptions are ignored.
|
||||
//
|
||||
// This option has no effect if WithGRPCConn is used.
|
||||
func WithDialOption(opts ...grpc.DialOption) Option {
|
||||
return fnOpt(func(c config) config {
|
||||
c.dialOptions = newSetting(opts)
|
||||
return c
|
||||
})
|
||||
}
|
||||
|
||||
// WithGRPCConn sets conn as the gRPC ClientConn used for all communication.
|
||||
//
|
||||
// This option takes precedence over any other option that relates to
|
||||
// establishing or persisting a gRPC connection to a target endpoint. Any
|
||||
// other option of those types passed will be ignored.
|
||||
//
|
||||
// It is the callers responsibility to close the passed conn. The Exporter
|
||||
// Shutdown method will not close this connection.
|
||||
func WithGRPCConn(conn *grpc.ClientConn) Option {
|
||||
return fnOpt(func(c config) config {
|
||||
c.gRPCConn = newSetting(conn)
|
||||
return c
|
||||
})
|
||||
}
|
||||
|
||||
// WithTimeout sets the max amount of time an Exporter will attempt an export.
|
||||
//
|
||||
// This takes precedence over any retry settings defined by WithRetry. Once
|
||||
// this time limit has been reached the export is abandoned and the log
|
||||
// data is dropped.
|
||||
//
|
||||
// If the OTEL_EXPORTER_OTLP_TIMEOUT or OTEL_EXPORTER_OTLP_LOGS_TIMEOUT
|
||||
// environment variable is set, and this option is not passed, that variable
|
||||
// value will be used. The value will be parsed as an integer representing the
|
||||
// timeout in milliseconds. If both are set,
|
||||
// OTEL_EXPORTER_OTLP_LOGS_TIMEOUT will take precedence.
|
||||
//
|
||||
// By default, if an environment variable is not set, and this option is not
|
||||
// passed, a timeout of 10 seconds will be used.
|
||||
func WithTimeout(duration time.Duration) Option {
|
||||
return fnOpt(func(c config) config {
|
||||
c.timeout = newSetting(duration)
|
||||
return c
|
||||
})
|
||||
}
|
||||
|
||||
// WithRetry sets the retry policy for transient retryable errors that are
|
||||
// returned by the target endpoint.
|
||||
//
|
||||
// If the target endpoint responds with not only a retryable error, but
|
||||
// explicitly returns a backoff time in the response, that time will take
|
||||
// precedence over these settings.
|
||||
//
|
||||
// These settings do not define any network retry strategy. That is entirely
|
||||
// handled by the gRPC ClientConn.
|
||||
//
|
||||
// If unset, the default retry policy will be used. It will retry the export
|
||||
// 5 seconds after receiving a retryable error and increase exponentially
|
||||
// after each error for no more than a total time of 1 minute.
|
||||
func WithRetry(rc RetryConfig) Option {
|
||||
return fnOpt(func(c config) config {
|
||||
c.retryCfg = newSetting(retry.Config(rc))
|
||||
return c
|
||||
})
|
||||
}
|
||||
|
||||
// convCompression returns the parsed compression encoded in s. NoCompression
|
||||
// and an errors are returned if s is unknown.
|
||||
func convCompression(s string) (Compression, error) {
|
||||
switch s {
|
||||
case "gzip":
|
||||
return GzipCompression, nil
|
||||
case "none", "":
|
||||
return NoCompression, nil
|
||||
}
|
||||
return NoCompression, fmt.Errorf("unknown compression: %s", s)
|
||||
}
|
||||
|
||||
// convEndpoint converts s from a URL string to an endpoint if s is a valid
|
||||
// URL. Otherwise, "" and an error are returned.
|
||||
func convEndpoint(s string) (string, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return u.Host, nil
|
||||
}
|
||||
|
||||
// convInsecure converts s from string to bool without case sensitivity.
|
||||
// If s is not valid returns error.
|
||||
func convInsecure(s string) (bool, error) {
|
||||
s = strings.ToLower(s)
|
||||
if s != "true" && s != "false" {
|
||||
return false, fmt.Errorf("can't convert %q to bool", s)
|
||||
}
|
||||
|
||||
return s == "true", nil
|
||||
}
|
||||
|
||||
// loadInsecureFromEnvEndpoint returns a resolver that fetches
|
||||
// insecure setting from envEndpoint is it possible.
|
||||
func loadInsecureFromEnvEndpoint(envEndpoint []string) resolver[bool] {
|
||||
return func(s setting[bool]) setting[bool] {
|
||||
if s.Set {
|
||||
// Passed, valid, options have precedence.
|
||||
return s
|
||||
}
|
||||
|
||||
for _, key := range envEndpoint {
|
||||
if vStr := os.Getenv(key); vStr != "" {
|
||||
u, err := url.Parse(vStr)
|
||||
if err != nil {
|
||||
otel.Handle(fmt.Errorf("invalid %s value %s: %w", key, vStr, err))
|
||||
continue
|
||||
}
|
||||
|
||||
return insecureFromScheme(s, u.Scheme)
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
// convHeaders converts the OTel environment variable header value s into a
|
||||
// mapping of header key to value. If s is invalid a partial result and error
|
||||
// are returned.
|
||||
func convHeaders(s string) (map[string]string, error) {
|
||||
out := make(map[string]string)
|
||||
var err error
|
||||
for _, header := range strings.Split(s, ",") {
|
||||
rawKey, rawVal, found := strings.Cut(header, "=")
|
||||
if !found {
|
||||
err = errors.Join(err, fmt.Errorf("invalid header: %s", header))
|
||||
continue
|
||||
}
|
||||
|
||||
escKey, e := url.PathUnescape(rawKey)
|
||||
if e != nil {
|
||||
err = errors.Join(err, fmt.Errorf("invalid header key: %s", rawKey))
|
||||
continue
|
||||
}
|
||||
key := strings.TrimSpace(escKey)
|
||||
|
||||
escVal, e := url.PathUnescape(rawVal)
|
||||
if e != nil {
|
||||
err = errors.Join(err, fmt.Errorf("invalid header value: %s", rawVal))
|
||||
continue
|
||||
}
|
||||
val := strings.TrimSpace(escVal)
|
||||
|
||||
out[key] = val
|
||||
}
|
||||
return out, err
|
||||
}
|
||||
|
||||
// convDuration converts s into a duration of milliseconds. If s does not
|
||||
// contain an integer, 0 and an error are returned.
|
||||
func convDuration(s string) (time.Duration, error) {
|
||||
d, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// OTel durations are defined in milliseconds.
|
||||
return time.Duration(d) * time.Millisecond, nil
|
||||
}
|
||||
|
||||
// loadEnvTLS returns a resolver that loads a *tls.Config from files defined by
|
||||
// the OTLP TLS environment variables. This will load both the rootCAs and
|
||||
// certificates used for mTLS.
|
||||
//
|
||||
// If the filepath defined is invalid or does not contain valid TLS files, an
|
||||
// error is passed to the OTel ErrorHandler and no TLS configuration is
|
||||
// provided.
|
||||
func loadEnvTLS[T *tls.Config]() resolver[T] {
|
||||
return func(s setting[T]) setting[T] {
|
||||
if s.Set {
|
||||
// Passed, valid, options have precedence.
|
||||
return s
|
||||
}
|
||||
|
||||
var rootCAs *x509.CertPool
|
||||
var err error
|
||||
for _, key := range envTLSCert {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
rootCAs, err = loadCertPool(v)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var certs []tls.Certificate
|
||||
for _, pair := range envTLSClient {
|
||||
cert := os.Getenv(pair.Certificate)
|
||||
key := os.Getenv(pair.Key)
|
||||
if cert != "" && key != "" {
|
||||
var e error
|
||||
certs, e = loadCertificates(cert, key)
|
||||
err = errors.Join(err, e)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to load TLS: %w", err)
|
||||
otel.Handle(err)
|
||||
} else if rootCAs != nil || certs != nil {
|
||||
s.Set = true
|
||||
s.Value = &tls.Config{RootCAs: rootCAs, Certificates: certs}
|
||||
}
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
// readFile is used for testing.
|
||||
var readFile = os.ReadFile
|
||||
|
||||
// loadCertPool loads and returns the *x509.CertPool found at path if it exists
|
||||
// and is valid. Otherwise, nil and an error is returned.
|
||||
func loadCertPool(path string) (*x509.CertPool, error) {
|
||||
b, err := readFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cp := x509.NewCertPool()
|
||||
if ok := cp.AppendCertsFromPEM(b); !ok {
|
||||
return nil, errors.New("certificate not added")
|
||||
}
|
||||
return cp, nil
|
||||
}
|
||||
|
||||
// loadCertificates loads and returns the tls.Certificate found at path if it
|
||||
// exists and is valid. Otherwise, nil and an error is returned.
|
||||
func loadCertificates(certPath, keyPath string) ([]tls.Certificate, error) {
|
||||
cert, err := readFile(certPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key, err := readFile(keyPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
crt, err := tls.X509KeyPair(cert, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []tls.Certificate{crt}, nil
|
||||
}
|
||||
|
||||
// insecureFromScheme return setting if the connection should
|
||||
// use client transport security or not.
|
||||
// Empty scheme doesn't force insecure setting.
|
||||
func insecureFromScheme(prev setting[bool], scheme string) setting[bool] {
|
||||
if scheme == "https" {
|
||||
return newSetting(false)
|
||||
} else if len(scheme) > 0 {
|
||||
return newSetting(true)
|
||||
}
|
||||
|
||||
return prev
|
||||
}
|
||||
|
||||
func compressorToCompression(compressor string) Compression {
|
||||
c, err := convCompression(compressor)
|
||||
if err != nil {
|
||||
otel.Handle(fmt.Errorf("%w, using no compression as default", err))
|
||||
return NoCompression
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// getEnv returns a resolver that will apply an environment variable value
|
||||
// associated with the first set key to a setting value. The conv function is
|
||||
// used to convert between the environment variable value and the setting type.
|
||||
//
|
||||
// If the input setting to the resolver is set, the environment variable will
|
||||
// not be applied.
|
||||
//
|
||||
// Any error returned from conv is sent to the OTel ErrorHandler and the
|
||||
// setting will not be updated.
|
||||
func getEnv[T any](keys []string, conv func(string) (T, error)) resolver[T] {
|
||||
return func(s setting[T]) setting[T] {
|
||||
if s.Set {
|
||||
// Passed, valid, options have precedence.
|
||||
return s
|
||||
}
|
||||
|
||||
for _, key := range keys {
|
||||
if vStr := os.Getenv(key); vStr != "" {
|
||||
v, err := conv(vStr)
|
||||
if err == nil {
|
||||
s.Value = v
|
||||
s.Set = true
|
||||
break
|
||||
}
|
||||
otel.Handle(fmt.Errorf("invalid %s value %s: %w", key, vStr, err))
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
63
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/doc.go
generated
vendored
Normal file
63
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/doc.go
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/*
|
||||
Package otlploggrpc provides an OTLP log exporter using gRPC. The exporter uses gRPC to
|
||||
transport OTLP protobuf payloads.
|
||||
|
||||
All Exporters must be created with [New].
|
||||
|
||||
The environment variables described below can be used for configuration.
|
||||
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_LOGS_ENDPOINT (default: "https://localhost:4317") -
|
||||
target to which the exporter sends telemetry.
|
||||
The target syntax is defined in https://github.com/grpc/grpc/blob/master/doc/naming.md.
|
||||
The value must contain a scheme ("http" or "https") and host.
|
||||
The value may additionally contain a port, and a path.
|
||||
The value should not contain a query string or fragment.
|
||||
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT takes precedence over OTEL_EXPORTER_OTLP_ENDPOINT.
|
||||
The configuration can be overridden by [WithEndpoint], [WithEndpointURL], [WithInsecure], and [WithGRPCConn] options.
|
||||
|
||||
OTEL_EXPORTER_OTLP_INSECURE, OTEL_EXPORTER_OTLP_LOGS_INSECURE (default: "false") -
|
||||
setting "true" disables client transport security for the exporter's gRPC connection.
|
||||
You can use this only when an endpoint is provided without scheme.
|
||||
OTEL_EXPORTER_OTLP_LOGS_INSECURE takes precedence over OTEL_EXPORTER_OTLP_INSECURE.
|
||||
The configuration can be overridden by [WithInsecure], [WithGRPCConn] options.
|
||||
|
||||
OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_LOGS_HEADERS (default: none) -
|
||||
key-value pairs used as gRPC metadata associated with gRPC requests.
|
||||
The value is expected to be represented in a format matching the [W3C Baggage HTTP Header Content Format],
|
||||
except that additional semi-colon delimited metadata is not supported.
|
||||
Example value: "key1=value1,key2=value2".
|
||||
OTEL_EXPORTER_OTLP_LOGS_HEADERS takes precedence over OTEL_EXPORTER_OTLP_HEADERS.
|
||||
The configuration can be overridden by [WithHeaders] option.
|
||||
|
||||
OTEL_EXPORTER_OTLP_TIMEOUT, OTEL_EXPORTER_OTLP_LOGS_TIMEOUT (default: "10000") -
|
||||
maximum time in milliseconds the OTLP exporter waits for each batch export.
|
||||
OTEL_EXPORTER_OTLP_LOGS_TIMEOUT takes precedence over OTEL_EXPORTER_OTLP_TIMEOUT.
|
||||
The configuration can be overridden by [WithTimeout] option.
|
||||
|
||||
OTEL_EXPORTER_OTLP_COMPRESSION, OTEL_EXPORTER_OTLP_LOGS_COMPRESSION (default: none) -
|
||||
the gRPC compressor the exporter uses.
|
||||
Supported value: "gzip".
|
||||
OTEL_EXPORTER_OTLP_LOGS_COMPRESSION takes precedence over OTEL_EXPORTER_OTLP_COMPRESSION.
|
||||
The configuration can be overridden by [WithCompressor], [WithGRPCConn] options.
|
||||
|
||||
OTEL_EXPORTER_OTLP_CERTIFICATE, OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE (default: none) -
|
||||
the filepath to the trusted certificate to use when verifying a server's TLS credentials.
|
||||
OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE takes precedence over OTEL_EXPORTER_OTLP_CERTIFICATE.
|
||||
The configuration can be overridden by [WithTLSCredentials], [WithGRPCConn] options.
|
||||
|
||||
OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE, OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE (default: none) -
|
||||
the filepath to the client certificate/chain trust for client's private key to use in mTLS communication in PEM format.
|
||||
OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE takes precedence over OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE.
|
||||
The configuration can be overridden by [WithTLSCredentials], [WithGRPCConn] options.
|
||||
|
||||
OTEL_EXPORTER_OTLP_CLIENT_KEY, OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY (default: none) -
|
||||
the filepath to the client's private key to use in mTLS communication in PEM format.
|
||||
OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY takes precedence over OTEL_EXPORTER_OTLP_CLIENT_KEY.
|
||||
The configuration can be overridden by [WithTLSCredentials], [WithGRPCConn] option.
|
||||
|
||||
[W3C Baggage HTTP Header Content Format]: https://www.w3.org/TR/baggage/#header-content
|
||||
*/
|
||||
package otlploggrpc // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc"
|
93
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/exporter.go
generated
vendored
Normal file
93
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/exporter.go
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otlploggrpc // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/internal/transform"
|
||||
"go.opentelemetry.io/otel/sdk/log"
|
||||
logpb "go.opentelemetry.io/proto/otlp/logs/v1"
|
||||
)
|
||||
|
||||
type logClient interface {
|
||||
UploadLogs(ctx context.Context, rl []*logpb.ResourceLogs) error
|
||||
Shutdown(context.Context) error
|
||||
}
|
||||
|
||||
// Exporter is a OpenTelemetry log Exporter. It transports log data encoded as
|
||||
// OTLP protobufs using gRPC.
|
||||
// All Exporters must be created with [New].
|
||||
type Exporter struct {
|
||||
// Ensure synchronous access to the client across all functionality.
|
||||
clientMu sync.Mutex
|
||||
client logClient
|
||||
|
||||
stopped atomic.Bool
|
||||
}
|
||||
|
||||
// Compile-time check Exporter implements [log.Exporter].
|
||||
var _ log.Exporter = (*Exporter)(nil)
|
||||
|
||||
// New returns a new [Exporter].
|
||||
//
|
||||
// It is recommended to use it with a [BatchProcessor]
|
||||
// or other processor exporting records asynchronously.
|
||||
func New(_ context.Context, options ...Option) (*Exporter, error) {
|
||||
cfg := newConfig(options)
|
||||
c, err := newClient(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newExporter(c), nil
|
||||
}
|
||||
|
||||
func newExporter(c logClient) *Exporter {
|
||||
var e Exporter
|
||||
e.client = c
|
||||
return &e
|
||||
}
|
||||
|
||||
var transformResourceLogs = transform.ResourceLogs
|
||||
|
||||
// Export transforms and transmits log records to an OTLP receiver.
|
||||
//
|
||||
// This method returns nil and drops records if called after Shutdown.
|
||||
// This method returns an error if the method is canceled by the passed context.
|
||||
func (e *Exporter) Export(ctx context.Context, records []log.Record) error {
|
||||
if e.stopped.Load() {
|
||||
return nil
|
||||
}
|
||||
|
||||
otlp := transformResourceLogs(records)
|
||||
if otlp == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
e.clientMu.Lock()
|
||||
defer e.clientMu.Unlock()
|
||||
return e.client.UploadLogs(ctx, otlp)
|
||||
}
|
||||
|
||||
// Shutdown shuts down the Exporter. Calls to Export or ForceFlush will perform
|
||||
// no operation after this is called.
|
||||
func (e *Exporter) Shutdown(ctx context.Context) error {
|
||||
if e.stopped.Swap(true) {
|
||||
return nil
|
||||
}
|
||||
|
||||
e.clientMu.Lock()
|
||||
defer e.clientMu.Unlock()
|
||||
|
||||
err := e.client.Shutdown(ctx)
|
||||
e.client = newNoopClient()
|
||||
return err
|
||||
}
|
||||
|
||||
// ForceFlush does nothing. The Exporter holds no state.
|
||||
func (e *Exporter) ForceFlush(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
145
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/internal/retry/retry.go
generated
vendored
Normal file
145
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/internal/retry/retry.go
generated
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/retry/retry.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package retry provides request retry functionality that can perform
|
||||
// configurable exponential backoff for transient errors and honor any
|
||||
// explicit throttle responses received.
|
||||
package retry // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/internal/retry"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/cenkalti/backoff/v4"
|
||||
)
|
||||
|
||||
// DefaultConfig are the recommended defaults to use.
|
||||
var DefaultConfig = Config{
|
||||
Enabled: true,
|
||||
InitialInterval: 5 * time.Second,
|
||||
MaxInterval: 30 * time.Second,
|
||||
MaxElapsedTime: time.Minute,
|
||||
}
|
||||
|
||||
// Config defines configuration for retrying batches in case of export failure
|
||||
// using an exponential backoff.
|
||||
type Config struct {
|
||||
// Enabled indicates whether to not retry sending batches in case of
|
||||
// export failure.
|
||||
Enabled bool
|
||||
// InitialInterval the time to wait after the first failure before
|
||||
// retrying.
|
||||
InitialInterval time.Duration
|
||||
// MaxInterval is the upper bound on backoff interval. Once this value is
|
||||
// reached the delay between consecutive retries will always be
|
||||
// `MaxInterval`.
|
||||
MaxInterval time.Duration
|
||||
// MaxElapsedTime is the maximum amount of time (including retries) spent
|
||||
// trying to send a request/batch. Once this value is reached, the data
|
||||
// is discarded.
|
||||
MaxElapsedTime time.Duration
|
||||
}
|
||||
|
||||
// RequestFunc wraps a request with retry logic.
|
||||
type RequestFunc func(context.Context, func(context.Context) error) error
|
||||
|
||||
// EvaluateFunc returns if an error is retry-able and if an explicit throttle
|
||||
// duration should be honored that was included in the error.
|
||||
//
|
||||
// The function must return true if the error argument is retry-able,
|
||||
// otherwise it must return false for the first return parameter.
|
||||
//
|
||||
// The function must return a non-zero time.Duration if the error contains
|
||||
// explicit throttle duration that should be honored, otherwise it must return
|
||||
// a zero valued time.Duration.
|
||||
type EvaluateFunc func(error) (bool, time.Duration)
|
||||
|
||||
// RequestFunc returns a RequestFunc using the evaluate function to determine
|
||||
// if requests can be retried and based on the exponential backoff
|
||||
// configuration of c.
|
||||
func (c Config) RequestFunc(evaluate EvaluateFunc) RequestFunc {
|
||||
if !c.Enabled {
|
||||
return func(ctx context.Context, fn func(context.Context) error) error {
|
||||
return fn(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
return func(ctx context.Context, fn func(context.Context) error) error {
|
||||
// Do not use NewExponentialBackOff since it calls Reset and the code here
|
||||
// must call Reset after changing the InitialInterval (this saves an
|
||||
// unnecessary call to Now).
|
||||
b := &backoff.ExponentialBackOff{
|
||||
InitialInterval: c.InitialInterval,
|
||||
RandomizationFactor: backoff.DefaultRandomizationFactor,
|
||||
Multiplier: backoff.DefaultMultiplier,
|
||||
MaxInterval: c.MaxInterval,
|
||||
MaxElapsedTime: c.MaxElapsedTime,
|
||||
Stop: backoff.Stop,
|
||||
Clock: backoff.SystemClock,
|
||||
}
|
||||
b.Reset()
|
||||
|
||||
for {
|
||||
err := fn(ctx)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
retryable, throttle := evaluate(err)
|
||||
if !retryable {
|
||||
return err
|
||||
}
|
||||
|
||||
bOff := b.NextBackOff()
|
||||
if bOff == backoff.Stop {
|
||||
return fmt.Errorf("max retry time elapsed: %w", err)
|
||||
}
|
||||
|
||||
// Wait for the greater of the backoff or throttle delay.
|
||||
var delay time.Duration
|
||||
if bOff > throttle {
|
||||
delay = bOff
|
||||
} else {
|
||||
elapsed := b.GetElapsedTime()
|
||||
if b.MaxElapsedTime != 0 && elapsed+throttle > b.MaxElapsedTime {
|
||||
return fmt.Errorf("max retry time would elapse: %w", err)
|
||||
}
|
||||
delay = throttle
|
||||
}
|
||||
|
||||
if ctxErr := waitFunc(ctx, delay); ctxErr != nil {
|
||||
return fmt.Errorf("%w: %w", ctxErr, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allow override for testing.
|
||||
var waitFunc = wait
|
||||
|
||||
// wait takes the caller's context, and the amount of time to wait. It will
|
||||
// return nil if the timer fires before or at the same time as the context's
|
||||
// deadline. This indicates that the call can be retried.
|
||||
func wait(ctx context.Context, delay time.Duration) error {
|
||||
timer := time.NewTimer(delay)
|
||||
defer timer.Stop()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// Handle the case where the timer and context deadline end
|
||||
// simultaneously by prioritizing the timer expiration nil value
|
||||
// response.
|
||||
select {
|
||||
case <-timer.C:
|
||||
default:
|
||||
return ctx.Err()
|
||||
}
|
||||
case <-timer.C:
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
391
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/internal/transform/log.go
generated
vendored
Normal file
391
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/internal/transform/log.go
generated
vendored
Normal file
@@ -0,0 +1,391 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/otlplog/transform/log.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package transform provides transformation functionality from the
|
||||
// sdk/log data-types into OTLP data-types.
|
||||
package transform // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/internal/transform"
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
cpb "go.opentelemetry.io/proto/otlp/common/v1"
|
||||
lpb "go.opentelemetry.io/proto/otlp/logs/v1"
|
||||
rpb "go.opentelemetry.io/proto/otlp/resource/v1"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
api "go.opentelemetry.io/otel/log"
|
||||
"go.opentelemetry.io/otel/sdk/instrumentation"
|
||||
"go.opentelemetry.io/otel/sdk/log"
|
||||
)
|
||||
|
||||
// ResourceLogs returns an slice of OTLP ResourceLogs generated from records.
|
||||
func ResourceLogs(records []log.Record) []*lpb.ResourceLogs {
|
||||
if len(records) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
resMap := make(map[attribute.Distinct]*lpb.ResourceLogs)
|
||||
|
||||
type key struct {
|
||||
r attribute.Distinct
|
||||
is instrumentation.Scope
|
||||
}
|
||||
scopeMap := make(map[key]*lpb.ScopeLogs)
|
||||
|
||||
var resources int
|
||||
for _, r := range records {
|
||||
res := r.Resource()
|
||||
rKey := res.Equivalent()
|
||||
scope := r.InstrumentationScope()
|
||||
k := key{
|
||||
r: rKey,
|
||||
is: scope,
|
||||
}
|
||||
sl, iOk := scopeMap[k]
|
||||
if !iOk {
|
||||
sl = new(lpb.ScopeLogs)
|
||||
var emptyScope instrumentation.Scope
|
||||
if scope != emptyScope {
|
||||
sl.Scope = &cpb.InstrumentationScope{
|
||||
Name: scope.Name,
|
||||
Version: scope.Version,
|
||||
Attributes: AttrIter(scope.Attributes.Iter()),
|
||||
}
|
||||
sl.SchemaUrl = scope.SchemaURL
|
||||
}
|
||||
scopeMap[k] = sl
|
||||
}
|
||||
|
||||
sl.LogRecords = append(sl.LogRecords, LogRecord(r))
|
||||
rl, rOk := resMap[rKey]
|
||||
if !rOk {
|
||||
resources++
|
||||
rl = new(lpb.ResourceLogs)
|
||||
if res.Len() > 0 {
|
||||
rl.Resource = &rpb.Resource{
|
||||
Attributes: AttrIter(res.Iter()),
|
||||
}
|
||||
}
|
||||
rl.SchemaUrl = res.SchemaURL()
|
||||
resMap[rKey] = rl
|
||||
}
|
||||
if !iOk {
|
||||
rl.ScopeLogs = append(rl.ScopeLogs, sl)
|
||||
}
|
||||
}
|
||||
|
||||
// Transform the categorized map into a slice
|
||||
resLogs := make([]*lpb.ResourceLogs, 0, resources)
|
||||
for _, rl := range resMap {
|
||||
resLogs = append(resLogs, rl)
|
||||
}
|
||||
|
||||
return resLogs
|
||||
}
|
||||
|
||||
// LogRecord returns an OTLP LogRecord generated from record.
|
||||
func LogRecord(record log.Record) *lpb.LogRecord {
|
||||
r := &lpb.LogRecord{
|
||||
TimeUnixNano: timeUnixNano(record.Timestamp()),
|
||||
ObservedTimeUnixNano: timeUnixNano(record.ObservedTimestamp()),
|
||||
EventName: record.EventName(),
|
||||
SeverityNumber: SeverityNumber(record.Severity()),
|
||||
SeverityText: record.SeverityText(),
|
||||
Body: LogAttrValue(record.Body()),
|
||||
Attributes: make([]*cpb.KeyValue, 0, record.AttributesLen()),
|
||||
Flags: uint32(record.TraceFlags()),
|
||||
// TODO: DroppedAttributesCount: /* ... */,
|
||||
}
|
||||
record.WalkAttributes(func(kv api.KeyValue) bool {
|
||||
r.Attributes = append(r.Attributes, LogAttr(kv))
|
||||
return true
|
||||
})
|
||||
if tID := record.TraceID(); tID.IsValid() {
|
||||
r.TraceId = tID[:]
|
||||
}
|
||||
if sID := record.SpanID(); sID.IsValid() {
|
||||
r.SpanId = sID[:]
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// timeUnixNano returns t as a Unix time, the number of nanoseconds elapsed
|
||||
// since January 1, 1970 UTC as uint64. The result is undefined if the Unix
|
||||
// time in nanoseconds cannot be represented by an int64 (a date before the
|
||||
// year 1678 or after 2262). timeUnixNano on the zero Time returns 0. The
|
||||
// result does not depend on the location associated with t.
|
||||
func timeUnixNano(t time.Time) uint64 {
|
||||
nano := t.UnixNano()
|
||||
if nano < 0 {
|
||||
return 0
|
||||
}
|
||||
return uint64(nano) // nolint:gosec // Overflow checked.
|
||||
}
|
||||
|
||||
// AttrIter transforms an [attribute.Iterator] into OTLP key-values.
|
||||
func AttrIter(iter attribute.Iterator) []*cpb.KeyValue {
|
||||
l := iter.Len()
|
||||
if l == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
out := make([]*cpb.KeyValue, 0, l)
|
||||
for iter.Next() {
|
||||
out = append(out, Attr(iter.Attribute()))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Attrs transforms a slice of [attribute.KeyValue] into OTLP key-values.
|
||||
func Attrs(attrs []attribute.KeyValue) []*cpb.KeyValue {
|
||||
if len(attrs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
out := make([]*cpb.KeyValue, 0, len(attrs))
|
||||
for _, kv := range attrs {
|
||||
out = append(out, Attr(kv))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Attr transforms an [attribute.KeyValue] into an OTLP key-value.
|
||||
func Attr(kv attribute.KeyValue) *cpb.KeyValue {
|
||||
return &cpb.KeyValue{Key: string(kv.Key), Value: AttrValue(kv.Value)}
|
||||
}
|
||||
|
||||
// AttrValue transforms an [attribute.Value] into an OTLP AnyValue.
|
||||
func AttrValue(v attribute.Value) *cpb.AnyValue {
|
||||
av := new(cpb.AnyValue)
|
||||
switch v.Type() {
|
||||
case attribute.BOOL:
|
||||
av.Value = &cpb.AnyValue_BoolValue{
|
||||
BoolValue: v.AsBool(),
|
||||
}
|
||||
case attribute.BOOLSLICE:
|
||||
av.Value = &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: boolSliceValues(v.AsBoolSlice()),
|
||||
},
|
||||
}
|
||||
case attribute.INT64:
|
||||
av.Value = &cpb.AnyValue_IntValue{
|
||||
IntValue: v.AsInt64(),
|
||||
}
|
||||
case attribute.INT64SLICE:
|
||||
av.Value = &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: int64SliceValues(v.AsInt64Slice()),
|
||||
},
|
||||
}
|
||||
case attribute.FLOAT64:
|
||||
av.Value = &cpb.AnyValue_DoubleValue{
|
||||
DoubleValue: v.AsFloat64(),
|
||||
}
|
||||
case attribute.FLOAT64SLICE:
|
||||
av.Value = &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: float64SliceValues(v.AsFloat64Slice()),
|
||||
},
|
||||
}
|
||||
case attribute.STRING:
|
||||
av.Value = &cpb.AnyValue_StringValue{
|
||||
StringValue: v.AsString(),
|
||||
}
|
||||
case attribute.STRINGSLICE:
|
||||
av.Value = &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: stringSliceValues(v.AsStringSlice()),
|
||||
},
|
||||
}
|
||||
default:
|
||||
av.Value = &cpb.AnyValue_StringValue{
|
||||
StringValue: "INVALID",
|
||||
}
|
||||
}
|
||||
return av
|
||||
}
|
||||
|
||||
func boolSliceValues(vals []bool) []*cpb.AnyValue {
|
||||
converted := make([]*cpb.AnyValue, len(vals))
|
||||
for i, v := range vals {
|
||||
converted[i] = &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_BoolValue{
|
||||
BoolValue: v,
|
||||
},
|
||||
}
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
||||
func int64SliceValues(vals []int64) []*cpb.AnyValue {
|
||||
converted := make([]*cpb.AnyValue, len(vals))
|
||||
for i, v := range vals {
|
||||
converted[i] = &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_IntValue{
|
||||
IntValue: v,
|
||||
},
|
||||
}
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
||||
func float64SliceValues(vals []float64) []*cpb.AnyValue {
|
||||
converted := make([]*cpb.AnyValue, len(vals))
|
||||
for i, v := range vals {
|
||||
converted[i] = &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_DoubleValue{
|
||||
DoubleValue: v,
|
||||
},
|
||||
}
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
||||
func stringSliceValues(vals []string) []*cpb.AnyValue {
|
||||
converted := make([]*cpb.AnyValue, len(vals))
|
||||
for i, v := range vals {
|
||||
converted[i] = &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_StringValue{
|
||||
StringValue: v,
|
||||
},
|
||||
}
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
||||
// Attrs transforms a slice of [api.KeyValue] into OTLP key-values.
|
||||
func LogAttrs(attrs []api.KeyValue) []*cpb.KeyValue {
|
||||
if len(attrs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
out := make([]*cpb.KeyValue, 0, len(attrs))
|
||||
for _, kv := range attrs {
|
||||
out = append(out, LogAttr(kv))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// LogAttr transforms an [api.KeyValue] into an OTLP key-value.
|
||||
func LogAttr(attr api.KeyValue) *cpb.KeyValue {
|
||||
return &cpb.KeyValue{
|
||||
Key: attr.Key,
|
||||
Value: LogAttrValue(attr.Value),
|
||||
}
|
||||
}
|
||||
|
||||
// LogAttrValues transforms a slice of [api.Value] into an OTLP []AnyValue.
|
||||
func LogAttrValues(vals []api.Value) []*cpb.AnyValue {
|
||||
if len(vals) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
out := make([]*cpb.AnyValue, 0, len(vals))
|
||||
for _, v := range vals {
|
||||
out = append(out, LogAttrValue(v))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// LogAttrValue transforms an [api.Value] into an OTLP AnyValue.
|
||||
func LogAttrValue(v api.Value) *cpb.AnyValue {
|
||||
av := new(cpb.AnyValue)
|
||||
switch v.Kind() {
|
||||
case api.KindBool:
|
||||
av.Value = &cpb.AnyValue_BoolValue{
|
||||
BoolValue: v.AsBool(),
|
||||
}
|
||||
case api.KindInt64:
|
||||
av.Value = &cpb.AnyValue_IntValue{
|
||||
IntValue: v.AsInt64(),
|
||||
}
|
||||
case api.KindFloat64:
|
||||
av.Value = &cpb.AnyValue_DoubleValue{
|
||||
DoubleValue: v.AsFloat64(),
|
||||
}
|
||||
case api.KindString:
|
||||
av.Value = &cpb.AnyValue_StringValue{
|
||||
StringValue: v.AsString(),
|
||||
}
|
||||
case api.KindBytes:
|
||||
av.Value = &cpb.AnyValue_BytesValue{
|
||||
BytesValue: v.AsBytes(),
|
||||
}
|
||||
case api.KindSlice:
|
||||
av.Value = &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: LogAttrValues(v.AsSlice()),
|
||||
},
|
||||
}
|
||||
case api.KindMap:
|
||||
av.Value = &cpb.AnyValue_KvlistValue{
|
||||
KvlistValue: &cpb.KeyValueList{
|
||||
Values: LogAttrs(v.AsMap()),
|
||||
},
|
||||
}
|
||||
default:
|
||||
av.Value = &cpb.AnyValue_StringValue{
|
||||
StringValue: "INVALID",
|
||||
}
|
||||
}
|
||||
return av
|
||||
}
|
||||
|
||||
// SeverityNumber transforms a [log.Severity] into an OTLP SeverityNumber.
|
||||
func SeverityNumber(s api.Severity) lpb.SeverityNumber {
|
||||
switch s {
|
||||
case api.SeverityTrace:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_TRACE
|
||||
case api.SeverityTrace2:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_TRACE2
|
||||
case api.SeverityTrace3:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_TRACE3
|
||||
case api.SeverityTrace4:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_TRACE4
|
||||
case api.SeverityDebug:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_DEBUG
|
||||
case api.SeverityDebug2:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_DEBUG2
|
||||
case api.SeverityDebug3:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_DEBUG3
|
||||
case api.SeverityDebug4:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_DEBUG4
|
||||
case api.SeverityInfo:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_INFO
|
||||
case api.SeverityInfo2:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_INFO2
|
||||
case api.SeverityInfo3:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_INFO3
|
||||
case api.SeverityInfo4:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_INFO4
|
||||
case api.SeverityWarn:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_WARN
|
||||
case api.SeverityWarn2:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_WARN2
|
||||
case api.SeverityWarn3:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_WARN3
|
||||
case api.SeverityWarn4:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_WARN4
|
||||
case api.SeverityError:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_ERROR
|
||||
case api.SeverityError2:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_ERROR2
|
||||
case api.SeverityError3:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_ERROR3
|
||||
case api.SeverityError4:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_ERROR4
|
||||
case api.SeverityFatal:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_FATAL
|
||||
case api.SeverityFatal2:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_FATAL2
|
||||
case api.SeverityFatal3:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_FATAL3
|
||||
case api.SeverityFatal4:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_FATAL4
|
||||
}
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_UNSPECIFIED
|
||||
}
|
9
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/version.go
generated
vendored
Normal file
9
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/version.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otlploggrpc // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc"
|
||||
|
||||
// Version is the current release version of the OpenTelemetry OTLP over gRPC logs exporter in use.
|
||||
func Version() string {
|
||||
return "0.11.0"
|
||||
}
|
201
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/LICENSE
generated
vendored
Normal file
201
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
3
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/README.md
generated
vendored
Normal file
3
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/README.md
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# OTLP Log HTTP Exporter
|
||||
|
||||
[](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp)
|
343
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/client.go
generated
vendored
Normal file
343
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/client.go
generated
vendored
Normal file
@@ -0,0 +1,343 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otlploghttp // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
collogpb "go.opentelemetry.io/proto/otlp/collector/logs/v1"
|
||||
logpb "go.opentelemetry.io/proto/otlp/logs/v1"
|
||||
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/internal/retry"
|
||||
)
|
||||
|
||||
type client struct {
|
||||
uploadLogs func(context.Context, []*logpb.ResourceLogs) error
|
||||
}
|
||||
|
||||
func (c *client) UploadLogs(ctx context.Context, rl []*logpb.ResourceLogs) error {
|
||||
if c.uploadLogs != nil {
|
||||
return c.uploadLogs(ctx, rl)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newNoopClient() *client {
|
||||
return &client{}
|
||||
}
|
||||
|
||||
// newHTTPClient creates a new HTTP log client.
|
||||
func newHTTPClient(cfg config) (*client, error) {
|
||||
hc := &http.Client{
|
||||
Transport: ourTransport,
|
||||
Timeout: cfg.timeout.Value,
|
||||
}
|
||||
|
||||
if cfg.tlsCfg.Value != nil || cfg.proxy.Value != nil {
|
||||
clonedTransport := ourTransport.Clone()
|
||||
hc.Transport = clonedTransport
|
||||
|
||||
if cfg.tlsCfg.Value != nil {
|
||||
clonedTransport.TLSClientConfig = cfg.tlsCfg.Value
|
||||
}
|
||||
if cfg.proxy.Value != nil {
|
||||
clonedTransport.Proxy = cfg.proxy.Value
|
||||
}
|
||||
}
|
||||
|
||||
u := &url.URL{
|
||||
Scheme: "https",
|
||||
Host: cfg.endpoint.Value,
|
||||
Path: cfg.path.Value,
|
||||
}
|
||||
if cfg.insecure.Value {
|
||||
u.Scheme = "http"
|
||||
}
|
||||
// Body is set when this is cloned during upload.
|
||||
req, err := http.NewRequest(http.MethodPost, u.String(), http.NoBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userAgent := "OTel Go OTLP over HTTP/protobuf logs exporter/" + Version()
|
||||
req.Header.Set("User-Agent", userAgent)
|
||||
|
||||
if n := len(cfg.headers.Value); n > 0 {
|
||||
for k, v := range cfg.headers.Value {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/x-protobuf")
|
||||
|
||||
c := &httpClient{
|
||||
compression: cfg.compression.Value,
|
||||
req: req,
|
||||
requestFunc: cfg.retryCfg.Value.RequestFunc(evaluate),
|
||||
client: hc,
|
||||
}
|
||||
return &client{uploadLogs: c.uploadLogs}, nil
|
||||
}
|
||||
|
||||
type httpClient struct {
|
||||
// req is cloned for every upload the client makes.
|
||||
req *http.Request
|
||||
compression Compression
|
||||
requestFunc retry.RequestFunc
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
// Keep it in sync with golang's DefaultTransport from net/http! We
|
||||
// have our own copy to avoid handling a situation where the
|
||||
// DefaultTransport is overwritten with some different implementation
|
||||
// of http.RoundTripper or it's modified by another package.
|
||||
var ourTransport = &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).DialContext,
|
||||
ForceAttemptHTTP2: true,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
}
|
||||
|
||||
func (c *httpClient) uploadLogs(ctx context.Context, data []*logpb.ResourceLogs) error {
|
||||
// The Exporter synchronizes access to client methods. This is not called
|
||||
// after the Exporter is shutdown. Only thing to do here is send data.
|
||||
|
||||
pbRequest := &collogpb.ExportLogsServiceRequest{ResourceLogs: data}
|
||||
body, err := proto.Marshal(pbRequest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
request, err := c.newRequest(ctx, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.requestFunc(ctx, func(iCtx context.Context) error {
|
||||
select {
|
||||
case <-iCtx.Done():
|
||||
return iCtx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
request.reset(iCtx)
|
||||
resp, err := c.client.Do(request.Request)
|
||||
var urlErr *url.Error
|
||||
if errors.As(err, &urlErr) && urlErr.Temporary() {
|
||||
return newResponseError(http.Header{}, err)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp != nil && resp.Body != nil {
|
||||
defer func() {
|
||||
if err := resp.Body.Close(); err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if sc := resp.StatusCode; sc >= 200 && sc <= 299 {
|
||||
// Success, do not retry.
|
||||
|
||||
// Read the partial success message, if any.
|
||||
var respData bytes.Buffer
|
||||
if _, err := io.Copy(&respData, resp.Body); err != nil {
|
||||
return err
|
||||
}
|
||||
if respData.Len() == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if resp.Header.Get("Content-Type") == "application/x-protobuf" {
|
||||
var respProto collogpb.ExportLogsServiceResponse
|
||||
if err := proto.Unmarshal(respData.Bytes(), &respProto); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if respProto.PartialSuccess != nil {
|
||||
msg := respProto.PartialSuccess.GetErrorMessage()
|
||||
n := respProto.PartialSuccess.GetRejectedLogRecords()
|
||||
if n != 0 || msg != "" {
|
||||
err := fmt.Errorf("OTLP partial success: %s (%d log records rejected)", msg, n)
|
||||
otel.Handle(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// Error cases.
|
||||
|
||||
// server may return a message with the response
|
||||
// body, so we read it to include in the error
|
||||
// message to be returned. It will help in
|
||||
// debugging the actual issue.
|
||||
var respData bytes.Buffer
|
||||
if _, err := io.Copy(&respData, resp.Body); err != nil {
|
||||
return err
|
||||
}
|
||||
respStr := strings.TrimSpace(respData.String())
|
||||
if len(respStr) == 0 {
|
||||
respStr = "(empty)"
|
||||
}
|
||||
bodyErr := fmt.Errorf("body: %s", respStr)
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusTooManyRequests,
|
||||
http.StatusBadGateway,
|
||||
http.StatusServiceUnavailable,
|
||||
http.StatusGatewayTimeout:
|
||||
// Retryable failure.
|
||||
return newResponseError(resp.Header, bodyErr)
|
||||
default:
|
||||
// Non-retryable failure.
|
||||
return fmt.Errorf("failed to send logs to %s: %s (%w)", request.URL, resp.Status, bodyErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
var gzPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
w := gzip.NewWriter(io.Discard)
|
||||
return w
|
||||
},
|
||||
}
|
||||
|
||||
func (c *httpClient) newRequest(ctx context.Context, body []byte) (request, error) {
|
||||
r := c.req.Clone(ctx)
|
||||
req := request{Request: r}
|
||||
|
||||
switch c.compression {
|
||||
case NoCompression:
|
||||
r.ContentLength = (int64)(len(body))
|
||||
req.bodyReader = bodyReader(body)
|
||||
case GzipCompression:
|
||||
// Ensure the content length is not used.
|
||||
r.ContentLength = -1
|
||||
r.Header.Set("Content-Encoding", "gzip")
|
||||
|
||||
gz := gzPool.Get().(*gzip.Writer)
|
||||
defer gzPool.Put(gz)
|
||||
|
||||
var b bytes.Buffer
|
||||
gz.Reset(&b)
|
||||
|
||||
if _, err := gz.Write(body); err != nil {
|
||||
return req, err
|
||||
}
|
||||
// Close needs to be called to ensure body is fully written.
|
||||
if err := gz.Close(); err != nil {
|
||||
return req, err
|
||||
}
|
||||
|
||||
req.bodyReader = bodyReader(b.Bytes())
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// bodyReader returns a closure returning a new reader for buf.
|
||||
func bodyReader(buf []byte) func() io.ReadCloser {
|
||||
return func() io.ReadCloser {
|
||||
return io.NopCloser(bytes.NewReader(buf))
|
||||
}
|
||||
}
|
||||
|
||||
// request wraps an http.Request with a resettable body reader.
|
||||
type request struct {
|
||||
*http.Request
|
||||
|
||||
// bodyReader allows the same body to be used for multiple requests.
|
||||
bodyReader func() io.ReadCloser
|
||||
}
|
||||
|
||||
// reset reinitializes the request Body and uses ctx for the request.
|
||||
func (r *request) reset(ctx context.Context) {
|
||||
r.Body = r.bodyReader()
|
||||
r.Request = r.WithContext(ctx)
|
||||
}
|
||||
|
||||
// retryableError represents a request failure that can be retried.
|
||||
type retryableError struct {
|
||||
throttle int64
|
||||
err error
|
||||
}
|
||||
|
||||
// newResponseError returns a retryableError and will extract any explicit
|
||||
// throttle delay contained in headers. The returned error wraps wrapped
|
||||
// if it is not nil.
|
||||
func newResponseError(header http.Header, wrapped error) error {
|
||||
var rErr retryableError
|
||||
if v := header.Get("Retry-After"); v != "" {
|
||||
if t, err := strconv.ParseInt(v, 10, 64); err == nil {
|
||||
rErr.throttle = t
|
||||
}
|
||||
}
|
||||
|
||||
rErr.err = wrapped
|
||||
return rErr
|
||||
}
|
||||
|
||||
func (e retryableError) Error() string {
|
||||
if e.err != nil {
|
||||
return fmt.Sprintf("retry-able request failure: %v", e.err.Error())
|
||||
}
|
||||
|
||||
return "retry-able request failure"
|
||||
}
|
||||
|
||||
func (e retryableError) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
func (e retryableError) As(target interface{}) bool {
|
||||
if e.err == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
switch v := target.(type) {
|
||||
case **retryableError:
|
||||
*v = &e
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// evaluate returns if err is retry-able. If it is and it includes an explicit
|
||||
// throttling delay, that delay is also returned.
|
||||
func evaluate(err error) (bool, time.Duration) {
|
||||
if err == nil {
|
||||
return false, 0
|
||||
}
|
||||
|
||||
// Do not use errors.As here, this should only be flattened one layer. If
|
||||
// there are several chained errors, all the errors above it will be
|
||||
// discarded if errors.As is used instead.
|
||||
rErr, ok := err.(retryableError) //nolint:errorlint
|
||||
if !ok {
|
||||
return false, 0
|
||||
}
|
||||
|
||||
return true, time.Duration(rErr.throttle)
|
||||
}
|
602
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/config.go
generated
vendored
Normal file
602
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/config.go
generated
vendored
Normal file
@@ -0,0 +1,602 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otlploghttp // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp"
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/internal/retry"
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
)
|
||||
|
||||
// Default values.
|
||||
var (
|
||||
defaultEndpoint = "localhost:4318"
|
||||
defaultPath = "/v1/logs"
|
||||
defaultTimeout = 10 * time.Second
|
||||
defaultProxy HTTPTransportProxyFunc = http.ProxyFromEnvironment
|
||||
defaultRetryCfg = retry.DefaultConfig
|
||||
)
|
||||
|
||||
// Environment variable keys.
|
||||
var (
|
||||
envEndpoint = []string{
|
||||
"OTEL_EXPORTER_OTLP_LOGS_ENDPOINT",
|
||||
"OTEL_EXPORTER_OTLP_ENDPOINT",
|
||||
}
|
||||
envInsecure = envEndpoint
|
||||
|
||||
// Split because these are parsed differently.
|
||||
envPathSignal = []string{"OTEL_EXPORTER_OTLP_LOGS_ENDPOINT"}
|
||||
envPathOTLP = []string{"OTEL_EXPORTER_OTLP_ENDPOINT"}
|
||||
|
||||
envHeaders = []string{
|
||||
"OTEL_EXPORTER_OTLP_LOGS_HEADERS",
|
||||
"OTEL_EXPORTER_OTLP_HEADERS",
|
||||
}
|
||||
|
||||
envCompression = []string{
|
||||
"OTEL_EXPORTER_OTLP_LOGS_COMPRESSION",
|
||||
"OTEL_EXPORTER_OTLP_COMPRESSION",
|
||||
}
|
||||
|
||||
envTimeout = []string{
|
||||
"OTEL_EXPORTER_OTLP_LOGS_TIMEOUT",
|
||||
"OTEL_EXPORTER_OTLP_TIMEOUT",
|
||||
}
|
||||
|
||||
envTLSCert = []string{
|
||||
"OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE",
|
||||
"OTEL_EXPORTER_OTLP_CERTIFICATE",
|
||||
}
|
||||
envTLSClient = []struct {
|
||||
Certificate string
|
||||
Key string
|
||||
}{
|
||||
{
|
||||
"OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE",
|
||||
"OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY",
|
||||
},
|
||||
{
|
||||
"OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE",
|
||||
"OTEL_EXPORTER_OTLP_CLIENT_KEY",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// Option applies an option to the Exporter.
|
||||
type Option interface {
|
||||
applyHTTPOption(config) config
|
||||
}
|
||||
|
||||
type fnOpt func(config) config
|
||||
|
||||
func (f fnOpt) applyHTTPOption(c config) config { return f(c) }
|
||||
|
||||
type config struct {
|
||||
endpoint setting[string]
|
||||
path setting[string]
|
||||
insecure setting[bool]
|
||||
tlsCfg setting[*tls.Config]
|
||||
headers setting[map[string]string]
|
||||
compression setting[Compression]
|
||||
timeout setting[time.Duration]
|
||||
proxy setting[HTTPTransportProxyFunc]
|
||||
retryCfg setting[retry.Config]
|
||||
}
|
||||
|
||||
func newConfig(options []Option) config {
|
||||
var c config
|
||||
for _, opt := range options {
|
||||
c = opt.applyHTTPOption(c)
|
||||
}
|
||||
|
||||
c.endpoint = c.endpoint.Resolve(
|
||||
getenv[string](envEndpoint, convEndpoint),
|
||||
fallback[string](defaultEndpoint),
|
||||
)
|
||||
c.path = c.path.Resolve(
|
||||
getenv[string](envPathSignal, convPathExact),
|
||||
getenv[string](envPathOTLP, convPath),
|
||||
fallback[string](defaultPath),
|
||||
)
|
||||
c.insecure = c.insecure.Resolve(
|
||||
getenv[bool](envInsecure, convInsecure),
|
||||
)
|
||||
c.tlsCfg = c.tlsCfg.Resolve(
|
||||
loadEnvTLS[*tls.Config](),
|
||||
)
|
||||
c.headers = c.headers.Resolve(
|
||||
getenv[map[string]string](envHeaders, convHeaders),
|
||||
)
|
||||
c.compression = c.compression.Resolve(
|
||||
getenv[Compression](envCompression, convCompression),
|
||||
)
|
||||
c.timeout = c.timeout.Resolve(
|
||||
getenv[time.Duration](envTimeout, convDuration),
|
||||
fallback[time.Duration](defaultTimeout),
|
||||
)
|
||||
c.proxy = c.proxy.Resolve(
|
||||
fallback[HTTPTransportProxyFunc](defaultProxy),
|
||||
)
|
||||
c.retryCfg = c.retryCfg.Resolve(
|
||||
fallback[retry.Config](defaultRetryCfg),
|
||||
)
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// WithEndpoint sets the target endpoint the Exporter will connect to. This
|
||||
// endpoint is specified as a host and optional port, no path or scheme should
|
||||
// be included (see WithInsecure and WithURLPath).
|
||||
//
|
||||
// If the OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_LOGS_ENDPOINT
|
||||
// environment variable is set, and this option is not passed, that variable
|
||||
// value will be used. If both environment variables are set,
|
||||
// OTEL_EXPORTER_OTLP_LOGS_ENDPOINT will take precedence. If an environment
|
||||
// variable is set, and this option is passed, this option will take precedence.
|
||||
//
|
||||
// If both this option and WithEndpointURL are used, the last used option will
|
||||
// take precedence.
|
||||
//
|
||||
// By default, if an environment variable is not set, and this option is not
|
||||
// passed, "localhost:4318" will be used.
|
||||
func WithEndpoint(endpoint string) Option {
|
||||
return fnOpt(func(c config) config {
|
||||
c.endpoint = newSetting(endpoint)
|
||||
return c
|
||||
})
|
||||
}
|
||||
|
||||
// WithEndpointURL sets the target endpoint URL the Exporter will connect to.
|
||||
//
|
||||
// If the OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_LOGS_ENDPOINT
|
||||
// environment variable is set, and this option is not passed, that variable
|
||||
// value will be used. If both environment variables are set,
|
||||
// OTEL_EXPORTER_OTLP_LOGS_ENDPOINT will take precedence. If an environment
|
||||
// variable is set, and this option is passed, this option will take precedence.
|
||||
//
|
||||
// If both this option and WithEndpoint are used, the last used option will
|
||||
// take precedence.
|
||||
//
|
||||
// If an invalid URL is provided, the default value will be kept.
|
||||
//
|
||||
// By default, if an environment variable is not set, and this option is not
|
||||
// passed, "localhost:4318" will be used.
|
||||
func WithEndpointURL(rawURL string) Option {
|
||||
u, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
global.Error(err, "otlplog: parse endpoint url", "url", rawURL)
|
||||
return fnOpt(func(c config) config { return c })
|
||||
}
|
||||
return fnOpt(func(c config) config {
|
||||
c.endpoint = newSetting(u.Host)
|
||||
c.path = newSetting(u.Path)
|
||||
c.insecure = newSetting(u.Scheme != "https")
|
||||
return c
|
||||
})
|
||||
}
|
||||
|
||||
// Compression describes the compression used for exported payloads.
|
||||
type Compression int
|
||||
|
||||
const (
|
||||
// NoCompression represents that no compression should be used.
|
||||
NoCompression Compression = iota
|
||||
// GzipCompression represents that gzip compression should be used.
|
||||
GzipCompression
|
||||
)
|
||||
|
||||
// WithCompression sets the compression strategy the Exporter will use to
|
||||
// compress the HTTP body.
|
||||
//
|
||||
// If the OTEL_EXPORTER_OTLP_COMPRESSION or
|
||||
// OTEL_EXPORTER_OTLP_LOGS_COMPRESSION environment variable is set, and
|
||||
// this option is not passed, that variable value will be used. That value can
|
||||
// be either "none" or "gzip". If both are set,
|
||||
// OTEL_EXPORTER_OTLP_LOGS_COMPRESSION will take precedence.
|
||||
//
|
||||
// By default, if an environment variable is not set, and this option is not
|
||||
// passed, no compression strategy will be used.
|
||||
func WithCompression(compression Compression) Option {
|
||||
return fnOpt(func(c config) config {
|
||||
c.compression = newSetting(compression)
|
||||
return c
|
||||
})
|
||||
}
|
||||
|
||||
// WithURLPath sets the URL path the Exporter will send requests to.
|
||||
//
|
||||
// If the OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_LOGS_ENDPOINT
|
||||
// environment variable is set, and this option is not passed, the path
|
||||
// contained in that variable value will be used. If both are set,
|
||||
// OTEL_EXPORTER_OTLP_LOGS_ENDPOINT will take precedence.
|
||||
//
|
||||
// By default, if an environment variable is not set, and this option is not
|
||||
// passed, "/v1/logs" will be used.
|
||||
func WithURLPath(urlPath string) Option {
|
||||
return fnOpt(func(c config) config {
|
||||
c.path = newSetting(urlPath)
|
||||
return c
|
||||
})
|
||||
}
|
||||
|
||||
// WithTLSClientConfig sets the TLS configuration the Exporter will use for
|
||||
// HTTP requests.
|
||||
//
|
||||
// If the OTEL_EXPORTER_OTLP_CERTIFICATE or
|
||||
// OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE environment variable is set, and
|
||||
// this option is not passed, that variable value will be used. The value will
|
||||
// be parsed the filepath of the TLS certificate chain to use. If both are
|
||||
// set, OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE will take precedence.
|
||||
//
|
||||
// By default, if an environment variable is not set, and this option is not
|
||||
// passed, the system default configuration is used.
|
||||
func WithTLSClientConfig(tlsCfg *tls.Config) Option {
|
||||
return fnOpt(func(c config) config {
|
||||
c.tlsCfg = newSetting(tlsCfg.Clone())
|
||||
return c
|
||||
})
|
||||
}
|
||||
|
||||
// WithInsecure disables client transport security for the Exporter's HTTP
|
||||
// connection.
|
||||
//
|
||||
// If the OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_LOGS_ENDPOINT
|
||||
// environment variable is set, and this option is not passed, that variable
|
||||
// value will be used to determine client security. If the endpoint has a
|
||||
// scheme of "http" or "unix" client security will be disabled. If both are
|
||||
// set, OTEL_EXPORTER_OTLP_LOGS_ENDPOINT will take precedence.
|
||||
//
|
||||
// By default, if an environment variable is not set, and this option is not
|
||||
// passed, client security will be used.
|
||||
func WithInsecure() Option {
|
||||
return fnOpt(func(c config) config {
|
||||
c.insecure = newSetting(true)
|
||||
return c
|
||||
})
|
||||
}
|
||||
|
||||
// WithHeaders will send the provided headers with each HTTP requests.
|
||||
//
|
||||
// If the OTEL_EXPORTER_OTLP_HEADERS or OTEL_EXPORTER_OTLP_LOGS_HEADERS
|
||||
// environment variable is set, and this option is not passed, that variable
|
||||
// value will be used. The value will be parsed as a list of key value pairs.
|
||||
// These pairs are expected to be in the W3C Correlation-Context format
|
||||
// without additional semi-colon delimited metadata (i.e. "k1=v1,k2=v2"). If
|
||||
// both are set, OTEL_EXPORTER_OTLP_LOGS_HEADERS will take precedence.
|
||||
//
|
||||
// By default, if an environment variable is not set, and this option is not
|
||||
// passed, no user headers will be set.
|
||||
func WithHeaders(headers map[string]string) Option {
|
||||
return fnOpt(func(c config) config {
|
||||
c.headers = newSetting(headers)
|
||||
return c
|
||||
})
|
||||
}
|
||||
|
||||
// WithTimeout sets the max amount of time an Exporter will attempt an export.
|
||||
//
|
||||
// This takes precedence over any retry settings defined by WithRetry. Once
|
||||
// this time limit has been reached the export is abandoned and the log data is
|
||||
// dropped.
|
||||
//
|
||||
// If the OTEL_EXPORTER_OTLP_TIMEOUT or OTEL_EXPORTER_OTLP_LOGS_TIMEOUT
|
||||
// environment variable is set, and this option is not passed, that variable
|
||||
// value will be used. The value will be parsed as an integer representing the
|
||||
// timeout in milliseconds. If both are set,
|
||||
// OTEL_EXPORTER_OTLP_LOGS_TIMEOUT will take precedence.
|
||||
//
|
||||
// By default, if an environment variable is not set, and this option is not
|
||||
// passed, a timeout of 10 seconds will be used.
|
||||
func WithTimeout(duration time.Duration) Option {
|
||||
return fnOpt(func(c config) config {
|
||||
c.timeout = newSetting(duration)
|
||||
return c
|
||||
})
|
||||
}
|
||||
|
||||
// RetryConfig defines configuration for retrying the export of log data that
|
||||
// failed.
|
||||
type RetryConfig retry.Config
|
||||
|
||||
// WithRetry sets the retry policy for transient retryable errors that are
|
||||
// returned by the target endpoint.
|
||||
//
|
||||
// If the target endpoint responds with not only a retryable error, but
|
||||
// explicitly returns a backoff time in the response, that time will take
|
||||
// precedence over these settings.
|
||||
//
|
||||
// If unset, the default retry policy will be used. It will retry the export
|
||||
// 5 seconds after receiving a retryable error and increase exponentially
|
||||
// after each error for no more than a total time of 1 minute.
|
||||
func WithRetry(rc RetryConfig) Option {
|
||||
return fnOpt(func(c config) config {
|
||||
c.retryCfg = newSetting(retry.Config(rc))
|
||||
return c
|
||||
})
|
||||
}
|
||||
|
||||
// HTTPTransportProxyFunc is a function that resolves which URL to use as proxy
|
||||
// for a given request. This type is compatible with http.Transport.Proxy and
|
||||
// can be used to set a custom proxy function to the OTLP HTTP client.
|
||||
type HTTPTransportProxyFunc func(*http.Request) (*url.URL, error)
|
||||
|
||||
// WithProxy sets the Proxy function the client will use to determine the
|
||||
// proxy to use for an HTTP request. If this option is not used, the client
|
||||
// will use [http.ProxyFromEnvironment].
|
||||
func WithProxy(pf HTTPTransportProxyFunc) Option {
|
||||
return fnOpt(func(c config) config {
|
||||
c.proxy = newSetting(pf)
|
||||
return c
|
||||
})
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// loadEnvTLS returns a resolver that loads a *tls.Config from files defined by
|
||||
// the OTLP TLS environment variables. This will load both the rootCAs and
|
||||
// certificates used for mTLS.
|
||||
//
|
||||
// If the filepath defined is invalid or does not contain valid TLS files, an
|
||||
// error is passed to the OTel ErrorHandler and no TLS configuration is
|
||||
// provided.
|
||||
func loadEnvTLS[T *tls.Config]() resolver[T] {
|
||||
return func(s setting[T]) setting[T] {
|
||||
if s.Set {
|
||||
// Passed, valid, options have precedence.
|
||||
return s
|
||||
}
|
||||
|
||||
var rootCAs *x509.CertPool
|
||||
var err error
|
||||
for _, key := range envTLSCert {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
rootCAs, err = loadCertPool(v)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var certs []tls.Certificate
|
||||
for _, pair := range envTLSClient {
|
||||
cert := os.Getenv(pair.Certificate)
|
||||
key := os.Getenv(pair.Key)
|
||||
if cert != "" && key != "" {
|
||||
var e error
|
||||
certs, e = loadCertificates(cert, key)
|
||||
err = errors.Join(err, e)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to load TLS: %w", err)
|
||||
otel.Handle(err)
|
||||
} else if rootCAs != nil || certs != nil {
|
||||
s.Set = true
|
||||
s.Value = &tls.Config{RootCAs: rootCAs, Certificates: certs}
|
||||
}
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
// readFile is used for testing.
|
||||
var readFile = os.ReadFile
|
||||
|
||||
// loadCertPool loads and returns the *x509.CertPool found at path if it exists
|
||||
// and is valid. Otherwise, nil and an error is returned.
|
||||
func loadCertPool(path string) (*x509.CertPool, error) {
|
||||
b, err := readFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cp := x509.NewCertPool()
|
||||
if ok := cp.AppendCertsFromPEM(b); !ok {
|
||||
return nil, errors.New("certificate not added")
|
||||
}
|
||||
return cp, nil
|
||||
}
|
||||
|
||||
// loadCertificates loads and returns the tls.Certificate found at path if it
|
||||
// exists and is valid. Otherwise, nil and an error is returned.
|
||||
func loadCertificates(certPath, keyPath string) ([]tls.Certificate, error) {
|
||||
cert, err := readFile(certPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key, err := readFile(keyPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
crt, err := tls.X509KeyPair(cert, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []tls.Certificate{crt}, nil
|
||||
}
|
||||
|
||||
// getenv returns a resolver that will apply an environment variable value
|
||||
// associated with the first set key to a setting value. The conv function is
|
||||
// used to convert between the environment variable value and the setting type.
|
||||
//
|
||||
// If the input setting to the resolver is set, the environment variable will
|
||||
// not be applied.
|
||||
//
|
||||
// Any error returned from conv is sent to the OTel ErrorHandler and the
|
||||
// setting will not be updated.
|
||||
func getenv[T any](keys []string, conv func(string) (T, error)) resolver[T] {
|
||||
return func(s setting[T]) setting[T] {
|
||||
if s.Set {
|
||||
// Passed, valid, options have precedence.
|
||||
return s
|
||||
}
|
||||
|
||||
for _, key := range keys {
|
||||
if vStr := os.Getenv(key); vStr != "" {
|
||||
v, err := conv(vStr)
|
||||
if err == nil {
|
||||
s.Value = v
|
||||
s.Set = true
|
||||
break
|
||||
}
|
||||
otel.Handle(fmt.Errorf("invalid %s value %s: %w", key, vStr, err))
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
// convEndpoint converts s from a URL string to an endpoint if s is a valid
|
||||
// URL. Otherwise, "" and an error are returned.
|
||||
func convEndpoint(s string) (string, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return u.Host, nil
|
||||
}
|
||||
|
||||
// convPathExact converts s from a URL string to the exact path if s is a valid
|
||||
// URL. Otherwise, "" and an error are returned.
|
||||
//
|
||||
// If the path contained in s is empty, "/" is returned.
|
||||
func convPathExact(s string) (string, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if u.Path == "" {
|
||||
return "/", nil
|
||||
}
|
||||
return u.Path, nil
|
||||
}
|
||||
|
||||
// convPath converts s from a URL string to an OTLP endpoint path if s is a
|
||||
// valid URL. Otherwise, "" and an error are returned.
|
||||
func convPath(s string) (string, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return u.Path + "/v1/logs", nil
|
||||
}
|
||||
|
||||
// convInsecure parses s as a URL string and returns if the connection should
|
||||
// use client transport security or not. If s is an invalid URL, false and an
|
||||
// error are returned.
|
||||
func convInsecure(s string) (bool, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return u.Scheme != "https", nil
|
||||
}
|
||||
|
||||
// convHeaders converts the OTel environment variable header value s into a
|
||||
// mapping of header key to value. If s is invalid a partial result and error
|
||||
// are returned.
|
||||
func convHeaders(s string) (map[string]string, error) {
|
||||
out := make(map[string]string)
|
||||
var err error
|
||||
for _, header := range strings.Split(s, ",") {
|
||||
rawKey, rawVal, found := strings.Cut(header, "=")
|
||||
if !found {
|
||||
err = errors.Join(err, fmt.Errorf("invalid header: %s", header))
|
||||
continue
|
||||
}
|
||||
|
||||
escKey, e := url.PathUnescape(rawKey)
|
||||
if e != nil {
|
||||
err = errors.Join(err, fmt.Errorf("invalid header key: %s", rawKey))
|
||||
continue
|
||||
}
|
||||
key := strings.TrimSpace(escKey)
|
||||
|
||||
escVal, e := url.PathUnescape(rawVal)
|
||||
if e != nil {
|
||||
err = errors.Join(err, fmt.Errorf("invalid header value: %s", rawVal))
|
||||
continue
|
||||
}
|
||||
val := strings.TrimSpace(escVal)
|
||||
|
||||
out[key] = val
|
||||
}
|
||||
return out, err
|
||||
}
|
||||
|
||||
// convCompression returns the parsed compression encoded in s. NoCompression
|
||||
// and an errors are returned if s is unknown.
|
||||
func convCompression(s string) (Compression, error) {
|
||||
switch s {
|
||||
case "gzip":
|
||||
return GzipCompression, nil
|
||||
case "none", "":
|
||||
return NoCompression, nil
|
||||
}
|
||||
return NoCompression, fmt.Errorf("unknown compression: %s", s)
|
||||
}
|
||||
|
||||
// convDuration converts s into a duration of milliseconds. If s does not
|
||||
// contain an integer, 0 and an error are returned.
|
||||
func convDuration(s string) (time.Duration, error) {
|
||||
d, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// OTel durations are defined in milliseconds.
|
||||
return time.Duration(d) * time.Millisecond, nil
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
63
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/doc.go
generated
vendored
Normal file
63
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/doc.go
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/*
|
||||
Package otlploghttp provides an OTLP log exporter. The exporter uses HTTP to
|
||||
transport OTLP protobuf payloads.
|
||||
|
||||
Exporter should be created using [New].
|
||||
|
||||
The environment variables described below can be used for configuration.
|
||||
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT (default: "https://localhost:4318") -
|
||||
target base URL ("/v1/logs" is appended) to which the exporter sends telemetry.
|
||||
The value must contain a scheme ("http" or "https") and host.
|
||||
The value may additionally contain a port and a path.
|
||||
The value should not contain a query string or fragment.
|
||||
The configuration can be overridden by OTEL_EXPORTER_OTLP_LOGS_ENDPOINT
|
||||
environment variable and by [WithEndpoint], [WithEndpointURL], [WithInsecure] options.
|
||||
|
||||
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT (default: "https://localhost:4318/v1/logs") -
|
||||
target URL to which the exporter sends telemetry.
|
||||
The value must contain a scheme ("http" or "https") and host.
|
||||
The value may additionally contain a port and a path.
|
||||
The value should not contain a query string or fragment.
|
||||
The configuration can be overridden by [WithEndpoint], [WithEndpointURL], [WithInsecure], and [WithURLPath] options.
|
||||
|
||||
OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_LOGS_HEADERS (default: none) -
|
||||
key-value pairs used as headers associated with HTTP requests.
|
||||
The value is expected to be represented in a format matching the [W3C Baggage HTTP Header Content Format],
|
||||
except that additional semi-colon delimited metadata is not supported.
|
||||
Example value: "key1=value1,key2=value2".
|
||||
OTEL_EXPORTER_OTLP_LOGS_HEADERS takes precedence over OTEL_EXPORTER_OTLP_HEADERS.
|
||||
The configuration can be overridden by [WithHeaders] option.
|
||||
|
||||
OTEL_EXPORTER_OTLP_TIMEOUT, OTEL_EXPORTER_OTLP_LOGS_TIMEOUT (default: "10000") -
|
||||
maximum time in milliseconds the OTLP exporter waits for each batch export.
|
||||
OTEL_EXPORTER_OTLP_LOGS_TIMEOUT takes precedence over OTEL_EXPORTER_OTLP_TIMEOUT.
|
||||
The configuration can be overridden by [WithTimeout] option.
|
||||
|
||||
OTEL_EXPORTER_OTLP_COMPRESSION, OTEL_EXPORTER_OTLP_LOGS_COMPRESSION (default: none) -
|
||||
the compression strategy the exporter uses to compress the HTTP body.
|
||||
Supported value: "gzip".
|
||||
OTEL_EXPORTER_OTLP_LOGS_COMPRESSION takes precedence over OTEL_EXPORTER_OTLP_COMPRESSION.
|
||||
The configuration can be overridden by [WithCompression] option.
|
||||
|
||||
OTEL_EXPORTER_OTLP_CERTIFICATE, OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE (default: none) -
|
||||
the filepath to the trusted certificate to use when verifying a server's TLS credentials.
|
||||
OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE takes precedence over OTEL_EXPORTER_OTLP_CERTIFICATE.
|
||||
The configuration can be overridden by [WithTLSClientConfig] option.
|
||||
|
||||
OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE, OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE (default: none) -
|
||||
the filepath to the client certificate/chain trust for client's private key to use in mTLS communication in PEM format.
|
||||
OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE takes precedence over OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE.
|
||||
The configuration can be overridden by [WithTLSClientConfig] option.
|
||||
|
||||
OTEL_EXPORTER_OTLP_CLIENT_KEY, OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY (default: none) -
|
||||
the filepath to the client's private key to use in mTLS communication in PEM format.
|
||||
OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY takes precedence over OTEL_EXPORTER_OTLP_CLIENT_KEY.
|
||||
The configuration can be overridden by [WithTLSClientConfig] option.
|
||||
|
||||
[W3C Baggage HTTP Header Content Format]: https://www.w3.org/TR/baggage/#header-content
|
||||
*/
|
||||
package otlploghttp // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp"
|
73
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/exporter.go
generated
vendored
Normal file
73
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/exporter.go
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otlploghttp // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync/atomic"
|
||||
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/internal/transform"
|
||||
"go.opentelemetry.io/otel/sdk/log"
|
||||
)
|
||||
|
||||
// Exporter is a OpenTelemetry log Exporter. It transports log data encoded as
|
||||
// OTLP protobufs using HTTP.
|
||||
// Exporter must be created with [New].
|
||||
type Exporter struct {
|
||||
client atomic.Pointer[client]
|
||||
stopped atomic.Bool
|
||||
}
|
||||
|
||||
// Compile-time check Exporter implements [log.Exporter].
|
||||
var _ log.Exporter = (*Exporter)(nil)
|
||||
|
||||
// New returns a new [Exporter].
|
||||
//
|
||||
// It is recommended to use it with a [BatchProcessor]
|
||||
// or other processor exporting records asynchronously.
|
||||
func New(_ context.Context, options ...Option) (*Exporter, error) {
|
||||
cfg := newConfig(options)
|
||||
c, err := newHTTPClient(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newExporter(c, cfg)
|
||||
}
|
||||
|
||||
func newExporter(c *client, _ config) (*Exporter, error) {
|
||||
e := &Exporter{}
|
||||
e.client.Store(c)
|
||||
return e, nil
|
||||
}
|
||||
|
||||
// Used for testing.
|
||||
var transformResourceLogs = transform.ResourceLogs
|
||||
|
||||
// Export transforms and transmits log records to an OTLP receiver.
|
||||
func (e *Exporter) Export(ctx context.Context, records []log.Record) error {
|
||||
if e.stopped.Load() {
|
||||
return nil
|
||||
}
|
||||
otlp := transformResourceLogs(records)
|
||||
if otlp == nil {
|
||||
return nil
|
||||
}
|
||||
return e.client.Load().UploadLogs(ctx, otlp)
|
||||
}
|
||||
|
||||
// Shutdown shuts down the Exporter. Calls to Export or ForceFlush will perform
|
||||
// no operation after this is called.
|
||||
func (e *Exporter) Shutdown(ctx context.Context) error {
|
||||
if e.stopped.Swap(true) {
|
||||
return nil
|
||||
}
|
||||
|
||||
e.client.Store(newNoopClient())
|
||||
return nil
|
||||
}
|
||||
|
||||
// ForceFlush does nothing. The Exporter holds no state.
|
||||
func (e *Exporter) ForceFlush(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
145
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/internal/retry/retry.go
generated
vendored
Normal file
145
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/internal/retry/retry.go
generated
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/retry/retry.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package retry provides request retry functionality that can perform
|
||||
// configurable exponential backoff for transient errors and honor any
|
||||
// explicit throttle responses received.
|
||||
package retry // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/internal/retry"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/cenkalti/backoff/v4"
|
||||
)
|
||||
|
||||
// DefaultConfig are the recommended defaults to use.
|
||||
var DefaultConfig = Config{
|
||||
Enabled: true,
|
||||
InitialInterval: 5 * time.Second,
|
||||
MaxInterval: 30 * time.Second,
|
||||
MaxElapsedTime: time.Minute,
|
||||
}
|
||||
|
||||
// Config defines configuration for retrying batches in case of export failure
|
||||
// using an exponential backoff.
|
||||
type Config struct {
|
||||
// Enabled indicates whether to not retry sending batches in case of
|
||||
// export failure.
|
||||
Enabled bool
|
||||
// InitialInterval the time to wait after the first failure before
|
||||
// retrying.
|
||||
InitialInterval time.Duration
|
||||
// MaxInterval is the upper bound on backoff interval. Once this value is
|
||||
// reached the delay between consecutive retries will always be
|
||||
// `MaxInterval`.
|
||||
MaxInterval time.Duration
|
||||
// MaxElapsedTime is the maximum amount of time (including retries) spent
|
||||
// trying to send a request/batch. Once this value is reached, the data
|
||||
// is discarded.
|
||||
MaxElapsedTime time.Duration
|
||||
}
|
||||
|
||||
// RequestFunc wraps a request with retry logic.
|
||||
type RequestFunc func(context.Context, func(context.Context) error) error
|
||||
|
||||
// EvaluateFunc returns if an error is retry-able and if an explicit throttle
|
||||
// duration should be honored that was included in the error.
|
||||
//
|
||||
// The function must return true if the error argument is retry-able,
|
||||
// otherwise it must return false for the first return parameter.
|
||||
//
|
||||
// The function must return a non-zero time.Duration if the error contains
|
||||
// explicit throttle duration that should be honored, otherwise it must return
|
||||
// a zero valued time.Duration.
|
||||
type EvaluateFunc func(error) (bool, time.Duration)
|
||||
|
||||
// RequestFunc returns a RequestFunc using the evaluate function to determine
|
||||
// if requests can be retried and based on the exponential backoff
|
||||
// configuration of c.
|
||||
func (c Config) RequestFunc(evaluate EvaluateFunc) RequestFunc {
|
||||
if !c.Enabled {
|
||||
return func(ctx context.Context, fn func(context.Context) error) error {
|
||||
return fn(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
return func(ctx context.Context, fn func(context.Context) error) error {
|
||||
// Do not use NewExponentialBackOff since it calls Reset and the code here
|
||||
// must call Reset after changing the InitialInterval (this saves an
|
||||
// unnecessary call to Now).
|
||||
b := &backoff.ExponentialBackOff{
|
||||
InitialInterval: c.InitialInterval,
|
||||
RandomizationFactor: backoff.DefaultRandomizationFactor,
|
||||
Multiplier: backoff.DefaultMultiplier,
|
||||
MaxInterval: c.MaxInterval,
|
||||
MaxElapsedTime: c.MaxElapsedTime,
|
||||
Stop: backoff.Stop,
|
||||
Clock: backoff.SystemClock,
|
||||
}
|
||||
b.Reset()
|
||||
|
||||
for {
|
||||
err := fn(ctx)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
retryable, throttle := evaluate(err)
|
||||
if !retryable {
|
||||
return err
|
||||
}
|
||||
|
||||
bOff := b.NextBackOff()
|
||||
if bOff == backoff.Stop {
|
||||
return fmt.Errorf("max retry time elapsed: %w", err)
|
||||
}
|
||||
|
||||
// Wait for the greater of the backoff or throttle delay.
|
||||
var delay time.Duration
|
||||
if bOff > throttle {
|
||||
delay = bOff
|
||||
} else {
|
||||
elapsed := b.GetElapsedTime()
|
||||
if b.MaxElapsedTime != 0 && elapsed+throttle > b.MaxElapsedTime {
|
||||
return fmt.Errorf("max retry time would elapse: %w", err)
|
||||
}
|
||||
delay = throttle
|
||||
}
|
||||
|
||||
if ctxErr := waitFunc(ctx, delay); ctxErr != nil {
|
||||
return fmt.Errorf("%w: %w", ctxErr, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allow override for testing.
|
||||
var waitFunc = wait
|
||||
|
||||
// wait takes the caller's context, and the amount of time to wait. It will
|
||||
// return nil if the timer fires before or at the same time as the context's
|
||||
// deadline. This indicates that the call can be retried.
|
||||
func wait(ctx context.Context, delay time.Duration) error {
|
||||
timer := time.NewTimer(delay)
|
||||
defer timer.Stop()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// Handle the case where the timer and context deadline end
|
||||
// simultaneously by prioritizing the timer expiration nil value
|
||||
// response.
|
||||
select {
|
||||
case <-timer.C:
|
||||
default:
|
||||
return ctx.Err()
|
||||
}
|
||||
case <-timer.C:
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
391
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/internal/transform/log.go
generated
vendored
Normal file
391
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/internal/transform/log.go
generated
vendored
Normal file
@@ -0,0 +1,391 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/otlplog/transform/log.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package transform provides transformation functionality from the
|
||||
// sdk/log data-types into OTLP data-types.
|
||||
package transform // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/internal/transform"
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
cpb "go.opentelemetry.io/proto/otlp/common/v1"
|
||||
lpb "go.opentelemetry.io/proto/otlp/logs/v1"
|
||||
rpb "go.opentelemetry.io/proto/otlp/resource/v1"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
api "go.opentelemetry.io/otel/log"
|
||||
"go.opentelemetry.io/otel/sdk/instrumentation"
|
||||
"go.opentelemetry.io/otel/sdk/log"
|
||||
)
|
||||
|
||||
// ResourceLogs returns an slice of OTLP ResourceLogs generated from records.
|
||||
func ResourceLogs(records []log.Record) []*lpb.ResourceLogs {
|
||||
if len(records) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
resMap := make(map[attribute.Distinct]*lpb.ResourceLogs)
|
||||
|
||||
type key struct {
|
||||
r attribute.Distinct
|
||||
is instrumentation.Scope
|
||||
}
|
||||
scopeMap := make(map[key]*lpb.ScopeLogs)
|
||||
|
||||
var resources int
|
||||
for _, r := range records {
|
||||
res := r.Resource()
|
||||
rKey := res.Equivalent()
|
||||
scope := r.InstrumentationScope()
|
||||
k := key{
|
||||
r: rKey,
|
||||
is: scope,
|
||||
}
|
||||
sl, iOk := scopeMap[k]
|
||||
if !iOk {
|
||||
sl = new(lpb.ScopeLogs)
|
||||
var emptyScope instrumentation.Scope
|
||||
if scope != emptyScope {
|
||||
sl.Scope = &cpb.InstrumentationScope{
|
||||
Name: scope.Name,
|
||||
Version: scope.Version,
|
||||
Attributes: AttrIter(scope.Attributes.Iter()),
|
||||
}
|
||||
sl.SchemaUrl = scope.SchemaURL
|
||||
}
|
||||
scopeMap[k] = sl
|
||||
}
|
||||
|
||||
sl.LogRecords = append(sl.LogRecords, LogRecord(r))
|
||||
rl, rOk := resMap[rKey]
|
||||
if !rOk {
|
||||
resources++
|
||||
rl = new(lpb.ResourceLogs)
|
||||
if res.Len() > 0 {
|
||||
rl.Resource = &rpb.Resource{
|
||||
Attributes: AttrIter(res.Iter()),
|
||||
}
|
||||
}
|
||||
rl.SchemaUrl = res.SchemaURL()
|
||||
resMap[rKey] = rl
|
||||
}
|
||||
if !iOk {
|
||||
rl.ScopeLogs = append(rl.ScopeLogs, sl)
|
||||
}
|
||||
}
|
||||
|
||||
// Transform the categorized map into a slice
|
||||
resLogs := make([]*lpb.ResourceLogs, 0, resources)
|
||||
for _, rl := range resMap {
|
||||
resLogs = append(resLogs, rl)
|
||||
}
|
||||
|
||||
return resLogs
|
||||
}
|
||||
|
||||
// LogRecord returns an OTLP LogRecord generated from record.
|
||||
func LogRecord(record log.Record) *lpb.LogRecord {
|
||||
r := &lpb.LogRecord{
|
||||
TimeUnixNano: timeUnixNano(record.Timestamp()),
|
||||
ObservedTimeUnixNano: timeUnixNano(record.ObservedTimestamp()),
|
||||
EventName: record.EventName(),
|
||||
SeverityNumber: SeverityNumber(record.Severity()),
|
||||
SeverityText: record.SeverityText(),
|
||||
Body: LogAttrValue(record.Body()),
|
||||
Attributes: make([]*cpb.KeyValue, 0, record.AttributesLen()),
|
||||
Flags: uint32(record.TraceFlags()),
|
||||
// TODO: DroppedAttributesCount: /* ... */,
|
||||
}
|
||||
record.WalkAttributes(func(kv api.KeyValue) bool {
|
||||
r.Attributes = append(r.Attributes, LogAttr(kv))
|
||||
return true
|
||||
})
|
||||
if tID := record.TraceID(); tID.IsValid() {
|
||||
r.TraceId = tID[:]
|
||||
}
|
||||
if sID := record.SpanID(); sID.IsValid() {
|
||||
r.SpanId = sID[:]
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// timeUnixNano returns t as a Unix time, the number of nanoseconds elapsed
|
||||
// since January 1, 1970 UTC as uint64. The result is undefined if the Unix
|
||||
// time in nanoseconds cannot be represented by an int64 (a date before the
|
||||
// year 1678 or after 2262). timeUnixNano on the zero Time returns 0. The
|
||||
// result does not depend on the location associated with t.
|
||||
func timeUnixNano(t time.Time) uint64 {
|
||||
nano := t.UnixNano()
|
||||
if nano < 0 {
|
||||
return 0
|
||||
}
|
||||
return uint64(nano) // nolint:gosec // Overflow checked.
|
||||
}
|
||||
|
||||
// AttrIter transforms an [attribute.Iterator] into OTLP key-values.
|
||||
func AttrIter(iter attribute.Iterator) []*cpb.KeyValue {
|
||||
l := iter.Len()
|
||||
if l == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
out := make([]*cpb.KeyValue, 0, l)
|
||||
for iter.Next() {
|
||||
out = append(out, Attr(iter.Attribute()))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Attrs transforms a slice of [attribute.KeyValue] into OTLP key-values.
|
||||
func Attrs(attrs []attribute.KeyValue) []*cpb.KeyValue {
|
||||
if len(attrs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
out := make([]*cpb.KeyValue, 0, len(attrs))
|
||||
for _, kv := range attrs {
|
||||
out = append(out, Attr(kv))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Attr transforms an [attribute.KeyValue] into an OTLP key-value.
|
||||
func Attr(kv attribute.KeyValue) *cpb.KeyValue {
|
||||
return &cpb.KeyValue{Key: string(kv.Key), Value: AttrValue(kv.Value)}
|
||||
}
|
||||
|
||||
// AttrValue transforms an [attribute.Value] into an OTLP AnyValue.
|
||||
func AttrValue(v attribute.Value) *cpb.AnyValue {
|
||||
av := new(cpb.AnyValue)
|
||||
switch v.Type() {
|
||||
case attribute.BOOL:
|
||||
av.Value = &cpb.AnyValue_BoolValue{
|
||||
BoolValue: v.AsBool(),
|
||||
}
|
||||
case attribute.BOOLSLICE:
|
||||
av.Value = &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: boolSliceValues(v.AsBoolSlice()),
|
||||
},
|
||||
}
|
||||
case attribute.INT64:
|
||||
av.Value = &cpb.AnyValue_IntValue{
|
||||
IntValue: v.AsInt64(),
|
||||
}
|
||||
case attribute.INT64SLICE:
|
||||
av.Value = &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: int64SliceValues(v.AsInt64Slice()),
|
||||
},
|
||||
}
|
||||
case attribute.FLOAT64:
|
||||
av.Value = &cpb.AnyValue_DoubleValue{
|
||||
DoubleValue: v.AsFloat64(),
|
||||
}
|
||||
case attribute.FLOAT64SLICE:
|
||||
av.Value = &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: float64SliceValues(v.AsFloat64Slice()),
|
||||
},
|
||||
}
|
||||
case attribute.STRING:
|
||||
av.Value = &cpb.AnyValue_StringValue{
|
||||
StringValue: v.AsString(),
|
||||
}
|
||||
case attribute.STRINGSLICE:
|
||||
av.Value = &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: stringSliceValues(v.AsStringSlice()),
|
||||
},
|
||||
}
|
||||
default:
|
||||
av.Value = &cpb.AnyValue_StringValue{
|
||||
StringValue: "INVALID",
|
||||
}
|
||||
}
|
||||
return av
|
||||
}
|
||||
|
||||
func boolSliceValues(vals []bool) []*cpb.AnyValue {
|
||||
converted := make([]*cpb.AnyValue, len(vals))
|
||||
for i, v := range vals {
|
||||
converted[i] = &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_BoolValue{
|
||||
BoolValue: v,
|
||||
},
|
||||
}
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
||||
func int64SliceValues(vals []int64) []*cpb.AnyValue {
|
||||
converted := make([]*cpb.AnyValue, len(vals))
|
||||
for i, v := range vals {
|
||||
converted[i] = &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_IntValue{
|
||||
IntValue: v,
|
||||
},
|
||||
}
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
||||
func float64SliceValues(vals []float64) []*cpb.AnyValue {
|
||||
converted := make([]*cpb.AnyValue, len(vals))
|
||||
for i, v := range vals {
|
||||
converted[i] = &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_DoubleValue{
|
||||
DoubleValue: v,
|
||||
},
|
||||
}
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
||||
func stringSliceValues(vals []string) []*cpb.AnyValue {
|
||||
converted := make([]*cpb.AnyValue, len(vals))
|
||||
for i, v := range vals {
|
||||
converted[i] = &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_StringValue{
|
||||
StringValue: v,
|
||||
},
|
||||
}
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
||||
// Attrs transforms a slice of [api.KeyValue] into OTLP key-values.
|
||||
func LogAttrs(attrs []api.KeyValue) []*cpb.KeyValue {
|
||||
if len(attrs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
out := make([]*cpb.KeyValue, 0, len(attrs))
|
||||
for _, kv := range attrs {
|
||||
out = append(out, LogAttr(kv))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// LogAttr transforms an [api.KeyValue] into an OTLP key-value.
|
||||
func LogAttr(attr api.KeyValue) *cpb.KeyValue {
|
||||
return &cpb.KeyValue{
|
||||
Key: attr.Key,
|
||||
Value: LogAttrValue(attr.Value),
|
||||
}
|
||||
}
|
||||
|
||||
// LogAttrValues transforms a slice of [api.Value] into an OTLP []AnyValue.
|
||||
func LogAttrValues(vals []api.Value) []*cpb.AnyValue {
|
||||
if len(vals) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
out := make([]*cpb.AnyValue, 0, len(vals))
|
||||
for _, v := range vals {
|
||||
out = append(out, LogAttrValue(v))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// LogAttrValue transforms an [api.Value] into an OTLP AnyValue.
|
||||
func LogAttrValue(v api.Value) *cpb.AnyValue {
|
||||
av := new(cpb.AnyValue)
|
||||
switch v.Kind() {
|
||||
case api.KindBool:
|
||||
av.Value = &cpb.AnyValue_BoolValue{
|
||||
BoolValue: v.AsBool(),
|
||||
}
|
||||
case api.KindInt64:
|
||||
av.Value = &cpb.AnyValue_IntValue{
|
||||
IntValue: v.AsInt64(),
|
||||
}
|
||||
case api.KindFloat64:
|
||||
av.Value = &cpb.AnyValue_DoubleValue{
|
||||
DoubleValue: v.AsFloat64(),
|
||||
}
|
||||
case api.KindString:
|
||||
av.Value = &cpb.AnyValue_StringValue{
|
||||
StringValue: v.AsString(),
|
||||
}
|
||||
case api.KindBytes:
|
||||
av.Value = &cpb.AnyValue_BytesValue{
|
||||
BytesValue: v.AsBytes(),
|
||||
}
|
||||
case api.KindSlice:
|
||||
av.Value = &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: LogAttrValues(v.AsSlice()),
|
||||
},
|
||||
}
|
||||
case api.KindMap:
|
||||
av.Value = &cpb.AnyValue_KvlistValue{
|
||||
KvlistValue: &cpb.KeyValueList{
|
||||
Values: LogAttrs(v.AsMap()),
|
||||
},
|
||||
}
|
||||
default:
|
||||
av.Value = &cpb.AnyValue_StringValue{
|
||||
StringValue: "INVALID",
|
||||
}
|
||||
}
|
||||
return av
|
||||
}
|
||||
|
||||
// SeverityNumber transforms a [log.Severity] into an OTLP SeverityNumber.
|
||||
func SeverityNumber(s api.Severity) lpb.SeverityNumber {
|
||||
switch s {
|
||||
case api.SeverityTrace:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_TRACE
|
||||
case api.SeverityTrace2:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_TRACE2
|
||||
case api.SeverityTrace3:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_TRACE3
|
||||
case api.SeverityTrace4:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_TRACE4
|
||||
case api.SeverityDebug:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_DEBUG
|
||||
case api.SeverityDebug2:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_DEBUG2
|
||||
case api.SeverityDebug3:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_DEBUG3
|
||||
case api.SeverityDebug4:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_DEBUG4
|
||||
case api.SeverityInfo:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_INFO
|
||||
case api.SeverityInfo2:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_INFO2
|
||||
case api.SeverityInfo3:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_INFO3
|
||||
case api.SeverityInfo4:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_INFO4
|
||||
case api.SeverityWarn:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_WARN
|
||||
case api.SeverityWarn2:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_WARN2
|
||||
case api.SeverityWarn3:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_WARN3
|
||||
case api.SeverityWarn4:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_WARN4
|
||||
case api.SeverityError:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_ERROR
|
||||
case api.SeverityError2:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_ERROR2
|
||||
case api.SeverityError3:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_ERROR3
|
||||
case api.SeverityError4:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_ERROR4
|
||||
case api.SeverityFatal:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_FATAL
|
||||
case api.SeverityFatal2:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_FATAL2
|
||||
case api.SeverityFatal3:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_FATAL3
|
||||
case api.SeverityFatal4:
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_FATAL4
|
||||
}
|
||||
return lpb.SeverityNumber_SEVERITY_NUMBER_UNSPECIFIED
|
||||
}
|
9
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/version.go
generated
vendored
Normal file
9
vendor/go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/version.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otlploghttp // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp"
|
||||
|
||||
// Version is the current release version of the OpenTelemetry OTLP over HTTP/protobuf logs exporter in use.
|
||||
func Version() string {
|
||||
return "0.11.0"
|
||||
}
|
201
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/LICENSE
generated
vendored
Normal file
201
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
3
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/README.md
generated
vendored
Normal file
3
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/README.md
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# OTLP Metric gRPC Exporter
|
||||
|
||||
[](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc)
|
205
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/client.go
generated
vendored
Normal file
205
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/client.go
generated
vendored
Normal file
@@ -0,0 +1,205 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otlpmetricgrpc // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"google.golang.org/genproto/googleapis/rpc/errdetails"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/oconf"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/retry"
|
||||
colmetricpb "go.opentelemetry.io/proto/otlp/collector/metrics/v1"
|
||||
metricpb "go.opentelemetry.io/proto/otlp/metrics/v1"
|
||||
)
|
||||
|
||||
type client struct {
|
||||
metadata metadata.MD
|
||||
exportTimeout time.Duration
|
||||
requestFunc retry.RequestFunc
|
||||
|
||||
// ourConn keeps track of where conn was created: true if created here in
|
||||
// NewClient, or false if passed with an option. This is important on
|
||||
// Shutdown as the conn should only be closed if we created it. Otherwise,
|
||||
// it is up to the processes that passed the conn to close it.
|
||||
ourConn bool
|
||||
conn *grpc.ClientConn
|
||||
msc colmetricpb.MetricsServiceClient
|
||||
}
|
||||
|
||||
// newClient creates a new gRPC metric client.
|
||||
func newClient(_ context.Context, cfg oconf.Config) (*client, error) {
|
||||
c := &client{
|
||||
exportTimeout: cfg.Metrics.Timeout,
|
||||
requestFunc: cfg.RetryConfig.RequestFunc(retryable),
|
||||
conn: cfg.GRPCConn,
|
||||
}
|
||||
|
||||
if len(cfg.Metrics.Headers) > 0 {
|
||||
c.metadata = metadata.New(cfg.Metrics.Headers)
|
||||
}
|
||||
|
||||
if c.conn == nil {
|
||||
// If the caller did not provide a ClientConn when the client was
|
||||
// created, create one using the configuration they did provide.
|
||||
userAgent := "OTel Go OTLP over gRPC metrics exporter/" + Version()
|
||||
dialOpts := []grpc.DialOption{grpc.WithUserAgent(userAgent)}
|
||||
dialOpts = append(dialOpts, cfg.DialOptions...)
|
||||
|
||||
conn, err := grpc.NewClient(cfg.Metrics.Endpoint, dialOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Keep track that we own the lifecycle of this conn and need to close
|
||||
// it on Shutdown.
|
||||
c.ourConn = true
|
||||
c.conn = conn
|
||||
}
|
||||
|
||||
c.msc = colmetricpb.NewMetricsServiceClient(c.conn)
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Shutdown shuts down the client, freeing all resource.
|
||||
//
|
||||
// Any active connections to a remote endpoint are closed if they were created
|
||||
// by the client. Any gRPC connection passed during creation using
|
||||
// WithGRPCConn will not be closed. It is the caller's responsibility to
|
||||
// handle cleanup of that resource.
|
||||
func (c *client) Shutdown(ctx context.Context) error {
|
||||
// The otlpmetric.Exporter synchronizes access to client methods and
|
||||
// ensures this is called only once. The only thing that needs to be done
|
||||
// here is to release any computational resources the client holds.
|
||||
|
||||
c.metadata = nil
|
||||
c.requestFunc = nil
|
||||
c.msc = nil
|
||||
|
||||
err := ctx.Err()
|
||||
if c.ourConn {
|
||||
closeErr := c.conn.Close()
|
||||
// A context timeout error takes precedence over this error.
|
||||
if err == nil && closeErr != nil {
|
||||
err = closeErr
|
||||
}
|
||||
}
|
||||
c.conn = nil
|
||||
return err
|
||||
}
|
||||
|
||||
// UploadMetrics sends protoMetrics to connected endpoint.
|
||||
//
|
||||
// Retryable errors from the server will be handled according to any
|
||||
// RetryConfig the client was created with.
|
||||
func (c *client) UploadMetrics(ctx context.Context, protoMetrics *metricpb.ResourceMetrics) error {
|
||||
// The otlpmetric.Exporter synchronizes access to client methods, and
|
||||
// ensures this is not called after the Exporter is shutdown. Only thing
|
||||
// to do here is send data.
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// Do not upload if the context is already expired.
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
ctx, cancel := c.exportContext(ctx)
|
||||
defer cancel()
|
||||
|
||||
return c.requestFunc(ctx, func(iCtx context.Context) error {
|
||||
resp, err := c.msc.Export(iCtx, &colmetricpb.ExportMetricsServiceRequest{
|
||||
ResourceMetrics: []*metricpb.ResourceMetrics{protoMetrics},
|
||||
})
|
||||
if resp != nil && resp.PartialSuccess != nil {
|
||||
msg := resp.PartialSuccess.GetErrorMessage()
|
||||
n := resp.PartialSuccess.GetRejectedDataPoints()
|
||||
if n != 0 || msg != "" {
|
||||
err := internal.MetricPartialSuccessError(n, msg)
|
||||
otel.Handle(err)
|
||||
}
|
||||
}
|
||||
// nil is converted to OK.
|
||||
if status.Code(err) == codes.OK {
|
||||
// Success.
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// exportContext returns a copy of parent with an appropriate deadline and
|
||||
// cancellation function based on the clients configured export timeout.
|
||||
//
|
||||
// It is the callers responsibility to cancel the returned context once its
|
||||
// use is complete, via the parent or directly with the returned CancelFunc, to
|
||||
// ensure all resources are correctly released.
|
||||
func (c *client) exportContext(parent context.Context) (context.Context, context.CancelFunc) {
|
||||
var (
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
)
|
||||
|
||||
if c.exportTimeout > 0 {
|
||||
ctx, cancel = context.WithTimeout(parent, c.exportTimeout)
|
||||
} else {
|
||||
ctx, cancel = context.WithCancel(parent)
|
||||
}
|
||||
|
||||
if c.metadata.Len() > 0 {
|
||||
md := c.metadata
|
||||
if outMD, ok := metadata.FromOutgoingContext(ctx); ok {
|
||||
md = metadata.Join(md, outMD)
|
||||
}
|
||||
|
||||
ctx = metadata.NewOutgoingContext(ctx, md)
|
||||
}
|
||||
|
||||
return ctx, cancel
|
||||
}
|
||||
|
||||
// retryable returns if err identifies a request that can be retried and a
|
||||
// duration to wait for if an explicit throttle time is included in err.
|
||||
func retryable(err error) (bool, time.Duration) {
|
||||
s := status.Convert(err)
|
||||
return retryableGRPCStatus(s)
|
||||
}
|
||||
|
||||
func retryableGRPCStatus(s *status.Status) (bool, time.Duration) {
|
||||
switch s.Code() {
|
||||
case codes.Canceled,
|
||||
codes.DeadlineExceeded,
|
||||
codes.Aborted,
|
||||
codes.OutOfRange,
|
||||
codes.Unavailable,
|
||||
codes.DataLoss:
|
||||
// Additionally, handle RetryInfo.
|
||||
_, d := throttleDelay(s)
|
||||
return true, d
|
||||
case codes.ResourceExhausted:
|
||||
// Retry only if the server signals that the recovery from resource exhaustion is possible.
|
||||
return throttleDelay(s)
|
||||
}
|
||||
|
||||
// Not a retry-able error.
|
||||
return false, 0
|
||||
}
|
||||
|
||||
// throttleDelay returns if the status is RetryInfo
|
||||
// and the duration to wait for if an explicit throttle time is included.
|
||||
func throttleDelay(s *status.Status) (bool, time.Duration) {
|
||||
for _, detail := range s.Details() {
|
||||
if t, ok := detail.(*errdetails.RetryInfo); ok {
|
||||
return true, t.RetryDelay.AsDuration()
|
||||
}
|
||||
}
|
||||
return false, 0
|
||||
}
|
266
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/config.go
generated
vendored
Normal file
266
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/config.go
generated
vendored
Normal file
@@ -0,0 +1,266 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otlpmetricgrpc // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/oconf"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/retry"
|
||||
"go.opentelemetry.io/otel/sdk/metric"
|
||||
)
|
||||
|
||||
// Option applies a configuration option to the Exporter.
|
||||
type Option interface {
|
||||
applyGRPCOption(oconf.Config) oconf.Config
|
||||
}
|
||||
|
||||
func asGRPCOptions(opts []Option) []oconf.GRPCOption {
|
||||
converted := make([]oconf.GRPCOption, len(opts))
|
||||
for i, o := range opts {
|
||||
converted[i] = oconf.NewGRPCOption(o.applyGRPCOption)
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
||||
// RetryConfig defines configuration for retrying the export of metric data
|
||||
// that failed.
|
||||
//
|
||||
// This configuration does not define any network retry strategy. That is
|
||||
// entirely handled by the gRPC ClientConn.
|
||||
type RetryConfig retry.Config
|
||||
|
||||
type wrappedOption struct {
|
||||
oconf.GRPCOption
|
||||
}
|
||||
|
||||
func (w wrappedOption) applyGRPCOption(cfg oconf.Config) oconf.Config {
|
||||
return w.ApplyGRPCOption(cfg)
|
||||
}
|
||||
|
||||
// WithInsecure disables client transport security for the Exporter's gRPC
|
||||
// connection, just like grpc.WithInsecure()
|
||||
// (https://pkg.go.dev/google.golang.org/grpc#WithInsecure) does.
|
||||
//
|
||||
// If the OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
|
||||
// environment variable is set, and this option is not passed, that variable
|
||||
// value will be used to determine client security. If the endpoint has a
|
||||
// scheme of "http" or "unix" client security will be disabled. If both are
|
||||
// set, OTEL_EXPORTER_OTLP_METRICS_ENDPOINT will take precedence.
|
||||
//
|
||||
// By default, if an environment variable is not set, and this option is not
|
||||
// passed, client security will be used.
|
||||
//
|
||||
// This option has no effect if WithGRPCConn is used.
|
||||
func WithInsecure() Option {
|
||||
return wrappedOption{oconf.WithInsecure()}
|
||||
}
|
||||
|
||||
// WithEndpoint sets the target endpoint the Exporter will connect to.
|
||||
//
|
||||
// If the OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
|
||||
// environment variable is set, and this option is not passed, that variable
|
||||
// value will be used. If both environment variables are set,
|
||||
// OTEL_EXPORTER_OTLP_METRICS_ENDPOINT will take precedence. If an environment
|
||||
// variable is set, and this option is passed, this option will take precedence.
|
||||
//
|
||||
// If both this option and WithEndpointURL are used, the last used option will
|
||||
// take precedence.
|
||||
//
|
||||
// By default, if an environment variable is not set, and this option is not
|
||||
// passed, "localhost:4317" will be used.
|
||||
//
|
||||
// This option has no effect if WithGRPCConn is used.
|
||||
func WithEndpoint(endpoint string) Option {
|
||||
return wrappedOption{oconf.WithEndpoint(endpoint)}
|
||||
}
|
||||
|
||||
// WithEndpointURL sets the target endpoint URL the Exporter will connect to.
|
||||
//
|
||||
// If the OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
|
||||
// environment variable is set, and this option is not passed, that variable
|
||||
// value will be used. If both environment variables are set,
|
||||
// OTEL_EXPORTER_OTLP_METRICS_ENDPOINT will take precedence. If an environment
|
||||
// variable is set, and this option is passed, this option will take precedence.
|
||||
//
|
||||
// If both this option and WithEndpoint are used, the last used option will
|
||||
// take precedence.
|
||||
//
|
||||
// If an invalid URL is provided, the default value will be kept.
|
||||
//
|
||||
// By default, if an environment variable is not set, and this option is not
|
||||
// passed, "localhost:4317" will be used.
|
||||
//
|
||||
// This option has no effect if WithGRPCConn is used.
|
||||
func WithEndpointURL(u string) Option {
|
||||
return wrappedOption{oconf.WithEndpointURL(u)}
|
||||
}
|
||||
|
||||
// WithReconnectionPeriod set the minimum amount of time between connection
|
||||
// attempts to the target endpoint.
|
||||
//
|
||||
// This option has no effect if WithGRPCConn is used.
|
||||
func WithReconnectionPeriod(rp time.Duration) Option {
|
||||
return wrappedOption{oconf.NewGRPCOption(func(cfg oconf.Config) oconf.Config {
|
||||
cfg.ReconnectionPeriod = rp
|
||||
return cfg
|
||||
})}
|
||||
}
|
||||
|
||||
func compressorToCompression(compressor string) oconf.Compression {
|
||||
if compressor == "gzip" {
|
||||
return oconf.GzipCompression
|
||||
}
|
||||
|
||||
otel.Handle(fmt.Errorf("invalid compression type: '%s', using no compression as default", compressor))
|
||||
return oconf.NoCompression
|
||||
}
|
||||
|
||||
// WithCompressor sets the compressor the gRPC client uses.
|
||||
// Supported compressor values: "gzip".
|
||||
//
|
||||
// If the OTEL_EXPORTER_OTLP_COMPRESSION or
|
||||
// OTEL_EXPORTER_OTLP_METRICS_COMPRESSION environment variable is set, and
|
||||
// this option is not passed, that variable value will be used. That value can
|
||||
// be either "none" or "gzip". If both are set,
|
||||
// OTEL_EXPORTER_OTLP_METRICS_COMPRESSION will take precedence.
|
||||
//
|
||||
// By default, if an environment variable is not set, and this option is not
|
||||
// passed, no compressor will be used.
|
||||
//
|
||||
// This option has no effect if WithGRPCConn is used.
|
||||
func WithCompressor(compressor string) Option {
|
||||
return wrappedOption{oconf.WithCompression(compressorToCompression(compressor))}
|
||||
}
|
||||
|
||||
// WithHeaders will send the provided headers with each gRPC requests.
|
||||
//
|
||||
// If the OTEL_EXPORTER_OTLP_HEADERS or OTEL_EXPORTER_OTLP_METRICS_HEADERS
|
||||
// environment variable is set, and this option is not passed, that variable
|
||||
// value will be used. The value will be parsed as a list of key value pairs.
|
||||
// These pairs are expected to be in the W3C Correlation-Context format
|
||||
// without additional semi-colon delimited metadata (i.e. "k1=v1,k2=v2"). If
|
||||
// both are set, OTEL_EXPORTER_OTLP_METRICS_HEADERS will take precedence.
|
||||
//
|
||||
// By default, if an environment variable is not set, and this option is not
|
||||
// passed, no user headers will be set.
|
||||
func WithHeaders(headers map[string]string) Option {
|
||||
return wrappedOption{oconf.WithHeaders(headers)}
|
||||
}
|
||||
|
||||
// WithTLSCredentials sets the gRPC connection to use creds.
|
||||
//
|
||||
// If the OTEL_EXPORTER_OTLP_CERTIFICATE or
|
||||
// OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE environment variable is set, and
|
||||
// this option is not passed, that variable value will be used. The value will
|
||||
// be parsed the filepath of the TLS certificate chain to use. If both are
|
||||
// set, OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE will take precedence.
|
||||
//
|
||||
// By default, if an environment variable is not set, and this option is not
|
||||
// passed, no TLS credentials will be used.
|
||||
//
|
||||
// This option has no effect if WithGRPCConn is used.
|
||||
func WithTLSCredentials(creds credentials.TransportCredentials) Option {
|
||||
return wrappedOption{oconf.NewGRPCOption(func(cfg oconf.Config) oconf.Config {
|
||||
cfg.Metrics.GRPCCredentials = creds
|
||||
return cfg
|
||||
})}
|
||||
}
|
||||
|
||||
// WithServiceConfig defines the default gRPC service config used.
|
||||
//
|
||||
// This option has no effect if WithGRPCConn is used.
|
||||
func WithServiceConfig(serviceConfig string) Option {
|
||||
return wrappedOption{oconf.NewGRPCOption(func(cfg oconf.Config) oconf.Config {
|
||||
cfg.ServiceConfig = serviceConfig
|
||||
return cfg
|
||||
})}
|
||||
}
|
||||
|
||||
// WithDialOption sets explicit grpc.DialOptions to use when establishing a
|
||||
// gRPC connection. The options here are appended to the internal grpc.DialOptions
|
||||
// used so they will take precedence over any other internal grpc.DialOptions
|
||||
// they might conflict with.
|
||||
// The [grpc.WithBlock], [grpc.WithTimeout], and [grpc.WithReturnConnectionError]
|
||||
// grpc.DialOptions are ignored.
|
||||
//
|
||||
// This option has no effect if WithGRPCConn is used.
|
||||
func WithDialOption(opts ...grpc.DialOption) Option {
|
||||
return wrappedOption{oconf.NewGRPCOption(func(cfg oconf.Config) oconf.Config {
|
||||
cfg.DialOptions = opts
|
||||
return cfg
|
||||
})}
|
||||
}
|
||||
|
||||
// WithGRPCConn sets conn as the gRPC ClientConn used for all communication.
|
||||
//
|
||||
// This option takes precedence over any other option that relates to
|
||||
// establishing or persisting a gRPC connection to a target endpoint. Any
|
||||
// other option of those types passed will be ignored.
|
||||
//
|
||||
// It is the callers responsibility to close the passed conn. The Exporter
|
||||
// Shutdown method will not close this connection.
|
||||
func WithGRPCConn(conn *grpc.ClientConn) Option {
|
||||
return wrappedOption{oconf.NewGRPCOption(func(cfg oconf.Config) oconf.Config {
|
||||
cfg.GRPCConn = conn
|
||||
return cfg
|
||||
})}
|
||||
}
|
||||
|
||||
// WithTimeout sets the max amount of time an Exporter will attempt an export.
|
||||
//
|
||||
// This takes precedence over any retry settings defined by WithRetry. Once
|
||||
// this time limit has been reached the export is abandoned and the metric
|
||||
// data is dropped.
|
||||
//
|
||||
// If the OTEL_EXPORTER_OTLP_TIMEOUT or OTEL_EXPORTER_OTLP_METRICS_TIMEOUT
|
||||
// environment variable is set, and this option is not passed, that variable
|
||||
// value will be used. The value will be parsed as an integer representing the
|
||||
// timeout in milliseconds. If both are set,
|
||||
// OTEL_EXPORTER_OTLP_METRICS_TIMEOUT will take precedence.
|
||||
//
|
||||
// By default, if an environment variable is not set, and this option is not
|
||||
// passed, a timeout of 10 seconds will be used.
|
||||
func WithTimeout(duration time.Duration) Option {
|
||||
return wrappedOption{oconf.WithTimeout(duration)}
|
||||
}
|
||||
|
||||
// WithRetry sets the retry policy for transient retryable errors that are
|
||||
// returned by the target endpoint.
|
||||
//
|
||||
// If the target endpoint responds with not only a retryable error, but
|
||||
// explicitly returns a backoff time in the response, that time will take
|
||||
// precedence over these settings.
|
||||
//
|
||||
// These settings do not define any network retry strategy. That is entirely
|
||||
// handled by the gRPC ClientConn.
|
||||
//
|
||||
// If unset, the default retry policy will be used. It will retry the export
|
||||
// 5 seconds after receiving a retryable error and increase exponentially
|
||||
// after each error for no more than a total time of 1 minute.
|
||||
func WithRetry(settings RetryConfig) Option {
|
||||
return wrappedOption{oconf.WithRetry(retry.Config(settings))}
|
||||
}
|
||||
|
||||
// WithTemporalitySelector sets the TemporalitySelector the client will use to
|
||||
// determine the Temporality of an instrument based on its kind. If this option
|
||||
// is not used, the client will use the DefaultTemporalitySelector from the
|
||||
// go.opentelemetry.io/otel/sdk/metric package.
|
||||
func WithTemporalitySelector(selector metric.TemporalitySelector) Option {
|
||||
return wrappedOption{oconf.WithTemporalitySelector(selector)}
|
||||
}
|
||||
|
||||
// WithAggregationSelector sets the AggregationSelector the client will use to
|
||||
// determine the aggregation to use for an instrument based on its kind. If
|
||||
// this option is not used, the reader will use the DefaultAggregationSelector
|
||||
// from the go.opentelemetry.io/otel/sdk/metric package, or the aggregation
|
||||
// explicitly passed for a view matching an instrument.
|
||||
func WithAggregationSelector(selector metric.AggregationSelector) Option {
|
||||
return wrappedOption{oconf.WithAggregationSelector(selector)}
|
||||
}
|
84
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/doc.go
generated
vendored
Normal file
84
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/doc.go
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/*
|
||||
Package otlpmetricgrpc provides an OTLP metrics exporter using gRPC.
|
||||
By default the telemetry is sent to https://localhost:4317.
|
||||
|
||||
Exporter should be created using [New] and used with a [metric.PeriodicReader].
|
||||
|
||||
The environment variables described below can be used for configuration.
|
||||
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_METRICS_ENDPOINT (default: "https://localhost:4317") -
|
||||
target to which the exporter sends telemetry.
|
||||
The target syntax is defined in https://github.com/grpc/grpc/blob/master/doc/naming.md.
|
||||
The value must contain a scheme ("http" or "https") and host.
|
||||
The value may additionally contain a port, and a path.
|
||||
The value should not contain a query string or fragment.
|
||||
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT takes precedence over OTEL_EXPORTER_OTLP_ENDPOINT.
|
||||
The configuration can be overridden by [WithEndpoint], [WithEndpointURL], [WithInsecure], and [WithGRPCConn] options.
|
||||
|
||||
OTEL_EXPORTER_OTLP_INSECURE, OTEL_EXPORTER_OTLP_METRICS_INSECURE (default: "false") -
|
||||
setting "true" disables client transport security for the exporter's gRPC connection.
|
||||
You can use this only when an endpoint is provided without the http or https scheme.
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_METRICS_ENDPOINT setting overrides
|
||||
the scheme defined via OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_METRICS_ENDPOINT.
|
||||
OTEL_EXPORTER_OTLP_METRICS_INSECURE takes precedence over OTEL_EXPORTER_OTLP_INSECURE.
|
||||
The configuration can be overridden by [WithInsecure], [WithGRPCConn] options.
|
||||
|
||||
OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_METRICS_HEADERS (default: none) -
|
||||
key-value pairs used as gRPC metadata associated with gRPC requests.
|
||||
The value is expected to be represented in a format matching the [W3C Baggage HTTP Header Content Format],
|
||||
except that additional semi-colon delimited metadata is not supported.
|
||||
Example value: "key1=value1,key2=value2".
|
||||
OTEL_EXPORTER_OTLP_METRICS_HEADERS takes precedence over OTEL_EXPORTER_OTLP_HEADERS.
|
||||
The configuration can be overridden by [WithHeaders] option.
|
||||
|
||||
OTEL_EXPORTER_OTLP_TIMEOUT, OTEL_EXPORTER_OTLP_METRICS_TIMEOUT (default: "10000") -
|
||||
maximum time in milliseconds the OTLP exporter waits for each batch export.
|
||||
OTEL_EXPORTER_OTLP_METRICS_TIMEOUT takes precedence over OTEL_EXPORTER_OTLP_TIMEOUT.
|
||||
The configuration can be overridden by [WithTimeout] option.
|
||||
|
||||
OTEL_EXPORTER_OTLP_COMPRESSION, OTEL_EXPORTER_OTLP_METRICS_COMPRESSION (default: none) -
|
||||
the gRPC compressor the exporter uses.
|
||||
Supported value: "gzip".
|
||||
OTEL_EXPORTER_OTLP_METRICS_COMPRESSION takes precedence over OTEL_EXPORTER_OTLP_COMPRESSION.
|
||||
The configuration can be overridden by [WithCompressor], [WithGRPCConn] options.
|
||||
|
||||
OTEL_EXPORTER_OTLP_CERTIFICATE, OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE (default: none) -
|
||||
the filepath to the trusted certificate to use when verifying a server's TLS credentials.
|
||||
OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE takes precedence over OTEL_EXPORTER_OTLP_CERTIFICATE.
|
||||
The configuration can be overridden by [WithTLSCredentials], [WithGRPCConn] options.
|
||||
|
||||
OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE, OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE (default: none) -
|
||||
the filepath to the client certificate/chain trust for client's private key to use in mTLS communication in PEM format.
|
||||
OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE takes precedence over OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE.
|
||||
The configuration can be overridden by [WithTLSCredentials], [WithGRPCConn] options.
|
||||
|
||||
OTEL_EXPORTER_OTLP_CLIENT_KEY, OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY (default: none) -
|
||||
the filepath to the client's private key to use in mTLS communication in PEM format.
|
||||
OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY takes precedence over OTEL_EXPORTER_OTLP_CLIENT_KEY.
|
||||
The configuration can be overridden by [WithTLSCredentials], [WithGRPCConn] option.
|
||||
|
||||
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE (default: "cumulative") -
|
||||
aggregation temporality to use on the basis of instrument kind. Supported values:
|
||||
- "cumulative" - Cumulative aggregation temporality for all instrument kinds,
|
||||
- "delta" - Delta aggregation temporality for Counter, Asynchronous Counter and Histogram instrument kinds;
|
||||
Cumulative aggregation for UpDownCounter and Asynchronous UpDownCounter instrument kinds,
|
||||
- "lowmemory" - Delta aggregation temporality for Synchronous Counter and Histogram instrument kinds;
|
||||
Cumulative aggregation temporality for Synchronous UpDownCounter, Asynchronous Counter, and Asynchronous UpDownCounter instrument kinds.
|
||||
|
||||
The configuration can be overridden by [WithTemporalitySelector] option.
|
||||
|
||||
OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION (default: "explicit_bucket_histogram") -
|
||||
default aggregation to use for histogram instruments. Supported values:
|
||||
- "explicit_bucket_histogram" - [Explicit Bucket Histogram Aggregation],
|
||||
- "base2_exponential_bucket_histogram" - [Base2 Exponential Bucket Histogram Aggregation].
|
||||
|
||||
The configuration can be overridden by [WithAggregationSelector] option.
|
||||
|
||||
[W3C Baggage HTTP Header Content Format]: https://www.w3.org/TR/baggage/#header-content
|
||||
[Explicit Bucket Histogram Aggregation]: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.26.0/specification/metrics/sdk.md#explicit-bucket-histogram-aggregation
|
||||
[Base2 Exponential Bucket Histogram Aggregation]: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.26.0/specification/metrics/sdk.md#base2-exponential-bucket-histogram-aggregation
|
||||
*/
|
||||
package otlpmetricgrpc // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
|
157
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/exporter.go
generated
vendored
Normal file
157
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/exporter.go
generated
vendored
Normal file
@@ -0,0 +1,157 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otlpmetricgrpc // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/oconf"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/transform"
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
"go.opentelemetry.io/otel/sdk/metric"
|
||||
"go.opentelemetry.io/otel/sdk/metric/metricdata"
|
||||
metricpb "go.opentelemetry.io/proto/otlp/metrics/v1"
|
||||
)
|
||||
|
||||
// Exporter is a OpenTelemetry metric Exporter using gRPC.
|
||||
type Exporter struct {
|
||||
// Ensure synchronous access to the client across all functionality.
|
||||
clientMu sync.Mutex
|
||||
client interface {
|
||||
UploadMetrics(context.Context, *metricpb.ResourceMetrics) error
|
||||
Shutdown(context.Context) error
|
||||
}
|
||||
|
||||
temporalitySelector metric.TemporalitySelector
|
||||
aggregationSelector metric.AggregationSelector
|
||||
|
||||
shutdownOnce sync.Once
|
||||
}
|
||||
|
||||
func newExporter(c *client, cfg oconf.Config) (*Exporter, error) {
|
||||
ts := cfg.Metrics.TemporalitySelector
|
||||
if ts == nil {
|
||||
ts = func(metric.InstrumentKind) metricdata.Temporality {
|
||||
return metricdata.CumulativeTemporality
|
||||
}
|
||||
}
|
||||
|
||||
as := cfg.Metrics.AggregationSelector
|
||||
if as == nil {
|
||||
as = metric.DefaultAggregationSelector
|
||||
}
|
||||
|
||||
return &Exporter{
|
||||
client: c,
|
||||
|
||||
temporalitySelector: ts,
|
||||
aggregationSelector: as,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Temporality returns the Temporality to use for an instrument kind.
|
||||
func (e *Exporter) Temporality(k metric.InstrumentKind) metricdata.Temporality {
|
||||
return e.temporalitySelector(k)
|
||||
}
|
||||
|
||||
// Aggregation returns the Aggregation to use for an instrument kind.
|
||||
func (e *Exporter) Aggregation(k metric.InstrumentKind) metric.Aggregation {
|
||||
return e.aggregationSelector(k)
|
||||
}
|
||||
|
||||
// Export transforms and transmits metric data to an OTLP receiver.
|
||||
//
|
||||
// This method returns an error if called after Shutdown.
|
||||
// This method returns an error if the method is canceled by the passed context.
|
||||
func (e *Exporter) Export(ctx context.Context, rm *metricdata.ResourceMetrics) error {
|
||||
defer global.Debug("OTLP/gRPC exporter export", "Data", rm)
|
||||
|
||||
otlpRm, err := transform.ResourceMetrics(rm)
|
||||
// Best effort upload of transformable metrics.
|
||||
e.clientMu.Lock()
|
||||
upErr := e.client.UploadMetrics(ctx, otlpRm)
|
||||
e.clientMu.Unlock()
|
||||
if upErr != nil {
|
||||
if err == nil {
|
||||
return fmt.Errorf("failed to upload metrics: %w", upErr)
|
||||
}
|
||||
// Merge the two errors.
|
||||
return fmt.Errorf("failed to upload incomplete metrics (%w): %w", err, upErr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// ForceFlush flushes any metric data held by an exporter.
|
||||
//
|
||||
// This method returns an error if called after Shutdown.
|
||||
// This method returns an error if the method is canceled by the passed context.
|
||||
//
|
||||
// This method is safe to call concurrently.
|
||||
func (e *Exporter) ForceFlush(ctx context.Context) error {
|
||||
// The exporter and client hold no state, nothing to flush.
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
// Shutdown flushes all metric data held by an exporter and releases any held
|
||||
// computational resources.
|
||||
//
|
||||
// This method returns an error if called after Shutdown.
|
||||
// This method returns an error if the method is canceled by the passed context.
|
||||
//
|
||||
// This method is safe to call concurrently.
|
||||
func (e *Exporter) Shutdown(ctx context.Context) error {
|
||||
err := errShutdown
|
||||
e.shutdownOnce.Do(func() {
|
||||
e.clientMu.Lock()
|
||||
client := e.client
|
||||
e.client = shutdownClient{}
|
||||
e.clientMu.Unlock()
|
||||
err = client.Shutdown(ctx)
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
var errShutdown = errors.New("gRPC exporter is shutdown")
|
||||
|
||||
type shutdownClient struct{}
|
||||
|
||||
func (c shutdownClient) err(ctx context.Context) error {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
return errShutdown
|
||||
}
|
||||
|
||||
func (c shutdownClient) UploadMetrics(ctx context.Context, _ *metricpb.ResourceMetrics) error {
|
||||
return c.err(ctx)
|
||||
}
|
||||
|
||||
func (c shutdownClient) Shutdown(ctx context.Context) error {
|
||||
return c.err(ctx)
|
||||
}
|
||||
|
||||
// MarshalLog returns logging data about the Exporter.
|
||||
func (e *Exporter) MarshalLog() interface{} {
|
||||
return struct{ Type string }{Type: "OTLP/gRPC"}
|
||||
}
|
||||
|
||||
// New returns an OpenTelemetry metric Exporter. The Exporter can be used with
|
||||
// a PeriodicReader to export OpenTelemetry metric data to an OTLP receiving
|
||||
// endpoint using gRPC.
|
||||
//
|
||||
// If an already established gRPC ClientConn is not passed in options using
|
||||
// WithGRPCConn, a connection to the OTLP endpoint will be established based
|
||||
// on options. If a connection cannot be establishes in the lifetime of ctx,
|
||||
// an error will be returned.
|
||||
func New(ctx context.Context, options ...Option) (*Exporter, error) {
|
||||
cfg := oconf.NewGRPCConfig(asGRPCOptions(options)...)
|
||||
c, err := newClient(ctx, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newExporter(c, cfg)
|
||||
}
|
215
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/envconfig/envconfig.go
generated
vendored
Normal file
215
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/envconfig/envconfig.go
generated
vendored
Normal file
@@ -0,0 +1,215 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/envconfig/envconfig.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package envconfig // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/envconfig"
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
)
|
||||
|
||||
// ConfigFn is the generic function used to set a config.
|
||||
type ConfigFn func(*EnvOptionsReader)
|
||||
|
||||
// EnvOptionsReader reads the required environment variables.
|
||||
type EnvOptionsReader struct {
|
||||
GetEnv func(string) string
|
||||
ReadFile func(string) ([]byte, error)
|
||||
Namespace string
|
||||
}
|
||||
|
||||
// Apply runs every ConfigFn.
|
||||
func (e *EnvOptionsReader) Apply(opts ...ConfigFn) {
|
||||
for _, o := range opts {
|
||||
o(e)
|
||||
}
|
||||
}
|
||||
|
||||
// GetEnvValue gets an OTLP environment variable value of the specified key
|
||||
// using the GetEnv function.
|
||||
// This function prepends the OTLP specified namespace to all key lookups.
|
||||
func (e *EnvOptionsReader) GetEnvValue(key string) (string, bool) {
|
||||
v := strings.TrimSpace(e.GetEnv(keyWithNamespace(e.Namespace, key)))
|
||||
return v, v != ""
|
||||
}
|
||||
|
||||
// WithString retrieves the specified config and passes it to ConfigFn as a string.
|
||||
func WithString(n string, fn func(string)) func(e *EnvOptionsReader) {
|
||||
return func(e *EnvOptionsReader) {
|
||||
if v, ok := e.GetEnvValue(n); ok {
|
||||
fn(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithBool returns a ConfigFn that reads the environment variable n and if it exists passes its parsed bool value to fn.
|
||||
func WithBool(n string, fn func(bool)) ConfigFn {
|
||||
return func(e *EnvOptionsReader) {
|
||||
if v, ok := e.GetEnvValue(n); ok {
|
||||
b := strings.ToLower(v) == "true"
|
||||
fn(b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithDuration retrieves the specified config and passes it to ConfigFn as a duration.
|
||||
func WithDuration(n string, fn func(time.Duration)) func(e *EnvOptionsReader) {
|
||||
return func(e *EnvOptionsReader) {
|
||||
if v, ok := e.GetEnvValue(n); ok {
|
||||
d, err := strconv.Atoi(v)
|
||||
if err != nil {
|
||||
global.Error(err, "parse duration", "input", v)
|
||||
return
|
||||
}
|
||||
fn(time.Duration(d) * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithHeaders retrieves the specified config and passes it to ConfigFn as a map of HTTP headers.
|
||||
func WithHeaders(n string, fn func(map[string]string)) func(e *EnvOptionsReader) {
|
||||
return func(e *EnvOptionsReader) {
|
||||
if v, ok := e.GetEnvValue(n); ok {
|
||||
fn(stringToHeader(v))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithURL retrieves the specified config and passes it to ConfigFn as a net/url.URL.
|
||||
func WithURL(n string, fn func(*url.URL)) func(e *EnvOptionsReader) {
|
||||
return func(e *EnvOptionsReader) {
|
||||
if v, ok := e.GetEnvValue(n); ok {
|
||||
u, err := url.Parse(v)
|
||||
if err != nil {
|
||||
global.Error(err, "parse url", "input", v)
|
||||
return
|
||||
}
|
||||
fn(u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithCertPool returns a ConfigFn that reads the environment variable n as a filepath to a TLS certificate pool. If it exists, it is parsed as a crypto/x509.CertPool and it is passed to fn.
|
||||
func WithCertPool(n string, fn func(*x509.CertPool)) ConfigFn {
|
||||
return func(e *EnvOptionsReader) {
|
||||
if v, ok := e.GetEnvValue(n); ok {
|
||||
b, err := e.ReadFile(v)
|
||||
if err != nil {
|
||||
global.Error(err, "read tls ca cert file", "file", v)
|
||||
return
|
||||
}
|
||||
c, err := createCertPool(b)
|
||||
if err != nil {
|
||||
global.Error(err, "create tls cert pool")
|
||||
return
|
||||
}
|
||||
fn(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithClientCert returns a ConfigFn that reads the environment variable nc and nk as filepaths to a client certificate and key pair. If they exists, they are parsed as a crypto/tls.Certificate and it is passed to fn.
|
||||
func WithClientCert(nc, nk string, fn func(tls.Certificate)) ConfigFn {
|
||||
return func(e *EnvOptionsReader) {
|
||||
vc, okc := e.GetEnvValue(nc)
|
||||
vk, okk := e.GetEnvValue(nk)
|
||||
if !okc || !okk {
|
||||
return
|
||||
}
|
||||
cert, err := e.ReadFile(vc)
|
||||
if err != nil {
|
||||
global.Error(err, "read tls client cert", "file", vc)
|
||||
return
|
||||
}
|
||||
key, err := e.ReadFile(vk)
|
||||
if err != nil {
|
||||
global.Error(err, "read tls client key", "file", vk)
|
||||
return
|
||||
}
|
||||
crt, err := tls.X509KeyPair(cert, key)
|
||||
if err != nil {
|
||||
global.Error(err, "create tls client key pair")
|
||||
return
|
||||
}
|
||||
fn(crt)
|
||||
}
|
||||
}
|
||||
|
||||
func keyWithNamespace(ns, key string) string {
|
||||
if ns == "" {
|
||||
return key
|
||||
}
|
||||
return fmt.Sprintf("%s_%s", ns, key)
|
||||
}
|
||||
|
||||
func stringToHeader(value string) map[string]string {
|
||||
headersPairs := strings.Split(value, ",")
|
||||
headers := make(map[string]string)
|
||||
|
||||
for _, header := range headersPairs {
|
||||
n, v, found := strings.Cut(header, "=")
|
||||
if !found {
|
||||
global.Error(errors.New("missing '="), "parse headers", "input", header)
|
||||
continue
|
||||
}
|
||||
|
||||
trimmedName := strings.TrimSpace(n)
|
||||
|
||||
// Validate the key.
|
||||
if !isValidHeaderKey(trimmedName) {
|
||||
global.Error(errors.New("invalid header key"), "parse headers", "key", trimmedName)
|
||||
continue
|
||||
}
|
||||
|
||||
// Only decode the value.
|
||||
value, err := url.PathUnescape(v)
|
||||
if err != nil {
|
||||
global.Error(err, "escape header value", "value", v)
|
||||
continue
|
||||
}
|
||||
trimmedValue := strings.TrimSpace(value)
|
||||
|
||||
headers[trimmedName] = trimmedValue
|
||||
}
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
func createCertPool(certBytes []byte) (*x509.CertPool, error) {
|
||||
cp := x509.NewCertPool()
|
||||
if ok := cp.AppendCertsFromPEM(certBytes); !ok {
|
||||
return nil, errors.New("failed to append certificate to the cert pool")
|
||||
}
|
||||
return cp, nil
|
||||
}
|
||||
|
||||
func isValidHeaderKey(key string) bool {
|
||||
if key == "" {
|
||||
return false
|
||||
}
|
||||
for _, c := range key {
|
||||
if !isTokenChar(c) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func isTokenChar(c rune) bool {
|
||||
return c <= unicode.MaxASCII && (unicode.IsLetter(c) ||
|
||||
unicode.IsDigit(c) ||
|
||||
c == '!' || c == '#' || c == '$' || c == '%' || c == '&' || c == '\'' || c == '*' ||
|
||||
c == '+' || c == '-' || c == '.' || c == '^' || c == '_' || c == '`' || c == '|' || c == '~')
|
||||
}
|
31
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/gen.go
generated
vendored
Normal file
31
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/gen.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package internal // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal"
|
||||
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/partialsuccess.go.tmpl "--data={}" --out=partialsuccess.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/partialsuccess_test.go.tmpl "--data={}" --out=partialsuccess_test.go
|
||||
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/retry/retry.go.tmpl "--data={}" --out=retry/retry.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/retry/retry_test.go.tmpl "--data={}" --out=retry/retry_test.go
|
||||
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/envconfig/envconfig.go.tmpl "--data={}" --out=envconfig/envconfig.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/envconfig/envconfig_test.go.tmpl "--data={}" --out=envconfig/envconfig_test.go
|
||||
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/oconf/envconfig.go.tmpl "--data={\"envconfigImportPath\": \"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/envconfig\"}" --out=oconf/envconfig.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/oconf/envconfig_test.go.tmpl "--data={}" --out=oconf/envconfig_test.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/oconf/options.go.tmpl "--data={\"retryImportPath\": \"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/retry\"}" --out=oconf/options.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/oconf/options_test.go.tmpl "--data={\"envconfigImportPath\": \"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/envconfig\"}" --out=oconf/options_test.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/oconf/optiontypes.go.tmpl "--data={}" --out=oconf/optiontypes.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/oconf/tls.go.tmpl "--data={}" --out=oconf/tls.go
|
||||
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/otest/client.go.tmpl "--data={}" --out=otest/client.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/otest/client_test.go.tmpl "--data={\"internalImportPath\": \"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal\"}" --out=otest/client_test.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/otest/collector.go.tmpl "--data={\"oconfImportPath\": \"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/oconf\"}" --out=otest/collector.go
|
||||
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/transform/attribute.go.tmpl "--data={}" --out=transform/attribute.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/transform/attribute_test.go.tmpl "--data={}" --out=transform/attribute_test.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/transform/error.go.tmpl "--data={}" --out=transform/error.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/transform/error_test.go.tmpl "--data={}" --out=transform/error_test.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/transform/metricdata.go.tmpl "--data={}" --out=transform/metricdata.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/transform/metricdata_test.go.tmpl "--data={}" --out=transform/metricdata_test.go
|
210
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/oconf/envconfig.go
generated
vendored
Normal file
210
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/oconf/envconfig.go
generated
vendored
Normal file
@@ -0,0 +1,210 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/otlpmetric/oconf/envconfig.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package oconf // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/oconf"
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/envconfig"
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
"go.opentelemetry.io/otel/sdk/metric"
|
||||
"go.opentelemetry.io/otel/sdk/metric/metricdata"
|
||||
)
|
||||
|
||||
// DefaultEnvOptionsReader is the default environments reader.
|
||||
var DefaultEnvOptionsReader = envconfig.EnvOptionsReader{
|
||||
GetEnv: os.Getenv,
|
||||
ReadFile: os.ReadFile,
|
||||
Namespace: "OTEL_EXPORTER_OTLP",
|
||||
}
|
||||
|
||||
// ApplyGRPCEnvConfigs applies the env configurations for gRPC.
|
||||
func ApplyGRPCEnvConfigs(cfg Config) Config {
|
||||
opts := getOptionsFromEnv()
|
||||
for _, opt := range opts {
|
||||
cfg = opt.ApplyGRPCOption(cfg)
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
// ApplyHTTPEnvConfigs applies the env configurations for HTTP.
|
||||
func ApplyHTTPEnvConfigs(cfg Config) Config {
|
||||
opts := getOptionsFromEnv()
|
||||
for _, opt := range opts {
|
||||
cfg = opt.ApplyHTTPOption(cfg)
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
func getOptionsFromEnv() []GenericOption {
|
||||
opts := []GenericOption{}
|
||||
|
||||
tlsConf := &tls.Config{}
|
||||
DefaultEnvOptionsReader.Apply(
|
||||
envconfig.WithURL("ENDPOINT", func(u *url.URL) {
|
||||
opts = append(opts, withEndpointScheme(u))
|
||||
opts = append(opts, newSplitOption(func(cfg Config) Config {
|
||||
cfg.Metrics.Endpoint = u.Host
|
||||
// For OTLP/HTTP endpoint URLs without a per-signal
|
||||
// configuration, the passed endpoint is used as a base URL
|
||||
// and the signals are sent to these paths relative to that.
|
||||
cfg.Metrics.URLPath = path.Join(u.Path, DefaultMetricsPath)
|
||||
return cfg
|
||||
}, withEndpointForGRPC(u)))
|
||||
}),
|
||||
envconfig.WithURL("METRICS_ENDPOINT", func(u *url.URL) {
|
||||
opts = append(opts, withEndpointScheme(u))
|
||||
opts = append(opts, newSplitOption(func(cfg Config) Config {
|
||||
cfg.Metrics.Endpoint = u.Host
|
||||
// For endpoint URLs for OTLP/HTTP per-signal variables, the
|
||||
// URL MUST be used as-is without any modification. The only
|
||||
// exception is that if an URL contains no path part, the root
|
||||
// path / MUST be used.
|
||||
path := u.Path
|
||||
if path == "" {
|
||||
path = "/"
|
||||
}
|
||||
cfg.Metrics.URLPath = path
|
||||
return cfg
|
||||
}, withEndpointForGRPC(u)))
|
||||
}),
|
||||
envconfig.WithCertPool("CERTIFICATE", func(p *x509.CertPool) { tlsConf.RootCAs = p }),
|
||||
envconfig.WithCertPool("METRICS_CERTIFICATE", func(p *x509.CertPool) { tlsConf.RootCAs = p }),
|
||||
envconfig.WithClientCert("CLIENT_CERTIFICATE", "CLIENT_KEY", func(c tls.Certificate) { tlsConf.Certificates = []tls.Certificate{c} }),
|
||||
envconfig.WithClientCert("METRICS_CLIENT_CERTIFICATE", "METRICS_CLIENT_KEY", func(c tls.Certificate) { tlsConf.Certificates = []tls.Certificate{c} }),
|
||||
envconfig.WithBool("INSECURE", func(b bool) { opts = append(opts, withInsecure(b)) }),
|
||||
envconfig.WithBool("METRICS_INSECURE", func(b bool) { opts = append(opts, withInsecure(b)) }),
|
||||
withTLSConfig(tlsConf, func(c *tls.Config) { opts = append(opts, WithTLSClientConfig(c)) }),
|
||||
envconfig.WithHeaders("HEADERS", func(h map[string]string) { opts = append(opts, WithHeaders(h)) }),
|
||||
envconfig.WithHeaders("METRICS_HEADERS", func(h map[string]string) { opts = append(opts, WithHeaders(h)) }),
|
||||
WithEnvCompression("COMPRESSION", func(c Compression) { opts = append(opts, WithCompression(c)) }),
|
||||
WithEnvCompression("METRICS_COMPRESSION", func(c Compression) { opts = append(opts, WithCompression(c)) }),
|
||||
envconfig.WithDuration("TIMEOUT", func(d time.Duration) { opts = append(opts, WithTimeout(d)) }),
|
||||
envconfig.WithDuration("METRICS_TIMEOUT", func(d time.Duration) { opts = append(opts, WithTimeout(d)) }),
|
||||
withEnvTemporalityPreference("METRICS_TEMPORALITY_PREFERENCE", func(t metric.TemporalitySelector) { opts = append(opts, WithTemporalitySelector(t)) }),
|
||||
withEnvAggPreference("METRICS_DEFAULT_HISTOGRAM_AGGREGATION", func(a metric.AggregationSelector) { opts = append(opts, WithAggregationSelector(a)) }),
|
||||
)
|
||||
|
||||
return opts
|
||||
}
|
||||
|
||||
func withEndpointForGRPC(u *url.URL) func(cfg Config) Config {
|
||||
return func(cfg Config) Config {
|
||||
// For OTLP/gRPC endpoints, this is the target to which the
|
||||
// exporter is going to send telemetry.
|
||||
cfg.Metrics.Endpoint = path.Join(u.Host, u.Path)
|
||||
return cfg
|
||||
}
|
||||
}
|
||||
|
||||
// WithEnvCompression retrieves the specified config and passes it to ConfigFn as a Compression.
|
||||
func WithEnvCompression(n string, fn func(Compression)) func(e *envconfig.EnvOptionsReader) {
|
||||
return func(e *envconfig.EnvOptionsReader) {
|
||||
if v, ok := e.GetEnvValue(n); ok {
|
||||
cp := NoCompression
|
||||
if v == "gzip" {
|
||||
cp = GzipCompression
|
||||
}
|
||||
|
||||
fn(cp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func withEndpointScheme(u *url.URL) GenericOption {
|
||||
switch strings.ToLower(u.Scheme) {
|
||||
case "http", "unix":
|
||||
return WithInsecure()
|
||||
default:
|
||||
return WithSecure()
|
||||
}
|
||||
}
|
||||
|
||||
// revive:disable-next-line:flag-parameter
|
||||
func withInsecure(b bool) GenericOption {
|
||||
if b {
|
||||
return WithInsecure()
|
||||
}
|
||||
return WithSecure()
|
||||
}
|
||||
|
||||
func withTLSConfig(c *tls.Config, fn func(*tls.Config)) func(e *envconfig.EnvOptionsReader) {
|
||||
return func(e *envconfig.EnvOptionsReader) {
|
||||
if c.RootCAs != nil || len(c.Certificates) > 0 {
|
||||
fn(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func withEnvTemporalityPreference(n string, fn func(metric.TemporalitySelector)) func(e *envconfig.EnvOptionsReader) {
|
||||
return func(e *envconfig.EnvOptionsReader) {
|
||||
if s, ok := e.GetEnvValue(n); ok {
|
||||
switch strings.ToLower(s) {
|
||||
case "cumulative":
|
||||
fn(cumulativeTemporality)
|
||||
case "delta":
|
||||
fn(deltaTemporality)
|
||||
case "lowmemory":
|
||||
fn(lowMemory)
|
||||
default:
|
||||
global.Warn("OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE is set to an invalid value, ignoring.", "value", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cumulativeTemporality(metric.InstrumentKind) metricdata.Temporality {
|
||||
return metricdata.CumulativeTemporality
|
||||
}
|
||||
|
||||
func deltaTemporality(ik metric.InstrumentKind) metricdata.Temporality {
|
||||
switch ik {
|
||||
case metric.InstrumentKindCounter, metric.InstrumentKindHistogram, metric.InstrumentKindObservableCounter:
|
||||
return metricdata.DeltaTemporality
|
||||
default:
|
||||
return metricdata.CumulativeTemporality
|
||||
}
|
||||
}
|
||||
|
||||
func lowMemory(ik metric.InstrumentKind) metricdata.Temporality {
|
||||
switch ik {
|
||||
case metric.InstrumentKindCounter, metric.InstrumentKindHistogram:
|
||||
return metricdata.DeltaTemporality
|
||||
default:
|
||||
return metricdata.CumulativeTemporality
|
||||
}
|
||||
}
|
||||
|
||||
func withEnvAggPreference(n string, fn func(metric.AggregationSelector)) func(e *envconfig.EnvOptionsReader) {
|
||||
return func(e *envconfig.EnvOptionsReader) {
|
||||
if s, ok := e.GetEnvValue(n); ok {
|
||||
switch strings.ToLower(s) {
|
||||
case "explicit_bucket_histogram":
|
||||
fn(metric.DefaultAggregationSelector)
|
||||
case "base2_exponential_bucket_histogram":
|
||||
fn(func(kind metric.InstrumentKind) metric.Aggregation {
|
||||
if kind == metric.InstrumentKindHistogram {
|
||||
return metric.AggregationBase2ExponentialHistogram{
|
||||
MaxSize: 160,
|
||||
MaxScale: 20,
|
||||
NoMinMax: false,
|
||||
}
|
||||
}
|
||||
return metric.DefaultAggregationSelector(kind)
|
||||
})
|
||||
default:
|
||||
global.Warn("OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION is set to an invalid value, ignoring.", "value", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
374
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/oconf/options.go
generated
vendored
Normal file
374
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/oconf/options.go
generated
vendored
Normal file
@@ -0,0 +1,374 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/otlpmetric/oconf/options.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package oconf // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/oconf"
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/backoff"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/encoding/gzip"
|
||||
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/retry"
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
"go.opentelemetry.io/otel/sdk/metric"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultMaxAttempts describes how many times the driver
|
||||
// should retry the sending of the payload in case of a
|
||||
// retryable error.
|
||||
DefaultMaxAttempts int = 5
|
||||
// DefaultMetricsPath is a default URL path for endpoint that
|
||||
// receives metrics.
|
||||
DefaultMetricsPath string = "/v1/metrics"
|
||||
// DefaultBackoff is a default base backoff time used in the
|
||||
// exponential backoff strategy.
|
||||
DefaultBackoff time.Duration = 300 * time.Millisecond
|
||||
// DefaultTimeout is a default max waiting time for the backend to process
|
||||
// each span or metrics batch.
|
||||
DefaultTimeout time.Duration = 10 * time.Second
|
||||
)
|
||||
|
||||
type (
|
||||
// HTTPTransportProxyFunc is a function that resolves which URL to use as proxy for a given request.
|
||||
// This type is compatible with `http.Transport.Proxy` and can be used to set a custom proxy function to the OTLP HTTP client.
|
||||
HTTPTransportProxyFunc func(*http.Request) (*url.URL, error)
|
||||
|
||||
SignalConfig struct {
|
||||
Endpoint string
|
||||
Insecure bool
|
||||
TLSCfg *tls.Config
|
||||
Headers map[string]string
|
||||
Compression Compression
|
||||
Timeout time.Duration
|
||||
URLPath string
|
||||
|
||||
// gRPC configurations
|
||||
GRPCCredentials credentials.TransportCredentials
|
||||
|
||||
TemporalitySelector metric.TemporalitySelector
|
||||
AggregationSelector metric.AggregationSelector
|
||||
|
||||
Proxy HTTPTransportProxyFunc
|
||||
}
|
||||
|
||||
Config struct {
|
||||
// Signal specific configurations
|
||||
Metrics SignalConfig
|
||||
|
||||
RetryConfig retry.Config
|
||||
|
||||
// gRPC configurations
|
||||
ReconnectionPeriod time.Duration
|
||||
ServiceConfig string
|
||||
DialOptions []grpc.DialOption
|
||||
GRPCConn *grpc.ClientConn
|
||||
}
|
||||
)
|
||||
|
||||
// NewHTTPConfig returns a new Config with all settings applied from opts and
|
||||
// any unset setting using the default HTTP config values.
|
||||
func NewHTTPConfig(opts ...HTTPOption) Config {
|
||||
cfg := Config{
|
||||
Metrics: SignalConfig{
|
||||
Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorHTTPPort),
|
||||
URLPath: DefaultMetricsPath,
|
||||
Compression: NoCompression,
|
||||
Timeout: DefaultTimeout,
|
||||
|
||||
TemporalitySelector: metric.DefaultTemporalitySelector,
|
||||
AggregationSelector: metric.DefaultAggregationSelector,
|
||||
},
|
||||
RetryConfig: retry.DefaultConfig,
|
||||
}
|
||||
cfg = ApplyHTTPEnvConfigs(cfg)
|
||||
for _, opt := range opts {
|
||||
cfg = opt.ApplyHTTPOption(cfg)
|
||||
}
|
||||
cfg.Metrics.URLPath = cleanPath(cfg.Metrics.URLPath, DefaultMetricsPath)
|
||||
return cfg
|
||||
}
|
||||
|
||||
// cleanPath returns a path with all spaces trimmed and all redundancies
|
||||
// removed. If urlPath is empty or cleaning it results in an empty string,
|
||||
// defaultPath is returned instead.
|
||||
func cleanPath(urlPath string, defaultPath string) string {
|
||||
tmp := path.Clean(strings.TrimSpace(urlPath))
|
||||
if tmp == "." {
|
||||
return defaultPath
|
||||
}
|
||||
if !path.IsAbs(tmp) {
|
||||
tmp = "/" + tmp
|
||||
}
|
||||
return tmp
|
||||
}
|
||||
|
||||
// NewGRPCConfig returns a new Config with all settings applied from opts and
|
||||
// any unset setting using the default gRPC config values.
|
||||
func NewGRPCConfig(opts ...GRPCOption) Config {
|
||||
cfg := Config{
|
||||
Metrics: SignalConfig{
|
||||
Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorGRPCPort),
|
||||
URLPath: DefaultMetricsPath,
|
||||
Compression: NoCompression,
|
||||
Timeout: DefaultTimeout,
|
||||
|
||||
TemporalitySelector: metric.DefaultTemporalitySelector,
|
||||
AggregationSelector: metric.DefaultAggregationSelector,
|
||||
},
|
||||
RetryConfig: retry.DefaultConfig,
|
||||
}
|
||||
cfg = ApplyGRPCEnvConfigs(cfg)
|
||||
for _, opt := range opts {
|
||||
cfg = opt.ApplyGRPCOption(cfg)
|
||||
}
|
||||
|
||||
if cfg.ServiceConfig != "" {
|
||||
cfg.DialOptions = append(cfg.DialOptions, grpc.WithDefaultServiceConfig(cfg.ServiceConfig))
|
||||
}
|
||||
// Prioritize GRPCCredentials over Insecure (passing both is an error).
|
||||
if cfg.Metrics.GRPCCredentials != nil {
|
||||
cfg.DialOptions = append(cfg.DialOptions, grpc.WithTransportCredentials(cfg.Metrics.GRPCCredentials))
|
||||
} else if cfg.Metrics.Insecure {
|
||||
cfg.DialOptions = append(cfg.DialOptions, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
} else {
|
||||
// Default to using the host's root CA.
|
||||
creds := credentials.NewTLS(nil)
|
||||
cfg.Metrics.GRPCCredentials = creds
|
||||
cfg.DialOptions = append(cfg.DialOptions, grpc.WithTransportCredentials(creds))
|
||||
}
|
||||
if cfg.Metrics.Compression == GzipCompression {
|
||||
cfg.DialOptions = append(cfg.DialOptions, grpc.WithDefaultCallOptions(grpc.UseCompressor(gzip.Name)))
|
||||
}
|
||||
if cfg.ReconnectionPeriod != 0 {
|
||||
p := grpc.ConnectParams{
|
||||
Backoff: backoff.DefaultConfig,
|
||||
MinConnectTimeout: cfg.ReconnectionPeriod,
|
||||
}
|
||||
cfg.DialOptions = append(cfg.DialOptions, grpc.WithConnectParams(p))
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
type (
|
||||
// GenericOption applies an option to the HTTP or gRPC driver.
|
||||
GenericOption interface {
|
||||
ApplyHTTPOption(Config) Config
|
||||
ApplyGRPCOption(Config) Config
|
||||
|
||||
// A private method to prevent users implementing the
|
||||
// interface and so future additions to it will not
|
||||
// violate compatibility.
|
||||
private()
|
||||
}
|
||||
|
||||
// HTTPOption applies an option to the HTTP driver.
|
||||
HTTPOption interface {
|
||||
ApplyHTTPOption(Config) Config
|
||||
|
||||
// A private method to prevent users implementing the
|
||||
// interface and so future additions to it will not
|
||||
// violate compatibility.
|
||||
private()
|
||||
}
|
||||
|
||||
// GRPCOption applies an option to the gRPC driver.
|
||||
GRPCOption interface {
|
||||
ApplyGRPCOption(Config) Config
|
||||
|
||||
// A private method to prevent users implementing the
|
||||
// interface and so future additions to it will not
|
||||
// violate compatibility.
|
||||
private()
|
||||
}
|
||||
)
|
||||
|
||||
// genericOption is an option that applies the same logic
|
||||
// for both gRPC and HTTP.
|
||||
type genericOption struct {
|
||||
fn func(Config) Config
|
||||
}
|
||||
|
||||
func (g *genericOption) ApplyGRPCOption(cfg Config) Config {
|
||||
return g.fn(cfg)
|
||||
}
|
||||
|
||||
func (g *genericOption) ApplyHTTPOption(cfg Config) Config {
|
||||
return g.fn(cfg)
|
||||
}
|
||||
|
||||
func (genericOption) private() {}
|
||||
|
||||
func newGenericOption(fn func(cfg Config) Config) GenericOption {
|
||||
return &genericOption{fn: fn}
|
||||
}
|
||||
|
||||
// splitOption is an option that applies different logics
|
||||
// for gRPC and HTTP.
|
||||
type splitOption struct {
|
||||
httpFn func(Config) Config
|
||||
grpcFn func(Config) Config
|
||||
}
|
||||
|
||||
func (g *splitOption) ApplyGRPCOption(cfg Config) Config {
|
||||
return g.grpcFn(cfg)
|
||||
}
|
||||
|
||||
func (g *splitOption) ApplyHTTPOption(cfg Config) Config {
|
||||
return g.httpFn(cfg)
|
||||
}
|
||||
|
||||
func (splitOption) private() {}
|
||||
|
||||
func newSplitOption(httpFn func(cfg Config) Config, grpcFn func(cfg Config) Config) GenericOption {
|
||||
return &splitOption{httpFn: httpFn, grpcFn: grpcFn}
|
||||
}
|
||||
|
||||
// httpOption is an option that is only applied to the HTTP driver.
|
||||
type httpOption struct {
|
||||
fn func(Config) Config
|
||||
}
|
||||
|
||||
func (h *httpOption) ApplyHTTPOption(cfg Config) Config {
|
||||
return h.fn(cfg)
|
||||
}
|
||||
|
||||
func (httpOption) private() {}
|
||||
|
||||
func NewHTTPOption(fn func(cfg Config) Config) HTTPOption {
|
||||
return &httpOption{fn: fn}
|
||||
}
|
||||
|
||||
// grpcOption is an option that is only applied to the gRPC driver.
|
||||
type grpcOption struct {
|
||||
fn func(Config) Config
|
||||
}
|
||||
|
||||
func (h *grpcOption) ApplyGRPCOption(cfg Config) Config {
|
||||
return h.fn(cfg)
|
||||
}
|
||||
|
||||
func (grpcOption) private() {}
|
||||
|
||||
func NewGRPCOption(fn func(cfg Config) Config) GRPCOption {
|
||||
return &grpcOption{fn: fn}
|
||||
}
|
||||
|
||||
// Generic Options
|
||||
|
||||
func WithEndpoint(endpoint string) GenericOption {
|
||||
return newGenericOption(func(cfg Config) Config {
|
||||
cfg.Metrics.Endpoint = endpoint
|
||||
return cfg
|
||||
})
|
||||
}
|
||||
|
||||
func WithEndpointURL(v string) GenericOption {
|
||||
return newGenericOption(func(cfg Config) Config {
|
||||
u, err := url.Parse(v)
|
||||
if err != nil {
|
||||
global.Error(err, "otlpmetric: parse endpoint url", "url", v)
|
||||
return cfg
|
||||
}
|
||||
|
||||
cfg.Metrics.Endpoint = u.Host
|
||||
cfg.Metrics.URLPath = u.Path
|
||||
cfg.Metrics.Insecure = u.Scheme != "https"
|
||||
|
||||
return cfg
|
||||
})
|
||||
}
|
||||
|
||||
func WithCompression(compression Compression) GenericOption {
|
||||
return newGenericOption(func(cfg Config) Config {
|
||||
cfg.Metrics.Compression = compression
|
||||
return cfg
|
||||
})
|
||||
}
|
||||
|
||||
func WithURLPath(urlPath string) GenericOption {
|
||||
return newGenericOption(func(cfg Config) Config {
|
||||
cfg.Metrics.URLPath = urlPath
|
||||
return cfg
|
||||
})
|
||||
}
|
||||
|
||||
func WithRetry(rc retry.Config) GenericOption {
|
||||
return newGenericOption(func(cfg Config) Config {
|
||||
cfg.RetryConfig = rc
|
||||
return cfg
|
||||
})
|
||||
}
|
||||
|
||||
func WithTLSClientConfig(tlsCfg *tls.Config) GenericOption {
|
||||
return newSplitOption(func(cfg Config) Config {
|
||||
cfg.Metrics.TLSCfg = tlsCfg.Clone()
|
||||
return cfg
|
||||
}, func(cfg Config) Config {
|
||||
cfg.Metrics.GRPCCredentials = credentials.NewTLS(tlsCfg)
|
||||
return cfg
|
||||
})
|
||||
}
|
||||
|
||||
func WithInsecure() GenericOption {
|
||||
return newGenericOption(func(cfg Config) Config {
|
||||
cfg.Metrics.Insecure = true
|
||||
return cfg
|
||||
})
|
||||
}
|
||||
|
||||
func WithSecure() GenericOption {
|
||||
return newGenericOption(func(cfg Config) Config {
|
||||
cfg.Metrics.Insecure = false
|
||||
return cfg
|
||||
})
|
||||
}
|
||||
|
||||
func WithHeaders(headers map[string]string) GenericOption {
|
||||
return newGenericOption(func(cfg Config) Config {
|
||||
cfg.Metrics.Headers = headers
|
||||
return cfg
|
||||
})
|
||||
}
|
||||
|
||||
func WithTimeout(duration time.Duration) GenericOption {
|
||||
return newGenericOption(func(cfg Config) Config {
|
||||
cfg.Metrics.Timeout = duration
|
||||
return cfg
|
||||
})
|
||||
}
|
||||
|
||||
func WithTemporalitySelector(selector metric.TemporalitySelector) GenericOption {
|
||||
return newGenericOption(func(cfg Config) Config {
|
||||
cfg.Metrics.TemporalitySelector = selector
|
||||
return cfg
|
||||
})
|
||||
}
|
||||
|
||||
func WithAggregationSelector(selector metric.AggregationSelector) GenericOption {
|
||||
return newGenericOption(func(cfg Config) Config {
|
||||
cfg.Metrics.AggregationSelector = selector
|
||||
return cfg
|
||||
})
|
||||
}
|
||||
|
||||
func WithProxy(pf HTTPTransportProxyFunc) GenericOption {
|
||||
return newGenericOption(func(cfg Config) Config {
|
||||
cfg.Metrics.Proxy = pf
|
||||
return cfg
|
||||
})
|
||||
}
|
47
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/oconf/optiontypes.go
generated
vendored
Normal file
47
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/oconf/optiontypes.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/otlpmetric/oconf/optiontypes.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package oconf // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/oconf"
|
||||
|
||||
import "time"
|
||||
|
||||
const (
|
||||
// DefaultCollectorGRPCPort is the default gRPC port of the collector.
|
||||
DefaultCollectorGRPCPort uint16 = 4317
|
||||
// DefaultCollectorHTTPPort is the default HTTP port of the collector.
|
||||
DefaultCollectorHTTPPort uint16 = 4318
|
||||
// DefaultCollectorHost is the host address the Exporter will attempt
|
||||
// connect to if no collector address is provided.
|
||||
DefaultCollectorHost string = "localhost"
|
||||
)
|
||||
|
||||
// Compression describes the compression used for payloads sent to the
|
||||
// collector.
|
||||
type Compression int
|
||||
|
||||
const (
|
||||
// NoCompression tells the driver to send payloads without
|
||||
// compression.
|
||||
NoCompression Compression = iota
|
||||
// GzipCompression tells the driver to send payloads after
|
||||
// compressing them with gzip.
|
||||
GzipCompression
|
||||
)
|
||||
|
||||
// RetrySettings defines configuration for retrying batches in case of export failure
|
||||
// using an exponential backoff.
|
||||
type RetrySettings struct {
|
||||
// Enabled indicates whether to not retry sending batches in case of export failure.
|
||||
Enabled bool
|
||||
// InitialInterval the time to wait after the first failure before retrying.
|
||||
InitialInterval time.Duration
|
||||
// MaxInterval is the upper bound on backoff interval. Once this value is reached the delay between
|
||||
// consecutive retries will always be `MaxInterval`.
|
||||
MaxInterval time.Duration
|
||||
// MaxElapsedTime is the maximum amount of time (including retries) spent trying to send a request/batch.
|
||||
// Once this value is reached, the data is discarded.
|
||||
MaxElapsedTime time.Duration
|
||||
}
|
38
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/oconf/tls.go
generated
vendored
Normal file
38
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/oconf/tls.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/otlpmetric/oconf/tls.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package oconf // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/oconf"
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
// ReadTLSConfigFromFile reads a PEM certificate file and creates
|
||||
// a tls.Config that will use this certificate to verify a server certificate.
|
||||
func ReadTLSConfigFromFile(path string) (*tls.Config, error) {
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return CreateTLSConfig(b)
|
||||
}
|
||||
|
||||
// CreateTLSConfig creates a tls.Config from a raw certificate bytes
|
||||
// to verify a server certificate.
|
||||
func CreateTLSConfig(certBytes []byte) (*tls.Config, error) {
|
||||
cp := x509.NewCertPool()
|
||||
if ok := cp.AppendCertsFromPEM(certBytes); !ok {
|
||||
return nil, errors.New("failed to append certificate to the cert pool")
|
||||
}
|
||||
|
||||
return &tls.Config{
|
||||
RootCAs: cp,
|
||||
}, nil
|
||||
}
|
56
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/partialsuccess.go
generated
vendored
Normal file
56
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/partialsuccess.go
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/partialsuccess.go
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package internal // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal"
|
||||
|
||||
import "fmt"
|
||||
|
||||
// PartialSuccess represents the underlying error for all handling
|
||||
// OTLP partial success messages. Use `errors.Is(err,
|
||||
// PartialSuccess{})` to test whether an error passed to the OTel
|
||||
// error handler belongs to this category.
|
||||
type PartialSuccess struct {
|
||||
ErrorMessage string
|
||||
RejectedItems int64
|
||||
RejectedKind string
|
||||
}
|
||||
|
||||
var _ error = PartialSuccess{}
|
||||
|
||||
// Error implements the error interface.
|
||||
func (ps PartialSuccess) Error() string {
|
||||
msg := ps.ErrorMessage
|
||||
if msg == "" {
|
||||
msg = "empty message"
|
||||
}
|
||||
return fmt.Sprintf("OTLP partial success: %s (%d %s rejected)", msg, ps.RejectedItems, ps.RejectedKind)
|
||||
}
|
||||
|
||||
// Is supports the errors.Is() interface.
|
||||
func (ps PartialSuccess) Is(err error) bool {
|
||||
_, ok := err.(PartialSuccess)
|
||||
return ok
|
||||
}
|
||||
|
||||
// TracePartialSuccessError returns an error describing a partial success
|
||||
// response for the trace signal.
|
||||
func TracePartialSuccessError(itemsRejected int64, errorMessage string) error {
|
||||
return PartialSuccess{
|
||||
ErrorMessage: errorMessage,
|
||||
RejectedItems: itemsRejected,
|
||||
RejectedKind: "spans",
|
||||
}
|
||||
}
|
||||
|
||||
// MetricPartialSuccessError returns an error describing a partial success
|
||||
// response for the metric signal.
|
||||
func MetricPartialSuccessError(itemsRejected int64, errorMessage string) error {
|
||||
return PartialSuccess{
|
||||
ErrorMessage: errorMessage,
|
||||
RejectedItems: itemsRejected,
|
||||
RejectedKind: "metric data points",
|
||||
}
|
||||
}
|
145
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/retry/retry.go
generated
vendored
Normal file
145
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/retry/retry.go
generated
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/retry/retry.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package retry provides request retry functionality that can perform
|
||||
// configurable exponential backoff for transient errors and honor any
|
||||
// explicit throttle responses received.
|
||||
package retry // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/retry"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/cenkalti/backoff/v4"
|
||||
)
|
||||
|
||||
// DefaultConfig are the recommended defaults to use.
|
||||
var DefaultConfig = Config{
|
||||
Enabled: true,
|
||||
InitialInterval: 5 * time.Second,
|
||||
MaxInterval: 30 * time.Second,
|
||||
MaxElapsedTime: time.Minute,
|
||||
}
|
||||
|
||||
// Config defines configuration for retrying batches in case of export failure
|
||||
// using an exponential backoff.
|
||||
type Config struct {
|
||||
// Enabled indicates whether to not retry sending batches in case of
|
||||
// export failure.
|
||||
Enabled bool
|
||||
// InitialInterval the time to wait after the first failure before
|
||||
// retrying.
|
||||
InitialInterval time.Duration
|
||||
// MaxInterval is the upper bound on backoff interval. Once this value is
|
||||
// reached the delay between consecutive retries will always be
|
||||
// `MaxInterval`.
|
||||
MaxInterval time.Duration
|
||||
// MaxElapsedTime is the maximum amount of time (including retries) spent
|
||||
// trying to send a request/batch. Once this value is reached, the data
|
||||
// is discarded.
|
||||
MaxElapsedTime time.Duration
|
||||
}
|
||||
|
||||
// RequestFunc wraps a request with retry logic.
|
||||
type RequestFunc func(context.Context, func(context.Context) error) error
|
||||
|
||||
// EvaluateFunc returns if an error is retry-able and if an explicit throttle
|
||||
// duration should be honored that was included in the error.
|
||||
//
|
||||
// The function must return true if the error argument is retry-able,
|
||||
// otherwise it must return false for the first return parameter.
|
||||
//
|
||||
// The function must return a non-zero time.Duration if the error contains
|
||||
// explicit throttle duration that should be honored, otherwise it must return
|
||||
// a zero valued time.Duration.
|
||||
type EvaluateFunc func(error) (bool, time.Duration)
|
||||
|
||||
// RequestFunc returns a RequestFunc using the evaluate function to determine
|
||||
// if requests can be retried and based on the exponential backoff
|
||||
// configuration of c.
|
||||
func (c Config) RequestFunc(evaluate EvaluateFunc) RequestFunc {
|
||||
if !c.Enabled {
|
||||
return func(ctx context.Context, fn func(context.Context) error) error {
|
||||
return fn(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
return func(ctx context.Context, fn func(context.Context) error) error {
|
||||
// Do not use NewExponentialBackOff since it calls Reset and the code here
|
||||
// must call Reset after changing the InitialInterval (this saves an
|
||||
// unnecessary call to Now).
|
||||
b := &backoff.ExponentialBackOff{
|
||||
InitialInterval: c.InitialInterval,
|
||||
RandomizationFactor: backoff.DefaultRandomizationFactor,
|
||||
Multiplier: backoff.DefaultMultiplier,
|
||||
MaxInterval: c.MaxInterval,
|
||||
MaxElapsedTime: c.MaxElapsedTime,
|
||||
Stop: backoff.Stop,
|
||||
Clock: backoff.SystemClock,
|
||||
}
|
||||
b.Reset()
|
||||
|
||||
for {
|
||||
err := fn(ctx)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
retryable, throttle := evaluate(err)
|
||||
if !retryable {
|
||||
return err
|
||||
}
|
||||
|
||||
bOff := b.NextBackOff()
|
||||
if bOff == backoff.Stop {
|
||||
return fmt.Errorf("max retry time elapsed: %w", err)
|
||||
}
|
||||
|
||||
// Wait for the greater of the backoff or throttle delay.
|
||||
var delay time.Duration
|
||||
if bOff > throttle {
|
||||
delay = bOff
|
||||
} else {
|
||||
elapsed := b.GetElapsedTime()
|
||||
if b.MaxElapsedTime != 0 && elapsed+throttle > b.MaxElapsedTime {
|
||||
return fmt.Errorf("max retry time would elapse: %w", err)
|
||||
}
|
||||
delay = throttle
|
||||
}
|
||||
|
||||
if ctxErr := waitFunc(ctx, delay); ctxErr != nil {
|
||||
return fmt.Errorf("%w: %w", ctxErr, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allow override for testing.
|
||||
var waitFunc = wait
|
||||
|
||||
// wait takes the caller's context, and the amount of time to wait. It will
|
||||
// return nil if the timer fires before or at the same time as the context's
|
||||
// deadline. This indicates that the call can be retried.
|
||||
func wait(ctx context.Context, delay time.Duration) error {
|
||||
timer := time.NewTimer(delay)
|
||||
defer timer.Stop()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// Handle the case where the timer and context deadline end
|
||||
// simultaneously by prioritizing the timer expiration nil value
|
||||
// response.
|
||||
select {
|
||||
case <-timer.C:
|
||||
default:
|
||||
return ctx.Err()
|
||||
}
|
||||
case <-timer.C:
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
144
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/transform/attribute.go
generated
vendored
Normal file
144
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/transform/attribute.go
generated
vendored
Normal file
@@ -0,0 +1,144 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/otlpmetric/transform/attribute.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package transform // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/transform"
|
||||
|
||||
import (
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
cpb "go.opentelemetry.io/proto/otlp/common/v1"
|
||||
)
|
||||
|
||||
// AttrIter transforms an attribute iterator into OTLP key-values.
|
||||
func AttrIter(iter attribute.Iterator) []*cpb.KeyValue {
|
||||
l := iter.Len()
|
||||
if l == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
out := make([]*cpb.KeyValue, 0, l)
|
||||
for iter.Next() {
|
||||
out = append(out, KeyValue(iter.Attribute()))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// KeyValues transforms a slice of attribute KeyValues into OTLP key-values.
|
||||
func KeyValues(attrs []attribute.KeyValue) []*cpb.KeyValue {
|
||||
if len(attrs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
out := make([]*cpb.KeyValue, 0, len(attrs))
|
||||
for _, kv := range attrs {
|
||||
out = append(out, KeyValue(kv))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// KeyValue transforms an attribute KeyValue into an OTLP key-value.
|
||||
func KeyValue(kv attribute.KeyValue) *cpb.KeyValue {
|
||||
return &cpb.KeyValue{Key: string(kv.Key), Value: Value(kv.Value)}
|
||||
}
|
||||
|
||||
// Value transforms an attribute Value into an OTLP AnyValue.
|
||||
func Value(v attribute.Value) *cpb.AnyValue {
|
||||
av := new(cpb.AnyValue)
|
||||
switch v.Type() {
|
||||
case attribute.BOOL:
|
||||
av.Value = &cpb.AnyValue_BoolValue{
|
||||
BoolValue: v.AsBool(),
|
||||
}
|
||||
case attribute.BOOLSLICE:
|
||||
av.Value = &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: boolSliceValues(v.AsBoolSlice()),
|
||||
},
|
||||
}
|
||||
case attribute.INT64:
|
||||
av.Value = &cpb.AnyValue_IntValue{
|
||||
IntValue: v.AsInt64(),
|
||||
}
|
||||
case attribute.INT64SLICE:
|
||||
av.Value = &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: int64SliceValues(v.AsInt64Slice()),
|
||||
},
|
||||
}
|
||||
case attribute.FLOAT64:
|
||||
av.Value = &cpb.AnyValue_DoubleValue{
|
||||
DoubleValue: v.AsFloat64(),
|
||||
}
|
||||
case attribute.FLOAT64SLICE:
|
||||
av.Value = &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: float64SliceValues(v.AsFloat64Slice()),
|
||||
},
|
||||
}
|
||||
case attribute.STRING:
|
||||
av.Value = &cpb.AnyValue_StringValue{
|
||||
StringValue: v.AsString(),
|
||||
}
|
||||
case attribute.STRINGSLICE:
|
||||
av.Value = &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: stringSliceValues(v.AsStringSlice()),
|
||||
},
|
||||
}
|
||||
default:
|
||||
av.Value = &cpb.AnyValue_StringValue{
|
||||
StringValue: "INVALID",
|
||||
}
|
||||
}
|
||||
return av
|
||||
}
|
||||
|
||||
func boolSliceValues(vals []bool) []*cpb.AnyValue {
|
||||
converted := make([]*cpb.AnyValue, len(vals))
|
||||
for i, v := range vals {
|
||||
converted[i] = &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_BoolValue{
|
||||
BoolValue: v,
|
||||
},
|
||||
}
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
||||
func int64SliceValues(vals []int64) []*cpb.AnyValue {
|
||||
converted := make([]*cpb.AnyValue, len(vals))
|
||||
for i, v := range vals {
|
||||
converted[i] = &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_IntValue{
|
||||
IntValue: v,
|
||||
},
|
||||
}
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
||||
func float64SliceValues(vals []float64) []*cpb.AnyValue {
|
||||
converted := make([]*cpb.AnyValue, len(vals))
|
||||
for i, v := range vals {
|
||||
converted[i] = &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_DoubleValue{
|
||||
DoubleValue: v,
|
||||
},
|
||||
}
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
||||
func stringSliceValues(vals []string) []*cpb.AnyValue {
|
||||
converted := make([]*cpb.AnyValue, len(vals))
|
||||
for i, v := range vals {
|
||||
converted[i] = &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_StringValue{
|
||||
StringValue: v,
|
||||
},
|
||||
}
|
||||
}
|
||||
return converted
|
||||
}
|
103
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/transform/error.go
generated
vendored
Normal file
103
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/transform/error.go
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/otlpmetric/transform/error.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package transform // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/transform"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
mpb "go.opentelemetry.io/proto/otlp/metrics/v1"
|
||||
)
|
||||
|
||||
var (
|
||||
errUnknownAggregation = errors.New("unknown aggregation")
|
||||
errUnknownTemporality = errors.New("unknown temporality")
|
||||
)
|
||||
|
||||
type errMetric struct {
|
||||
m *mpb.Metric
|
||||
err error
|
||||
}
|
||||
|
||||
func (e errMetric) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
func (e errMetric) Error() string {
|
||||
format := "invalid metric (name: %q, description: %q, unit: %q): %s"
|
||||
return fmt.Sprintf(format, e.m.Name, e.m.Description, e.m.Unit, e.err)
|
||||
}
|
||||
|
||||
func (e errMetric) Is(target error) bool {
|
||||
return errors.Is(e.err, target)
|
||||
}
|
||||
|
||||
// multiErr is used by the data-type transform functions to wrap multiple
|
||||
// errors into a single return value. The error message will show all errors
|
||||
// as a list and scope them by the datatype name that is returning them.
|
||||
type multiErr struct {
|
||||
datatype string
|
||||
errs []error
|
||||
}
|
||||
|
||||
// errOrNil returns nil if e contains no errors, otherwise it returns e.
|
||||
func (e *multiErr) errOrNil() error {
|
||||
if len(e.errs) == 0 {
|
||||
return nil
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// append adds err to e. If err is a multiErr, its errs are flattened into e.
|
||||
func (e *multiErr) append(err error) {
|
||||
// Do not use errors.As here, this should only be flattened one layer. If
|
||||
// there is a *multiErr several steps down the chain, all the errors above
|
||||
// it will be discarded if errors.As is used instead.
|
||||
switch other := err.(type) { //nolint:errorlint
|
||||
case *multiErr:
|
||||
// Flatten err errors into e.
|
||||
e.errs = append(e.errs, other.errs...)
|
||||
default:
|
||||
e.errs = append(e.errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *multiErr) Error() string {
|
||||
es := make([]string, len(e.errs))
|
||||
for i, err := range e.errs {
|
||||
es[i] = fmt.Sprintf("* %s", err)
|
||||
}
|
||||
|
||||
format := "%d errors occurred transforming %s:\n\t%s"
|
||||
return fmt.Sprintf(format, len(es), e.datatype, strings.Join(es, "\n\t"))
|
||||
}
|
||||
|
||||
func (e *multiErr) Unwrap() error {
|
||||
switch len(e.errs) {
|
||||
case 0:
|
||||
return nil
|
||||
case 1:
|
||||
return e.errs[0]
|
||||
}
|
||||
|
||||
// Return a multiErr without the leading error.
|
||||
cp := &multiErr{
|
||||
datatype: e.datatype,
|
||||
errs: make([]error, len(e.errs)-1),
|
||||
}
|
||||
copy(cp.errs, e.errs[1:])
|
||||
return cp
|
||||
}
|
||||
|
||||
func (e *multiErr) Is(target error) bool {
|
||||
if len(e.errs) == 0 {
|
||||
return false
|
||||
}
|
||||
// Check if the first error is target.
|
||||
return errors.Is(e.errs[0], target)
|
||||
}
|
350
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/transform/metricdata.go
generated
vendored
Normal file
350
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/transform/metricdata.go
generated
vendored
Normal file
@@ -0,0 +1,350 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/otlpmetric/transform/metricdata.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package transform provides transformation functionality from the
|
||||
// sdk/metric/metricdata data-types into OTLP data-types.
|
||||
package transform // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/transform"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/sdk/metric/metricdata"
|
||||
cpb "go.opentelemetry.io/proto/otlp/common/v1"
|
||||
mpb "go.opentelemetry.io/proto/otlp/metrics/v1"
|
||||
rpb "go.opentelemetry.io/proto/otlp/resource/v1"
|
||||
)
|
||||
|
||||
// ResourceMetrics returns an OTLP ResourceMetrics generated from rm. If rm
|
||||
// contains invalid ScopeMetrics, an error will be returned along with an OTLP
|
||||
// ResourceMetrics that contains partial OTLP ScopeMetrics.
|
||||
func ResourceMetrics(rm *metricdata.ResourceMetrics) (*mpb.ResourceMetrics, error) {
|
||||
sms, err := ScopeMetrics(rm.ScopeMetrics)
|
||||
return &mpb.ResourceMetrics{
|
||||
Resource: &rpb.Resource{
|
||||
Attributes: AttrIter(rm.Resource.Iter()),
|
||||
},
|
||||
ScopeMetrics: sms,
|
||||
SchemaUrl: rm.Resource.SchemaURL(),
|
||||
}, err
|
||||
}
|
||||
|
||||
// ScopeMetrics returns a slice of OTLP ScopeMetrics generated from sms. If
|
||||
// sms contains invalid metric values, an error will be returned along with a
|
||||
// slice that contains partial OTLP ScopeMetrics.
|
||||
func ScopeMetrics(sms []metricdata.ScopeMetrics) ([]*mpb.ScopeMetrics, error) {
|
||||
errs := &multiErr{datatype: "ScopeMetrics"}
|
||||
out := make([]*mpb.ScopeMetrics, 0, len(sms))
|
||||
for _, sm := range sms {
|
||||
ms, err := Metrics(sm.Metrics)
|
||||
if err != nil {
|
||||
errs.append(err)
|
||||
}
|
||||
|
||||
out = append(out, &mpb.ScopeMetrics{
|
||||
Scope: &cpb.InstrumentationScope{
|
||||
Name: sm.Scope.Name,
|
||||
Version: sm.Scope.Version,
|
||||
Attributes: AttrIter(sm.Scope.Attributes.Iter()),
|
||||
},
|
||||
Metrics: ms,
|
||||
SchemaUrl: sm.Scope.SchemaURL,
|
||||
})
|
||||
}
|
||||
return out, errs.errOrNil()
|
||||
}
|
||||
|
||||
// Metrics returns a slice of OTLP Metric generated from ms. If ms contains
|
||||
// invalid metric values, an error will be returned along with a slice that
|
||||
// contains partial OTLP Metrics.
|
||||
func Metrics(ms []metricdata.Metrics) ([]*mpb.Metric, error) {
|
||||
errs := &multiErr{datatype: "Metrics"}
|
||||
out := make([]*mpb.Metric, 0, len(ms))
|
||||
for _, m := range ms {
|
||||
o, err := metric(m)
|
||||
if err != nil {
|
||||
// Do not include invalid data. Drop the metric, report the error.
|
||||
errs.append(errMetric{m: o, err: err})
|
||||
continue
|
||||
}
|
||||
out = append(out, o)
|
||||
}
|
||||
return out, errs.errOrNil()
|
||||
}
|
||||
|
||||
func metric(m metricdata.Metrics) (*mpb.Metric, error) {
|
||||
var err error
|
||||
out := &mpb.Metric{
|
||||
Name: m.Name,
|
||||
Description: m.Description,
|
||||
Unit: m.Unit,
|
||||
}
|
||||
switch a := m.Data.(type) {
|
||||
case metricdata.Gauge[int64]:
|
||||
out.Data = Gauge(a)
|
||||
case metricdata.Gauge[float64]:
|
||||
out.Data = Gauge(a)
|
||||
case metricdata.Sum[int64]:
|
||||
out.Data, err = Sum(a)
|
||||
case metricdata.Sum[float64]:
|
||||
out.Data, err = Sum(a)
|
||||
case metricdata.Histogram[int64]:
|
||||
out.Data, err = Histogram(a)
|
||||
case metricdata.Histogram[float64]:
|
||||
out.Data, err = Histogram(a)
|
||||
case metricdata.ExponentialHistogram[int64]:
|
||||
out.Data, err = ExponentialHistogram(a)
|
||||
case metricdata.ExponentialHistogram[float64]:
|
||||
out.Data, err = ExponentialHistogram(a)
|
||||
case metricdata.Summary:
|
||||
out.Data = Summary(a)
|
||||
default:
|
||||
return out, fmt.Errorf("%w: %T", errUnknownAggregation, a)
|
||||
}
|
||||
return out, err
|
||||
}
|
||||
|
||||
// Gauge returns an OTLP Metric_Gauge generated from g.
|
||||
func Gauge[N int64 | float64](g metricdata.Gauge[N]) *mpb.Metric_Gauge {
|
||||
return &mpb.Metric_Gauge{
|
||||
Gauge: &mpb.Gauge{
|
||||
DataPoints: DataPoints(g.DataPoints),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Sum returns an OTLP Metric_Sum generated from s. An error is returned
|
||||
// if the temporality of s is unknown.
|
||||
func Sum[N int64 | float64](s metricdata.Sum[N]) (*mpb.Metric_Sum, error) {
|
||||
t, err := Temporality(s.Temporality)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mpb.Metric_Sum{
|
||||
Sum: &mpb.Sum{
|
||||
AggregationTemporality: t,
|
||||
IsMonotonic: s.IsMonotonic,
|
||||
DataPoints: DataPoints(s.DataPoints),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DataPoints returns a slice of OTLP NumberDataPoint generated from dPts.
|
||||
func DataPoints[N int64 | float64](dPts []metricdata.DataPoint[N]) []*mpb.NumberDataPoint {
|
||||
out := make([]*mpb.NumberDataPoint, 0, len(dPts))
|
||||
for _, dPt := range dPts {
|
||||
ndp := &mpb.NumberDataPoint{
|
||||
Attributes: AttrIter(dPt.Attributes.Iter()),
|
||||
StartTimeUnixNano: timeUnixNano(dPt.StartTime),
|
||||
TimeUnixNano: timeUnixNano(dPt.Time),
|
||||
Exemplars: Exemplars(dPt.Exemplars),
|
||||
}
|
||||
switch v := any(dPt.Value).(type) {
|
||||
case int64:
|
||||
ndp.Value = &mpb.NumberDataPoint_AsInt{
|
||||
AsInt: v,
|
||||
}
|
||||
case float64:
|
||||
ndp.Value = &mpb.NumberDataPoint_AsDouble{
|
||||
AsDouble: v,
|
||||
}
|
||||
}
|
||||
out = append(out, ndp)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Histogram returns an OTLP Metric_Histogram generated from h. An error is
|
||||
// returned if the temporality of h is unknown.
|
||||
func Histogram[N int64 | float64](h metricdata.Histogram[N]) (*mpb.Metric_Histogram, error) {
|
||||
t, err := Temporality(h.Temporality)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mpb.Metric_Histogram{
|
||||
Histogram: &mpb.Histogram{
|
||||
AggregationTemporality: t,
|
||||
DataPoints: HistogramDataPoints(h.DataPoints),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// HistogramDataPoints returns a slice of OTLP HistogramDataPoint generated
|
||||
// from dPts.
|
||||
func HistogramDataPoints[N int64 | float64](dPts []metricdata.HistogramDataPoint[N]) []*mpb.HistogramDataPoint {
|
||||
out := make([]*mpb.HistogramDataPoint, 0, len(dPts))
|
||||
for _, dPt := range dPts {
|
||||
sum := float64(dPt.Sum)
|
||||
hdp := &mpb.HistogramDataPoint{
|
||||
Attributes: AttrIter(dPt.Attributes.Iter()),
|
||||
StartTimeUnixNano: timeUnixNano(dPt.StartTime),
|
||||
TimeUnixNano: timeUnixNano(dPt.Time),
|
||||
Count: dPt.Count,
|
||||
Sum: &sum,
|
||||
BucketCounts: dPt.BucketCounts,
|
||||
ExplicitBounds: dPt.Bounds,
|
||||
Exemplars: Exemplars(dPt.Exemplars),
|
||||
}
|
||||
if v, ok := dPt.Min.Value(); ok {
|
||||
vF64 := float64(v)
|
||||
hdp.Min = &vF64
|
||||
}
|
||||
if v, ok := dPt.Max.Value(); ok {
|
||||
vF64 := float64(v)
|
||||
hdp.Max = &vF64
|
||||
}
|
||||
out = append(out, hdp)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// ExponentialHistogram returns an OTLP Metric_ExponentialHistogram generated from h. An error is
|
||||
// returned if the temporality of h is unknown.
|
||||
func ExponentialHistogram[N int64 | float64](h metricdata.ExponentialHistogram[N]) (*mpb.Metric_ExponentialHistogram, error) {
|
||||
t, err := Temporality(h.Temporality)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mpb.Metric_ExponentialHistogram{
|
||||
ExponentialHistogram: &mpb.ExponentialHistogram{
|
||||
AggregationTemporality: t,
|
||||
DataPoints: ExponentialHistogramDataPoints(h.DataPoints),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ExponentialHistogramDataPoints returns a slice of OTLP ExponentialHistogramDataPoint generated
|
||||
// from dPts.
|
||||
func ExponentialHistogramDataPoints[N int64 | float64](dPts []metricdata.ExponentialHistogramDataPoint[N]) []*mpb.ExponentialHistogramDataPoint {
|
||||
out := make([]*mpb.ExponentialHistogramDataPoint, 0, len(dPts))
|
||||
for _, dPt := range dPts {
|
||||
sum := float64(dPt.Sum)
|
||||
ehdp := &mpb.ExponentialHistogramDataPoint{
|
||||
Attributes: AttrIter(dPt.Attributes.Iter()),
|
||||
StartTimeUnixNano: timeUnixNano(dPt.StartTime),
|
||||
TimeUnixNano: timeUnixNano(dPt.Time),
|
||||
Count: dPt.Count,
|
||||
Sum: &sum,
|
||||
Scale: dPt.Scale,
|
||||
ZeroCount: dPt.ZeroCount,
|
||||
Exemplars: Exemplars(dPt.Exemplars),
|
||||
|
||||
Positive: ExponentialHistogramDataPointBuckets(dPt.PositiveBucket),
|
||||
Negative: ExponentialHistogramDataPointBuckets(dPt.NegativeBucket),
|
||||
}
|
||||
if v, ok := dPt.Min.Value(); ok {
|
||||
vF64 := float64(v)
|
||||
ehdp.Min = &vF64
|
||||
}
|
||||
if v, ok := dPt.Max.Value(); ok {
|
||||
vF64 := float64(v)
|
||||
ehdp.Max = &vF64
|
||||
}
|
||||
out = append(out, ehdp)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// ExponentialHistogramDataPointBuckets returns an OTLP ExponentialHistogramDataPoint_Buckets generated
|
||||
// from bucket.
|
||||
func ExponentialHistogramDataPointBuckets(bucket metricdata.ExponentialBucket) *mpb.ExponentialHistogramDataPoint_Buckets {
|
||||
return &mpb.ExponentialHistogramDataPoint_Buckets{
|
||||
Offset: bucket.Offset,
|
||||
BucketCounts: bucket.Counts,
|
||||
}
|
||||
}
|
||||
|
||||
// Temporality returns an OTLP AggregationTemporality generated from t. If t
|
||||
// is unknown, an error is returned along with the invalid
|
||||
// AggregationTemporality_AGGREGATION_TEMPORALITY_UNSPECIFIED.
|
||||
func Temporality(t metricdata.Temporality) (mpb.AggregationTemporality, error) {
|
||||
switch t {
|
||||
case metricdata.DeltaTemporality:
|
||||
return mpb.AggregationTemporality_AGGREGATION_TEMPORALITY_DELTA, nil
|
||||
case metricdata.CumulativeTemporality:
|
||||
return mpb.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE, nil
|
||||
default:
|
||||
err := fmt.Errorf("%w: %s", errUnknownTemporality, t)
|
||||
return mpb.AggregationTemporality_AGGREGATION_TEMPORALITY_UNSPECIFIED, err
|
||||
}
|
||||
}
|
||||
|
||||
// timeUnixNano returns t as a Unix time, the number of nanoseconds elapsed
|
||||
// since January 1, 1970 UTC as uint64.
|
||||
// The result is undefined if the Unix time
|
||||
// in nanoseconds cannot be represented by an int64
|
||||
// (a date before the year 1678 or after 2262).
|
||||
// timeUnixNano on the zero Time returns 0.
|
||||
// The result does not depend on the location associated with t.
|
||||
func timeUnixNano(t time.Time) uint64 {
|
||||
return uint64(max(0, t.UnixNano())) // nolint:gosec // Overflow checked.
|
||||
}
|
||||
|
||||
// Exemplars returns a slice of OTLP Exemplars generated from exemplars.
|
||||
func Exemplars[N int64 | float64](exemplars []metricdata.Exemplar[N]) []*mpb.Exemplar {
|
||||
out := make([]*mpb.Exemplar, 0, len(exemplars))
|
||||
for _, exemplar := range exemplars {
|
||||
e := &mpb.Exemplar{
|
||||
FilteredAttributes: KeyValues(exemplar.FilteredAttributes),
|
||||
TimeUnixNano: timeUnixNano(exemplar.Time),
|
||||
SpanId: exemplar.SpanID,
|
||||
TraceId: exemplar.TraceID,
|
||||
}
|
||||
switch v := any(exemplar.Value).(type) {
|
||||
case int64:
|
||||
e.Value = &mpb.Exemplar_AsInt{
|
||||
AsInt: v,
|
||||
}
|
||||
case float64:
|
||||
e.Value = &mpb.Exemplar_AsDouble{
|
||||
AsDouble: v,
|
||||
}
|
||||
}
|
||||
out = append(out, e)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Summary returns an OTLP Metric_Summary generated from s.
|
||||
func Summary(s metricdata.Summary) *mpb.Metric_Summary {
|
||||
return &mpb.Metric_Summary{
|
||||
Summary: &mpb.Summary{
|
||||
DataPoints: SummaryDataPoints(s.DataPoints),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// SummaryDataPoints returns a slice of OTLP SummaryDataPoint generated from
|
||||
// dPts.
|
||||
func SummaryDataPoints(dPts []metricdata.SummaryDataPoint) []*mpb.SummaryDataPoint {
|
||||
out := make([]*mpb.SummaryDataPoint, 0, len(dPts))
|
||||
for _, dPt := range dPts {
|
||||
sdp := &mpb.SummaryDataPoint{
|
||||
Attributes: AttrIter(dPt.Attributes.Iter()),
|
||||
StartTimeUnixNano: timeUnixNano(dPt.StartTime),
|
||||
TimeUnixNano: timeUnixNano(dPt.Time),
|
||||
Count: dPt.Count,
|
||||
Sum: dPt.Sum,
|
||||
QuantileValues: QuantileValues(dPt.QuantileValues),
|
||||
}
|
||||
out = append(out, sdp)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// QuantileValues returns a slice of OTLP SummaryDataPoint_ValueAtQuantile
|
||||
// generated from quantiles.
|
||||
func QuantileValues(quantiles []metricdata.QuantileValue) []*mpb.SummaryDataPoint_ValueAtQuantile {
|
||||
out := make([]*mpb.SummaryDataPoint_ValueAtQuantile, 0, len(quantiles))
|
||||
for _, q := range quantiles {
|
||||
quantile := &mpb.SummaryDataPoint_ValueAtQuantile{
|
||||
Quantile: q.Quantile,
|
||||
Value: q.Value,
|
||||
}
|
||||
out = append(out, quantile)
|
||||
}
|
||||
return out
|
||||
}
|
9
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/version.go
generated
vendored
Normal file
9
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/version.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otlpmetricgrpc // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
|
||||
|
||||
// Version is the current release version of the OpenTelemetry OTLP over gRPC metrics exporter in use.
|
||||
func Version() string {
|
||||
return "1.35.0"
|
||||
}
|
201
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/LICENSE
generated
vendored
Normal file
201
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
3
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/README.md
generated
vendored
Normal file
3
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/README.md
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# OTLP Metric HTTP Exporter
|
||||
|
||||
[](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp)
|
346
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/client.go
generated
vendored
Normal file
346
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/client.go
generated
vendored
Normal file
@@ -0,0 +1,346 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otlpmetrichttp // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/oconf"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/retry"
|
||||
colmetricpb "go.opentelemetry.io/proto/otlp/collector/metrics/v1"
|
||||
metricpb "go.opentelemetry.io/proto/otlp/metrics/v1"
|
||||
)
|
||||
|
||||
type client struct {
|
||||
// req is cloned for every upload the client makes.
|
||||
req *http.Request
|
||||
compression Compression
|
||||
requestFunc retry.RequestFunc
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
// Keep it in sync with golang's DefaultTransport from net/http! We
|
||||
// have our own copy to avoid handling a situation where the
|
||||
// DefaultTransport is overwritten with some different implementation
|
||||
// of http.RoundTripper or it's modified by another package.
|
||||
var ourTransport = &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).DialContext,
|
||||
ForceAttemptHTTP2: true,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
}
|
||||
|
||||
// newClient creates a new HTTP metric client.
|
||||
func newClient(cfg oconf.Config) (*client, error) {
|
||||
httpClient := &http.Client{
|
||||
Transport: ourTransport,
|
||||
Timeout: cfg.Metrics.Timeout,
|
||||
}
|
||||
|
||||
if cfg.Metrics.TLSCfg != nil || cfg.Metrics.Proxy != nil {
|
||||
clonedTransport := ourTransport.Clone()
|
||||
httpClient.Transport = clonedTransport
|
||||
|
||||
if cfg.Metrics.TLSCfg != nil {
|
||||
clonedTransport.TLSClientConfig = cfg.Metrics.TLSCfg
|
||||
}
|
||||
if cfg.Metrics.Proxy != nil {
|
||||
clonedTransport.Proxy = cfg.Metrics.Proxy
|
||||
}
|
||||
}
|
||||
|
||||
u := &url.URL{
|
||||
Scheme: "https",
|
||||
Host: cfg.Metrics.Endpoint,
|
||||
Path: cfg.Metrics.URLPath,
|
||||
}
|
||||
if cfg.Metrics.Insecure {
|
||||
u.Scheme = "http"
|
||||
}
|
||||
// Body is set when this is cloned during upload.
|
||||
req, err := http.NewRequest(http.MethodPost, u.String(), http.NoBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userAgent := "OTel Go OTLP over HTTP/protobuf metrics exporter/" + Version()
|
||||
req.Header.Set("User-Agent", userAgent)
|
||||
|
||||
if n := len(cfg.Metrics.Headers); n > 0 {
|
||||
for k, v := range cfg.Metrics.Headers {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/x-protobuf")
|
||||
|
||||
return &client{
|
||||
compression: Compression(cfg.Metrics.Compression),
|
||||
req: req,
|
||||
requestFunc: cfg.RetryConfig.RequestFunc(evaluate),
|
||||
httpClient: httpClient,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Shutdown shuts down the client, freeing all resources.
|
||||
func (c *client) Shutdown(ctx context.Context) error {
|
||||
// The otlpmetric.Exporter synchronizes access to client methods and
|
||||
// ensures this is called only once. The only thing that needs to be done
|
||||
// here is to release any computational resources the client holds.
|
||||
|
||||
c.requestFunc = nil
|
||||
c.httpClient = nil
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
// UploadMetrics sends protoMetrics to the connected endpoint.
|
||||
//
|
||||
// Retryable errors from the server will be handled according to any
|
||||
// RetryConfig the client was created with.
|
||||
func (c *client) UploadMetrics(ctx context.Context, protoMetrics *metricpb.ResourceMetrics) error {
|
||||
// The otlpmetric.Exporter synchronizes access to client methods, and
|
||||
// ensures this is not called after the Exporter is shutdown. Only thing
|
||||
// to do here is send data.
|
||||
|
||||
pbRequest := &colmetricpb.ExportMetricsServiceRequest{
|
||||
ResourceMetrics: []*metricpb.ResourceMetrics{protoMetrics},
|
||||
}
|
||||
body, err := proto.Marshal(pbRequest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
request, err := c.newRequest(ctx, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.requestFunc(ctx, func(iCtx context.Context) error {
|
||||
select {
|
||||
case <-iCtx.Done():
|
||||
return iCtx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
request.reset(iCtx)
|
||||
resp, err := c.httpClient.Do(request.Request)
|
||||
var urlErr *url.Error
|
||||
if errors.As(err, &urlErr) && urlErr.Temporary() {
|
||||
return newResponseError(http.Header{}, err)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp != nil && resp.Body != nil {
|
||||
defer func() {
|
||||
if err := resp.Body.Close(); err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if sc := resp.StatusCode; sc >= 200 && sc <= 299 {
|
||||
// Success, do not retry.
|
||||
|
||||
// Read the partial success message, if any.
|
||||
var respData bytes.Buffer
|
||||
if _, err := io.Copy(&respData, resp.Body); err != nil {
|
||||
return err
|
||||
}
|
||||
if respData.Len() == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if resp.Header.Get("Content-Type") == "application/x-protobuf" {
|
||||
var respProto colmetricpb.ExportMetricsServiceResponse
|
||||
if err := proto.Unmarshal(respData.Bytes(), &respProto); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if respProto.PartialSuccess != nil {
|
||||
msg := respProto.PartialSuccess.GetErrorMessage()
|
||||
n := respProto.PartialSuccess.GetRejectedDataPoints()
|
||||
if n != 0 || msg != "" {
|
||||
err := internal.MetricPartialSuccessError(n, msg)
|
||||
otel.Handle(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// Error cases.
|
||||
|
||||
// server may return a message with the response
|
||||
// body, so we read it to include in the error
|
||||
// message to be returned. It will help in
|
||||
// debugging the actual issue.
|
||||
var respData bytes.Buffer
|
||||
if _, err := io.Copy(&respData, resp.Body); err != nil {
|
||||
return err
|
||||
}
|
||||
respStr := strings.TrimSpace(respData.String())
|
||||
if len(respStr) == 0 {
|
||||
respStr = "(empty)"
|
||||
}
|
||||
bodyErr := fmt.Errorf("body: %s", respStr)
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusTooManyRequests,
|
||||
http.StatusBadGateway,
|
||||
http.StatusServiceUnavailable,
|
||||
http.StatusGatewayTimeout:
|
||||
// Retryable failure.
|
||||
return newResponseError(resp.Header, bodyErr)
|
||||
default:
|
||||
// Non-retryable failure.
|
||||
return fmt.Errorf("failed to send metrics to %s: %s (%w)", request.URL, resp.Status, bodyErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
var gzPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
w := gzip.NewWriter(io.Discard)
|
||||
return w
|
||||
},
|
||||
}
|
||||
|
||||
func (c *client) newRequest(ctx context.Context, body []byte) (request, error) {
|
||||
r := c.req.Clone(ctx)
|
||||
req := request{Request: r}
|
||||
|
||||
switch c.compression {
|
||||
case NoCompression:
|
||||
r.ContentLength = (int64)(len(body))
|
||||
req.bodyReader = bodyReader(body)
|
||||
case GzipCompression:
|
||||
// Ensure the content length is not used.
|
||||
r.ContentLength = -1
|
||||
r.Header.Set("Content-Encoding", "gzip")
|
||||
|
||||
gz := gzPool.Get().(*gzip.Writer)
|
||||
defer gzPool.Put(gz)
|
||||
|
||||
var b bytes.Buffer
|
||||
gz.Reset(&b)
|
||||
|
||||
if _, err := gz.Write(body); err != nil {
|
||||
return req, err
|
||||
}
|
||||
// Close needs to be called to ensure body is fully written.
|
||||
if err := gz.Close(); err != nil {
|
||||
return req, err
|
||||
}
|
||||
|
||||
req.bodyReader = bodyReader(b.Bytes())
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// bodyReader returns a closure returning a new reader for buf.
|
||||
func bodyReader(buf []byte) func() io.ReadCloser {
|
||||
return func() io.ReadCloser {
|
||||
return io.NopCloser(bytes.NewReader(buf))
|
||||
}
|
||||
}
|
||||
|
||||
// request wraps an http.Request with a resettable body reader.
|
||||
type request struct {
|
||||
*http.Request
|
||||
|
||||
// bodyReader allows the same body to be used for multiple requests.
|
||||
bodyReader func() io.ReadCloser
|
||||
}
|
||||
|
||||
// reset reinitializes the request Body and uses ctx for the request.
|
||||
func (r *request) reset(ctx context.Context) {
|
||||
r.Body = r.bodyReader()
|
||||
r.Request = r.Request.WithContext(ctx)
|
||||
}
|
||||
|
||||
// retryableError represents a request failure that can be retried.
|
||||
type retryableError struct {
|
||||
throttle int64
|
||||
err error
|
||||
}
|
||||
|
||||
// newResponseError returns a retryableError and will extract any explicit
|
||||
// throttle delay contained in headers. The returned error wraps wrapped
|
||||
// if it is not nil.
|
||||
func newResponseError(header http.Header, wrapped error) error {
|
||||
var rErr retryableError
|
||||
if v := header.Get("Retry-After"); v != "" {
|
||||
if t, err := strconv.ParseInt(v, 10, 64); err == nil {
|
||||
rErr.throttle = t
|
||||
}
|
||||
}
|
||||
|
||||
rErr.err = wrapped
|
||||
return rErr
|
||||
}
|
||||
|
||||
func (e retryableError) Error() string {
|
||||
if e.err != nil {
|
||||
return "retry-able request failure: " + e.err.Error()
|
||||
}
|
||||
|
||||
return "retry-able request failure"
|
||||
}
|
||||
|
||||
func (e retryableError) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
func (e retryableError) As(target interface{}) bool {
|
||||
if e.err == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
switch v := target.(type) {
|
||||
case **retryableError:
|
||||
*v = &e
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// evaluate returns if err is retry-able. If it is and it includes an explicit
|
||||
// throttling delay, that delay is also returned.
|
||||
func evaluate(err error) (bool, time.Duration) {
|
||||
if err == nil {
|
||||
return false, 0
|
||||
}
|
||||
|
||||
// Do not use errors.As here, this should only be flattened one layer. If
|
||||
// there are several chained errors, all the errors above it will be
|
||||
// discarded if errors.As is used instead.
|
||||
rErr, ok := err.(retryableError) //nolint:errorlint
|
||||
if !ok {
|
||||
return false, 0
|
||||
}
|
||||
|
||||
return true, time.Duration(rErr.throttle)
|
||||
}
|
224
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/config.go
generated
vendored
Normal file
224
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/config.go
generated
vendored
Normal file
@@ -0,0 +1,224 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otlpmetrichttp // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp"
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/oconf"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/retry"
|
||||
"go.opentelemetry.io/otel/sdk/metric"
|
||||
)
|
||||
|
||||
// Compression describes the compression used for payloads sent to the
|
||||
// collector.
|
||||
type Compression oconf.Compression
|
||||
|
||||
// HTTPTransportProxyFunc is a function that resolves which URL to use as proxy for a given request.
|
||||
// This type is compatible with http.Transport.Proxy and can be used to set a custom proxy function
|
||||
// to the OTLP HTTP client.
|
||||
type HTTPTransportProxyFunc func(*http.Request) (*url.URL, error)
|
||||
|
||||
const (
|
||||
// NoCompression tells the driver to send payloads without
|
||||
// compression.
|
||||
NoCompression = Compression(oconf.NoCompression)
|
||||
// GzipCompression tells the driver to send payloads after
|
||||
// compressing them with gzip.
|
||||
GzipCompression = Compression(oconf.GzipCompression)
|
||||
)
|
||||
|
||||
// Option applies an option to the Exporter.
|
||||
type Option interface {
|
||||
applyHTTPOption(oconf.Config) oconf.Config
|
||||
}
|
||||
|
||||
func asHTTPOptions(opts []Option) []oconf.HTTPOption {
|
||||
converted := make([]oconf.HTTPOption, len(opts))
|
||||
for i, o := range opts {
|
||||
converted[i] = oconf.NewHTTPOption(o.applyHTTPOption)
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
||||
// RetryConfig defines configuration for retrying the export of metric data
|
||||
// that failed.
|
||||
type RetryConfig retry.Config
|
||||
|
||||
type wrappedOption struct {
|
||||
oconf.HTTPOption
|
||||
}
|
||||
|
||||
func (w wrappedOption) applyHTTPOption(cfg oconf.Config) oconf.Config {
|
||||
return w.ApplyHTTPOption(cfg)
|
||||
}
|
||||
|
||||
// WithEndpoint sets the target endpoint the Exporter will connect to. This
|
||||
// endpoint is specified as a host and optional port, no path or scheme should
|
||||
// be included (see WithInsecure and WithURLPath).
|
||||
//
|
||||
// If the OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
|
||||
// environment variable is set, and this option is not passed, that variable
|
||||
// value will be used. If both environment variables are set,
|
||||
// OTEL_EXPORTER_OTLP_METRICS_ENDPOINT will take precedence. If an environment
|
||||
// variable is set, and this option is passed, this option will take precedence.
|
||||
//
|
||||
// By default, if an environment variable is not set, and this option is not
|
||||
// passed, "localhost:4318" will be used.
|
||||
func WithEndpoint(endpoint string) Option {
|
||||
return wrappedOption{oconf.WithEndpoint(endpoint)}
|
||||
}
|
||||
|
||||
// WithEndpointURL sets the target endpoint URL the Exporter will connect to.
|
||||
//
|
||||
// If the OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
|
||||
// environment variable is set, and this option is not passed, that variable
|
||||
// value will be used. If both environment variables are set,
|
||||
// OTEL_EXPORTER_OTLP_METRICS_ENDPOINT will take precedence. If an environment
|
||||
// variable is set, and this option is passed, this option will take precedence.
|
||||
//
|
||||
// If both this option and WithEndpoint are used, the last used option will
|
||||
// take precedence.
|
||||
//
|
||||
// If an invalid URL is provided, the default value will be kept.
|
||||
//
|
||||
// By default, if an environment variable is not set, and this option is not
|
||||
// passed, "localhost:4318" will be used.
|
||||
//
|
||||
// This option has no effect if WithGRPCConn is used.
|
||||
func WithEndpointURL(u string) Option {
|
||||
return wrappedOption{oconf.WithEndpointURL(u)}
|
||||
}
|
||||
|
||||
// WithCompression sets the compression strategy the Exporter will use to
|
||||
// compress the HTTP body.
|
||||
//
|
||||
// If the OTEL_EXPORTER_OTLP_COMPRESSION or
|
||||
// OTEL_EXPORTER_OTLP_METRICS_COMPRESSION environment variable is set, and
|
||||
// this option is not passed, that variable value will be used. That value can
|
||||
// be either "none" or "gzip". If both are set,
|
||||
// OTEL_EXPORTER_OTLP_METRICS_COMPRESSION will take precedence.
|
||||
//
|
||||
// By default, if an environment variable is not set, and this option is not
|
||||
// passed, no compression strategy will be used.
|
||||
func WithCompression(compression Compression) Option {
|
||||
return wrappedOption{oconf.WithCompression(oconf.Compression(compression))}
|
||||
}
|
||||
|
||||
// WithURLPath sets the URL path the Exporter will send requests to.
|
||||
//
|
||||
// If the OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
|
||||
// environment variable is set, and this option is not passed, the path
|
||||
// contained in that variable value will be used. If both are set,
|
||||
// OTEL_EXPORTER_OTLP_METRICS_ENDPOINT will take precedence.
|
||||
//
|
||||
// By default, if an environment variable is not set, and this option is not
|
||||
// passed, "/v1/metrics" will be used.
|
||||
func WithURLPath(urlPath string) Option {
|
||||
return wrappedOption{oconf.WithURLPath(urlPath)}
|
||||
}
|
||||
|
||||
// WithTLSClientConfig sets the TLS configuration the Exporter will use for
|
||||
// HTTP requests.
|
||||
//
|
||||
// If the OTEL_EXPORTER_OTLP_CERTIFICATE or
|
||||
// OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE environment variable is set, and
|
||||
// this option is not passed, that variable value will be used. The value will
|
||||
// be parsed the filepath of the TLS certificate chain to use. If both are
|
||||
// set, OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE will take precedence.
|
||||
//
|
||||
// By default, if an environment variable is not set, and this option is not
|
||||
// passed, the system default configuration is used.
|
||||
func WithTLSClientConfig(tlsCfg *tls.Config) Option {
|
||||
return wrappedOption{oconf.WithTLSClientConfig(tlsCfg)}
|
||||
}
|
||||
|
||||
// WithInsecure disables client transport security for the Exporter's HTTP
|
||||
// connection.
|
||||
//
|
||||
// If the OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
|
||||
// environment variable is set, and this option is not passed, that variable
|
||||
// value will be used to determine client security. If the endpoint has a
|
||||
// scheme of "http" or "unix" client security will be disabled. If both are
|
||||
// set, OTEL_EXPORTER_OTLP_METRICS_ENDPOINT will take precedence.
|
||||
//
|
||||
// By default, if an environment variable is not set, and this option is not
|
||||
// passed, client security will be used.
|
||||
func WithInsecure() Option {
|
||||
return wrappedOption{oconf.WithInsecure()}
|
||||
}
|
||||
|
||||
// WithHeaders will send the provided headers with each HTTP requests.
|
||||
//
|
||||
// If the OTEL_EXPORTER_OTLP_HEADERS or OTEL_EXPORTER_OTLP_METRICS_HEADERS
|
||||
// environment variable is set, and this option is not passed, that variable
|
||||
// value will be used. The value will be parsed as a list of key value pairs.
|
||||
// These pairs are expected to be in the W3C Correlation-Context format
|
||||
// without additional semi-colon delimited metadata (i.e. "k1=v1,k2=v2"). If
|
||||
// both are set, OTEL_EXPORTER_OTLP_METRICS_HEADERS will take precedence.
|
||||
//
|
||||
// By default, if an environment variable is not set, and this option is not
|
||||
// passed, no user headers will be set.
|
||||
func WithHeaders(headers map[string]string) Option {
|
||||
return wrappedOption{oconf.WithHeaders(headers)}
|
||||
}
|
||||
|
||||
// WithTimeout sets the max amount of time an Exporter will attempt an export.
|
||||
//
|
||||
// This takes precedence over any retry settings defined by WithRetry. Once
|
||||
// this time limit has been reached the export is abandoned and the metric
|
||||
// data is dropped.
|
||||
//
|
||||
// If the OTEL_EXPORTER_OTLP_TIMEOUT or OTEL_EXPORTER_OTLP_METRICS_TIMEOUT
|
||||
// environment variable is set, and this option is not passed, that variable
|
||||
// value will be used. The value will be parsed as an integer representing the
|
||||
// timeout in milliseconds. If both are set,
|
||||
// OTEL_EXPORTER_OTLP_METRICS_TIMEOUT will take precedence.
|
||||
//
|
||||
// By default, if an environment variable is not set, and this option is not
|
||||
// passed, a timeout of 10 seconds will be used.
|
||||
func WithTimeout(duration time.Duration) Option {
|
||||
return wrappedOption{oconf.WithTimeout(duration)}
|
||||
}
|
||||
|
||||
// WithRetry sets the retry policy for transient retryable errors that are
|
||||
// returned by the target endpoint.
|
||||
//
|
||||
// If the target endpoint responds with not only a retryable error, but
|
||||
// explicitly returns a backoff time in the response, that time will take
|
||||
// precedence over these settings.
|
||||
//
|
||||
// If unset, the default retry policy will be used. It will retry the export
|
||||
// 5 seconds after receiving a retryable error and increase exponentially
|
||||
// after each error for no more than a total time of 1 minute.
|
||||
func WithRetry(rc RetryConfig) Option {
|
||||
return wrappedOption{oconf.WithRetry(retry.Config(rc))}
|
||||
}
|
||||
|
||||
// WithTemporalitySelector sets the TemporalitySelector the client will use to
|
||||
// determine the Temporality of an instrument based on its kind. If this option
|
||||
// is not used, the client will use the DefaultTemporalitySelector from the
|
||||
// go.opentelemetry.io/otel/sdk/metric package.
|
||||
func WithTemporalitySelector(selector metric.TemporalitySelector) Option {
|
||||
return wrappedOption{oconf.WithTemporalitySelector(selector)}
|
||||
}
|
||||
|
||||
// WithAggregationSelector sets the AggregationSelector the client will use to
|
||||
// determine the aggregation to use for an instrument based on its kind. If
|
||||
// this option is not used, the reader will use the DefaultAggregationSelector
|
||||
// from the go.opentelemetry.io/otel/sdk/metric package, or the aggregation
|
||||
// explicitly passed for a view matching an instrument.
|
||||
func WithAggregationSelector(selector metric.AggregationSelector) Option {
|
||||
return wrappedOption{oconf.WithAggregationSelector(selector)}
|
||||
}
|
||||
|
||||
// WithProxy sets the Proxy function the client will use to determine the
|
||||
// proxy to use for an HTTP request. If this option is not used, the client
|
||||
// will use [http.ProxyFromEnvironment].
|
||||
func WithProxy(pf HTTPTransportProxyFunc) Option {
|
||||
return wrappedOption{oconf.WithProxy(oconf.HTTPTransportProxyFunc(pf))}
|
||||
}
|
82
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/doc.go
generated
vendored
Normal file
82
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/doc.go
generated
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/*
|
||||
Package otlpmetrichttp provides an OTLP metrics exporter using HTTP with protobuf payloads.
|
||||
By default the telemetry is sent to https://localhost:4318/v1/metrics.
|
||||
|
||||
Exporter should be created using [New] and used with a [metric.PeriodicReader].
|
||||
|
||||
The environment variables described below can be used for configuration.
|
||||
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT (default: "https://localhost:4318") -
|
||||
target base URL ("/v1/metrics" is appended) to which the exporter sends telemetry.
|
||||
The value must contain a scheme ("http" or "https") and host.
|
||||
The value may additionally contain a port and a path.
|
||||
The value should not contain a query string or fragment.
|
||||
The configuration can be overridden by OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
|
||||
environment variable and by [WithEndpoint], [WithEndpointURL], and [WithInsecure] options.
|
||||
|
||||
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT (default: "https://localhost:4318/v1/metrics") -
|
||||
target URL to which the exporter sends telemetry.
|
||||
The value must contain a scheme ("http" or "https") and host.
|
||||
The value may additionally contain a port and a path.
|
||||
The value should not contain a query string or fragment.
|
||||
The configuration can be overridden by [WithEndpoint], [WithEndpointURL], [WithInsecure], and [WithURLPath] options.
|
||||
|
||||
OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_METRICS_HEADERS (default: none) -
|
||||
key-value pairs used as headers associated with HTTP requests.
|
||||
The value is expected to be represented in a format matching the [W3C Baggage HTTP Header Content Format],
|
||||
except that additional semi-colon delimited metadata is not supported.
|
||||
Example value: "key1=value1,key2=value2".
|
||||
OTEL_EXPORTER_OTLP_METRICS_HEADERS takes precedence over OTEL_EXPORTER_OTLP_HEADERS.
|
||||
The configuration can be overridden by [WithHeaders] option.
|
||||
|
||||
OTEL_EXPORTER_OTLP_TIMEOUT, OTEL_EXPORTER_OTLP_METRICS_TIMEOUT (default: "10000") -
|
||||
maximum time in milliseconds the OTLP exporter waits for each batch export.
|
||||
OTEL_EXPORTER_OTLP_METRICS_TIMEOUT takes precedence over OTEL_EXPORTER_OTLP_TIMEOUT.
|
||||
The configuration can be overridden by [WithTimeout] option.
|
||||
|
||||
OTEL_EXPORTER_OTLP_COMPRESSION, OTEL_EXPORTER_OTLP_METRICS_COMPRESSION (default: none) -
|
||||
compression strategy the exporter uses to compress the HTTP body.
|
||||
Supported values: "gzip".
|
||||
OTEL_EXPORTER_OTLP_METRICS_COMPRESSION takes precedence over OTEL_EXPORTER_OTLP_COMPRESSION.
|
||||
The configuration can be overridden by [WithCompression] option.
|
||||
|
||||
OTEL_EXPORTER_OTLP_CERTIFICATE, OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE (default: none) -
|
||||
filepath to the trusted certificate to use when verifying a server's TLS credentials.
|
||||
OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE takes precedence over OTEL_EXPORTER_OTLP_CERTIFICATE.
|
||||
The configuration can be overridden by [WithTLSClientConfig] option.
|
||||
|
||||
OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE, OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE (default: none) -
|
||||
filepath to the client certificate/chain trust for client's private key to use in mTLS communication in PEM format.
|
||||
OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE takes precedence over OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE.
|
||||
The configuration can be overridden by [WithTLSClientConfig] option.
|
||||
|
||||
OTEL_EXPORTER_OTLP_CLIENT_KEY, OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY (default: none) -
|
||||
filepath to the client's private key to use in mTLS communication in PEM format.
|
||||
OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY takes precedence over OTEL_EXPORTER_OTLP_CLIENT_KEY.
|
||||
The configuration can be overridden by [WithTLSClientConfig] option.
|
||||
|
||||
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE (default: "cumulative") -
|
||||
aggregation temporality to use on the basis of instrument kind. Supported values:
|
||||
- "cumulative" - Cumulative aggregation temporality for all instrument kinds,
|
||||
- "delta" - Delta aggregation temporality for Counter, Asynchronous Counter and Histogram instrument kinds;
|
||||
Cumulative aggregation for UpDownCounter and Asynchronous UpDownCounter instrument kinds,
|
||||
- "lowmemory" - Delta aggregation temporality for Synchronous Counter and Histogram instrument kinds;
|
||||
Cumulative aggregation temporality for Synchronous UpDownCounter, Asynchronous Counter, and Asynchronous UpDownCounter instrument kinds.
|
||||
|
||||
The configuration can be overridden by [WithTemporalitySelector] option.
|
||||
|
||||
OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION (default: "explicit_bucket_histogram") -
|
||||
default aggregation to use for histogram instruments. Supported values:
|
||||
- "explicit_bucket_histogram" - [Explicit Bucket Histogram Aggregation],
|
||||
- "base2_exponential_bucket_histogram" - [Base2 Exponential Bucket Histogram Aggregation].
|
||||
|
||||
The configuration can be overridden by [WithAggregationSelector] option.
|
||||
|
||||
[W3C Baggage HTTP Header Content Format]: https://www.w3.org/TR/baggage/#header-content
|
||||
[Explicit Bucket Histogram Aggregation]: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.26.0/specification/metrics/sdk.md#explicit-bucket-histogram-aggregation
|
||||
[Base2 Exponential Bucket Histogram Aggregation]: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.26.0/specification/metrics/sdk.md#base2-exponential-bucket-histogram-aggregation
|
||||
*/
|
||||
package otlpmetrichttp // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp"
|
152
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/exporter.go
generated
vendored
Normal file
152
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/exporter.go
generated
vendored
Normal file
@@ -0,0 +1,152 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otlpmetrichttp // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/oconf"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/transform"
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
"go.opentelemetry.io/otel/sdk/metric"
|
||||
"go.opentelemetry.io/otel/sdk/metric/metricdata"
|
||||
metricpb "go.opentelemetry.io/proto/otlp/metrics/v1"
|
||||
)
|
||||
|
||||
// Exporter is a OpenTelemetry metric Exporter using protobufs over HTTP.
|
||||
type Exporter struct {
|
||||
// Ensure synchronous access to the client across all functionality.
|
||||
clientMu sync.Mutex
|
||||
client interface {
|
||||
UploadMetrics(context.Context, *metricpb.ResourceMetrics) error
|
||||
Shutdown(context.Context) error
|
||||
}
|
||||
|
||||
temporalitySelector metric.TemporalitySelector
|
||||
aggregationSelector metric.AggregationSelector
|
||||
|
||||
shutdownOnce sync.Once
|
||||
}
|
||||
|
||||
func newExporter(c *client, cfg oconf.Config) (*Exporter, error) {
|
||||
ts := cfg.Metrics.TemporalitySelector
|
||||
if ts == nil {
|
||||
ts = func(metric.InstrumentKind) metricdata.Temporality {
|
||||
return metricdata.CumulativeTemporality
|
||||
}
|
||||
}
|
||||
|
||||
as := cfg.Metrics.AggregationSelector
|
||||
if as == nil {
|
||||
as = metric.DefaultAggregationSelector
|
||||
}
|
||||
|
||||
return &Exporter{
|
||||
client: c,
|
||||
|
||||
temporalitySelector: ts,
|
||||
aggregationSelector: as,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Temporality returns the Temporality to use for an instrument kind.
|
||||
func (e *Exporter) Temporality(k metric.InstrumentKind) metricdata.Temporality {
|
||||
return e.temporalitySelector(k)
|
||||
}
|
||||
|
||||
// Aggregation returns the Aggregation to use for an instrument kind.
|
||||
func (e *Exporter) Aggregation(k metric.InstrumentKind) metric.Aggregation {
|
||||
return e.aggregationSelector(k)
|
||||
}
|
||||
|
||||
// Export transforms and transmits metric data to an OTLP receiver.
|
||||
//
|
||||
// This method returns an error if called after Shutdown.
|
||||
// This method returns an error if the method is canceled by the passed context.
|
||||
func (e *Exporter) Export(ctx context.Context, rm *metricdata.ResourceMetrics) error {
|
||||
defer global.Debug("OTLP/HTTP exporter export", "Data", rm)
|
||||
|
||||
otlpRm, err := transform.ResourceMetrics(rm)
|
||||
// Best effort upload of transformable metrics.
|
||||
e.clientMu.Lock()
|
||||
upErr := e.client.UploadMetrics(ctx, otlpRm)
|
||||
e.clientMu.Unlock()
|
||||
if upErr != nil {
|
||||
if err == nil {
|
||||
return fmt.Errorf("failed to upload metrics: %w", upErr)
|
||||
}
|
||||
// Merge the two errors.
|
||||
return fmt.Errorf("failed to upload incomplete metrics (%w): %w", err, upErr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// ForceFlush flushes any metric data held by an exporter.
|
||||
//
|
||||
// This method returns an error if called after Shutdown.
|
||||
// This method returns an error if the method is canceled by the passed context.
|
||||
//
|
||||
// This method is safe to call concurrently.
|
||||
func (e *Exporter) ForceFlush(ctx context.Context) error {
|
||||
// The exporter and client hold no state, nothing to flush.
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
// Shutdown flushes all metric data held by an exporter and releases any held
|
||||
// computational resources.
|
||||
//
|
||||
// This method returns an error if called after Shutdown.
|
||||
// This method returns an error if the method is canceled by the passed context.
|
||||
//
|
||||
// This method is safe to call concurrently.
|
||||
func (e *Exporter) Shutdown(ctx context.Context) error {
|
||||
err := errShutdown
|
||||
e.shutdownOnce.Do(func() {
|
||||
e.clientMu.Lock()
|
||||
client := e.client
|
||||
e.client = shutdownClient{}
|
||||
e.clientMu.Unlock()
|
||||
err = client.Shutdown(ctx)
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
var errShutdown = errors.New("HTTP exporter is shutdown")
|
||||
|
||||
type shutdownClient struct{}
|
||||
|
||||
func (c shutdownClient) err(ctx context.Context) error {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
return errShutdown
|
||||
}
|
||||
|
||||
func (c shutdownClient) UploadMetrics(ctx context.Context, _ *metricpb.ResourceMetrics) error {
|
||||
return c.err(ctx)
|
||||
}
|
||||
|
||||
func (c shutdownClient) Shutdown(ctx context.Context) error {
|
||||
return c.err(ctx)
|
||||
}
|
||||
|
||||
// MarshalLog returns logging data about the Exporter.
|
||||
func (e *Exporter) MarshalLog() interface{} {
|
||||
return struct{ Type string }{Type: "OTLP/HTTP"}
|
||||
}
|
||||
|
||||
// New returns an OpenTelemetry metric Exporter. The Exporter can be used with
|
||||
// a PeriodicReader to export OpenTelemetry metric data to an OTLP receiving
|
||||
// endpoint using protobufs over HTTP.
|
||||
func New(_ context.Context, opts ...Option) (*Exporter, error) {
|
||||
cfg := oconf.NewHTTPConfig(asHTTPOptions(opts)...)
|
||||
c, err := newClient(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newExporter(c, cfg)
|
||||
}
|
215
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/envconfig/envconfig.go
generated
vendored
Normal file
215
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/envconfig/envconfig.go
generated
vendored
Normal file
@@ -0,0 +1,215 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/envconfig/envconfig.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package envconfig // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/envconfig"
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
)
|
||||
|
||||
// ConfigFn is the generic function used to set a config.
|
||||
type ConfigFn func(*EnvOptionsReader)
|
||||
|
||||
// EnvOptionsReader reads the required environment variables.
|
||||
type EnvOptionsReader struct {
|
||||
GetEnv func(string) string
|
||||
ReadFile func(string) ([]byte, error)
|
||||
Namespace string
|
||||
}
|
||||
|
||||
// Apply runs every ConfigFn.
|
||||
func (e *EnvOptionsReader) Apply(opts ...ConfigFn) {
|
||||
for _, o := range opts {
|
||||
o(e)
|
||||
}
|
||||
}
|
||||
|
||||
// GetEnvValue gets an OTLP environment variable value of the specified key
|
||||
// using the GetEnv function.
|
||||
// This function prepends the OTLP specified namespace to all key lookups.
|
||||
func (e *EnvOptionsReader) GetEnvValue(key string) (string, bool) {
|
||||
v := strings.TrimSpace(e.GetEnv(keyWithNamespace(e.Namespace, key)))
|
||||
return v, v != ""
|
||||
}
|
||||
|
||||
// WithString retrieves the specified config and passes it to ConfigFn as a string.
|
||||
func WithString(n string, fn func(string)) func(e *EnvOptionsReader) {
|
||||
return func(e *EnvOptionsReader) {
|
||||
if v, ok := e.GetEnvValue(n); ok {
|
||||
fn(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithBool returns a ConfigFn that reads the environment variable n and if it exists passes its parsed bool value to fn.
|
||||
func WithBool(n string, fn func(bool)) ConfigFn {
|
||||
return func(e *EnvOptionsReader) {
|
||||
if v, ok := e.GetEnvValue(n); ok {
|
||||
b := strings.ToLower(v) == "true"
|
||||
fn(b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithDuration retrieves the specified config and passes it to ConfigFn as a duration.
|
||||
func WithDuration(n string, fn func(time.Duration)) func(e *EnvOptionsReader) {
|
||||
return func(e *EnvOptionsReader) {
|
||||
if v, ok := e.GetEnvValue(n); ok {
|
||||
d, err := strconv.Atoi(v)
|
||||
if err != nil {
|
||||
global.Error(err, "parse duration", "input", v)
|
||||
return
|
||||
}
|
||||
fn(time.Duration(d) * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithHeaders retrieves the specified config and passes it to ConfigFn as a map of HTTP headers.
|
||||
func WithHeaders(n string, fn func(map[string]string)) func(e *EnvOptionsReader) {
|
||||
return func(e *EnvOptionsReader) {
|
||||
if v, ok := e.GetEnvValue(n); ok {
|
||||
fn(stringToHeader(v))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithURL retrieves the specified config and passes it to ConfigFn as a net/url.URL.
|
||||
func WithURL(n string, fn func(*url.URL)) func(e *EnvOptionsReader) {
|
||||
return func(e *EnvOptionsReader) {
|
||||
if v, ok := e.GetEnvValue(n); ok {
|
||||
u, err := url.Parse(v)
|
||||
if err != nil {
|
||||
global.Error(err, "parse url", "input", v)
|
||||
return
|
||||
}
|
||||
fn(u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithCertPool returns a ConfigFn that reads the environment variable n as a filepath to a TLS certificate pool. If it exists, it is parsed as a crypto/x509.CertPool and it is passed to fn.
|
||||
func WithCertPool(n string, fn func(*x509.CertPool)) ConfigFn {
|
||||
return func(e *EnvOptionsReader) {
|
||||
if v, ok := e.GetEnvValue(n); ok {
|
||||
b, err := e.ReadFile(v)
|
||||
if err != nil {
|
||||
global.Error(err, "read tls ca cert file", "file", v)
|
||||
return
|
||||
}
|
||||
c, err := createCertPool(b)
|
||||
if err != nil {
|
||||
global.Error(err, "create tls cert pool")
|
||||
return
|
||||
}
|
||||
fn(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithClientCert returns a ConfigFn that reads the environment variable nc and nk as filepaths to a client certificate and key pair. If they exists, they are parsed as a crypto/tls.Certificate and it is passed to fn.
|
||||
func WithClientCert(nc, nk string, fn func(tls.Certificate)) ConfigFn {
|
||||
return func(e *EnvOptionsReader) {
|
||||
vc, okc := e.GetEnvValue(nc)
|
||||
vk, okk := e.GetEnvValue(nk)
|
||||
if !okc || !okk {
|
||||
return
|
||||
}
|
||||
cert, err := e.ReadFile(vc)
|
||||
if err != nil {
|
||||
global.Error(err, "read tls client cert", "file", vc)
|
||||
return
|
||||
}
|
||||
key, err := e.ReadFile(vk)
|
||||
if err != nil {
|
||||
global.Error(err, "read tls client key", "file", vk)
|
||||
return
|
||||
}
|
||||
crt, err := tls.X509KeyPair(cert, key)
|
||||
if err != nil {
|
||||
global.Error(err, "create tls client key pair")
|
||||
return
|
||||
}
|
||||
fn(crt)
|
||||
}
|
||||
}
|
||||
|
||||
func keyWithNamespace(ns, key string) string {
|
||||
if ns == "" {
|
||||
return key
|
||||
}
|
||||
return fmt.Sprintf("%s_%s", ns, key)
|
||||
}
|
||||
|
||||
func stringToHeader(value string) map[string]string {
|
||||
headersPairs := strings.Split(value, ",")
|
||||
headers := make(map[string]string)
|
||||
|
||||
for _, header := range headersPairs {
|
||||
n, v, found := strings.Cut(header, "=")
|
||||
if !found {
|
||||
global.Error(errors.New("missing '="), "parse headers", "input", header)
|
||||
continue
|
||||
}
|
||||
|
||||
trimmedName := strings.TrimSpace(n)
|
||||
|
||||
// Validate the key.
|
||||
if !isValidHeaderKey(trimmedName) {
|
||||
global.Error(errors.New("invalid header key"), "parse headers", "key", trimmedName)
|
||||
continue
|
||||
}
|
||||
|
||||
// Only decode the value.
|
||||
value, err := url.PathUnescape(v)
|
||||
if err != nil {
|
||||
global.Error(err, "escape header value", "value", v)
|
||||
continue
|
||||
}
|
||||
trimmedValue := strings.TrimSpace(value)
|
||||
|
||||
headers[trimmedName] = trimmedValue
|
||||
}
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
func createCertPool(certBytes []byte) (*x509.CertPool, error) {
|
||||
cp := x509.NewCertPool()
|
||||
if ok := cp.AppendCertsFromPEM(certBytes); !ok {
|
||||
return nil, errors.New("failed to append certificate to the cert pool")
|
||||
}
|
||||
return cp, nil
|
||||
}
|
||||
|
||||
func isValidHeaderKey(key string) bool {
|
||||
if key == "" {
|
||||
return false
|
||||
}
|
||||
for _, c := range key {
|
||||
if !isTokenChar(c) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func isTokenChar(c rune) bool {
|
||||
return c <= unicode.MaxASCII && (unicode.IsLetter(c) ||
|
||||
unicode.IsDigit(c) ||
|
||||
c == '!' || c == '#' || c == '$' || c == '%' || c == '&' || c == '\'' || c == '*' ||
|
||||
c == '+' || c == '-' || c == '.' || c == '^' || c == '_' || c == '`' || c == '|' || c == '~')
|
||||
}
|
31
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/gen.go
generated
vendored
Normal file
31
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/gen.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package internal // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal"
|
||||
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/partialsuccess.go.tmpl "--data={}" --out=partialsuccess.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/partialsuccess_test.go.tmpl "--data={}" --out=partialsuccess_test.go
|
||||
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/retry/retry.go.tmpl "--data={}" --out=retry/retry.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/retry/retry_test.go.tmpl "--data={}" --out=retry/retry_test.go
|
||||
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/envconfig/envconfig.go.tmpl "--data={}" --out=envconfig/envconfig.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/envconfig/envconfig_test.go.tmpl "--data={}" --out=envconfig/envconfig_test.go
|
||||
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/oconf/envconfig.go.tmpl "--data={\"envconfigImportPath\": \"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/envconfig\"}" --out=oconf/envconfig.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/oconf/envconfig_test.go.tmpl "--data={}" --out=oconf/envconfig_test.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/oconf/options.go.tmpl "--data={\"retryImportPath\": \"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/retry\"}" --out=oconf/options.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/oconf/options_test.go.tmpl "--data={\"envconfigImportPath\": \"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/envconfig\"}" --out=oconf/options_test.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/oconf/optiontypes.go.tmpl "--data={}" --out=oconf/optiontypes.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/oconf/tls.go.tmpl "--data={}" --out=oconf/tls.go
|
||||
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/otest/client.go.tmpl "--data={}" --out=otest/client.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/otest/client_test.go.tmpl "--data={\"internalImportPath\": \"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal\"}" --out=otest/client_test.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/otest/collector.go.tmpl "--data={\"oconfImportPath\": \"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/oconf\"}" --out=otest/collector.go
|
||||
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/transform/attribute.go.tmpl "--data={}" --out=transform/attribute.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/transform/attribute_test.go.tmpl "--data={}" --out=transform/attribute_test.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/transform/error.go.tmpl "--data={}" --out=transform/error.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/transform/error_test.go.tmpl "--data={}" --out=transform/error_test.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/transform/metricdata.go.tmpl "--data={}" --out=transform/metricdata.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/transform/metricdata_test.go.tmpl "--data={}" --out=transform/metricdata_test.go
|
210
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/oconf/envconfig.go
generated
vendored
Normal file
210
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/oconf/envconfig.go
generated
vendored
Normal file
@@ -0,0 +1,210 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/otlpmetric/oconf/envconfig.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package oconf // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/oconf"
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/envconfig"
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
"go.opentelemetry.io/otel/sdk/metric"
|
||||
"go.opentelemetry.io/otel/sdk/metric/metricdata"
|
||||
)
|
||||
|
||||
// DefaultEnvOptionsReader is the default environments reader.
|
||||
var DefaultEnvOptionsReader = envconfig.EnvOptionsReader{
|
||||
GetEnv: os.Getenv,
|
||||
ReadFile: os.ReadFile,
|
||||
Namespace: "OTEL_EXPORTER_OTLP",
|
||||
}
|
||||
|
||||
// ApplyGRPCEnvConfigs applies the env configurations for gRPC.
|
||||
func ApplyGRPCEnvConfigs(cfg Config) Config {
|
||||
opts := getOptionsFromEnv()
|
||||
for _, opt := range opts {
|
||||
cfg = opt.ApplyGRPCOption(cfg)
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
// ApplyHTTPEnvConfigs applies the env configurations for HTTP.
|
||||
func ApplyHTTPEnvConfigs(cfg Config) Config {
|
||||
opts := getOptionsFromEnv()
|
||||
for _, opt := range opts {
|
||||
cfg = opt.ApplyHTTPOption(cfg)
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
func getOptionsFromEnv() []GenericOption {
|
||||
opts := []GenericOption{}
|
||||
|
||||
tlsConf := &tls.Config{}
|
||||
DefaultEnvOptionsReader.Apply(
|
||||
envconfig.WithURL("ENDPOINT", func(u *url.URL) {
|
||||
opts = append(opts, withEndpointScheme(u))
|
||||
opts = append(opts, newSplitOption(func(cfg Config) Config {
|
||||
cfg.Metrics.Endpoint = u.Host
|
||||
// For OTLP/HTTP endpoint URLs without a per-signal
|
||||
// configuration, the passed endpoint is used as a base URL
|
||||
// and the signals are sent to these paths relative to that.
|
||||
cfg.Metrics.URLPath = path.Join(u.Path, DefaultMetricsPath)
|
||||
return cfg
|
||||
}, withEndpointForGRPC(u)))
|
||||
}),
|
||||
envconfig.WithURL("METRICS_ENDPOINT", func(u *url.URL) {
|
||||
opts = append(opts, withEndpointScheme(u))
|
||||
opts = append(opts, newSplitOption(func(cfg Config) Config {
|
||||
cfg.Metrics.Endpoint = u.Host
|
||||
// For endpoint URLs for OTLP/HTTP per-signal variables, the
|
||||
// URL MUST be used as-is without any modification. The only
|
||||
// exception is that if an URL contains no path part, the root
|
||||
// path / MUST be used.
|
||||
path := u.Path
|
||||
if path == "" {
|
||||
path = "/"
|
||||
}
|
||||
cfg.Metrics.URLPath = path
|
||||
return cfg
|
||||
}, withEndpointForGRPC(u)))
|
||||
}),
|
||||
envconfig.WithCertPool("CERTIFICATE", func(p *x509.CertPool) { tlsConf.RootCAs = p }),
|
||||
envconfig.WithCertPool("METRICS_CERTIFICATE", func(p *x509.CertPool) { tlsConf.RootCAs = p }),
|
||||
envconfig.WithClientCert("CLIENT_CERTIFICATE", "CLIENT_KEY", func(c tls.Certificate) { tlsConf.Certificates = []tls.Certificate{c} }),
|
||||
envconfig.WithClientCert("METRICS_CLIENT_CERTIFICATE", "METRICS_CLIENT_KEY", func(c tls.Certificate) { tlsConf.Certificates = []tls.Certificate{c} }),
|
||||
envconfig.WithBool("INSECURE", func(b bool) { opts = append(opts, withInsecure(b)) }),
|
||||
envconfig.WithBool("METRICS_INSECURE", func(b bool) { opts = append(opts, withInsecure(b)) }),
|
||||
withTLSConfig(tlsConf, func(c *tls.Config) { opts = append(opts, WithTLSClientConfig(c)) }),
|
||||
envconfig.WithHeaders("HEADERS", func(h map[string]string) { opts = append(opts, WithHeaders(h)) }),
|
||||
envconfig.WithHeaders("METRICS_HEADERS", func(h map[string]string) { opts = append(opts, WithHeaders(h)) }),
|
||||
WithEnvCompression("COMPRESSION", func(c Compression) { opts = append(opts, WithCompression(c)) }),
|
||||
WithEnvCompression("METRICS_COMPRESSION", func(c Compression) { opts = append(opts, WithCompression(c)) }),
|
||||
envconfig.WithDuration("TIMEOUT", func(d time.Duration) { opts = append(opts, WithTimeout(d)) }),
|
||||
envconfig.WithDuration("METRICS_TIMEOUT", func(d time.Duration) { opts = append(opts, WithTimeout(d)) }),
|
||||
withEnvTemporalityPreference("METRICS_TEMPORALITY_PREFERENCE", func(t metric.TemporalitySelector) { opts = append(opts, WithTemporalitySelector(t)) }),
|
||||
withEnvAggPreference("METRICS_DEFAULT_HISTOGRAM_AGGREGATION", func(a metric.AggregationSelector) { opts = append(opts, WithAggregationSelector(a)) }),
|
||||
)
|
||||
|
||||
return opts
|
||||
}
|
||||
|
||||
func withEndpointForGRPC(u *url.URL) func(cfg Config) Config {
|
||||
return func(cfg Config) Config {
|
||||
// For OTLP/gRPC endpoints, this is the target to which the
|
||||
// exporter is going to send telemetry.
|
||||
cfg.Metrics.Endpoint = path.Join(u.Host, u.Path)
|
||||
return cfg
|
||||
}
|
||||
}
|
||||
|
||||
// WithEnvCompression retrieves the specified config and passes it to ConfigFn as a Compression.
|
||||
func WithEnvCompression(n string, fn func(Compression)) func(e *envconfig.EnvOptionsReader) {
|
||||
return func(e *envconfig.EnvOptionsReader) {
|
||||
if v, ok := e.GetEnvValue(n); ok {
|
||||
cp := NoCompression
|
||||
if v == "gzip" {
|
||||
cp = GzipCompression
|
||||
}
|
||||
|
||||
fn(cp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func withEndpointScheme(u *url.URL) GenericOption {
|
||||
switch strings.ToLower(u.Scheme) {
|
||||
case "http", "unix":
|
||||
return WithInsecure()
|
||||
default:
|
||||
return WithSecure()
|
||||
}
|
||||
}
|
||||
|
||||
// revive:disable-next-line:flag-parameter
|
||||
func withInsecure(b bool) GenericOption {
|
||||
if b {
|
||||
return WithInsecure()
|
||||
}
|
||||
return WithSecure()
|
||||
}
|
||||
|
||||
func withTLSConfig(c *tls.Config, fn func(*tls.Config)) func(e *envconfig.EnvOptionsReader) {
|
||||
return func(e *envconfig.EnvOptionsReader) {
|
||||
if c.RootCAs != nil || len(c.Certificates) > 0 {
|
||||
fn(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func withEnvTemporalityPreference(n string, fn func(metric.TemporalitySelector)) func(e *envconfig.EnvOptionsReader) {
|
||||
return func(e *envconfig.EnvOptionsReader) {
|
||||
if s, ok := e.GetEnvValue(n); ok {
|
||||
switch strings.ToLower(s) {
|
||||
case "cumulative":
|
||||
fn(cumulativeTemporality)
|
||||
case "delta":
|
||||
fn(deltaTemporality)
|
||||
case "lowmemory":
|
||||
fn(lowMemory)
|
||||
default:
|
||||
global.Warn("OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE is set to an invalid value, ignoring.", "value", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cumulativeTemporality(metric.InstrumentKind) metricdata.Temporality {
|
||||
return metricdata.CumulativeTemporality
|
||||
}
|
||||
|
||||
func deltaTemporality(ik metric.InstrumentKind) metricdata.Temporality {
|
||||
switch ik {
|
||||
case metric.InstrumentKindCounter, metric.InstrumentKindHistogram, metric.InstrumentKindObservableCounter:
|
||||
return metricdata.DeltaTemporality
|
||||
default:
|
||||
return metricdata.CumulativeTemporality
|
||||
}
|
||||
}
|
||||
|
||||
func lowMemory(ik metric.InstrumentKind) metricdata.Temporality {
|
||||
switch ik {
|
||||
case metric.InstrumentKindCounter, metric.InstrumentKindHistogram:
|
||||
return metricdata.DeltaTemporality
|
||||
default:
|
||||
return metricdata.CumulativeTemporality
|
||||
}
|
||||
}
|
||||
|
||||
func withEnvAggPreference(n string, fn func(metric.AggregationSelector)) func(e *envconfig.EnvOptionsReader) {
|
||||
return func(e *envconfig.EnvOptionsReader) {
|
||||
if s, ok := e.GetEnvValue(n); ok {
|
||||
switch strings.ToLower(s) {
|
||||
case "explicit_bucket_histogram":
|
||||
fn(metric.DefaultAggregationSelector)
|
||||
case "base2_exponential_bucket_histogram":
|
||||
fn(func(kind metric.InstrumentKind) metric.Aggregation {
|
||||
if kind == metric.InstrumentKindHistogram {
|
||||
return metric.AggregationBase2ExponentialHistogram{
|
||||
MaxSize: 160,
|
||||
MaxScale: 20,
|
||||
NoMinMax: false,
|
||||
}
|
||||
}
|
||||
return metric.DefaultAggregationSelector(kind)
|
||||
})
|
||||
default:
|
||||
global.Warn("OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION is set to an invalid value, ignoring.", "value", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
374
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/oconf/options.go
generated
vendored
Normal file
374
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/oconf/options.go
generated
vendored
Normal file
@@ -0,0 +1,374 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/otlpmetric/oconf/options.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package oconf // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/oconf"
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/backoff"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/encoding/gzip"
|
||||
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/retry"
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
"go.opentelemetry.io/otel/sdk/metric"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultMaxAttempts describes how many times the driver
|
||||
// should retry the sending of the payload in case of a
|
||||
// retryable error.
|
||||
DefaultMaxAttempts int = 5
|
||||
// DefaultMetricsPath is a default URL path for endpoint that
|
||||
// receives metrics.
|
||||
DefaultMetricsPath string = "/v1/metrics"
|
||||
// DefaultBackoff is a default base backoff time used in the
|
||||
// exponential backoff strategy.
|
||||
DefaultBackoff time.Duration = 300 * time.Millisecond
|
||||
// DefaultTimeout is a default max waiting time for the backend to process
|
||||
// each span or metrics batch.
|
||||
DefaultTimeout time.Duration = 10 * time.Second
|
||||
)
|
||||
|
||||
type (
|
||||
// HTTPTransportProxyFunc is a function that resolves which URL to use as proxy for a given request.
|
||||
// This type is compatible with `http.Transport.Proxy` and can be used to set a custom proxy function to the OTLP HTTP client.
|
||||
HTTPTransportProxyFunc func(*http.Request) (*url.URL, error)
|
||||
|
||||
SignalConfig struct {
|
||||
Endpoint string
|
||||
Insecure bool
|
||||
TLSCfg *tls.Config
|
||||
Headers map[string]string
|
||||
Compression Compression
|
||||
Timeout time.Duration
|
||||
URLPath string
|
||||
|
||||
// gRPC configurations
|
||||
GRPCCredentials credentials.TransportCredentials
|
||||
|
||||
TemporalitySelector metric.TemporalitySelector
|
||||
AggregationSelector metric.AggregationSelector
|
||||
|
||||
Proxy HTTPTransportProxyFunc
|
||||
}
|
||||
|
||||
Config struct {
|
||||
// Signal specific configurations
|
||||
Metrics SignalConfig
|
||||
|
||||
RetryConfig retry.Config
|
||||
|
||||
// gRPC configurations
|
||||
ReconnectionPeriod time.Duration
|
||||
ServiceConfig string
|
||||
DialOptions []grpc.DialOption
|
||||
GRPCConn *grpc.ClientConn
|
||||
}
|
||||
)
|
||||
|
||||
// NewHTTPConfig returns a new Config with all settings applied from opts and
|
||||
// any unset setting using the default HTTP config values.
|
||||
func NewHTTPConfig(opts ...HTTPOption) Config {
|
||||
cfg := Config{
|
||||
Metrics: SignalConfig{
|
||||
Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorHTTPPort),
|
||||
URLPath: DefaultMetricsPath,
|
||||
Compression: NoCompression,
|
||||
Timeout: DefaultTimeout,
|
||||
|
||||
TemporalitySelector: metric.DefaultTemporalitySelector,
|
||||
AggregationSelector: metric.DefaultAggregationSelector,
|
||||
},
|
||||
RetryConfig: retry.DefaultConfig,
|
||||
}
|
||||
cfg = ApplyHTTPEnvConfigs(cfg)
|
||||
for _, opt := range opts {
|
||||
cfg = opt.ApplyHTTPOption(cfg)
|
||||
}
|
||||
cfg.Metrics.URLPath = cleanPath(cfg.Metrics.URLPath, DefaultMetricsPath)
|
||||
return cfg
|
||||
}
|
||||
|
||||
// cleanPath returns a path with all spaces trimmed and all redundancies
|
||||
// removed. If urlPath is empty or cleaning it results in an empty string,
|
||||
// defaultPath is returned instead.
|
||||
func cleanPath(urlPath string, defaultPath string) string {
|
||||
tmp := path.Clean(strings.TrimSpace(urlPath))
|
||||
if tmp == "." {
|
||||
return defaultPath
|
||||
}
|
||||
if !path.IsAbs(tmp) {
|
||||
tmp = "/" + tmp
|
||||
}
|
||||
return tmp
|
||||
}
|
||||
|
||||
// NewGRPCConfig returns a new Config with all settings applied from opts and
|
||||
// any unset setting using the default gRPC config values.
|
||||
func NewGRPCConfig(opts ...GRPCOption) Config {
|
||||
cfg := Config{
|
||||
Metrics: SignalConfig{
|
||||
Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorGRPCPort),
|
||||
URLPath: DefaultMetricsPath,
|
||||
Compression: NoCompression,
|
||||
Timeout: DefaultTimeout,
|
||||
|
||||
TemporalitySelector: metric.DefaultTemporalitySelector,
|
||||
AggregationSelector: metric.DefaultAggregationSelector,
|
||||
},
|
||||
RetryConfig: retry.DefaultConfig,
|
||||
}
|
||||
cfg = ApplyGRPCEnvConfigs(cfg)
|
||||
for _, opt := range opts {
|
||||
cfg = opt.ApplyGRPCOption(cfg)
|
||||
}
|
||||
|
||||
if cfg.ServiceConfig != "" {
|
||||
cfg.DialOptions = append(cfg.DialOptions, grpc.WithDefaultServiceConfig(cfg.ServiceConfig))
|
||||
}
|
||||
// Prioritize GRPCCredentials over Insecure (passing both is an error).
|
||||
if cfg.Metrics.GRPCCredentials != nil {
|
||||
cfg.DialOptions = append(cfg.DialOptions, grpc.WithTransportCredentials(cfg.Metrics.GRPCCredentials))
|
||||
} else if cfg.Metrics.Insecure {
|
||||
cfg.DialOptions = append(cfg.DialOptions, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
} else {
|
||||
// Default to using the host's root CA.
|
||||
creds := credentials.NewTLS(nil)
|
||||
cfg.Metrics.GRPCCredentials = creds
|
||||
cfg.DialOptions = append(cfg.DialOptions, grpc.WithTransportCredentials(creds))
|
||||
}
|
||||
if cfg.Metrics.Compression == GzipCompression {
|
||||
cfg.DialOptions = append(cfg.DialOptions, grpc.WithDefaultCallOptions(grpc.UseCompressor(gzip.Name)))
|
||||
}
|
||||
if cfg.ReconnectionPeriod != 0 {
|
||||
p := grpc.ConnectParams{
|
||||
Backoff: backoff.DefaultConfig,
|
||||
MinConnectTimeout: cfg.ReconnectionPeriod,
|
||||
}
|
||||
cfg.DialOptions = append(cfg.DialOptions, grpc.WithConnectParams(p))
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
type (
|
||||
// GenericOption applies an option to the HTTP or gRPC driver.
|
||||
GenericOption interface {
|
||||
ApplyHTTPOption(Config) Config
|
||||
ApplyGRPCOption(Config) Config
|
||||
|
||||
// A private method to prevent users implementing the
|
||||
// interface and so future additions to it will not
|
||||
// violate compatibility.
|
||||
private()
|
||||
}
|
||||
|
||||
// HTTPOption applies an option to the HTTP driver.
|
||||
HTTPOption interface {
|
||||
ApplyHTTPOption(Config) Config
|
||||
|
||||
// A private method to prevent users implementing the
|
||||
// interface and so future additions to it will not
|
||||
// violate compatibility.
|
||||
private()
|
||||
}
|
||||
|
||||
// GRPCOption applies an option to the gRPC driver.
|
||||
GRPCOption interface {
|
||||
ApplyGRPCOption(Config) Config
|
||||
|
||||
// A private method to prevent users implementing the
|
||||
// interface and so future additions to it will not
|
||||
// violate compatibility.
|
||||
private()
|
||||
}
|
||||
)
|
||||
|
||||
// genericOption is an option that applies the same logic
|
||||
// for both gRPC and HTTP.
|
||||
type genericOption struct {
|
||||
fn func(Config) Config
|
||||
}
|
||||
|
||||
func (g *genericOption) ApplyGRPCOption(cfg Config) Config {
|
||||
return g.fn(cfg)
|
||||
}
|
||||
|
||||
func (g *genericOption) ApplyHTTPOption(cfg Config) Config {
|
||||
return g.fn(cfg)
|
||||
}
|
||||
|
||||
func (genericOption) private() {}
|
||||
|
||||
func newGenericOption(fn func(cfg Config) Config) GenericOption {
|
||||
return &genericOption{fn: fn}
|
||||
}
|
||||
|
||||
// splitOption is an option that applies different logics
|
||||
// for gRPC and HTTP.
|
||||
type splitOption struct {
|
||||
httpFn func(Config) Config
|
||||
grpcFn func(Config) Config
|
||||
}
|
||||
|
||||
func (g *splitOption) ApplyGRPCOption(cfg Config) Config {
|
||||
return g.grpcFn(cfg)
|
||||
}
|
||||
|
||||
func (g *splitOption) ApplyHTTPOption(cfg Config) Config {
|
||||
return g.httpFn(cfg)
|
||||
}
|
||||
|
||||
func (splitOption) private() {}
|
||||
|
||||
func newSplitOption(httpFn func(cfg Config) Config, grpcFn func(cfg Config) Config) GenericOption {
|
||||
return &splitOption{httpFn: httpFn, grpcFn: grpcFn}
|
||||
}
|
||||
|
||||
// httpOption is an option that is only applied to the HTTP driver.
|
||||
type httpOption struct {
|
||||
fn func(Config) Config
|
||||
}
|
||||
|
||||
func (h *httpOption) ApplyHTTPOption(cfg Config) Config {
|
||||
return h.fn(cfg)
|
||||
}
|
||||
|
||||
func (httpOption) private() {}
|
||||
|
||||
func NewHTTPOption(fn func(cfg Config) Config) HTTPOption {
|
||||
return &httpOption{fn: fn}
|
||||
}
|
||||
|
||||
// grpcOption is an option that is only applied to the gRPC driver.
|
||||
type grpcOption struct {
|
||||
fn func(Config) Config
|
||||
}
|
||||
|
||||
func (h *grpcOption) ApplyGRPCOption(cfg Config) Config {
|
||||
return h.fn(cfg)
|
||||
}
|
||||
|
||||
func (grpcOption) private() {}
|
||||
|
||||
func NewGRPCOption(fn func(cfg Config) Config) GRPCOption {
|
||||
return &grpcOption{fn: fn}
|
||||
}
|
||||
|
||||
// Generic Options
|
||||
|
||||
func WithEndpoint(endpoint string) GenericOption {
|
||||
return newGenericOption(func(cfg Config) Config {
|
||||
cfg.Metrics.Endpoint = endpoint
|
||||
return cfg
|
||||
})
|
||||
}
|
||||
|
||||
func WithEndpointURL(v string) GenericOption {
|
||||
return newGenericOption(func(cfg Config) Config {
|
||||
u, err := url.Parse(v)
|
||||
if err != nil {
|
||||
global.Error(err, "otlpmetric: parse endpoint url", "url", v)
|
||||
return cfg
|
||||
}
|
||||
|
||||
cfg.Metrics.Endpoint = u.Host
|
||||
cfg.Metrics.URLPath = u.Path
|
||||
cfg.Metrics.Insecure = u.Scheme != "https"
|
||||
|
||||
return cfg
|
||||
})
|
||||
}
|
||||
|
||||
func WithCompression(compression Compression) GenericOption {
|
||||
return newGenericOption(func(cfg Config) Config {
|
||||
cfg.Metrics.Compression = compression
|
||||
return cfg
|
||||
})
|
||||
}
|
||||
|
||||
func WithURLPath(urlPath string) GenericOption {
|
||||
return newGenericOption(func(cfg Config) Config {
|
||||
cfg.Metrics.URLPath = urlPath
|
||||
return cfg
|
||||
})
|
||||
}
|
||||
|
||||
func WithRetry(rc retry.Config) GenericOption {
|
||||
return newGenericOption(func(cfg Config) Config {
|
||||
cfg.RetryConfig = rc
|
||||
return cfg
|
||||
})
|
||||
}
|
||||
|
||||
func WithTLSClientConfig(tlsCfg *tls.Config) GenericOption {
|
||||
return newSplitOption(func(cfg Config) Config {
|
||||
cfg.Metrics.TLSCfg = tlsCfg.Clone()
|
||||
return cfg
|
||||
}, func(cfg Config) Config {
|
||||
cfg.Metrics.GRPCCredentials = credentials.NewTLS(tlsCfg)
|
||||
return cfg
|
||||
})
|
||||
}
|
||||
|
||||
func WithInsecure() GenericOption {
|
||||
return newGenericOption(func(cfg Config) Config {
|
||||
cfg.Metrics.Insecure = true
|
||||
return cfg
|
||||
})
|
||||
}
|
||||
|
||||
func WithSecure() GenericOption {
|
||||
return newGenericOption(func(cfg Config) Config {
|
||||
cfg.Metrics.Insecure = false
|
||||
return cfg
|
||||
})
|
||||
}
|
||||
|
||||
func WithHeaders(headers map[string]string) GenericOption {
|
||||
return newGenericOption(func(cfg Config) Config {
|
||||
cfg.Metrics.Headers = headers
|
||||
return cfg
|
||||
})
|
||||
}
|
||||
|
||||
func WithTimeout(duration time.Duration) GenericOption {
|
||||
return newGenericOption(func(cfg Config) Config {
|
||||
cfg.Metrics.Timeout = duration
|
||||
return cfg
|
||||
})
|
||||
}
|
||||
|
||||
func WithTemporalitySelector(selector metric.TemporalitySelector) GenericOption {
|
||||
return newGenericOption(func(cfg Config) Config {
|
||||
cfg.Metrics.TemporalitySelector = selector
|
||||
return cfg
|
||||
})
|
||||
}
|
||||
|
||||
func WithAggregationSelector(selector metric.AggregationSelector) GenericOption {
|
||||
return newGenericOption(func(cfg Config) Config {
|
||||
cfg.Metrics.AggregationSelector = selector
|
||||
return cfg
|
||||
})
|
||||
}
|
||||
|
||||
func WithProxy(pf HTTPTransportProxyFunc) GenericOption {
|
||||
return newGenericOption(func(cfg Config) Config {
|
||||
cfg.Metrics.Proxy = pf
|
||||
return cfg
|
||||
})
|
||||
}
|
47
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/oconf/optiontypes.go
generated
vendored
Normal file
47
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/oconf/optiontypes.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/otlpmetric/oconf/optiontypes.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package oconf // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/oconf"
|
||||
|
||||
import "time"
|
||||
|
||||
const (
|
||||
// DefaultCollectorGRPCPort is the default gRPC port of the collector.
|
||||
DefaultCollectorGRPCPort uint16 = 4317
|
||||
// DefaultCollectorHTTPPort is the default HTTP port of the collector.
|
||||
DefaultCollectorHTTPPort uint16 = 4318
|
||||
// DefaultCollectorHost is the host address the Exporter will attempt
|
||||
// connect to if no collector address is provided.
|
||||
DefaultCollectorHost string = "localhost"
|
||||
)
|
||||
|
||||
// Compression describes the compression used for payloads sent to the
|
||||
// collector.
|
||||
type Compression int
|
||||
|
||||
const (
|
||||
// NoCompression tells the driver to send payloads without
|
||||
// compression.
|
||||
NoCompression Compression = iota
|
||||
// GzipCompression tells the driver to send payloads after
|
||||
// compressing them with gzip.
|
||||
GzipCompression
|
||||
)
|
||||
|
||||
// RetrySettings defines configuration for retrying batches in case of export failure
|
||||
// using an exponential backoff.
|
||||
type RetrySettings struct {
|
||||
// Enabled indicates whether to not retry sending batches in case of export failure.
|
||||
Enabled bool
|
||||
// InitialInterval the time to wait after the first failure before retrying.
|
||||
InitialInterval time.Duration
|
||||
// MaxInterval is the upper bound on backoff interval. Once this value is reached the delay between
|
||||
// consecutive retries will always be `MaxInterval`.
|
||||
MaxInterval time.Duration
|
||||
// MaxElapsedTime is the maximum amount of time (including retries) spent trying to send a request/batch.
|
||||
// Once this value is reached, the data is discarded.
|
||||
MaxElapsedTime time.Duration
|
||||
}
|
38
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/oconf/tls.go
generated
vendored
Normal file
38
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/oconf/tls.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/otlpmetric/oconf/tls.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package oconf // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/oconf"
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
// ReadTLSConfigFromFile reads a PEM certificate file and creates
|
||||
// a tls.Config that will use this certificate to verify a server certificate.
|
||||
func ReadTLSConfigFromFile(path string) (*tls.Config, error) {
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return CreateTLSConfig(b)
|
||||
}
|
||||
|
||||
// CreateTLSConfig creates a tls.Config from a raw certificate bytes
|
||||
// to verify a server certificate.
|
||||
func CreateTLSConfig(certBytes []byte) (*tls.Config, error) {
|
||||
cp := x509.NewCertPool()
|
||||
if ok := cp.AppendCertsFromPEM(certBytes); !ok {
|
||||
return nil, errors.New("failed to append certificate to the cert pool")
|
||||
}
|
||||
|
||||
return &tls.Config{
|
||||
RootCAs: cp,
|
||||
}, nil
|
||||
}
|
56
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/partialsuccess.go
generated
vendored
Normal file
56
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/partialsuccess.go
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/partialsuccess.go
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package internal // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal"
|
||||
|
||||
import "fmt"
|
||||
|
||||
// PartialSuccess represents the underlying error for all handling
|
||||
// OTLP partial success messages. Use `errors.Is(err,
|
||||
// PartialSuccess{})` to test whether an error passed to the OTel
|
||||
// error handler belongs to this category.
|
||||
type PartialSuccess struct {
|
||||
ErrorMessage string
|
||||
RejectedItems int64
|
||||
RejectedKind string
|
||||
}
|
||||
|
||||
var _ error = PartialSuccess{}
|
||||
|
||||
// Error implements the error interface.
|
||||
func (ps PartialSuccess) Error() string {
|
||||
msg := ps.ErrorMessage
|
||||
if msg == "" {
|
||||
msg = "empty message"
|
||||
}
|
||||
return fmt.Sprintf("OTLP partial success: %s (%d %s rejected)", msg, ps.RejectedItems, ps.RejectedKind)
|
||||
}
|
||||
|
||||
// Is supports the errors.Is() interface.
|
||||
func (ps PartialSuccess) Is(err error) bool {
|
||||
_, ok := err.(PartialSuccess)
|
||||
return ok
|
||||
}
|
||||
|
||||
// TracePartialSuccessError returns an error describing a partial success
|
||||
// response for the trace signal.
|
||||
func TracePartialSuccessError(itemsRejected int64, errorMessage string) error {
|
||||
return PartialSuccess{
|
||||
ErrorMessage: errorMessage,
|
||||
RejectedItems: itemsRejected,
|
||||
RejectedKind: "spans",
|
||||
}
|
||||
}
|
||||
|
||||
// MetricPartialSuccessError returns an error describing a partial success
|
||||
// response for the metric signal.
|
||||
func MetricPartialSuccessError(itemsRejected int64, errorMessage string) error {
|
||||
return PartialSuccess{
|
||||
ErrorMessage: errorMessage,
|
||||
RejectedItems: itemsRejected,
|
||||
RejectedKind: "metric data points",
|
||||
}
|
||||
}
|
145
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/retry/retry.go
generated
vendored
Normal file
145
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/retry/retry.go
generated
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/retry/retry.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package retry provides request retry functionality that can perform
|
||||
// configurable exponential backoff for transient errors and honor any
|
||||
// explicit throttle responses received.
|
||||
package retry // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/retry"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/cenkalti/backoff/v4"
|
||||
)
|
||||
|
||||
// DefaultConfig are the recommended defaults to use.
|
||||
var DefaultConfig = Config{
|
||||
Enabled: true,
|
||||
InitialInterval: 5 * time.Second,
|
||||
MaxInterval: 30 * time.Second,
|
||||
MaxElapsedTime: time.Minute,
|
||||
}
|
||||
|
||||
// Config defines configuration for retrying batches in case of export failure
|
||||
// using an exponential backoff.
|
||||
type Config struct {
|
||||
// Enabled indicates whether to not retry sending batches in case of
|
||||
// export failure.
|
||||
Enabled bool
|
||||
// InitialInterval the time to wait after the first failure before
|
||||
// retrying.
|
||||
InitialInterval time.Duration
|
||||
// MaxInterval is the upper bound on backoff interval. Once this value is
|
||||
// reached the delay between consecutive retries will always be
|
||||
// `MaxInterval`.
|
||||
MaxInterval time.Duration
|
||||
// MaxElapsedTime is the maximum amount of time (including retries) spent
|
||||
// trying to send a request/batch. Once this value is reached, the data
|
||||
// is discarded.
|
||||
MaxElapsedTime time.Duration
|
||||
}
|
||||
|
||||
// RequestFunc wraps a request with retry logic.
|
||||
type RequestFunc func(context.Context, func(context.Context) error) error
|
||||
|
||||
// EvaluateFunc returns if an error is retry-able and if an explicit throttle
|
||||
// duration should be honored that was included in the error.
|
||||
//
|
||||
// The function must return true if the error argument is retry-able,
|
||||
// otherwise it must return false for the first return parameter.
|
||||
//
|
||||
// The function must return a non-zero time.Duration if the error contains
|
||||
// explicit throttle duration that should be honored, otherwise it must return
|
||||
// a zero valued time.Duration.
|
||||
type EvaluateFunc func(error) (bool, time.Duration)
|
||||
|
||||
// RequestFunc returns a RequestFunc using the evaluate function to determine
|
||||
// if requests can be retried and based on the exponential backoff
|
||||
// configuration of c.
|
||||
func (c Config) RequestFunc(evaluate EvaluateFunc) RequestFunc {
|
||||
if !c.Enabled {
|
||||
return func(ctx context.Context, fn func(context.Context) error) error {
|
||||
return fn(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
return func(ctx context.Context, fn func(context.Context) error) error {
|
||||
// Do not use NewExponentialBackOff since it calls Reset and the code here
|
||||
// must call Reset after changing the InitialInterval (this saves an
|
||||
// unnecessary call to Now).
|
||||
b := &backoff.ExponentialBackOff{
|
||||
InitialInterval: c.InitialInterval,
|
||||
RandomizationFactor: backoff.DefaultRandomizationFactor,
|
||||
Multiplier: backoff.DefaultMultiplier,
|
||||
MaxInterval: c.MaxInterval,
|
||||
MaxElapsedTime: c.MaxElapsedTime,
|
||||
Stop: backoff.Stop,
|
||||
Clock: backoff.SystemClock,
|
||||
}
|
||||
b.Reset()
|
||||
|
||||
for {
|
||||
err := fn(ctx)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
retryable, throttle := evaluate(err)
|
||||
if !retryable {
|
||||
return err
|
||||
}
|
||||
|
||||
bOff := b.NextBackOff()
|
||||
if bOff == backoff.Stop {
|
||||
return fmt.Errorf("max retry time elapsed: %w", err)
|
||||
}
|
||||
|
||||
// Wait for the greater of the backoff or throttle delay.
|
||||
var delay time.Duration
|
||||
if bOff > throttle {
|
||||
delay = bOff
|
||||
} else {
|
||||
elapsed := b.GetElapsedTime()
|
||||
if b.MaxElapsedTime != 0 && elapsed+throttle > b.MaxElapsedTime {
|
||||
return fmt.Errorf("max retry time would elapse: %w", err)
|
||||
}
|
||||
delay = throttle
|
||||
}
|
||||
|
||||
if ctxErr := waitFunc(ctx, delay); ctxErr != nil {
|
||||
return fmt.Errorf("%w: %w", ctxErr, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allow override for testing.
|
||||
var waitFunc = wait
|
||||
|
||||
// wait takes the caller's context, and the amount of time to wait. It will
|
||||
// return nil if the timer fires before or at the same time as the context's
|
||||
// deadline. This indicates that the call can be retried.
|
||||
func wait(ctx context.Context, delay time.Duration) error {
|
||||
timer := time.NewTimer(delay)
|
||||
defer timer.Stop()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// Handle the case where the timer and context deadline end
|
||||
// simultaneously by prioritizing the timer expiration nil value
|
||||
// response.
|
||||
select {
|
||||
case <-timer.C:
|
||||
default:
|
||||
return ctx.Err()
|
||||
}
|
||||
case <-timer.C:
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
144
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/transform/attribute.go
generated
vendored
Normal file
144
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/transform/attribute.go
generated
vendored
Normal file
@@ -0,0 +1,144 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/otlpmetric/transform/attribute.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package transform // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/transform"
|
||||
|
||||
import (
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
cpb "go.opentelemetry.io/proto/otlp/common/v1"
|
||||
)
|
||||
|
||||
// AttrIter transforms an attribute iterator into OTLP key-values.
|
||||
func AttrIter(iter attribute.Iterator) []*cpb.KeyValue {
|
||||
l := iter.Len()
|
||||
if l == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
out := make([]*cpb.KeyValue, 0, l)
|
||||
for iter.Next() {
|
||||
out = append(out, KeyValue(iter.Attribute()))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// KeyValues transforms a slice of attribute KeyValues into OTLP key-values.
|
||||
func KeyValues(attrs []attribute.KeyValue) []*cpb.KeyValue {
|
||||
if len(attrs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
out := make([]*cpb.KeyValue, 0, len(attrs))
|
||||
for _, kv := range attrs {
|
||||
out = append(out, KeyValue(kv))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// KeyValue transforms an attribute KeyValue into an OTLP key-value.
|
||||
func KeyValue(kv attribute.KeyValue) *cpb.KeyValue {
|
||||
return &cpb.KeyValue{Key: string(kv.Key), Value: Value(kv.Value)}
|
||||
}
|
||||
|
||||
// Value transforms an attribute Value into an OTLP AnyValue.
|
||||
func Value(v attribute.Value) *cpb.AnyValue {
|
||||
av := new(cpb.AnyValue)
|
||||
switch v.Type() {
|
||||
case attribute.BOOL:
|
||||
av.Value = &cpb.AnyValue_BoolValue{
|
||||
BoolValue: v.AsBool(),
|
||||
}
|
||||
case attribute.BOOLSLICE:
|
||||
av.Value = &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: boolSliceValues(v.AsBoolSlice()),
|
||||
},
|
||||
}
|
||||
case attribute.INT64:
|
||||
av.Value = &cpb.AnyValue_IntValue{
|
||||
IntValue: v.AsInt64(),
|
||||
}
|
||||
case attribute.INT64SLICE:
|
||||
av.Value = &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: int64SliceValues(v.AsInt64Slice()),
|
||||
},
|
||||
}
|
||||
case attribute.FLOAT64:
|
||||
av.Value = &cpb.AnyValue_DoubleValue{
|
||||
DoubleValue: v.AsFloat64(),
|
||||
}
|
||||
case attribute.FLOAT64SLICE:
|
||||
av.Value = &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: float64SliceValues(v.AsFloat64Slice()),
|
||||
},
|
||||
}
|
||||
case attribute.STRING:
|
||||
av.Value = &cpb.AnyValue_StringValue{
|
||||
StringValue: v.AsString(),
|
||||
}
|
||||
case attribute.STRINGSLICE:
|
||||
av.Value = &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: stringSliceValues(v.AsStringSlice()),
|
||||
},
|
||||
}
|
||||
default:
|
||||
av.Value = &cpb.AnyValue_StringValue{
|
||||
StringValue: "INVALID",
|
||||
}
|
||||
}
|
||||
return av
|
||||
}
|
||||
|
||||
func boolSliceValues(vals []bool) []*cpb.AnyValue {
|
||||
converted := make([]*cpb.AnyValue, len(vals))
|
||||
for i, v := range vals {
|
||||
converted[i] = &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_BoolValue{
|
||||
BoolValue: v,
|
||||
},
|
||||
}
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
||||
func int64SliceValues(vals []int64) []*cpb.AnyValue {
|
||||
converted := make([]*cpb.AnyValue, len(vals))
|
||||
for i, v := range vals {
|
||||
converted[i] = &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_IntValue{
|
||||
IntValue: v,
|
||||
},
|
||||
}
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
||||
func float64SliceValues(vals []float64) []*cpb.AnyValue {
|
||||
converted := make([]*cpb.AnyValue, len(vals))
|
||||
for i, v := range vals {
|
||||
converted[i] = &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_DoubleValue{
|
||||
DoubleValue: v,
|
||||
},
|
||||
}
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
||||
func stringSliceValues(vals []string) []*cpb.AnyValue {
|
||||
converted := make([]*cpb.AnyValue, len(vals))
|
||||
for i, v := range vals {
|
||||
converted[i] = &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_StringValue{
|
||||
StringValue: v,
|
||||
},
|
||||
}
|
||||
}
|
||||
return converted
|
||||
}
|
103
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/transform/error.go
generated
vendored
Normal file
103
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/transform/error.go
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/otlpmetric/transform/error.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package transform // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/transform"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
mpb "go.opentelemetry.io/proto/otlp/metrics/v1"
|
||||
)
|
||||
|
||||
var (
|
||||
errUnknownAggregation = errors.New("unknown aggregation")
|
||||
errUnknownTemporality = errors.New("unknown temporality")
|
||||
)
|
||||
|
||||
type errMetric struct {
|
||||
m *mpb.Metric
|
||||
err error
|
||||
}
|
||||
|
||||
func (e errMetric) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
func (e errMetric) Error() string {
|
||||
format := "invalid metric (name: %q, description: %q, unit: %q): %s"
|
||||
return fmt.Sprintf(format, e.m.Name, e.m.Description, e.m.Unit, e.err)
|
||||
}
|
||||
|
||||
func (e errMetric) Is(target error) bool {
|
||||
return errors.Is(e.err, target)
|
||||
}
|
||||
|
||||
// multiErr is used by the data-type transform functions to wrap multiple
|
||||
// errors into a single return value. The error message will show all errors
|
||||
// as a list and scope them by the datatype name that is returning them.
|
||||
type multiErr struct {
|
||||
datatype string
|
||||
errs []error
|
||||
}
|
||||
|
||||
// errOrNil returns nil if e contains no errors, otherwise it returns e.
|
||||
func (e *multiErr) errOrNil() error {
|
||||
if len(e.errs) == 0 {
|
||||
return nil
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// append adds err to e. If err is a multiErr, its errs are flattened into e.
|
||||
func (e *multiErr) append(err error) {
|
||||
// Do not use errors.As here, this should only be flattened one layer. If
|
||||
// there is a *multiErr several steps down the chain, all the errors above
|
||||
// it will be discarded if errors.As is used instead.
|
||||
switch other := err.(type) { //nolint:errorlint
|
||||
case *multiErr:
|
||||
// Flatten err errors into e.
|
||||
e.errs = append(e.errs, other.errs...)
|
||||
default:
|
||||
e.errs = append(e.errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *multiErr) Error() string {
|
||||
es := make([]string, len(e.errs))
|
||||
for i, err := range e.errs {
|
||||
es[i] = fmt.Sprintf("* %s", err)
|
||||
}
|
||||
|
||||
format := "%d errors occurred transforming %s:\n\t%s"
|
||||
return fmt.Sprintf(format, len(es), e.datatype, strings.Join(es, "\n\t"))
|
||||
}
|
||||
|
||||
func (e *multiErr) Unwrap() error {
|
||||
switch len(e.errs) {
|
||||
case 0:
|
||||
return nil
|
||||
case 1:
|
||||
return e.errs[0]
|
||||
}
|
||||
|
||||
// Return a multiErr without the leading error.
|
||||
cp := &multiErr{
|
||||
datatype: e.datatype,
|
||||
errs: make([]error, len(e.errs)-1),
|
||||
}
|
||||
copy(cp.errs, e.errs[1:])
|
||||
return cp
|
||||
}
|
||||
|
||||
func (e *multiErr) Is(target error) bool {
|
||||
if len(e.errs) == 0 {
|
||||
return false
|
||||
}
|
||||
// Check if the first error is target.
|
||||
return errors.Is(e.errs[0], target)
|
||||
}
|
350
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/transform/metricdata.go
generated
vendored
Normal file
350
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/transform/metricdata.go
generated
vendored
Normal file
@@ -0,0 +1,350 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/otlpmetric/transform/metricdata.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package transform provides transformation functionality from the
|
||||
// sdk/metric/metricdata data-types into OTLP data-types.
|
||||
package transform // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/transform"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/sdk/metric/metricdata"
|
||||
cpb "go.opentelemetry.io/proto/otlp/common/v1"
|
||||
mpb "go.opentelemetry.io/proto/otlp/metrics/v1"
|
||||
rpb "go.opentelemetry.io/proto/otlp/resource/v1"
|
||||
)
|
||||
|
||||
// ResourceMetrics returns an OTLP ResourceMetrics generated from rm. If rm
|
||||
// contains invalid ScopeMetrics, an error will be returned along with an OTLP
|
||||
// ResourceMetrics that contains partial OTLP ScopeMetrics.
|
||||
func ResourceMetrics(rm *metricdata.ResourceMetrics) (*mpb.ResourceMetrics, error) {
|
||||
sms, err := ScopeMetrics(rm.ScopeMetrics)
|
||||
return &mpb.ResourceMetrics{
|
||||
Resource: &rpb.Resource{
|
||||
Attributes: AttrIter(rm.Resource.Iter()),
|
||||
},
|
||||
ScopeMetrics: sms,
|
||||
SchemaUrl: rm.Resource.SchemaURL(),
|
||||
}, err
|
||||
}
|
||||
|
||||
// ScopeMetrics returns a slice of OTLP ScopeMetrics generated from sms. If
|
||||
// sms contains invalid metric values, an error will be returned along with a
|
||||
// slice that contains partial OTLP ScopeMetrics.
|
||||
func ScopeMetrics(sms []metricdata.ScopeMetrics) ([]*mpb.ScopeMetrics, error) {
|
||||
errs := &multiErr{datatype: "ScopeMetrics"}
|
||||
out := make([]*mpb.ScopeMetrics, 0, len(sms))
|
||||
for _, sm := range sms {
|
||||
ms, err := Metrics(sm.Metrics)
|
||||
if err != nil {
|
||||
errs.append(err)
|
||||
}
|
||||
|
||||
out = append(out, &mpb.ScopeMetrics{
|
||||
Scope: &cpb.InstrumentationScope{
|
||||
Name: sm.Scope.Name,
|
||||
Version: sm.Scope.Version,
|
||||
Attributes: AttrIter(sm.Scope.Attributes.Iter()),
|
||||
},
|
||||
Metrics: ms,
|
||||
SchemaUrl: sm.Scope.SchemaURL,
|
||||
})
|
||||
}
|
||||
return out, errs.errOrNil()
|
||||
}
|
||||
|
||||
// Metrics returns a slice of OTLP Metric generated from ms. If ms contains
|
||||
// invalid metric values, an error will be returned along with a slice that
|
||||
// contains partial OTLP Metrics.
|
||||
func Metrics(ms []metricdata.Metrics) ([]*mpb.Metric, error) {
|
||||
errs := &multiErr{datatype: "Metrics"}
|
||||
out := make([]*mpb.Metric, 0, len(ms))
|
||||
for _, m := range ms {
|
||||
o, err := metric(m)
|
||||
if err != nil {
|
||||
// Do not include invalid data. Drop the metric, report the error.
|
||||
errs.append(errMetric{m: o, err: err})
|
||||
continue
|
||||
}
|
||||
out = append(out, o)
|
||||
}
|
||||
return out, errs.errOrNil()
|
||||
}
|
||||
|
||||
func metric(m metricdata.Metrics) (*mpb.Metric, error) {
|
||||
var err error
|
||||
out := &mpb.Metric{
|
||||
Name: m.Name,
|
||||
Description: m.Description,
|
||||
Unit: m.Unit,
|
||||
}
|
||||
switch a := m.Data.(type) {
|
||||
case metricdata.Gauge[int64]:
|
||||
out.Data = Gauge(a)
|
||||
case metricdata.Gauge[float64]:
|
||||
out.Data = Gauge(a)
|
||||
case metricdata.Sum[int64]:
|
||||
out.Data, err = Sum(a)
|
||||
case metricdata.Sum[float64]:
|
||||
out.Data, err = Sum(a)
|
||||
case metricdata.Histogram[int64]:
|
||||
out.Data, err = Histogram(a)
|
||||
case metricdata.Histogram[float64]:
|
||||
out.Data, err = Histogram(a)
|
||||
case metricdata.ExponentialHistogram[int64]:
|
||||
out.Data, err = ExponentialHistogram(a)
|
||||
case metricdata.ExponentialHistogram[float64]:
|
||||
out.Data, err = ExponentialHistogram(a)
|
||||
case metricdata.Summary:
|
||||
out.Data = Summary(a)
|
||||
default:
|
||||
return out, fmt.Errorf("%w: %T", errUnknownAggregation, a)
|
||||
}
|
||||
return out, err
|
||||
}
|
||||
|
||||
// Gauge returns an OTLP Metric_Gauge generated from g.
|
||||
func Gauge[N int64 | float64](g metricdata.Gauge[N]) *mpb.Metric_Gauge {
|
||||
return &mpb.Metric_Gauge{
|
||||
Gauge: &mpb.Gauge{
|
||||
DataPoints: DataPoints(g.DataPoints),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Sum returns an OTLP Metric_Sum generated from s. An error is returned
|
||||
// if the temporality of s is unknown.
|
||||
func Sum[N int64 | float64](s metricdata.Sum[N]) (*mpb.Metric_Sum, error) {
|
||||
t, err := Temporality(s.Temporality)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mpb.Metric_Sum{
|
||||
Sum: &mpb.Sum{
|
||||
AggregationTemporality: t,
|
||||
IsMonotonic: s.IsMonotonic,
|
||||
DataPoints: DataPoints(s.DataPoints),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DataPoints returns a slice of OTLP NumberDataPoint generated from dPts.
|
||||
func DataPoints[N int64 | float64](dPts []metricdata.DataPoint[N]) []*mpb.NumberDataPoint {
|
||||
out := make([]*mpb.NumberDataPoint, 0, len(dPts))
|
||||
for _, dPt := range dPts {
|
||||
ndp := &mpb.NumberDataPoint{
|
||||
Attributes: AttrIter(dPt.Attributes.Iter()),
|
||||
StartTimeUnixNano: timeUnixNano(dPt.StartTime),
|
||||
TimeUnixNano: timeUnixNano(dPt.Time),
|
||||
Exemplars: Exemplars(dPt.Exemplars),
|
||||
}
|
||||
switch v := any(dPt.Value).(type) {
|
||||
case int64:
|
||||
ndp.Value = &mpb.NumberDataPoint_AsInt{
|
||||
AsInt: v,
|
||||
}
|
||||
case float64:
|
||||
ndp.Value = &mpb.NumberDataPoint_AsDouble{
|
||||
AsDouble: v,
|
||||
}
|
||||
}
|
||||
out = append(out, ndp)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Histogram returns an OTLP Metric_Histogram generated from h. An error is
|
||||
// returned if the temporality of h is unknown.
|
||||
func Histogram[N int64 | float64](h metricdata.Histogram[N]) (*mpb.Metric_Histogram, error) {
|
||||
t, err := Temporality(h.Temporality)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mpb.Metric_Histogram{
|
||||
Histogram: &mpb.Histogram{
|
||||
AggregationTemporality: t,
|
||||
DataPoints: HistogramDataPoints(h.DataPoints),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// HistogramDataPoints returns a slice of OTLP HistogramDataPoint generated
|
||||
// from dPts.
|
||||
func HistogramDataPoints[N int64 | float64](dPts []metricdata.HistogramDataPoint[N]) []*mpb.HistogramDataPoint {
|
||||
out := make([]*mpb.HistogramDataPoint, 0, len(dPts))
|
||||
for _, dPt := range dPts {
|
||||
sum := float64(dPt.Sum)
|
||||
hdp := &mpb.HistogramDataPoint{
|
||||
Attributes: AttrIter(dPt.Attributes.Iter()),
|
||||
StartTimeUnixNano: timeUnixNano(dPt.StartTime),
|
||||
TimeUnixNano: timeUnixNano(dPt.Time),
|
||||
Count: dPt.Count,
|
||||
Sum: &sum,
|
||||
BucketCounts: dPt.BucketCounts,
|
||||
ExplicitBounds: dPt.Bounds,
|
||||
Exemplars: Exemplars(dPt.Exemplars),
|
||||
}
|
||||
if v, ok := dPt.Min.Value(); ok {
|
||||
vF64 := float64(v)
|
||||
hdp.Min = &vF64
|
||||
}
|
||||
if v, ok := dPt.Max.Value(); ok {
|
||||
vF64 := float64(v)
|
||||
hdp.Max = &vF64
|
||||
}
|
||||
out = append(out, hdp)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// ExponentialHistogram returns an OTLP Metric_ExponentialHistogram generated from h. An error is
|
||||
// returned if the temporality of h is unknown.
|
||||
func ExponentialHistogram[N int64 | float64](h metricdata.ExponentialHistogram[N]) (*mpb.Metric_ExponentialHistogram, error) {
|
||||
t, err := Temporality(h.Temporality)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mpb.Metric_ExponentialHistogram{
|
||||
ExponentialHistogram: &mpb.ExponentialHistogram{
|
||||
AggregationTemporality: t,
|
||||
DataPoints: ExponentialHistogramDataPoints(h.DataPoints),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ExponentialHistogramDataPoints returns a slice of OTLP ExponentialHistogramDataPoint generated
|
||||
// from dPts.
|
||||
func ExponentialHistogramDataPoints[N int64 | float64](dPts []metricdata.ExponentialHistogramDataPoint[N]) []*mpb.ExponentialHistogramDataPoint {
|
||||
out := make([]*mpb.ExponentialHistogramDataPoint, 0, len(dPts))
|
||||
for _, dPt := range dPts {
|
||||
sum := float64(dPt.Sum)
|
||||
ehdp := &mpb.ExponentialHistogramDataPoint{
|
||||
Attributes: AttrIter(dPt.Attributes.Iter()),
|
||||
StartTimeUnixNano: timeUnixNano(dPt.StartTime),
|
||||
TimeUnixNano: timeUnixNano(dPt.Time),
|
||||
Count: dPt.Count,
|
||||
Sum: &sum,
|
||||
Scale: dPt.Scale,
|
||||
ZeroCount: dPt.ZeroCount,
|
||||
Exemplars: Exemplars(dPt.Exemplars),
|
||||
|
||||
Positive: ExponentialHistogramDataPointBuckets(dPt.PositiveBucket),
|
||||
Negative: ExponentialHistogramDataPointBuckets(dPt.NegativeBucket),
|
||||
}
|
||||
if v, ok := dPt.Min.Value(); ok {
|
||||
vF64 := float64(v)
|
||||
ehdp.Min = &vF64
|
||||
}
|
||||
if v, ok := dPt.Max.Value(); ok {
|
||||
vF64 := float64(v)
|
||||
ehdp.Max = &vF64
|
||||
}
|
||||
out = append(out, ehdp)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// ExponentialHistogramDataPointBuckets returns an OTLP ExponentialHistogramDataPoint_Buckets generated
|
||||
// from bucket.
|
||||
func ExponentialHistogramDataPointBuckets(bucket metricdata.ExponentialBucket) *mpb.ExponentialHistogramDataPoint_Buckets {
|
||||
return &mpb.ExponentialHistogramDataPoint_Buckets{
|
||||
Offset: bucket.Offset,
|
||||
BucketCounts: bucket.Counts,
|
||||
}
|
||||
}
|
||||
|
||||
// Temporality returns an OTLP AggregationTemporality generated from t. If t
|
||||
// is unknown, an error is returned along with the invalid
|
||||
// AggregationTemporality_AGGREGATION_TEMPORALITY_UNSPECIFIED.
|
||||
func Temporality(t metricdata.Temporality) (mpb.AggregationTemporality, error) {
|
||||
switch t {
|
||||
case metricdata.DeltaTemporality:
|
||||
return mpb.AggregationTemporality_AGGREGATION_TEMPORALITY_DELTA, nil
|
||||
case metricdata.CumulativeTemporality:
|
||||
return mpb.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE, nil
|
||||
default:
|
||||
err := fmt.Errorf("%w: %s", errUnknownTemporality, t)
|
||||
return mpb.AggregationTemporality_AGGREGATION_TEMPORALITY_UNSPECIFIED, err
|
||||
}
|
||||
}
|
||||
|
||||
// timeUnixNano returns t as a Unix time, the number of nanoseconds elapsed
|
||||
// since January 1, 1970 UTC as uint64.
|
||||
// The result is undefined if the Unix time
|
||||
// in nanoseconds cannot be represented by an int64
|
||||
// (a date before the year 1678 or after 2262).
|
||||
// timeUnixNano on the zero Time returns 0.
|
||||
// The result does not depend on the location associated with t.
|
||||
func timeUnixNano(t time.Time) uint64 {
|
||||
return uint64(max(0, t.UnixNano())) // nolint:gosec // Overflow checked.
|
||||
}
|
||||
|
||||
// Exemplars returns a slice of OTLP Exemplars generated from exemplars.
|
||||
func Exemplars[N int64 | float64](exemplars []metricdata.Exemplar[N]) []*mpb.Exemplar {
|
||||
out := make([]*mpb.Exemplar, 0, len(exemplars))
|
||||
for _, exemplar := range exemplars {
|
||||
e := &mpb.Exemplar{
|
||||
FilteredAttributes: KeyValues(exemplar.FilteredAttributes),
|
||||
TimeUnixNano: timeUnixNano(exemplar.Time),
|
||||
SpanId: exemplar.SpanID,
|
||||
TraceId: exemplar.TraceID,
|
||||
}
|
||||
switch v := any(exemplar.Value).(type) {
|
||||
case int64:
|
||||
e.Value = &mpb.Exemplar_AsInt{
|
||||
AsInt: v,
|
||||
}
|
||||
case float64:
|
||||
e.Value = &mpb.Exemplar_AsDouble{
|
||||
AsDouble: v,
|
||||
}
|
||||
}
|
||||
out = append(out, e)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Summary returns an OTLP Metric_Summary generated from s.
|
||||
func Summary(s metricdata.Summary) *mpb.Metric_Summary {
|
||||
return &mpb.Metric_Summary{
|
||||
Summary: &mpb.Summary{
|
||||
DataPoints: SummaryDataPoints(s.DataPoints),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// SummaryDataPoints returns a slice of OTLP SummaryDataPoint generated from
|
||||
// dPts.
|
||||
func SummaryDataPoints(dPts []metricdata.SummaryDataPoint) []*mpb.SummaryDataPoint {
|
||||
out := make([]*mpb.SummaryDataPoint, 0, len(dPts))
|
||||
for _, dPt := range dPts {
|
||||
sdp := &mpb.SummaryDataPoint{
|
||||
Attributes: AttrIter(dPt.Attributes.Iter()),
|
||||
StartTimeUnixNano: timeUnixNano(dPt.StartTime),
|
||||
TimeUnixNano: timeUnixNano(dPt.Time),
|
||||
Count: dPt.Count,
|
||||
Sum: dPt.Sum,
|
||||
QuantileValues: QuantileValues(dPt.QuantileValues),
|
||||
}
|
||||
out = append(out, sdp)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// QuantileValues returns a slice of OTLP SummaryDataPoint_ValueAtQuantile
|
||||
// generated from quantiles.
|
||||
func QuantileValues(quantiles []metricdata.QuantileValue) []*mpb.SummaryDataPoint_ValueAtQuantile {
|
||||
out := make([]*mpb.SummaryDataPoint_ValueAtQuantile, 0, len(quantiles))
|
||||
for _, q := range quantiles {
|
||||
quantile := &mpb.SummaryDataPoint_ValueAtQuantile{
|
||||
Quantile: q.Quantile,
|
||||
Value: q.Value,
|
||||
}
|
||||
out = append(out, quantile)
|
||||
}
|
||||
return out
|
||||
}
|
9
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/version.go
generated
vendored
Normal file
9
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/version.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otlpmetrichttp // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp"
|
||||
|
||||
// Version is the current release version of the OpenTelemetry OTLP over HTTP/protobuf metrics exporter in use.
|
||||
func Version() string {
|
||||
return "1.35.0"
|
||||
}
|
201
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutlog/LICENSE
generated
vendored
Normal file
201
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutlog/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
3
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutlog/README.md
generated
vendored
Normal file
3
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutlog/README.md
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# STDOUT Log Exporter
|
||||
|
||||
[](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/stdout/stdoutlog)
|
85
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutlog/config.go
generated
vendored
Normal file
85
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutlog/config.go
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package stdoutlog // import "go.opentelemetry.io/otel/exporters/stdout/stdoutlog"
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultWriter io.Writer = os.Stdout
|
||||
defaultPrettyPrint = false
|
||||
defaultTimestamps = true
|
||||
)
|
||||
|
||||
// config contains options for the STDOUT exporter.
|
||||
type config struct {
|
||||
// Writer is the destination. If not set, os.Stdout is used.
|
||||
Writer io.Writer
|
||||
|
||||
// PrettyPrint will encode the output into readable JSON. Default is
|
||||
// false.
|
||||
PrettyPrint bool
|
||||
|
||||
// Timestamps specifies if timestamps should be printed. Default is
|
||||
// true.
|
||||
Timestamps bool
|
||||
}
|
||||
|
||||
// newConfig creates a validated Config configured with options.
|
||||
func newConfig(options []Option) config {
|
||||
cfg := config{
|
||||
Writer: defaultWriter,
|
||||
PrettyPrint: defaultPrettyPrint,
|
||||
Timestamps: defaultTimestamps,
|
||||
}
|
||||
for _, opt := range options {
|
||||
cfg = opt.apply(cfg)
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
// Option sets the configuration value for an Exporter.
|
||||
type Option interface {
|
||||
apply(config) config
|
||||
}
|
||||
|
||||
// WithWriter sets the export stream destination.
|
||||
func WithWriter(w io.Writer) Option {
|
||||
return writerOption{w}
|
||||
}
|
||||
|
||||
type writerOption struct {
|
||||
W io.Writer
|
||||
}
|
||||
|
||||
func (o writerOption) apply(cfg config) config {
|
||||
cfg.Writer = o.W
|
||||
return cfg
|
||||
}
|
||||
|
||||
// WithPrettyPrint prettifies the emitted output.
|
||||
func WithPrettyPrint() Option {
|
||||
return prettyPrintOption(true)
|
||||
}
|
||||
|
||||
type prettyPrintOption bool
|
||||
|
||||
func (o prettyPrintOption) apply(cfg config) config {
|
||||
cfg.PrettyPrint = bool(o)
|
||||
return cfg
|
||||
}
|
||||
|
||||
// WithoutTimestamps sets the export stream to not include timestamps.
|
||||
func WithoutTimestamps() Option {
|
||||
return timestampsOption(false)
|
||||
}
|
||||
|
||||
type timestampsOption bool
|
||||
|
||||
func (o timestampsOption) apply(cfg config) config {
|
||||
cfg.Timestamps = bool(o)
|
||||
return cfg
|
||||
}
|
12
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutlog/doc.go
generated
vendored
Normal file
12
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutlog/doc.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package stdoutlog provides an exporter for OpenTelemetry log
|
||||
// telemetry.
|
||||
//
|
||||
// The exporter is intended to be used for testing and debugging, it is not
|
||||
// meant for production use. Additionally, it does not provide an interchange
|
||||
// format for OpenTelemetry that is supported with any stability or
|
||||
// compatibility guarantees. If these are needed features, please use the OTLP
|
||||
// exporter instead.
|
||||
package stdoutlog // import "go.opentelemetry.io/otel/exporters/stdout/stdoutlog"
|
72
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutlog/exporter.go
generated
vendored
Normal file
72
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutlog/exporter.go
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package stdoutlog // import "go.opentelemetry.io/otel/exporters/stdout/stdoutlog"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"sync/atomic"
|
||||
|
||||
"go.opentelemetry.io/otel/sdk/log"
|
||||
)
|
||||
|
||||
var _ log.Exporter = &Exporter{}
|
||||
|
||||
// Exporter writes JSON-encoded log records to an [io.Writer] ([os.Stdout] by default).
|
||||
// Exporter must be created with [New].
|
||||
type Exporter struct {
|
||||
encoder atomic.Pointer[json.Encoder]
|
||||
timestamps bool
|
||||
}
|
||||
|
||||
// New creates an [Exporter].
|
||||
func New(options ...Option) (*Exporter, error) {
|
||||
cfg := newConfig(options)
|
||||
|
||||
enc := json.NewEncoder(cfg.Writer)
|
||||
if cfg.PrettyPrint {
|
||||
enc.SetIndent("", "\t")
|
||||
}
|
||||
|
||||
e := Exporter{
|
||||
timestamps: cfg.Timestamps,
|
||||
}
|
||||
e.encoder.Store(enc)
|
||||
|
||||
return &e, nil
|
||||
}
|
||||
|
||||
// Export exports log records to writer.
|
||||
func (e *Exporter) Export(ctx context.Context, records []log.Record) error {
|
||||
enc := e.encoder.Load()
|
||||
if enc == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, record := range records {
|
||||
// Honor context cancellation.
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Encode record, one by one.
|
||||
recordJSON := e.newRecordJSON(record)
|
||||
if err := enc.Encode(recordJSON); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Shutdown shuts down the Exporter.
|
||||
// Calls to Export will perform no operation after this is called.
|
||||
func (e *Exporter) Shutdown(context.Context) error {
|
||||
e.encoder.Store(nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ForceFlush performs no action.
|
||||
func (e *Exporter) ForceFlush(context.Context) error {
|
||||
return nil
|
||||
}
|
132
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutlog/record.go
generated
vendored
Normal file
132
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutlog/record.go
generated
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package stdoutlog // import "go.opentelemetry.io/otel/exporters/stdout/stdoutlog"
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/log"
|
||||
"go.opentelemetry.io/otel/sdk/instrumentation"
|
||||
sdklog "go.opentelemetry.io/otel/sdk/log"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
func newValue(v log.Value) value {
|
||||
return value{Value: v}
|
||||
}
|
||||
|
||||
type value struct {
|
||||
log.Value
|
||||
}
|
||||
|
||||
// MarshalJSON implements a custom marshal function to encode log.Value.
|
||||
func (v value) MarshalJSON() ([]byte, error) {
|
||||
var jsonVal struct {
|
||||
Type string
|
||||
Value interface{}
|
||||
}
|
||||
jsonVal.Type = v.Kind().String()
|
||||
|
||||
switch v.Kind() {
|
||||
case log.KindString:
|
||||
jsonVal.Value = v.AsString()
|
||||
case log.KindInt64:
|
||||
jsonVal.Value = v.AsInt64()
|
||||
case log.KindFloat64:
|
||||
jsonVal.Value = v.AsFloat64()
|
||||
case log.KindBool:
|
||||
jsonVal.Value = v.AsBool()
|
||||
case log.KindBytes:
|
||||
jsonVal.Value = v.AsBytes()
|
||||
case log.KindMap:
|
||||
m := v.AsMap()
|
||||
values := make([]keyValue, 0, len(m))
|
||||
for _, kv := range m {
|
||||
values = append(values, keyValue{
|
||||
Key: kv.Key,
|
||||
Value: newValue(kv.Value),
|
||||
})
|
||||
}
|
||||
|
||||
jsonVal.Value = values
|
||||
case log.KindSlice:
|
||||
s := v.AsSlice()
|
||||
values := make([]value, 0, len(s))
|
||||
for _, e := range s {
|
||||
values = append(values, newValue(e))
|
||||
}
|
||||
|
||||
jsonVal.Value = values
|
||||
case log.KindEmpty:
|
||||
jsonVal.Value = nil
|
||||
default:
|
||||
return nil, errors.New("invalid Kind")
|
||||
}
|
||||
|
||||
return json.Marshal(jsonVal)
|
||||
}
|
||||
|
||||
type keyValue struct {
|
||||
Key string
|
||||
Value value
|
||||
}
|
||||
|
||||
// recordJSON is a JSON-serializable representation of a Record.
|
||||
type recordJSON struct {
|
||||
Timestamp *time.Time `json:",omitempty"`
|
||||
ObservedTimestamp *time.Time `json:",omitempty"`
|
||||
EventName string `json:",omitempty"`
|
||||
Severity log.Severity
|
||||
SeverityText string
|
||||
Body value
|
||||
Attributes []keyValue
|
||||
TraceID trace.TraceID
|
||||
SpanID trace.SpanID
|
||||
TraceFlags trace.TraceFlags
|
||||
Resource *resource.Resource
|
||||
Scope instrumentation.Scope
|
||||
DroppedAttributes int
|
||||
}
|
||||
|
||||
func (e *Exporter) newRecordJSON(r sdklog.Record) recordJSON {
|
||||
res := r.Resource()
|
||||
newRecord := recordJSON{
|
||||
EventName: r.EventName(),
|
||||
Severity: r.Severity(),
|
||||
SeverityText: r.SeverityText(),
|
||||
Body: newValue(r.Body()),
|
||||
|
||||
TraceID: r.TraceID(),
|
||||
SpanID: r.SpanID(),
|
||||
TraceFlags: r.TraceFlags(),
|
||||
|
||||
Attributes: make([]keyValue, 0, r.AttributesLen()),
|
||||
|
||||
Resource: &res,
|
||||
Scope: r.InstrumentationScope(),
|
||||
|
||||
DroppedAttributes: r.DroppedAttributes(),
|
||||
}
|
||||
|
||||
r.WalkAttributes(func(kv log.KeyValue) bool {
|
||||
newRecord.Attributes = append(newRecord.Attributes, keyValue{
|
||||
Key: kv.Key,
|
||||
Value: newValue(kv.Value),
|
||||
})
|
||||
return true
|
||||
})
|
||||
|
||||
if e.timestamps {
|
||||
timestamp := r.Timestamp()
|
||||
newRecord.Timestamp = ×tamp
|
||||
|
||||
observedTimestamp := r.ObservedTimestamp()
|
||||
newRecord.ObservedTimestamp = &observedTimestamp
|
||||
}
|
||||
|
||||
return newRecord
|
||||
}
|
201
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutmetric/LICENSE
generated
vendored
Normal file
201
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutmetric/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
3
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutmetric/README.md
generated
vendored
Normal file
3
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutmetric/README.md
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# STDOUT Metric Exporter
|
||||
|
||||
[](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/stdout/stdoutmetric)
|
132
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutmetric/config.go
generated
vendored
Normal file
132
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutmetric/config.go
generated
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package stdoutmetric // import "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric"
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"go.opentelemetry.io/otel/sdk/metric"
|
||||
)
|
||||
|
||||
// config contains options for the exporter.
|
||||
type config struct {
|
||||
prettyPrint bool
|
||||
encoder *encoderHolder
|
||||
temporalitySelector metric.TemporalitySelector
|
||||
aggregationSelector metric.AggregationSelector
|
||||
redactTimestamps bool
|
||||
}
|
||||
|
||||
// newConfig creates a validated config configured with options.
|
||||
func newConfig(options ...Option) config {
|
||||
cfg := config{}
|
||||
for _, opt := range options {
|
||||
cfg = opt.apply(cfg)
|
||||
}
|
||||
|
||||
if cfg.encoder == nil {
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
cfg.encoder = &encoderHolder{encoder: enc}
|
||||
}
|
||||
|
||||
if cfg.prettyPrint {
|
||||
if e, ok := cfg.encoder.encoder.(*json.Encoder); ok {
|
||||
e.SetIndent("", "\t")
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.temporalitySelector == nil {
|
||||
cfg.temporalitySelector = metric.DefaultTemporalitySelector
|
||||
}
|
||||
|
||||
if cfg.aggregationSelector == nil {
|
||||
cfg.aggregationSelector = metric.DefaultAggregationSelector
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
// Option sets exporter option values.
|
||||
type Option interface {
|
||||
apply(config) config
|
||||
}
|
||||
|
||||
type optionFunc func(config) config
|
||||
|
||||
func (o optionFunc) apply(c config) config {
|
||||
return o(c)
|
||||
}
|
||||
|
||||
// WithEncoder sets the exporter to use encoder to encode all the metric
|
||||
// data-types to an output.
|
||||
func WithEncoder(encoder Encoder) Option {
|
||||
return optionFunc(func(c config) config {
|
||||
if encoder != nil {
|
||||
c.encoder = &encoderHolder{encoder: encoder}
|
||||
}
|
||||
return c
|
||||
})
|
||||
}
|
||||
|
||||
// WithWriter sets the export stream destination.
|
||||
// Using this option overrides any previously set encoder.
|
||||
func WithWriter(w io.Writer) Option {
|
||||
return WithEncoder(json.NewEncoder(w))
|
||||
}
|
||||
|
||||
// WithPrettyPrint prettifies the emitted output.
|
||||
// This option only works if the encoder is a *json.Encoder, as is the case
|
||||
// when using `WithWriter`.
|
||||
func WithPrettyPrint() Option {
|
||||
return optionFunc(func(c config) config {
|
||||
c.prettyPrint = true
|
||||
return c
|
||||
})
|
||||
}
|
||||
|
||||
// WithTemporalitySelector sets the TemporalitySelector the exporter will use
|
||||
// to determine the Temporality of an instrument based on its kind. If this
|
||||
// option is not used, the exporter will use the DefaultTemporalitySelector
|
||||
// from the go.opentelemetry.io/otel/sdk/metric package.
|
||||
func WithTemporalitySelector(selector metric.TemporalitySelector) Option {
|
||||
return temporalitySelectorOption{selector: selector}
|
||||
}
|
||||
|
||||
type temporalitySelectorOption struct {
|
||||
selector metric.TemporalitySelector
|
||||
}
|
||||
|
||||
func (t temporalitySelectorOption) apply(c config) config {
|
||||
c.temporalitySelector = t.selector
|
||||
return c
|
||||
}
|
||||
|
||||
// WithAggregationSelector sets the AggregationSelector the exporter will use
|
||||
// to determine the aggregation to use for an instrument based on its kind. If
|
||||
// this option is not used, the exporter will use the
|
||||
// DefaultAggregationSelector from the go.opentelemetry.io/otel/sdk/metric
|
||||
// package or the aggregation explicitly passed for a view matching an
|
||||
// instrument.
|
||||
func WithAggregationSelector(selector metric.AggregationSelector) Option {
|
||||
return aggregationSelectorOption{selector: selector}
|
||||
}
|
||||
|
||||
type aggregationSelectorOption struct {
|
||||
selector metric.AggregationSelector
|
||||
}
|
||||
|
||||
func (t aggregationSelectorOption) apply(c config) config {
|
||||
c.aggregationSelector = t.selector
|
||||
return c
|
||||
}
|
||||
|
||||
// WithoutTimestamps sets all timestamps to zero in the output stream.
|
||||
func WithoutTimestamps() Option {
|
||||
return optionFunc(func(c config) config {
|
||||
c.redactTimestamps = true
|
||||
return c
|
||||
})
|
||||
}
|
12
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutmetric/doc.go
generated
vendored
Normal file
12
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutmetric/doc.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package stdoutmetric provides an exporter for OpenTelemetry metric
|
||||
// telemetry.
|
||||
//
|
||||
// The exporter is intended to be used for testing and debugging, it is not
|
||||
// meant for production use. Additionally, it does not provide an interchange
|
||||
// format for OpenTelemetry that is supported with any stability or
|
||||
// compatibility guarantees. If these are needed features, please use the OTLP
|
||||
// exporter instead.
|
||||
package stdoutmetric // import "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric"
|
31
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutmetric/encoder.go
generated
vendored
Normal file
31
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutmetric/encoder.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package stdoutmetric // import "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Encoder encodes and outputs OpenTelemetry metric data-types as human
|
||||
// readable text.
|
||||
type Encoder interface {
|
||||
// Encode handles the encoding and writing of OpenTelemetry metric data.
|
||||
Encode(v any) error
|
||||
}
|
||||
|
||||
// encoderHolder is the concrete type used to wrap an Encoder so it can be
|
||||
// used as a atomic.Value type.
|
||||
type encoderHolder struct {
|
||||
encoder Encoder
|
||||
}
|
||||
|
||||
func (e encoderHolder) Encode(v any) error { return e.encoder.Encode(v) }
|
||||
|
||||
// shutdownEncoder is used when the exporter is shutdown. It always returns
|
||||
// errShutdown when Encode is called.
|
||||
type shutdownEncoder struct{}
|
||||
|
||||
var errShutdown = errors.New("exporter shutdown")
|
||||
|
||||
func (shutdownEncoder) Encode(any) error { return errShutdown }
|
159
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutmetric/exporter.go
generated
vendored
Normal file
159
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutmetric/exporter.go
generated
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package stdoutmetric // import "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
"go.opentelemetry.io/otel/sdk/metric"
|
||||
"go.opentelemetry.io/otel/sdk/metric/metricdata"
|
||||
)
|
||||
|
||||
// exporter is an OpenTelemetry metric exporter.
|
||||
type exporter struct {
|
||||
encVal atomic.Value // encoderHolder
|
||||
|
||||
shutdownOnce sync.Once
|
||||
|
||||
temporalitySelector metric.TemporalitySelector
|
||||
aggregationSelector metric.AggregationSelector
|
||||
|
||||
redactTimestamps bool
|
||||
}
|
||||
|
||||
// New returns a configured metric exporter.
|
||||
//
|
||||
// If no options are passed, the default exporter returned will use a JSON
|
||||
// encoder with tab indentations that output to STDOUT.
|
||||
func New(options ...Option) (metric.Exporter, error) {
|
||||
cfg := newConfig(options...)
|
||||
exp := &exporter{
|
||||
temporalitySelector: cfg.temporalitySelector,
|
||||
aggregationSelector: cfg.aggregationSelector,
|
||||
redactTimestamps: cfg.redactTimestamps,
|
||||
}
|
||||
exp.encVal.Store(*cfg.encoder)
|
||||
return exp, nil
|
||||
}
|
||||
|
||||
func (e *exporter) Temporality(k metric.InstrumentKind) metricdata.Temporality {
|
||||
return e.temporalitySelector(k)
|
||||
}
|
||||
|
||||
func (e *exporter) Aggregation(k metric.InstrumentKind) metric.Aggregation {
|
||||
return e.aggregationSelector(k)
|
||||
}
|
||||
|
||||
func (e *exporter) Export(ctx context.Context, data *metricdata.ResourceMetrics) error {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
if e.redactTimestamps {
|
||||
redactTimestamps(data)
|
||||
}
|
||||
|
||||
global.Debug("STDOUT exporter export", "Data", data)
|
||||
|
||||
return e.encVal.Load().(encoderHolder).Encode(data)
|
||||
}
|
||||
|
||||
func (e *exporter) ForceFlush(context.Context) error {
|
||||
// exporter holds no state, nothing to flush.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *exporter) Shutdown(context.Context) error {
|
||||
e.shutdownOnce.Do(func() {
|
||||
e.encVal.Store(encoderHolder{
|
||||
encoder: shutdownEncoder{},
|
||||
})
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *exporter) MarshalLog() interface{} {
|
||||
return struct{ Type string }{Type: "STDOUT"}
|
||||
}
|
||||
|
||||
func redactTimestamps(orig *metricdata.ResourceMetrics) {
|
||||
for i, sm := range orig.ScopeMetrics {
|
||||
metrics := sm.Metrics
|
||||
for j, m := range metrics {
|
||||
data := m.Data
|
||||
orig.ScopeMetrics[i].Metrics[j].Data = redactAggregationTimestamps(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var errUnknownAggType = errors.New("unknown aggregation type")
|
||||
|
||||
func redactAggregationTimestamps(orig metricdata.Aggregation) metricdata.Aggregation {
|
||||
switch a := orig.(type) {
|
||||
case metricdata.Sum[float64]:
|
||||
return metricdata.Sum[float64]{
|
||||
Temporality: a.Temporality,
|
||||
DataPoints: redactDataPointTimestamps(a.DataPoints),
|
||||
IsMonotonic: a.IsMonotonic,
|
||||
}
|
||||
case metricdata.Sum[int64]:
|
||||
return metricdata.Sum[int64]{
|
||||
Temporality: a.Temporality,
|
||||
DataPoints: redactDataPointTimestamps(a.DataPoints),
|
||||
IsMonotonic: a.IsMonotonic,
|
||||
}
|
||||
case metricdata.Gauge[float64]:
|
||||
return metricdata.Gauge[float64]{
|
||||
DataPoints: redactDataPointTimestamps(a.DataPoints),
|
||||
}
|
||||
case metricdata.Gauge[int64]:
|
||||
return metricdata.Gauge[int64]{
|
||||
DataPoints: redactDataPointTimestamps(a.DataPoints),
|
||||
}
|
||||
case metricdata.Histogram[int64]:
|
||||
return metricdata.Histogram[int64]{
|
||||
Temporality: a.Temporality,
|
||||
DataPoints: redactHistogramTimestamps(a.DataPoints),
|
||||
}
|
||||
case metricdata.Histogram[float64]:
|
||||
return metricdata.Histogram[float64]{
|
||||
Temporality: a.Temporality,
|
||||
DataPoints: redactHistogramTimestamps(a.DataPoints),
|
||||
}
|
||||
default:
|
||||
global.Error(errUnknownAggType, fmt.Sprintf("%T", a))
|
||||
return orig
|
||||
}
|
||||
}
|
||||
|
||||
func redactHistogramTimestamps[T int64 | float64](hdp []metricdata.HistogramDataPoint[T]) []metricdata.HistogramDataPoint[T] {
|
||||
out := make([]metricdata.HistogramDataPoint[T], len(hdp))
|
||||
for i, dp := range hdp {
|
||||
out[i] = metricdata.HistogramDataPoint[T]{
|
||||
Attributes: dp.Attributes,
|
||||
Count: dp.Count,
|
||||
Sum: dp.Sum,
|
||||
Bounds: dp.Bounds,
|
||||
BucketCounts: dp.BucketCounts,
|
||||
Min: dp.Min,
|
||||
Max: dp.Max,
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func redactDataPointTimestamps[T int64 | float64](sdp []metricdata.DataPoint[T]) []metricdata.DataPoint[T] {
|
||||
out := make([]metricdata.DataPoint[T], len(sdp))
|
||||
for i, dp := range sdp {
|
||||
out[i] = metricdata.DataPoint[T]{
|
||||
Attributes: dp.Attributes,
|
||||
Value: dp.Value,
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
201
vendor/go.opentelemetry.io/otel/exporters/stdout/stdouttrace/LICENSE
generated
vendored
Normal file
201
vendor/go.opentelemetry.io/otel/exporters/stdout/stdouttrace/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
3
vendor/go.opentelemetry.io/otel/exporters/stdout/stdouttrace/README.md
generated
vendored
Normal file
3
vendor/go.opentelemetry.io/otel/exporters/stdout/stdouttrace/README.md
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# STDOUT Trace Exporter
|
||||
|
||||
[](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/stdout/stdouttrace)
|
85
vendor/go.opentelemetry.io/otel/exporters/stdout/stdouttrace/config.go
generated
vendored
Normal file
85
vendor/go.opentelemetry.io/otel/exporters/stdout/stdouttrace/config.go
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package stdouttrace // import "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultWriter = os.Stdout
|
||||
defaultPrettyPrint = false
|
||||
defaultTimestamps = true
|
||||
)
|
||||
|
||||
// config contains options for the STDOUT exporter.
|
||||
type config struct {
|
||||
// Writer is the destination. If not set, os.Stdout is used.
|
||||
Writer io.Writer
|
||||
|
||||
// PrettyPrint will encode the output into readable JSON. Default is
|
||||
// false.
|
||||
PrettyPrint bool
|
||||
|
||||
// Timestamps specifies if timestamps should be printed. Default is
|
||||
// true.
|
||||
Timestamps bool
|
||||
}
|
||||
|
||||
// newConfig creates a validated Config configured with options.
|
||||
func newConfig(options ...Option) config {
|
||||
cfg := config{
|
||||
Writer: defaultWriter,
|
||||
PrettyPrint: defaultPrettyPrint,
|
||||
Timestamps: defaultTimestamps,
|
||||
}
|
||||
for _, opt := range options {
|
||||
cfg = opt.apply(cfg)
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
// Option sets the value of an option for a Config.
|
||||
type Option interface {
|
||||
apply(config) config
|
||||
}
|
||||
|
||||
// WithWriter sets the export stream destination.
|
||||
func WithWriter(w io.Writer) Option {
|
||||
return writerOption{w}
|
||||
}
|
||||
|
||||
type writerOption struct {
|
||||
W io.Writer
|
||||
}
|
||||
|
||||
func (o writerOption) apply(cfg config) config {
|
||||
cfg.Writer = o.W
|
||||
return cfg
|
||||
}
|
||||
|
||||
// WithPrettyPrint prettifies the emitted output.
|
||||
func WithPrettyPrint() Option {
|
||||
return prettyPrintOption(true)
|
||||
}
|
||||
|
||||
type prettyPrintOption bool
|
||||
|
||||
func (o prettyPrintOption) apply(cfg config) config {
|
||||
cfg.PrettyPrint = bool(o)
|
||||
return cfg
|
||||
}
|
||||
|
||||
// WithoutTimestamps sets the export stream to not include timestamps.
|
||||
func WithoutTimestamps() Option {
|
||||
return timestampsOption(false)
|
||||
}
|
||||
|
||||
type timestampsOption bool
|
||||
|
||||
func (o timestampsOption) apply(cfg config) config {
|
||||
cfg.Timestamps = bool(o)
|
||||
return cfg
|
||||
}
|
6
vendor/go.opentelemetry.io/otel/exporters/stdout/stdouttrace/doc.go
generated
vendored
Normal file
6
vendor/go.opentelemetry.io/otel/exporters/stdout/stdouttrace/doc.go
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package stdouttrace contains an OpenTelemetry exporter for tracing
|
||||
// telemetry to be written to an output destination as JSON.
|
||||
package stdouttrace // import "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
|
103
vendor/go.opentelemetry.io/otel/exporters/stdout/stdouttrace/trace.go
generated
vendored
Normal file
103
vendor/go.opentelemetry.io/otel/exporters/stdout/stdouttrace/trace.go
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package stdouttrace // import "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/sdk/trace"
|
||||
"go.opentelemetry.io/otel/sdk/trace/tracetest"
|
||||
)
|
||||
|
||||
var zeroTime time.Time
|
||||
|
||||
var _ trace.SpanExporter = &Exporter{}
|
||||
|
||||
// New creates an Exporter with the passed options.
|
||||
func New(options ...Option) (*Exporter, error) {
|
||||
cfg := newConfig(options...)
|
||||
|
||||
enc := json.NewEncoder(cfg.Writer)
|
||||
if cfg.PrettyPrint {
|
||||
enc.SetIndent("", "\t")
|
||||
}
|
||||
|
||||
return &Exporter{
|
||||
encoder: enc,
|
||||
timestamps: cfg.Timestamps,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Exporter is an implementation of trace.SpanSyncer that writes spans to stdout.
|
||||
type Exporter struct {
|
||||
encoder *json.Encoder
|
||||
encoderMu sync.Mutex
|
||||
timestamps bool
|
||||
|
||||
stoppedMu sync.RWMutex
|
||||
stopped bool
|
||||
}
|
||||
|
||||
// ExportSpans writes spans in json format to stdout.
|
||||
func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
e.stoppedMu.RLock()
|
||||
stopped := e.stopped
|
||||
e.stoppedMu.RUnlock()
|
||||
if stopped {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(spans) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
stubs := tracetest.SpanStubsFromReadOnlySpans(spans)
|
||||
|
||||
e.encoderMu.Lock()
|
||||
defer e.encoderMu.Unlock()
|
||||
for i := range stubs {
|
||||
stub := &stubs[i]
|
||||
// Remove timestamps
|
||||
if !e.timestamps {
|
||||
stub.StartTime = zeroTime
|
||||
stub.EndTime = zeroTime
|
||||
for j := range stub.Events {
|
||||
ev := &stub.Events[j]
|
||||
ev.Time = zeroTime
|
||||
}
|
||||
}
|
||||
|
||||
// Encode span stubs, one by one
|
||||
if err := e.encoder.Encode(stub); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Shutdown is called to stop the exporter, it performs no action.
|
||||
func (e *Exporter) Shutdown(ctx context.Context) error {
|
||||
e.stoppedMu.Lock()
|
||||
e.stopped = true
|
||||
e.stoppedMu.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalLog is the marshaling function used by the logging system to represent this Exporter.
|
||||
func (e *Exporter) MarshalLog() interface{} {
|
||||
return struct {
|
||||
Type string
|
||||
WithTimestamps bool
|
||||
}{
|
||||
Type: "stdout",
|
||||
WithTimestamps: e.timestamps,
|
||||
}
|
||||
}
|
634
vendor/go.opentelemetry.io/otel/log/DESIGN.md
generated
vendored
Normal file
634
vendor/go.opentelemetry.io/otel/log/DESIGN.md
generated
vendored
Normal file
@@ -0,0 +1,634 @@
|
||||
# Logs API
|
||||
|
||||
## Abstract
|
||||
|
||||
`go.opentelemetry.io/otel/log` provides
|
||||
[Logs API](https://opentelemetry.io/docs/specs/otel/logs/api/).
|
||||
|
||||
The prototype was created in
|
||||
[#4725](https://github.com/open-telemetry/opentelemetry-go/pull/4725).
|
||||
|
||||
## Background
|
||||
|
||||
The key challenge is to create a performant API compliant with the [specification](https://opentelemetry.io/docs/specs/otel/logs/api/)
|
||||
with an intuitive and user friendly design.
|
||||
Performance is seen as one of the most important characteristics of logging libraries in Go.
|
||||
|
||||
## Design
|
||||
|
||||
This proposed design aims to:
|
||||
|
||||
- be specification compliant,
|
||||
- be similar to Trace and Metrics API,
|
||||
- take advantage of both OpenTelemetry and `slog` experience to achieve acceptable performance.
|
||||
|
||||
### Module structure
|
||||
|
||||
The API is published as a single `go.opentelemetry.io/otel/log` Go module.
|
||||
|
||||
The package structure is similar to Trace API and Metrics API.
|
||||
The Go module consists of the following packages:
|
||||
|
||||
- `go.opentelemetry.io/otel/log`
|
||||
- `go.opentelemetry.io/otel/log/embedded`
|
||||
- `go.opentelemetry.io/otel/log/logtest`
|
||||
- `go.opentelemetry.io/otel/log/noop`
|
||||
|
||||
Rejected alternative:
|
||||
|
||||
- [Reuse slog](#reuse-slog)
|
||||
|
||||
### LoggerProvider
|
||||
|
||||
The [`LoggerProvider` abstraction](https://opentelemetry.io/docs/specs/otel/logs/api/#loggerprovider)
|
||||
is defined as `LoggerProvider` interface in [provider.go](provider.go).
|
||||
|
||||
The specification may add new operations to `LoggerProvider`.
|
||||
The interface may have methods added without a package major version bump.
|
||||
This embeds `embedded.LoggerProvider` to help inform an API implementation
|
||||
author about this non-standard API evolution.
|
||||
This approach is already used in Trace API and Metrics API.
|
||||
|
||||
#### LoggerProvider.Logger
|
||||
|
||||
The `Logger` method implements the [`Get a Logger` operation](https://opentelemetry.io/docs/specs/otel/logs/api/#get-a-logger).
|
||||
|
||||
The required `name` parameter is accepted as a `string` method argument.
|
||||
|
||||
The `LoggerOption` options are defined to support optional parameters.
|
||||
|
||||
Implementation requirements:
|
||||
|
||||
- The [specification requires](https://opentelemetry.io/docs/specs/otel/logs/api/#concurrency-requirements)
|
||||
the method to be safe to be called concurrently.
|
||||
|
||||
- The method should use some default name if the passed name is empty
|
||||
in order to meet the [specification's SDK requirement](https://opentelemetry.io/docs/specs/otel/logs/sdk/#logger-creation)
|
||||
to return a working logger when an invalid name is passed
|
||||
as well as to resemble the behavior of getting tracers and meters.
|
||||
|
||||
`Logger` can be extended by adding new `LoggerOption` options
|
||||
and adding new exported fields to the `LoggerConfig` struct.
|
||||
This design is already used in Trace API for getting tracers
|
||||
and in Metrics API for getting meters.
|
||||
|
||||
Rejected alternative:
|
||||
|
||||
- [Passing struct as parameter to LoggerProvider.Logger](#passing-struct-as-parameter-to-loggerproviderlogger).
|
||||
|
||||
### Logger
|
||||
|
||||
The [`Logger` abstraction](https://opentelemetry.io/docs/specs/otel/logs/api/#logger)
|
||||
is defined as `Logger` interface in [logger.go](logger.go).
|
||||
|
||||
The specification may add new operations to `Logger`.
|
||||
The interface may have methods added without a package major version bump.
|
||||
This embeds `embedded.Logger` to help inform an API implementation
|
||||
author about this non-standard API evolution.
|
||||
This approach is already used in Trace API and Metrics API.
|
||||
|
||||
### Logger.Emit
|
||||
|
||||
The `Emit` method implements the [`Emit a LogRecord` operation](https://opentelemetry.io/docs/specs/otel/logs/api/#emit-a-logrecord).
|
||||
|
||||
[`Context` associated with the `LogRecord`](https://opentelemetry.io/docs/specs/otel/context/)
|
||||
is accepted as a `context.Context` method argument.
|
||||
|
||||
Calls to `Emit` are supposed to be on the hot path.
|
||||
Therefore, in order to reduce the number of heap allocations,
|
||||
the [`LogRecord` abstraction](https://opentelemetry.io/docs/specs/otel/logs/api/#emit-a-logrecord),
|
||||
is defined as `Record` struct in [record.go](record.go).
|
||||
|
||||
[`Timestamp`](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-timestamp)
|
||||
is accessed using following methods:
|
||||
|
||||
```go
|
||||
func (r *Record) Timestamp() time.Time
|
||||
func (r *Record) SetTimestamp(t time.Time)
|
||||
```
|
||||
|
||||
[`ObservedTimestamp`](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-observedtimestamp)
|
||||
is accessed using following methods:
|
||||
|
||||
```go
|
||||
func (r *Record) ObservedTimestamp() time.Time
|
||||
func (r *Record) SetObservedTimestamp(t time.Time)
|
||||
```
|
||||
|
||||
[`EventName`](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-eventname)
|
||||
is accessed using following methods:
|
||||
|
||||
```go
|
||||
func (r *Record) EventName() string
|
||||
func (r *Record) SetEventName(s string)
|
||||
```
|
||||
|
||||
[`SeverityNumber`](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-severitynumber)
|
||||
is accessed using following methods:
|
||||
|
||||
```go
|
||||
func (r *Record) Severity() Severity
|
||||
func (r *Record) SetSeverity(s Severity)
|
||||
```
|
||||
|
||||
`Severity` type is defined in [severity.go](severity.go).
|
||||
The constants are are based on
|
||||
[Displaying Severity recommendation](https://opentelemetry.io/docs/specs/otel/logs/data-model/#displaying-severity).
|
||||
Additionally, `Severity[Level]` constants are defined to make the API more readable and user friendly.
|
||||
|
||||
[`SeverityText`](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-severitytext)
|
||||
is accessed using following methods:
|
||||
|
||||
```go
|
||||
func (r *Record) SeverityText() string
|
||||
func (r *Record) SetSeverityText(s string)
|
||||
```
|
||||
|
||||
[`Body`](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-body)
|
||||
is accessed using following methods:
|
||||
|
||||
```go
|
||||
func (r *Record) Body() Value
|
||||
func (r *Record) SetBody(v Value)
|
||||
```
|
||||
|
||||
[Log record attributes](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-attributes)
|
||||
are accessed using following methods:
|
||||
|
||||
```go
|
||||
func (r *Record) WalkAttributes(f func(KeyValue) bool)
|
||||
func (r *Record) AddAttributes(attrs ...KeyValue)
|
||||
```
|
||||
|
||||
`Record` has a `AttributesLen` method that returns
|
||||
the number of attributes to allow slice preallocation
|
||||
when converting records to a different representation:
|
||||
|
||||
```go
|
||||
func (r *Record) AttributesLen() int
|
||||
```
|
||||
|
||||
The records attributes design and implementation is based on
|
||||
[`slog.Record`](https://pkg.go.dev/log/slog#Record).
|
||||
It allows achieving high-performance access and manipulation of the attributes
|
||||
while keeping the API user friendly.
|
||||
It relieves the user from making his own improvements
|
||||
for reducing the number of allocations when passing attributes.
|
||||
|
||||
The abstractions described in
|
||||
[the specification](https://opentelemetry.io/docs/specs/otel/logs/#new-first-party-application-logs)
|
||||
are defined in [keyvalue.go](keyvalue.go).
|
||||
|
||||
`Value` is representing `any`.
|
||||
`KeyValue` is representing a key(string)-value(`any`) pair.
|
||||
|
||||
`Kind` is an enumeration used for specifying the underlying value type.
|
||||
`KindEmpty` is used for an empty (zero) value.
|
||||
`KindBool` is used for boolean value.
|
||||
`KindFloat64` is used for a double precision floating point (IEEE 754-1985) value.
|
||||
`KindInt64` is used for a signed integer value.
|
||||
`KindString` is used for a string value.
|
||||
`KindBytes` is used for a slice of bytes (in spec: A byte array).
|
||||
`KindSlice` is used for a slice of values (in spec: an array (a list) of any values).
|
||||
`KindMap` is used for a slice of key-value pairs (in spec: `map<string, any>`).
|
||||
|
||||
These types are defined in `go.opentelemetry.io/otel/log` package
|
||||
as they are tightly coupled with the API and different from common attributes.
|
||||
|
||||
The internal implementation of `Value` is based on
|
||||
[`slog.Value`](https://pkg.go.dev/log/slog#Value)
|
||||
and the API is mostly inspired by
|
||||
[`attribute.Value`](https://pkg.go.dev/go.opentelemetry.io/otel/attribute#Value).
|
||||
The benchmarks[^1] show that the implementation is more performant than
|
||||
[`attribute.Value`](https://pkg.go.dev/go.opentelemetry.io/otel/attribute#Value).
|
||||
|
||||
The value accessors (`func (v Value) As[Kind]` methods) must not panic,
|
||||
as it would violate the [specification](https://opentelemetry.io/docs/specs/otel/error-handling/):
|
||||
|
||||
> API methods MUST NOT throw unhandled exceptions when used incorrectly by end
|
||||
> users. The API and SDK SHOULD provide safe defaults for missing or invalid
|
||||
> arguments. [...] Whenever the library suppresses an error that would otherwise
|
||||
> have been exposed to the user, the library SHOULD log the error using
|
||||
> language-specific conventions.
|
||||
|
||||
Therefore, the value accessors should return a zero value
|
||||
and log an error when a bad accessor is called.
|
||||
|
||||
The `Severity`, `Kind`, `Value`, `KeyValue` may implement
|
||||
the [`fmt.Stringer`](https://pkg.go.dev/fmt#Stringer) interface.
|
||||
However, it is not needed for the first stable release
|
||||
and the `String` methods can be added later.
|
||||
|
||||
The caller must not subsequently mutate the record passed to `Emit`.
|
||||
This would allow the implementation to not clone the record,
|
||||
but simply retain, modify or discard it.
|
||||
The implementation may still choose to clone the record or copy its attributes
|
||||
if it needs to retain or modify it,
|
||||
e.g. in case of asynchronous processing to eliminate the possibility of data races,
|
||||
because the user can technically reuse the record and add new attributes
|
||||
after the call (even when the documentation says that the caller must not do it).
|
||||
|
||||
Implementation requirements:
|
||||
|
||||
- The [specification requires](https://opentelemetry.io/docs/specs/otel/logs/api/#concurrency-requirements)
|
||||
the method to be safe to be called concurrently.
|
||||
|
||||
- The method must not interrupt the record processing if the context is canceled
|
||||
per ["ignoring context cancellation" guideline](../CONTRIBUTING.md#ignoring-context-cancellation).
|
||||
|
||||
- The [specification requires](https://opentelemetry.io/docs/specs/otel/logs/api/#emit-a-logrecord)
|
||||
use the current time as observed timestamp if the passed is empty.
|
||||
|
||||
- The method should handle the trace context passed via `ctx` argument in order to meet the
|
||||
[specification's SDK requirement](https://opentelemetry.io/docs/specs/otel/logs/sdk/#readablelogrecord)
|
||||
to populate the trace context fields from the resolved context.
|
||||
|
||||
`Emit` can be extended by adding new exported fields to the `Record` struct.
|
||||
|
||||
Rejected alternatives:
|
||||
|
||||
- [Record as interface](#record-as-interface)
|
||||
- [Options as parameter to Logger.Emit](#options-as-parameter-to-loggeremit)
|
||||
- [Passing record as pointer to Logger.Emit](#passing-record-as-pointer-to-loggeremit)
|
||||
- [Logger.WithAttributes](#loggerwithattributes)
|
||||
- [Record attributes as slice](#record-attributes-as-slice)
|
||||
- [Use any instead of defining Value](#use-any-instead-of-defining-value)
|
||||
- [Severity type encapsulating number and text](#severity-type-encapsulating-number-and-text)
|
||||
- [Reuse attribute package](#reuse-attribute-package)
|
||||
- [Mix receiver types for Record](#mix-receiver-types-for-record)
|
||||
- [Add XYZ method to Logger](#add-xyz-method-to-logger)
|
||||
- [Rename KeyValue to Attr](#rename-keyvalue-to-attr)
|
||||
|
||||
### Logger.Enabled
|
||||
|
||||
The `Enabled` method implements the [`Enabled` operation](https://opentelemetry.io/docs/specs/otel/logs/api/#enabled).
|
||||
|
||||
[`Context` associated with the `LogRecord`](https://opentelemetry.io/docs/specs/otel/context/)
|
||||
is accepted as a `context.Context` method argument.
|
||||
|
||||
Calls to `Enabled` are supposed to be on the hot path and the list of arguments
|
||||
can be extendend in future. Therefore, in order to reduce the number of heap
|
||||
allocations and make it possible to handle new arguments, `Enabled` accepts
|
||||
a `EnabledParameters` struct, defined in [logger.go](logger.go), as the second
|
||||
method argument.
|
||||
|
||||
The `EnabledParameters` uses fields, instead of getters and setters, to allow
|
||||
simpler usage which allows configuring the `EnabledParameters` in the same line
|
||||
where `Enabled` is called.
|
||||
|
||||
### noop package
|
||||
|
||||
The `go.opentelemetry.io/otel/log/noop` package provides
|
||||
[Logs API No-Op Implementation](https://opentelemetry.io/docs/specs/otel/logs/noop/).
|
||||
|
||||
### Trace context correlation
|
||||
|
||||
The bridge implementation should do its best to pass
|
||||
the `ctx` containing the trace context from the caller
|
||||
so it can later be passed via `Logger.Emit`.
|
||||
|
||||
It is not expected that users (caller or bridge implementation) reconstruct
|
||||
a `context.Context`. Reconstructing a `context.Context` with
|
||||
[`trace.ContextWithSpanContext`](https://pkg.go.dev/go.opentelemetry.io/otel/trace#ContextWithSpanContext)
|
||||
and [`trace.NewSpanContext`](https://pkg.go.dev/go.opentelemetry.io/otel/trace#NewSpanContext)
|
||||
would usually involve more memory allocations.
|
||||
|
||||
The logging libraries which have recording methods that accepts `context.Context`,
|
||||
such us [`slog`](https://pkg.go.dev/log/slog),
|
||||
[`logrus`](https://pkg.go.dev/github.com/sirupsen/logrus),
|
||||
[`zerolog`](https://pkg.go.dev/github.com/rs/zerolog),
|
||||
makes passing the trace context trivial.
|
||||
|
||||
However, some libraries do not accept a `context.Context` in their recording methods.
|
||||
Structured logging libraries,
|
||||
such as [`logr`](https://pkg.go.dev/github.com/go-logr/logr)
|
||||
and [`zap`](https://pkg.go.dev/go.uber.org/zap),
|
||||
offer passing `any` type as a log attribute/field.
|
||||
Therefore, their bridge implementations can define a "special" log attributes/field
|
||||
that will be used to capture the trace context.
|
||||
|
||||
[The prototype](https://github.com/open-telemetry/opentelemetry-go/pull/4725)
|
||||
has bridge implementations that handle trace context correlation efficiently.
|
||||
|
||||
## Benchmarking
|
||||
|
||||
The benchmarks take inspiration from [`slog`](https://pkg.go.dev/log/slog),
|
||||
because for the Go team it was also critical to create API that would be fast
|
||||
and interoperable with existing logging packages.[^2][^3]
|
||||
|
||||
The benchmark results can be found in [the prototype](https://github.com/open-telemetry/opentelemetry-go/pull/4725).
|
||||
|
||||
## Rejected alternatives
|
||||
|
||||
### Reuse slog
|
||||
|
||||
The API must not be coupled to [`slog`](https://pkg.go.dev/log/slog),
|
||||
nor any other logging library.
|
||||
|
||||
The API needs to evolve orthogonally to `slog`.
|
||||
|
||||
`slog` is not compliant with the [Logs API](https://opentelemetry.io/docs/specs/otel/logs/api/).
|
||||
and we cannot expect the Go team to make `slog` compliant with it.
|
||||
|
||||
The interoperability can be achieved using [a log bridge](https://opentelemetry.io/docs/specs/otel/glossary/#log-appender--bridge).
|
||||
|
||||
You can read more about OpenTelemetry Logs design on [opentelemetry.io](https://opentelemetry.io/docs/concepts/signals/logs/).
|
||||
|
||||
### Record as interface
|
||||
|
||||
`Record` is defined as a `struct` because of the following reasons.
|
||||
|
||||
Log record is a value object without any behavior.
|
||||
It is used as data input for Logger methods.
|
||||
|
||||
The log record resembles the instrument config structs like [metric.Float64CounterConfig](https://pkg.go.dev/go.opentelemetry.io/otel/metric#Float64CounterConfig).
|
||||
|
||||
Using `struct` instead of `interface` improves the performance as e.g.
|
||||
indirect calls are less optimized,
|
||||
usage of interfaces tend to increase heap allocations.[^3]
|
||||
|
||||
### Options as parameter to Logger.Emit
|
||||
|
||||
One of the initial ideas was to have:
|
||||
|
||||
```go
|
||||
type Logger interface{
|
||||
embedded.Logger
|
||||
Emit(ctx context.Context, options ...RecordOption)
|
||||
}
|
||||
```
|
||||
|
||||
The main reason was that design would be similar
|
||||
to the [Meter API](https://pkg.go.dev/go.opentelemetry.io/otel/metric#Meter)
|
||||
for creating instruments.
|
||||
|
||||
However, passing `Record` directly, instead of using options,
|
||||
is more performant as it reduces heap allocations.[^4]
|
||||
|
||||
Another advantage of passing `Record` is that API would not have functions like `NewRecord(options...)`,
|
||||
which would be used by the SDK and not by the users.
|
||||
|
||||
Finally, the definition would be similar to [`slog.Handler.Handle`](https://pkg.go.dev/log/slog#Handler)
|
||||
that was designed to provide optimization opportunities.[^2]
|
||||
|
||||
### Passing record as pointer to Logger.Emit
|
||||
|
||||
So far the benchmarks do not show differences that would
|
||||
favor passing the record via pointer (and vice versa).
|
||||
|
||||
Passing via value feels safer because of the following reasons.
|
||||
|
||||
The user would not be able to pass `nil`.
|
||||
Therefore, it reduces the possibility to have a nil pointer dereference.
|
||||
|
||||
It should reduce the possibility of a heap allocation.
|
||||
|
||||
It follows the design of [`slog.Handler`](https://pkg.go.dev/log/slog#Handler).
|
||||
|
||||
If follows one of Google's Go Style Decisions
|
||||
to prefer [passing values](https://google.github.io/styleguide/go/decisions#pass-values).
|
||||
|
||||
### Passing struct as parameter to LoggerProvider.Logger
|
||||
|
||||
Similarly to `Logger.Emit`, we could have something like:
|
||||
|
||||
```go
|
||||
type LoggerProvider interface{
|
||||
embedded.LoggerProvider
|
||||
Logger(name string, config LoggerConfig)
|
||||
}
|
||||
```
|
||||
|
||||
The drawback of this idea would be that this would be
|
||||
a different design from Trace and Metrics API.
|
||||
|
||||
The performance of acquiring a logger is not as critical
|
||||
as the performance of emitting a log record. While a single
|
||||
HTTP/RPC handler could write hundreds of logs, it should not
|
||||
create a new logger for each log entry.
|
||||
The bridge implementation should reuse loggers whenever possible.
|
||||
|
||||
### Logger.WithAttributes
|
||||
|
||||
We could add `WithAttributes` to the `Logger` interface.
|
||||
Then `Record` could be a simple struct with only exported fields.
|
||||
The idea was that the SDK would implement the performance improvements
|
||||
instead of doing it in the API.
|
||||
This would allow having different optimization strategies.
|
||||
|
||||
During the analysis[^5], it occurred that the main problem of this proposal
|
||||
is that the variadic slice passed to an interface method is always heap allocated.
|
||||
|
||||
Moreover, the logger returned by `WithAttribute` was allocated on the heap.
|
||||
|
||||
Lastly, the proposal was not specification compliant.
|
||||
|
||||
### Record attributes as slice
|
||||
|
||||
One of the proposals[^6] was to have `Record` as a simple struct:
|
||||
|
||||
```go
|
||||
type Record struct {
|
||||
Timestamp time.Time
|
||||
ObservedTimestamp time.Time
|
||||
EventName string
|
||||
Severity Severity
|
||||
SeverityText string
|
||||
Body Value
|
||||
Attributes []KeyValue
|
||||
}
|
||||
```
|
||||
|
||||
The bridge implementations could use [`sync.Pool`](https://pkg.go.dev/sync#Pool)
|
||||
for reducing the number of allocations when passing attributes.
|
||||
|
||||
The benchmarks results were better.
|
||||
|
||||
In such a design, most bridges would have a `sync.Pool`
|
||||
to reduce the number of heap allocations.
|
||||
However, the `sync.Pool` will not work correctly with API implementations
|
||||
that would take ownership of the record
|
||||
(e.g. implementations that do not copy records for asynchronous processing).
|
||||
The current design, even in case of improper API implementation,
|
||||
has lower chances of encountering a bug as most bridges would
|
||||
create a record, pass it, and forget about it.
|
||||
|
||||
For reference, here is the reason why `slog` does not use `sync.Pool`[^3]
|
||||
as well:
|
||||
|
||||
> We can use a sync pool for records though we decided not to.
|
||||
You can but it's a bad idea for us. Why?
|
||||
Because users have control of Records.
|
||||
Handler writers can get their hands on a record
|
||||
and we'd have to ask them to free it
|
||||
or try to free it magically at some some point.
|
||||
But either way, they could get themselves in trouble by freeing it twice
|
||||
or holding on to one after they free it.
|
||||
That's a use after free bug and that's why `zerolog` was problematic for us.
|
||||
`zerolog` as as part of its speed exposes a pool allocated value to users
|
||||
if you use `zerolog` the normal way, that you'll see in all the examples,
|
||||
you will never encounter a problem.
|
||||
But if you do something a little out of the ordinary you can get
|
||||
use after free bugs and we just didn't want to put that in the standard library.
|
||||
|
||||
Therefore, we decided to not follow the proposal as it is
|
||||
less user friendly (users and bridges would use e.g. a `sync.Pool` to reduce
|
||||
the number of heap allocation), less safe (more prone to use after free bugs
|
||||
and race conditions), and the benchmark differences were not significant.
|
||||
|
||||
### Use any instead of defining Value
|
||||
|
||||
[Logs Data Model](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-body)
|
||||
defines Body to be `any`.
|
||||
One could propose to define `Body` (and attribute values) as `any`
|
||||
instead of a defining a new type (`Value`).
|
||||
|
||||
First of all, [`any` type defined in the specification](https://opentelemetry.io/docs/specs/otel/logs/data-model/#type-any)
|
||||
is not the same as `any` (`interface{}`) in Go.
|
||||
|
||||
Moreover, using `any` as a field would decrease the performance.[^7]
|
||||
|
||||
Notice it will be still possible to add following kind and factories
|
||||
in a backwards compatible way:
|
||||
|
||||
```go
|
||||
const KindMap Kind
|
||||
|
||||
func AnyValue(value any) KeyValue
|
||||
|
||||
func Any(key string, value any) KeyValue
|
||||
```
|
||||
|
||||
However, currently, it would not be specification compliant.
|
||||
|
||||
### Severity type encapsulating number and text
|
||||
|
||||
We could combine severity into a single field defining a type:
|
||||
|
||||
```go
|
||||
type Severity struct {
|
||||
Number SeverityNumber
|
||||
Text string
|
||||
}
|
||||
```
|
||||
|
||||
However, the [Logs Data Model](https://opentelemetry.io/docs/specs/otel/logs/data-model/#log-and-event-record-definition)
|
||||
define it as independent fields.
|
||||
It should be more user friendly to have them separated.
|
||||
Especially when having getter and setter methods, setting one value
|
||||
when the other is already set would be unpleasant.
|
||||
|
||||
### Reuse attribute package
|
||||
|
||||
It was tempting to reuse the existing
|
||||
[https://pkg.go.dev/go.opentelemetry.io/otel/attribute] package
|
||||
for defining log attributes and body.
|
||||
|
||||
However, this would be wrong because [the log attribute definition](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-attributes)
|
||||
is different from [the common attribute definition](https://opentelemetry.io/docs/specs/otel/common/#attribute).
|
||||
|
||||
Moreover, it there is nothing telling that [the body definition](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-body)
|
||||
has anything in common with a common attribute value.
|
||||
|
||||
Therefore, we define new types representing the abstract types defined
|
||||
in the [Logs Data Model](https://opentelemetry.io/docs/specs/otel/logs/data-model/#definitions-used-in-this-document).
|
||||
|
||||
### Mix receiver types for Record
|
||||
|
||||
Methods of [`slog.Record`](https://pkg.go.dev/log/slog#Record)
|
||||
have different receiver types.
|
||||
|
||||
In `log/slog` GitHub issue we can only find that the reason is:[^8]
|
||||
|
||||
>> some receiver of Record struct is by value
|
||||
> Passing Records by value means they incur no heap allocation.
|
||||
> That improves performance overall, even though they are copied.
|
||||
|
||||
However, the benchmarks do not show any noticeable differences.[^9]
|
||||
|
||||
The compiler is smart-enough to not make a heap allocation for any of these methods.
|
||||
The use of a pointer receiver does not cause any heap allocation.
|
||||
From Go FAQ:[^10]
|
||||
|
||||
> In the current compilers, if a variable has its address taken,
|
||||
> that variable is a candidate for allocation on the heap.
|
||||
> However, a basic escape analysis recognizes some cases
|
||||
> when such variables will not live past the return from the function
|
||||
> and can reside on the stack.
|
||||
|
||||
The [Understanding Allocations: the Stack and the Heap](https://www.youtube.com/watch?v=ZMZpH4yT7M0)
|
||||
presentation by Jacob Walker describes the escape analysis with details.
|
||||
|
||||
Moreover, also from Go FAQ:[^10]
|
||||
|
||||
> Also, if a local variable is very large,
|
||||
> it might make more sense to store it on the heap rather than the stack.
|
||||
|
||||
Therefore, even if we use a value receiver and the value is very large
|
||||
it may be heap allocated.
|
||||
|
||||
Both [Go Code Review Comments](https://go.dev/wiki/CodeReviewComments#receiver-type)
|
||||
and [Google's Go Style Decisions](https://google.github.io/styleguide/go/decisions#receiver-type)
|
||||
highly recommend making the methods for a type either all pointer methods
|
||||
or all value methods. Google's Go Style Decisions even goes further and says:
|
||||
|
||||
> There is a lot of misinformation about whether passing a value or a pointer
|
||||
> to a function can affect performance.
|
||||
> The compiler can choose to pass pointers to values on the stack
|
||||
> as well as copying values on the stack,
|
||||
> but these considerations should not outweigh the readability
|
||||
> and correctness of the code in most circumstances.
|
||||
> When the performance does matter, it is important to profile both approaches
|
||||
> with a realistic benchmark before deciding that one approach outperforms the other.
|
||||
|
||||
Because, the benchmarks[^9] do not proof any performance difference
|
||||
and the general recommendation is to not mix receiver types,
|
||||
we decided to use pointer receivers for all `Record` methods.
|
||||
|
||||
### Add XYZ method to Logger
|
||||
|
||||
The `Logger` does not have methods like `SetSeverity`, etc.
|
||||
as the Logs API needs to follow (be compliant with)
|
||||
the [specification](https://opentelemetry.io/docs/specs/otel/logs/api/)
|
||||
|
||||
### Rename KeyValue to Attr
|
||||
|
||||
There was a proposal to rename `KeyValue` to `Attr` (or `Attribute`).[^11]
|
||||
New developers may not intuitively know that `log.KeyValue` is an attribute in
|
||||
the OpenTelemetry parlance.
|
||||
|
||||
During the discussion we agreed to keep the `KeyValue` name.
|
||||
|
||||
The type is used in multiple semantics:
|
||||
|
||||
- as a log attribute,
|
||||
- as a map item,
|
||||
- as a log record Body.
|
||||
|
||||
As for map item semantics, this type is a key-value pair, not an attribute.
|
||||
Naming the type as `Attr` would convey semantical meaning
|
||||
that would not be correct for a map.
|
||||
|
||||
We expect that most of the Logs API users will be OpenTelemetry contributors.
|
||||
We plan to implement bridges for the most popular logging libraries ourselves.
|
||||
Given we will all have the context needed to disambiguate these overlapping
|
||||
names, developers' confusion should not be an issue.
|
||||
|
||||
For bridges not developed by us,
|
||||
developers will likely look at our existing bridges for inspiration.
|
||||
Our correct use of these types will be a reference to them.
|
||||
|
||||
At last, we provide `ValueFromAttribute` and `KeyValueFromAttribute`
|
||||
to offer reuse of `attribute.Value` and `attribute.KeyValue`.
|
||||
|
||||
[^1]: [Handle structured body and attributes](https://github.com/pellared/opentelemetry-go/pull/7)
|
||||
[^2]: Jonathan Amsterdam, [The Go Blog: Structured Logging with slog](https://go.dev/blog/slog)
|
||||
[^3]: Jonathan Amsterdam, [GopherCon Europe 2023: A Fast Structured Logging Package](https://www.youtube.com/watch?v=tC4Jt3i62ns)
|
||||
[^4]: [Emit definition discussion with benchmarks](https://github.com/open-telemetry/opentelemetry-go/pull/4725#discussion_r1400869566)
|
||||
[^5]: [Logger.WithAttributes analysis](https://github.com/pellared/opentelemetry-go/pull/3)
|
||||
[^6]: [Record attributes as field and use sync.Pool for reducing allocations](https://github.com/pellared/opentelemetry-go/pull/4) and [Record attributes based on slog.Record](https://github.com/pellared/opentelemetry-go/pull/6)
|
||||
[^7]: [Record.Body as any](https://github.com/pellared/opentelemetry-go/pull/5)
|
||||
[^8]: [log/slog: structured, leveled logging](https://github.com/golang/go/issues/56345#issuecomment-1302563756)
|
||||
[^9]: [Record with pointer receivers only](https://github.com/pellared/opentelemetry-go/pull/8)
|
||||
[^10]: [Go FAQ: Stack or heap](https://go.dev/doc/faq#stack_or_heap)
|
||||
[^11]: [Rename KeyValue to Attr discussion](https://github.com/open-telemetry/opentelemetry-go/pull/4809#discussion_r1476080093)
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user