Skip to content

Commit f1073ef

Browse files
authored
[receiver/solacereceiver]: Updated the format for generated metrics and included new Solace receiver metric attribute (#34541)
**Description:** Updated the format for generated metrics. Included a `receiver_name` attribute that identifies the Solace receiver that generated the metrics. **Link to tracking Issue:** N/A **Testing:** Updated unit tests to accommodate passing of attributes for metrics **Documentation:** N/A
1 parent 45bdfb0 commit f1073ef

File tree

8 files changed

+121
-49
lines changed

8 files changed

+121
-49
lines changed

.chloggen/fix_metrics_prefix.yaml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
7+
component: solacereceiver
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: "Updated the format for generated metrics. Included a `receiver_name` attribute that identifies the Solace receiver that generated the metrics"
11+
12+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
13+
issues: [34541]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext:
19+
20+
# If your change doesn't affect end users or the exported elements of any package,
21+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
22+
# Optional: The change log or logs in which this entry should be included.
23+
# e.g. '[user]' or '[user, api]'
24+
# Include 'user' if the change is relevant to end users.
25+
# Include 'api' if there is a change to a library API.
26+
# Default: '[user]'
27+
change_logs: [user]

receiver/solacereceiver/receiver.go

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import (
1515
"go.opentelemetry.io/collector/consumer"
1616
"go.opentelemetry.io/collector/consumer/consumererror"
1717
"go.opentelemetry.io/collector/receiver"
18+
"go.opentelemetry.io/otel/attribute"
19+
"go.opentelemetry.io/otel/metric"
1820
"go.uber.org/zap"
1921

2022
"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/solacereceiver/internal/metadata"
@@ -38,6 +40,10 @@ const (
3840
flowControlStateControlled
3941
)
4042

43+
const (
44+
brokerComponenteNameAttr = "receiver_name"
45+
)
46+
4147
// solaceTracesReceiver uses azure AMQP to consume and handle telemetry data from SOlace. Implements receiver.Traces
4248
type solaceTracesReceiver struct {
4349
// config is the receiver.Config instance used to build the receiver
@@ -56,6 +62,8 @@ type solaceTracesReceiver struct {
5662
terminating *atomic.Bool
5763
// retryTimeout is the timeout between connection attempts
5864
retryTimeout time.Duration
65+
// Other Attributes including the ID of the receiver Solace broker's component name
66+
metricAttrs attribute.Set
5967
}
6068

6169
// newTracesReceiver creates a new solaceTraceReceiver as a receiver.Traces
@@ -73,7 +81,18 @@ func newTracesReceiver(config *Config, set receiver.Settings, nextConsumer consu
7381
return nil, err
7482
}
7583

76-
unmarshaller := newTracesUnmarshaller(set.Logger, telemetryBuilder)
84+
// solaceBrokerAttrs - including the component name of the connected Solace broker
85+
receiverName := set.ID.Name()
86+
if receiverName != "" {
87+
receiverName = "solace/" + receiverName
88+
} else {
89+
receiverName = "solace"
90+
}
91+
solaceBrokerAttrs := attribute.NewSet(
92+
attribute.String(brokerComponenteNameAttr, receiverName),
93+
)
94+
95+
unmarshaller := newTracesUnmarshaller(set.Logger, telemetryBuilder, solaceBrokerAttrs)
7796

7897
return &solaceTracesReceiver{
7998
config: config,
@@ -85,13 +104,15 @@ func newTracesReceiver(config *Config, set receiver.Settings, nextConsumer consu
85104
factory: factory,
86105
retryTimeout: 1 * time.Second,
87106
terminating: &atomic.Bool{},
107+
metricAttrs: solaceBrokerAttrs,
88108
}, nil
89109
}
90110

91111
// Start implements component.Receiver::Start
92112
func (s *solaceTracesReceiver) Start(ctx context.Context, _ component.Host) error {
93-
s.telemetryBuilder.SolacereceiverReceiverStatus.Record(ctx, int64(receiverStateStarting))
94-
s.telemetryBuilder.SolacereceiverReceiverFlowControlStatus.Record(ctx, int64(flowControlStateClear))
113+
// set the component name for the connected Solace broker
114+
s.telemetryBuilder.SolacereceiverReceiverStatus.Record(ctx, int64(receiverStateStarting), metric.WithAttributeSet(s.metricAttrs))
115+
s.telemetryBuilder.SolacereceiverReceiverFlowControlStatus.Record(ctx, int64(flowControlStateClear), metric.WithAttributeSet(s.metricAttrs))
95116
var cancelableContext context.Context
96117
cancelableContext, s.cancel = context.WithCancel(context.Background())
97118

@@ -108,13 +129,14 @@ func (s *solaceTracesReceiver) Shutdown(_ context.Context) error {
108129
if s.cancel == nil {
109130
return nil
110131
}
132+
// set the component name for the connected Solace broker
111133
s.terminating.Store(true)
112-
s.telemetryBuilder.SolacereceiverReceiverStatus.Record(context.Background(), int64(receiverStateTerminating))
134+
s.telemetryBuilder.SolacereceiverReceiverStatus.Record(context.Background(), int64(receiverStateTerminating), metric.WithAttributeSet(s.metricAttrs))
113135
s.settings.Logger.Info("Shutdown waiting for all components to complete")
114136
s.cancel() // cancels the context passed to the reconnection loop
115137
s.shutdownWaitGroup.Wait()
116138
s.settings.Logger.Info("Receiver shutdown successfully")
117-
s.telemetryBuilder.SolacereceiverReceiverStatus.Record(context.Background(), int64(receiverStateTerminated))
139+
s.telemetryBuilder.SolacereceiverReceiverStatus.Record(context.Background(), int64(receiverStateTerminated), metric.WithAttributeSet(s.metricAttrs))
118140
return nil
119141
}
120142

@@ -130,7 +152,7 @@ func (s *solaceTracesReceiver) connectAndReceive(ctx context.Context) {
130152
disable := false
131153

132154
// indicate we are in connecting state at the start
133-
s.telemetryBuilder.SolacereceiverReceiverStatus.Record(context.Background(), int64(receiverStateConnecting))
155+
s.telemetryBuilder.SolacereceiverReceiverStatus.Record(context.Background(), int64(receiverStateConnecting), metric.WithAttributeSet(s.metricAttrs))
134156

135157
reconnectionLoop:
136158
for !disable {
@@ -156,7 +178,7 @@ reconnectionLoop:
156178

157179
if err := service.dial(ctx); err != nil {
158180
s.settings.Logger.Debug("Encountered error while connecting messaging service", zap.Error(err))
159-
s.telemetryBuilder.SolacereceiverFailedReconnections.Add(ctx, 1)
181+
s.telemetryBuilder.SolacereceiverFailedReconnections.Add(ctx, 1, metric.WithAttributeSet(s.metricAttrs))
160182
return
161183
}
162184
// dial was successful, record the connected state
@@ -165,7 +187,7 @@ reconnectionLoop:
165187
if err := s.receiveMessages(ctx, service); err != nil {
166188
s.settings.Logger.Debug("Encountered error while receiving messages", zap.Error(err))
167189
if errors.Is(err, errUpgradeRequired) {
168-
s.telemetryBuilder.SolacereceiverNeedUpgrade.Record(ctx, 1)
190+
s.telemetryBuilder.SolacereceiverNeedUpgrade.Record(ctx, 1, metric.WithAttributeSet(s.metricAttrs))
169191
disable = true
170192
return
171193
}
@@ -182,7 +204,7 @@ reconnectionLoop:
182204
// this state transition were to happen, it would be short lived.
183205
func (s *solaceTracesReceiver) recordConnectionState(state receiverState) {
184206
if !s.terminating.Load() {
185-
s.telemetryBuilder.SolacereceiverReceiverStatus.Record(context.Background(), int64(state))
207+
s.telemetryBuilder.SolacereceiverReceiverStatus.Record(context.Background(), int64(state), metric.WithAttributeSet(s.metricAttrs))
186208
}
187209
}
188210

@@ -220,18 +242,18 @@ func (s *solaceTracesReceiver) receiveMessage(ctx context.Context, service messa
220242
}
221243
}()
222244
// message received successfully
223-
s.telemetryBuilder.SolacereceiverReceivedSpanMessages.Add(ctx, 1)
245+
s.telemetryBuilder.SolacereceiverReceivedSpanMessages.Add(ctx, 1, metric.WithAttributeSet(s.metricAttrs))
224246
// unmarshal the message. unmarshalling errors are not fatal unless the version is unknown
225247
traces, unmarshalErr := s.unmarshaller.unmarshal(msg)
226248
if unmarshalErr != nil {
227249
s.settings.Logger.Error("Encountered error while unmarshalling message", zap.Error(unmarshalErr))
228-
s.telemetryBuilder.SolacereceiverFatalUnmarshallingErrors.Add(ctx, 1)
250+
s.telemetryBuilder.SolacereceiverFatalUnmarshallingErrors.Add(ctx, 1, metric.WithAttributeSet(s.metricAttrs))
229251
if errors.Is(unmarshalErr, errUpgradeRequired) {
230252
disposition = service.failed // if we don't know the version, reject the trace message since we will disable the receiver
231253
return unmarshalErr
232254
}
233-
s.telemetryBuilder.SolacereceiverDroppedSpanMessages.Add(ctx, 1) // if the error is some other unmarshalling error, we will ack the message and drop the content
234-
return nil // don't propagate error, but don't continue forwarding traces
255+
s.telemetryBuilder.SolacereceiverDroppedSpanMessages.Add(ctx, 1, metric.WithAttributeSet(s.metricAttrs)) // if the error is some other unmarshalling error, we will ack the message and drop the content
256+
return nil // don't propagate error, but don't continue forwarding traces
235257
}
236258

237259
var flowControlCount int64
@@ -245,10 +267,10 @@ flowControlLoop:
245267
s.settings.Logger.Info("Encountered temporary error while forwarding traces to next receiver, will allow redelivery", zap.Error(forwardErr))
246268
// handle flow control metrics
247269
if flowControlCount == 0 {
248-
s.telemetryBuilder.SolacereceiverReceiverFlowControlStatus.Record(ctx, int64(flowControlStateControlled))
270+
s.telemetryBuilder.SolacereceiverReceiverFlowControlStatus.Record(ctx, int64(flowControlStateControlled), metric.WithAttributeSet(s.metricAttrs))
249271
}
250272
flowControlCount++
251-
s.telemetryBuilder.SolacereceiverReceiverFlowControlRecentRetries.Record(ctx, flowControlCount)
273+
s.telemetryBuilder.SolacereceiverReceiverFlowControlRecentRetries.Record(ctx, flowControlCount, metric.WithAttributeSet(s.metricAttrs))
252274
// Backpressure scenario. For now, we are only delayed retry, eventually we may need to handle this
253275
delayTimer := time.NewTimer(s.config.Flow.DelayedRetry.Delay)
254276
select {
@@ -261,21 +283,21 @@ flowControlLoop:
261283
}
262284
} else { // error is permanent, we want to accept the message and increment the number of dropped messages
263285
s.settings.Logger.Warn("Encountered permanent error while forwarding traces to next receiver, will swallow trace", zap.Error(forwardErr))
264-
s.telemetryBuilder.SolacereceiverDroppedSpanMessages.Add(ctx, 1)
286+
s.telemetryBuilder.SolacereceiverDroppedSpanMessages.Add(ctx, 1, metric.WithAttributeSet(s.metricAttrs))
265287
break flowControlLoop
266288
}
267289
} else {
268290
// no forward error
269-
s.telemetryBuilder.SolacereceiverReportedSpans.Add(ctx, int64(traces.SpanCount()))
291+
s.telemetryBuilder.SolacereceiverReportedSpans.Add(ctx, int64(traces.SpanCount()), metric.WithAttributeSet(s.metricAttrs))
270292
break flowControlLoop
271293
}
272294
}
273295
// Make sure to clear the stats no matter what, unless we were interrupted in which case we should preserve the last state
274296
if flowControlCount != 0 {
275-
s.telemetryBuilder.SolacereceiverReceiverFlowControlStatus.Record(ctx, int64(flowControlStateClear))
276-
s.telemetryBuilder.SolacereceiverReceiverFlowControlTotal.Add(ctx, 1)
297+
s.telemetryBuilder.SolacereceiverReceiverFlowControlStatus.Record(ctx, int64(flowControlStateClear), metric.WithAttributeSet(s.metricAttrs))
298+
s.telemetryBuilder.SolacereceiverReceiverFlowControlTotal.Add(ctx, 1, metric.WithAttributeSet(s.metricAttrs))
277299
if flowControlCount == 1 {
278-
s.telemetryBuilder.SolacereceiverReceiverFlowControlWithSingleSuccessfulRetry.Add(ctx, 1)
300+
s.telemetryBuilder.SolacereceiverReceiverFlowControlWithSingleSuccessfulRetry.Add(ctx, 1, metric.WithAttributeSet(s.metricAttrs))
279301
}
280302
}
281303
return nil

receiver/solacereceiver/unmarshaller.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"go.opentelemetry.io/collector/pdata/pcommon"
1111
"go.opentelemetry.io/collector/pdata/ptrace"
12+
"go.opentelemetry.io/otel/attribute"
1213
"go.uber.org/zap"
1314

1415
"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/solacereceiver/internal/metadata"
@@ -22,18 +23,20 @@ type tracesUnmarshaller interface {
2223
}
2324

2425
// newUnmarshalleer returns a new unmarshaller ready for message unmarshalling
25-
func newTracesUnmarshaller(logger *zap.Logger, telemetryBuilder *metadata.TelemetryBuilder) tracesUnmarshaller {
26+
func newTracesUnmarshaller(logger *zap.Logger, telemetryBuilder *metadata.TelemetryBuilder, metricAttrs attribute.Set) tracesUnmarshaller {
2627
return &solaceTracesUnmarshaller{
2728
logger: logger,
2829
telemetryBuilder: telemetryBuilder,
2930
// v1 unmarshaller is implemented by solaceMessageUnmarshallerV1
3031
receiveUnmarshallerV1: &brokerTraceReceiveUnmarshallerV1{
3132
logger: logger,
3233
telemetryBuilder: telemetryBuilder,
34+
metricAttrs: metricAttrs,
3335
},
3436
egressUnmarshallerV1: &brokerTraceEgressUnmarshallerV1{
3537
logger: logger,
3638
telemetryBuilder: telemetryBuilder,
39+
metricAttrs: metricAttrs,
3740
},
3841
}
3942
}

receiver/solacereceiver/unmarshaller_egress.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111

1212
"go.opentelemetry.io/collector/pdata/pcommon"
1313
"go.opentelemetry.io/collector/pdata/ptrace"
14+
"go.opentelemetry.io/otel/attribute"
15+
"go.opentelemetry.io/otel/metric"
1416
"go.uber.org/zap"
1517
"google.golang.org/protobuf/proto"
1618

@@ -21,6 +23,7 @@ import (
2123
type brokerTraceEgressUnmarshallerV1 struct {
2224
logger *zap.Logger
2325
telemetryBuilder *metadata.TelemetryBuilder
26+
metricAttrs attribute.Set // othere Otel attributes (to add to the metrics)
2427
}
2528

2629
func (u *brokerTraceEgressUnmarshallerV1) unmarshal(message *inboundMessage) (ptrace.Traces, error) {
@@ -68,9 +71,9 @@ func (u *brokerTraceEgressUnmarshallerV1) mapEgressSpan(spanData *egress_v1.Span
6871
u.mapTransactionEvent(transactionEvent, clientSpan.Events().AppendEmpty())
6972
}
7073
} else {
71-
// unknown span type, drop the span
72-
u.logger.Warn("Received egress span with unknown span type, is the collector out of date?")
73-
u.telemetryBuilder.SolacereceiverDroppedEgressSpans.Add(context.Background(), 1)
74+
// malformed/incomplete egress span received, drop the span
75+
u.logger.Warn("Received egress span with no span type, could be malformed egress span?")
76+
u.telemetryBuilder.SolacereceiverDroppedEgressSpans.Add(context.Background(), 1, metric.WithAttributeSet(u.metricAttrs))
7477
}
7578
}
7679

@@ -143,7 +146,7 @@ func (u *brokerTraceEgressUnmarshallerV1) mapSendSpan(sendSpan *egress_v1.SpanDa
143146
attributes.PutStr(sourceKindKey, queueKind)
144147
default:
145148
u.logger.Warn(fmt.Sprintf("Unknown source type %T", casted))
146-
u.telemetryBuilder.SolacereceiverRecoverableUnmarshallingErrors.Add(context.Background(), 1)
149+
u.telemetryBuilder.SolacereceiverRecoverableUnmarshallingErrors.Add(context.Background(), 1, metric.WithAttributeSet(u.metricAttrs))
147150
name = unknownSendName
148151
}
149152
span.SetName(name + sendNameSuffix)
@@ -196,7 +199,7 @@ func (u *brokerTraceEgressUnmarshallerV1) mapTransactionEvent(transactionEvent *
196199
// Set the name to the unknown transaction event type to ensure forward compat.
197200
name = fmt.Sprintf("Unknown Transaction Event (%s)", transactionEvent.GetType().String())
198201
u.logger.Warn(fmt.Sprintf("Received span with unknown transaction event %s", transactionEvent.GetType()))
199-
u.telemetryBuilder.SolacereceiverRecoverableUnmarshallingErrors.Add(context.Background(), 1)
202+
u.telemetryBuilder.SolacereceiverRecoverableUnmarshallingErrors.Add(context.Background(), 1, metric.WithAttributeSet(u.metricAttrs))
200203
}
201204
clientEvent.SetName(name)
202205
clientEvent.SetTimestamp(pcommon.Timestamp(transactionEvent.TimeUnixNano))
@@ -212,7 +215,7 @@ func (u *brokerTraceEgressUnmarshallerV1) mapTransactionEvent(transactionEvent *
212215
default:
213216
initiator = fmt.Sprintf("Unknown Transaction Initiator (%s)", transactionEvent.GetInitiator().String())
214217
u.logger.Warn(fmt.Sprintf("Received span with unknown transaction initiator %s", transactionEvent.GetInitiator()))
215-
u.telemetryBuilder.SolacereceiverRecoverableUnmarshallingErrors.Add(context.Background(), 1)
218+
u.telemetryBuilder.SolacereceiverRecoverableUnmarshallingErrors.Add(context.Background(), 1, metric.WithAttributeSet(u.metricAttrs))
216219
}
217220
clientEvent.Attributes().PutStr(transactionInitiatorEventKey, initiator)
218221
// conditionally set the error description if one occurred, otherwise omit
@@ -233,7 +236,7 @@ func (u *brokerTraceEgressUnmarshallerV1) mapTransactionEvent(transactionEvent *
233236
clientEvent.Attributes().PutStr(transactionXIDEventKey, xidString)
234237
default:
235238
u.logger.Warn(fmt.Sprintf("Unknown transaction ID type %T", transactionID))
236-
u.telemetryBuilder.SolacereceiverRecoverableUnmarshallingErrors.Add(context.Background(), 1)
239+
u.telemetryBuilder.SolacereceiverRecoverableUnmarshallingErrors.Add(context.Background(), 1, metric.WithAttributeSet(u.metricAttrs))
237240
}
238241
}
239242

receiver/solacereceiver/unmarshaller_egress_test.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/stretchr/testify/require"
1212
"go.opentelemetry.io/collector/pdata/pcommon"
1313
"go.opentelemetry.io/collector/pdata/ptrace"
14+
"go.opentelemetry.io/otel/attribute"
1415
"go.opentelemetry.io/otel/sdk/metric/metricdata"
1516
"go.uber.org/zap"
1617

@@ -315,7 +316,8 @@ func TestEgressUnmarshallerEgressSpan(t *testing.T) {
315316
IsMonotonic: true,
316317
DataPoints: []metricdata.DataPoint[int64]{
317318
{
318-
Value: 1,
319+
Value: 1,
320+
Attributes: u.metricAttrs,
319321
},
320322
},
321323
},
@@ -438,7 +440,8 @@ func TestEgressUnmarshallerSendSpanAttributes(t *testing.T) {
438440
IsMonotonic: true,
439441
DataPoints: []metricdata.DataPoint[int64]{
440442
{
441-
Value: tt.expectedUnmarshallingErrors,
443+
Value: tt.expectedUnmarshallingErrors,
444+
Attributes: u.metricAttrs,
442445
},
443446
},
444447
},
@@ -649,7 +652,8 @@ func TestEgressUnmarshallerTransactionEvent(t *testing.T) {
649652
IsMonotonic: true,
650653
DataPoints: []metricdata.DataPoint[int64]{
651654
{
652-
Value: tt.expectedUnmarshallingErrors,
655+
Value: tt.expectedUnmarshallingErrors,
656+
Attributes: u.metricAttrs,
653657
},
654658
},
655659
},
@@ -664,5 +668,6 @@ func newTestEgressV1Unmarshaller(t *testing.T) (*brokerTraceEgressUnmarshallerV1
664668
tt := setupTestTelemetry()
665669
builder, err := metadata.NewTelemetryBuilder(tt.NewSettings().TelemetrySettings)
666670
require.NoError(t, err)
667-
return &brokerTraceEgressUnmarshallerV1{zap.NewNop(), builder}, tt
671+
metricAttr := attribute.NewSet(attribute.String("receiver_name", tt.NewSettings().ID.Name()))
672+
return &brokerTraceEgressUnmarshallerV1{zap.NewNop(), builder, metricAttr}, tt
668673
}

0 commit comments

Comments
 (0)