-
Notifications
You must be signed in to change notification settings - Fork 1.2k
feat: add self-observability metrics to otlpmetricgrpc metric exporters #7120
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
minimAluminiumalism
wants to merge
15
commits into
open-telemetry:main
Choose a base branch
from
minimAluminiumalism:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,356
−5
Open
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
7938b6d
feat: add self-observability metrics to OTLP metric exporters
minimAluminiumalism 8c6576b
use semantic conventions for self-observability metric names
minimAluminiumalism 208e490
fix CI pipeline failure
minimAluminiumalism 4271508
fix CI pipeline failure
minimAluminiumalism 7508037
modify changelog and go doc
minimAluminiumalism 89273ec
remove otlpmetrichttp implementation
minimAluminiumalism 5bfa165
Merge branch 'main' of https://github.com/open-telemetry/opentelemetr…
minimAluminiumalism dfeedaf
update doc
minimAluminiumalism 9cc2b4e
remove go template
minimAluminiumalism 0a1f30c
remove go template
minimAluminiumalism 0b13731
fix unit tests issues
minimAluminiumalism a280dd3
fix comments
minimAluminiumalism b95f9e9
Merge branch 'main' of https://github.com/open-telemetry/opentelemetr…
minimAluminiumalism b01825b
fix CI pipeline failures
minimAluminiumalism 894c26c
Merge branch 'main' of https://github.com/open-telemetry/opentelemetr…
minimAluminiumalism File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
188 changes: 188 additions & 0 deletions
188
exporters/otlp/otlpmetric/otlpmetricgrpc/internal/selfobservability/selfobservability.go
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
// Package selfobservability provides self-observability metrics for OTLP metric exporters. | ||
// This is an experimental feature controlled by the x.SelfObservability feature flag. | ||
package selfobservability // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/selfobservability" | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"net/url" | ||
"strconv" | ||
"strings" | ||
"sync/atomic" | ||
"time" | ||
|
||
"go.opentelemetry.io/otel" | ||
"go.opentelemetry.io/otel/attribute" | ||
"go.opentelemetry.io/otel/metric" | ||
"go.opentelemetry.io/otel/sdk" | ||
"go.opentelemetry.io/otel/sdk/metric/metricdata" | ||
semconv "go.opentelemetry.io/otel/semconv/v1.36.0" | ||
"go.opentelemetry.io/otel/semconv/v1.36.0/otelconv" | ||
|
||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/x" | ||
) | ||
|
||
// exporterIDCounter is used to generate unique component names for exporters. | ||
var exporterIDCounter atomic.Uint64 | ||
|
||
// nextExporterID returns the next unique exporter ID. | ||
func nextExporterID() uint64 { | ||
return exporterIDCounter.Add(1) - 1 | ||
} | ||
|
||
// ExporterMetrics holds the self-observability metric instruments for an OTLP metric exporter. | ||
type ExporterMetrics struct { | ||
exported otelconv.SDKExporterMetricDataPointExported | ||
inflight otelconv.SDKExporterMetricDataPointInflight | ||
duration otelconv.SDKExporterOperationDuration | ||
attrs []attribute.KeyValue | ||
enabled bool | ||
} | ||
|
||
// NewExporterMetrics creates a new ExporterMetrics instance. | ||
// If self-observability is disabled, returns a no-op instance. | ||
func NewExporterMetrics(componentType, serverAddress string, serverPort int) *ExporterMetrics { | ||
em := &ExporterMetrics{ | ||
enabled: x.SelfObservability.Enabled(), | ||
} | ||
|
||
if !em.enabled { | ||
return em | ||
} | ||
|
||
meter := otel.GetMeterProvider().Meter( | ||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc", | ||
metric.WithInstrumentationVersion(sdk.Version()), | ||
metric.WithSchemaURL(semconv.SchemaURL), | ||
) | ||
|
||
var err error | ||
em.exported, err = otelconv.NewSDKExporterMetricDataPointExported(meter) | ||
if err != nil { | ||
em.enabled = false | ||
return em | ||
} | ||
|
||
em.inflight, err = otelconv.NewSDKExporterMetricDataPointInflight(meter) | ||
if err != nil { | ||
em.enabled = false | ||
return em | ||
} | ||
|
||
em.duration, err = otelconv.NewSDKExporterOperationDuration(meter) | ||
if err != nil { | ||
em.enabled = false | ||
return em | ||
} | ||
|
||
// Set up common attributes | ||
componentName := fmt.Sprintf("%s/%d", componentType, nextExporterID()) | ||
em.attrs = []attribute.KeyValue{ | ||
semconv.OTelComponentTypeKey.String(componentType), | ||
semconv.OTelComponentName(componentName), | ||
semconv.ServerAddress(serverAddress), | ||
semconv.ServerPort(serverPort), | ||
} | ||
|
||
return em | ||
} | ||
|
||
// TrackExport tracks an export operation and returns a function to complete the tracking. | ||
// The returned function should be called when the export operation completes. | ||
func (em *ExporterMetrics) TrackExport(ctx context.Context, rm *metricdata.ResourceMetrics) func(error) { | ||
if !em.enabled { | ||
return func(error) {} | ||
} | ||
|
||
dataPointCount := countDataPoints(rm) | ||
startTime := time.Now() | ||
|
||
em.inflight.Add(ctx, dataPointCount, em.attrs...) | ||
|
||
return func(err error) { | ||
em.inflight.Add(ctx, -dataPointCount, em.attrs...) | ||
|
||
duration := time.Since(startTime).Seconds() | ||
|
||
attrs := make([]attribute.KeyValue, len(em.attrs), len(em.attrs)+1) | ||
copy(attrs, em.attrs) | ||
if err != nil { | ||
attrs = append(attrs, semconv.ErrorTypeOther) | ||
} | ||
em.duration.Inst().Record(ctx, duration, metric.WithAttributes(attrs...)) | ||
|
||
if err == nil { | ||
em.exported.Add(ctx, dataPointCount, em.attrs...) | ||
} | ||
} | ||
} | ||
|
||
// countDataPoints counts the total number of data points in a ResourceMetrics. | ||
func countDataPoints(rm *metricdata.ResourceMetrics) int64 { | ||
if rm == nil { | ||
return 0 | ||
} | ||
|
||
var total int64 | ||
for _, sm := range rm.ScopeMetrics { | ||
for _, m := range sm.Metrics { | ||
switch data := m.Data.(type) { | ||
case metricdata.Gauge[int64]: | ||
total += int64(len(data.DataPoints)) | ||
case metricdata.Gauge[float64]: | ||
total += int64(len(data.DataPoints)) | ||
case metricdata.Sum[int64]: | ||
total += int64(len(data.DataPoints)) | ||
case metricdata.Sum[float64]: | ||
total += int64(len(data.DataPoints)) | ||
case metricdata.Histogram[int64]: | ||
total += int64(len(data.DataPoints)) | ||
case metricdata.Histogram[float64]: | ||
total += int64(len(data.DataPoints)) | ||
case metricdata.ExponentialHistogram[int64]: | ||
total += int64(len(data.DataPoints)) | ||
case metricdata.ExponentialHistogram[float64]: | ||
total += int64(len(data.DataPoints)) | ||
case metricdata.Summary: | ||
total += int64(len(data.DataPoints)) | ||
} | ||
} | ||
} | ||
return total | ||
} | ||
|
||
// ParseEndpoint extracts server address and port from an endpoint URL. | ||
// Returns defaults if parsing fails or endpoint is empty. | ||
func ParseEndpoint(endpoint string) (address string, port int) { | ||
address = "localhost" | ||
port = 4317 | ||
|
||
if endpoint == "" { | ||
return | ||
} | ||
|
||
// Handle endpoint without scheme | ||
if !strings.Contains(endpoint, "://") { | ||
endpoint = "http://" + endpoint | ||
} | ||
|
||
u, err := url.Parse(endpoint) | ||
if err != nil { | ||
return | ||
} | ||
|
||
if u.Hostname() != "" { | ||
address = u.Hostname() | ||
} | ||
|
||
if u.Port() != "" { | ||
if p, err := strconv.Atoi(u.Port()); err == nil { | ||
port = p | ||
} | ||
} | ||
|
||
return | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.