Skip to content

Commit e77201b

Browse files
committed
[chore] Merge observability report sender for all signals, remove duplicate code
Signed-off-by: Bogdan Drutu <[email protected]>
1 parent 808e4a8 commit e77201b

16 files changed

+227
-405
lines changed

exporter/exporterhelper/internal/base_exporter.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ type BaseExporter struct {
6767
}
6868

6969
func NewBaseExporter(set exporter.Settings, signal pipeline.Signal, osf ObsrepSenderFactory, options ...Option) (*BaseExporter, error) {
70-
obsReport, err := NewExporter(ObsReportSettings{ExporterSettings: set, Signal: signal})
70+
obsReport, err := NewObsReport(ObsReportSettings{ExporterSettings: set, Signal: signal})
7171
if err != nil {
7272
return nil, err
7373
}
@@ -121,7 +121,7 @@ func NewBaseExporter(set exporter.Settings, signal pipeline.Signal, osf ObsrepSe
121121
return be, nil
122122
}
123123

124-
// send sends the request using the first sender in the chain.
124+
// Send sends the request using the first sender in the chain.
125125
func (be *BaseExporter) Send(ctx context.Context, req internal.Request) error {
126126
err := be.QueueSender.Send(ctx, req)
127127
if err != nil {
@@ -282,7 +282,7 @@ func WithMarshaler(marshaler exporterqueue.Marshaler[internal.Request]) Option {
282282
}
283283
}
284284

285-
// withUnmarshaler is used to set the request unmarshaler for the new exporter helper.
285+
// WithUnmarshaler is used to set the request unmarshaler for the new exporter helper.
286286
// It must be provided as the first option when creating a new exporter helper.
287287
func WithUnmarshaler(unmarshaler exporterqueue.Unmarshaler[internal.Request]) Option {
288288
return func(o *BaseExporter) error {
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package internal // import "go.opentelemetry.io/collector/exporter/exporterhelper/internal"
5+
6+
import (
7+
"context"
8+
9+
"go.opentelemetry.io/otel/attribute"
10+
"go.opentelemetry.io/otel/codes"
11+
"go.opentelemetry.io/otel/metric"
12+
"go.opentelemetry.io/otel/trace"
13+
14+
"go.opentelemetry.io/collector/exporter"
15+
"go.opentelemetry.io/collector/exporter/exporterhelper/internal/metadata"
16+
"go.opentelemetry.io/collector/pipeline"
17+
)
18+
19+
const (
20+
// spanNameSep is duplicate between receiver and exporter.
21+
spanNameSep = "/"
22+
23+
// ExporterKey used to identify exporters in metrics and traces.
24+
ExporterKey = "exporter"
25+
26+
// DataTypeKey used to identify the data type in the queue size metric.
27+
DataTypeKey = "data_type"
28+
29+
// ItemsSent used to track number of items sent by exporters.
30+
ItemsSent = "items.sent"
31+
// ItemsFailed used to track number of items that failed to be sent by exporters.
32+
ItemsFailed = "items.failed"
33+
)
34+
35+
// ObsReport is a helper to add observability to an exporter.
36+
type ObsReport struct {
37+
spanName string
38+
tracer trace.Tracer
39+
Signal pipeline.Signal
40+
41+
spanAttrs trace.SpanStartEventOption
42+
metricAttr metric.MeasurementOption
43+
TelemetryBuilder *metadata.TelemetryBuilder
44+
enqueueFailedInst metric.Int64Counter
45+
itemsSentInst metric.Int64Counter
46+
itemsFailedInst metric.Int64Counter
47+
}
48+
49+
// ObsReportSettings are settings for creating an ObsReport.
50+
type ObsReportSettings struct {
51+
ExporterSettings exporter.Settings
52+
Signal pipeline.Signal
53+
}
54+
55+
func NewObsReport(set ObsReportSettings) (*ObsReport, error) {
56+
telemetryBuilder, err := metadata.NewTelemetryBuilder(set.ExporterSettings.TelemetrySettings)
57+
if err != nil {
58+
return nil, err
59+
}
60+
61+
idStr := set.ExporterSettings.ID.String()
62+
expAttr := attribute.String(ExporterKey, idStr)
63+
64+
or := &ObsReport{
65+
spanName: ExporterKey + spanNameSep + idStr + spanNameSep + set.Signal.String(),
66+
tracer: metadata.Tracer(set.ExporterSettings.TelemetrySettings),
67+
Signal: set.Signal,
68+
spanAttrs: trace.WithAttributes(expAttr, attribute.String(DataTypeKey, set.Signal.String())),
69+
metricAttr: metric.WithAttributeSet(attribute.NewSet(expAttr)),
70+
TelemetryBuilder: telemetryBuilder,
71+
}
72+
73+
switch set.Signal {
74+
case pipeline.SignalTraces:
75+
or.enqueueFailedInst = or.TelemetryBuilder.ExporterEnqueueFailedSpans
76+
or.itemsSentInst = or.TelemetryBuilder.ExporterSentSpans
77+
or.itemsFailedInst = or.TelemetryBuilder.ExporterSendFailedSpans
78+
79+
case pipeline.SignalMetrics:
80+
or.enqueueFailedInst = or.TelemetryBuilder.ExporterEnqueueFailedMetricPoints
81+
or.itemsSentInst = or.TelemetryBuilder.ExporterSentMetricPoints
82+
or.itemsFailedInst = or.TelemetryBuilder.ExporterSendFailedMetricPoints
83+
84+
case pipeline.SignalLogs:
85+
or.enqueueFailedInst = or.TelemetryBuilder.ExporterEnqueueFailedLogRecords
86+
or.itemsSentInst = or.TelemetryBuilder.ExporterSentLogRecords
87+
or.itemsFailedInst = or.TelemetryBuilder.ExporterSendFailedLogRecords
88+
}
89+
90+
return or, nil
91+
}
92+
93+
// StartOp creates the span used to trace the operation. Returning
94+
// the updated context and the created span.
95+
func (or *ObsReport) StartOp(ctx context.Context) context.Context {
96+
ctx, _ = or.tracer.Start(ctx, or.spanName, or.spanAttrs)
97+
return ctx
98+
}
99+
100+
// EndOp completes the export operation that was started with StartOp.
101+
func (or *ObsReport) EndOp(ctx context.Context, numLogRecords int, err error) {
102+
numSent, numFailedToSend := toNumItems(numLogRecords, err)
103+
104+
// No metrics recorded for profiles.
105+
if or.itemsSentInst != nil {
106+
or.itemsSentInst.Add(ctx, numSent, or.metricAttr)
107+
}
108+
// No metrics recorded for profiles.
109+
if or.itemsFailedInst != nil {
110+
or.itemsFailedInst.Add(ctx, numFailedToSend, or.metricAttr)
111+
}
112+
113+
span := trace.SpanFromContext(ctx)
114+
defer span.End()
115+
// End the span according to errors.
116+
if span.IsRecording() {
117+
span.SetAttributes(
118+
attribute.Int64(ItemsSent, numSent),
119+
attribute.Int64(ItemsFailed, numFailedToSend),
120+
)
121+
if err != nil {
122+
span.SetStatus(codes.Error, err.Error())
123+
}
124+
}
125+
}
126+
127+
func toNumItems(numExportedItems int, err error) (int64, int64) {
128+
if err != nil {
129+
return 0, int64(numExportedItems)
130+
}
131+
return int64(numExportedItems), 0
132+
}
133+
134+
func (or *ObsReport) RecordEnqueueFailure(ctx context.Context, failed int64) {
135+
// No metrics recorded for profiles.
136+
if or.enqueueFailedInst == nil {
137+
return
138+
}
139+
140+
or.enqueueFailedInst.Add(ctx, failed, or.metricAttr)
141+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package internal // import "go.opentelemetry.io/collector/exporter/exporterhelper/internal"
5+
6+
import (
7+
"context"
8+
9+
"go.opentelemetry.io/collector/exporter/internal"
10+
)
11+
12+
type obsReportSender[K internal.Request] struct {
13+
BaseSender[K]
14+
obsrep *ObsReport
15+
}
16+
17+
func NewObsReportSender[K internal.Request](obsrep *ObsReport) Sender[K] {
18+
return &obsReportSender[K]{obsrep: obsrep}
19+
}
20+
21+
func (ors *obsReportSender[K]) Send(ctx context.Context, req K) error {
22+
c := ors.obsrep.StartOp(ctx)
23+
items := req.ItemsCount()
24+
// Forward the data to the next consumer (this pusher is the next).
25+
err := ors.NextSender.Send(c, req)
26+
ors.obsrep.EndOp(c, items, err)
27+
return err
28+
}

0 commit comments

Comments
 (0)