opentelemetry-collector/service/internal/graph/connector.go

341 lines
10 KiB
Go

// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package graph // import "go.opentelemetry.io/collector/service/internal/graph"
import (
"context"
otelattr "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/connector"
"go.opentelemetry.io/collector/connector/xconnector"
"go.opentelemetry.io/collector/consumer"
"go.opentelemetry.io/collector/consumer/xconsumer"
"go.opentelemetry.io/collector/internal/telemetry"
"go.opentelemetry.io/collector/pipeline"
"go.opentelemetry.io/collector/pipeline/xpipeline"
"go.opentelemetry.io/collector/service/internal/attribute"
"go.opentelemetry.io/collector/service/internal/builders"
"go.opentelemetry.io/collector/service/internal/capabilityconsumer"
"go.opentelemetry.io/collector/service/internal/metadata"
"go.opentelemetry.io/collector/service/internal/obsconsumer"
)
const pipelineIDAttrKey = "otelcol.pipeline.id"
var _ consumerNode = (*connectorNode)(nil)
type connectorNode struct {
attribute.Attributes
componentID component.ID
exprPipelineType pipeline.Signal
rcvrPipelineType pipeline.Signal
component.Component
consumer baseConsumer
}
func newConnectorNode(exprPipelineType, rcvrPipelineType pipeline.Signal, connID component.ID) *connectorNode {
return &connectorNode{
Attributes: attribute.Connector(exprPipelineType, rcvrPipelineType, connID),
componentID: connID,
exprPipelineType: exprPipelineType,
rcvrPipelineType: rcvrPipelineType,
}
}
func (n *connectorNode) getConsumer() baseConsumer {
return n.consumer
}
func (n *connectorNode) buildComponent(
ctx context.Context,
tel component.TelemetrySettings,
info component.BuildInfo,
builder *builders.ConnectorBuilder,
nexts []baseConsumer,
) error {
set := connector.Settings{
ID: n.componentID,
TelemetrySettings: telemetry.WithAttributeSet(tel, *n.Set()),
BuildInfo: info,
}
switch n.rcvrPipelineType {
case pipeline.SignalTraces:
return n.buildTraces(ctx, set, builder, nexts)
case pipeline.SignalMetrics:
return n.buildMetrics(ctx, set, builder, nexts)
case pipeline.SignalLogs:
return n.buildLogs(ctx, set, builder, nexts)
case xpipeline.SignalProfiles:
return n.buildProfiles(ctx, set, builder, nexts)
}
return nil
}
func (n *connectorNode) buildTraces(
ctx context.Context,
set connector.Settings,
builder *builders.ConnectorBuilder,
nexts []baseConsumer,
) error {
tb, err := metadata.NewTelemetryBuilder(set.TelemetrySettings)
if err != nil {
return err
}
consumers := make(map[pipeline.ID]consumer.Traces, len(nexts))
for _, next := range nexts {
consumers[next.(*capabilitiesNode).pipelineID] = obsconsumer.NewTraces(
next.(consumer.Traces),
tb.ConnectorProducedItems, tb.ConnectorProducedSize,
obsconsumer.WithStaticDataPointAttribute(
otelattr.String(
pipelineIDAttrKey,
next.(*capabilitiesNode).pipelineID.String(),
),
),
)
}
next := connector.NewTracesRouter(consumers)
switch n.exprPipelineType {
case pipeline.SignalTraces:
n.Component, err = builder.CreateTracesToTraces(ctx, set, next)
if err != nil {
return err
}
// Connectors which might pass along data must inherit capabilities of all nexts
n.consumer = obsconsumer.NewTraces(
capabilityconsumer.NewTraces(
n.Component.(consumer.Traces),
aggregateCap(n.Component.(consumer.Traces), nexts),
),
tb.ConnectorConsumedItems, tb.ConnectorConsumedSize,
)
case pipeline.SignalMetrics:
n.Component, err = builder.CreateMetricsToTraces(ctx, set, next)
if err != nil {
return err
}
n.consumer = obsconsumer.NewMetrics(n.Component.(consumer.Metrics), tb.ConnectorConsumedItems, tb.ConnectorConsumedSize)
case pipeline.SignalLogs:
n.Component, err = builder.CreateLogsToTraces(ctx, set, next)
if err != nil {
return err
}
n.consumer = obsconsumer.NewLogs(n.Component.(consumer.Logs), tb.ConnectorConsumedItems, tb.ConnectorConsumedSize)
case xpipeline.SignalProfiles:
n.Component, err = builder.CreateProfilesToTraces(ctx, set, next)
if err != nil {
return err
}
n.consumer = obsconsumer.NewProfiles(n.Component.(xconsumer.Profiles), tb.ConnectorConsumedItems, tb.ConnectorConsumedSize)
}
return nil
}
func (n *connectorNode) buildMetrics(
ctx context.Context,
set connector.Settings,
builder *builders.ConnectorBuilder,
nexts []baseConsumer,
) error {
tb, err := metadata.NewTelemetryBuilder(set.TelemetrySettings)
if err != nil {
return err
}
consumers := make(map[pipeline.ID]consumer.Metrics, len(nexts))
for _, next := range nexts {
consumers[next.(*capabilitiesNode).pipelineID] = obsconsumer.NewMetrics(
next.(consumer.Metrics),
tb.ConnectorProducedItems, tb.ConnectorProducedSize,
obsconsumer.WithStaticDataPointAttribute(
otelattr.String(
pipelineIDAttrKey,
next.(*capabilitiesNode).pipelineID.String(),
),
),
)
}
next := connector.NewMetricsRouter(consumers)
switch n.exprPipelineType {
case pipeline.SignalMetrics:
n.Component, err = builder.CreateMetricsToMetrics(ctx, set, next)
if err != nil {
return err
}
// Connectors which might pass along data must inherit capabilities of all nexts
n.consumer = obsconsumer.NewMetrics(
capabilityconsumer.NewMetrics(
n.Component.(consumer.Metrics),
aggregateCap(n.Component.(consumer.Metrics), nexts),
),
tb.ConnectorConsumedItems, tb.ConnectorConsumedSize,
)
case pipeline.SignalTraces:
n.Component, err = builder.CreateTracesToMetrics(ctx, set, next)
if err != nil {
return err
}
n.consumer = obsconsumer.NewTraces(n.Component.(consumer.Traces), tb.ConnectorConsumedItems, tb.ConnectorConsumedSize)
case pipeline.SignalLogs:
n.Component, err = builder.CreateLogsToMetrics(ctx, set, next)
if err != nil {
return err
}
n.consumer = obsconsumer.NewLogs(n.Component.(consumer.Logs), tb.ConnectorConsumedItems, tb.ConnectorConsumedSize)
case xpipeline.SignalProfiles:
n.Component, err = builder.CreateProfilesToMetrics(ctx, set, next)
if err != nil {
return err
}
n.consumer = obsconsumer.NewProfiles(n.Component.(xconsumer.Profiles), tb.ConnectorConsumedItems, tb.ConnectorConsumedSize)
}
return nil
}
func (n *connectorNode) buildLogs(
ctx context.Context,
set connector.Settings,
builder *builders.ConnectorBuilder,
nexts []baseConsumer,
) error {
tb, err := metadata.NewTelemetryBuilder(set.TelemetrySettings)
if err != nil {
return err
}
consumers := make(map[pipeline.ID]consumer.Logs, len(nexts))
for _, next := range nexts {
consumers[next.(*capabilitiesNode).pipelineID] = obsconsumer.NewLogs(
next.(consumer.Logs),
tb.ConnectorProducedItems, tb.ConnectorProducedSize,
obsconsumer.WithStaticDataPointAttribute(
otelattr.String(
pipelineIDAttrKey,
next.(*capabilitiesNode).pipelineID.String(),
),
),
)
}
next := connector.NewLogsRouter(consumers)
switch n.exprPipelineType {
case pipeline.SignalLogs:
n.Component, err = builder.CreateLogsToLogs(ctx, set, next)
if err != nil {
return err
}
// Connectors which might pass along data must inherit capabilities of all nexts
n.consumer = obsconsumer.NewLogs(
capabilityconsumer.NewLogs(
n.Component.(consumer.Logs),
aggregateCap(n.Component.(consumer.Logs), nexts),
),
tb.ConnectorConsumedItems, tb.ConnectorConsumedSize,
)
case pipeline.SignalTraces:
n.Component, err = builder.CreateTracesToLogs(ctx, set, next)
if err != nil {
return err
}
n.consumer = obsconsumer.NewTraces(n.Component.(consumer.Traces), tb.ConnectorConsumedItems, tb.ConnectorConsumedSize)
case pipeline.SignalMetrics:
n.Component, err = builder.CreateMetricsToLogs(ctx, set, next)
if err != nil {
return err
}
n.consumer = obsconsumer.NewMetrics(n.Component.(consumer.Metrics), tb.ConnectorConsumedItems, tb.ConnectorConsumedSize)
case xpipeline.SignalProfiles:
n.Component, err = builder.CreateProfilesToLogs(ctx, set, next)
if err != nil {
return err
}
n.consumer = obsconsumer.NewProfiles(n.Component.(xconsumer.Profiles), tb.ConnectorConsumedItems, tb.ConnectorConsumedSize)
}
return nil
}
func (n *connectorNode) buildProfiles(
ctx context.Context,
set connector.Settings,
builder *builders.ConnectorBuilder,
nexts []baseConsumer,
) error {
tb, err := metadata.NewTelemetryBuilder(set.TelemetrySettings)
if err != nil {
return err
}
consumers := make(map[pipeline.ID]xconsumer.Profiles, len(nexts))
for _, next := range nexts {
consumers[next.(*capabilitiesNode).pipelineID] = obsconsumer.NewProfiles(
next.(xconsumer.Profiles),
tb.ConnectorProducedItems, tb.ConnectorProducedSize,
obsconsumer.WithStaticDataPointAttribute(
otelattr.String(
pipelineIDAttrKey,
next.(*capabilitiesNode).pipelineID.String(),
),
),
)
}
next := xconnector.NewProfilesRouter(consumers)
switch n.exprPipelineType {
case xpipeline.SignalProfiles:
n.Component, err = builder.CreateProfilesToProfiles(ctx, set, next)
if err != nil {
return err
}
// Connectors which might pass along data must inherit capabilities of all nexts
n.consumer = obsconsumer.NewProfiles(
capabilityconsumer.NewProfiles(
n.Component.(xconsumer.Profiles),
aggregateCap(n.Component.(xconsumer.Profiles), nexts),
),
tb.ConnectorConsumedItems, tb.ConnectorConsumedSize,
)
case pipeline.SignalTraces:
n.Component, err = builder.CreateTracesToProfiles(ctx, set, next)
if err != nil {
return err
}
n.consumer = obsconsumer.NewTraces(n.Component.(consumer.Traces), tb.ConnectorConsumedItems, tb.ConnectorConsumedSize)
case pipeline.SignalMetrics:
n.Component, err = builder.CreateMetricsToProfiles(ctx, set, next)
if err != nil {
return err
}
n.consumer = obsconsumer.NewMetrics(n.Component.(consumer.Metrics), tb.ConnectorConsumedItems, tb.ConnectorConsumedSize)
case pipeline.SignalLogs:
n.Component, err = builder.CreateLogsToProfiles(ctx, set, next)
if err != nil {
return err
}
n.consumer = obsconsumer.NewLogs(n.Component.(consumer.Logs), tb.ConnectorConsumedItems, tb.ConnectorConsumedSize)
}
return nil
}
// When connecting pipelines of the same data type, the connector must
// inherit the capabilities of pipelines in which it is acting as a receiver.
// Since the incoming and outgoing data types are the same, we must also consider
// that the connector itself may mutate the data and pass it along.
func aggregateCap(base baseConsumer, nexts []baseConsumer) consumer.Capabilities {
capabilities := base.Capabilities()
for _, next := range nexts {
capabilities.MutatesData = capabilities.MutatesData || next.Capabilities().MutatesData
}
return capabilities
}