diff --git a/.chloggen/otlp-grpc-alias.yaml b/.chloggen/otlp-grpc-alias.yaml new file mode 100644 index 00000000000..be8c31c9a20 --- /dev/null +++ b/.chloggen/otlp-grpc-alias.yaml @@ -0,0 +1,29 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. receiver/otlp) +component: exporter/otlp + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Add `otlp_grpc` alias for `otlp` exporter to make the protocol explicit and distinguish it from `otlphttp`. + +# One or more tracking issues or pull requests related to the change +issues: [14099] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: | + The OTLP gRPC exporter can now be configured using either `otlp` or `otlp_grpc` as the exporter type. + Both are equivalent and refer to the same OTLP gRPC exporter. The `otlp_grpc` alias makes the protocol + explicit and helps distinguish it from the `otlphttp` exporter. + +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [user] + diff --git a/cmd/otelcorecol/components.go b/cmd/otelcorecol/components.go index 0e169846144..90277dd1fad 100644 --- a/cmd/otelcorecol/components.go +++ b/cmd/otelcorecol/components.go @@ -61,6 +61,9 @@ func components() (otelcol.Factories, error) { if err != nil { return otelcol.Factories{}, err } + // Register the otlp_grpc alias for the otlp exporter + aliasFactory := otlpexporter.NewFactoryWithAlias() + factories.Exporters[aliasFactory.Type()] = aliasFactory factories.ExporterModules = make(map[component.Type]string, len(factories.Exporters)) factories.ExporterModules[debugexporter.NewFactory().Type()] = "go.opentelemetry.io/collector/exporter/debugexporter v0.140.0" factories.ExporterModules[nopexporter.NewFactory().Type()] = "go.opentelemetry.io/collector/exporter/nopexporter v0.140.0" diff --git a/exporter/otlpexporter/README.md b/exporter/otlpexporter/README.md index 8c4f09a247e..748225c0447 100644 --- a/exporter/otlpexporter/README.md +++ b/exporter/otlpexporter/README.md @@ -20,6 +20,8 @@ Export data via gRPC using [OTLP]( https://github.com/open-telemetry/opentelemetry-proto/blob/main/docs/specification.md) format. By default, this exporter requires TLS and offers queued retry capabilities. +This exporter can be configured using either `otlp` or `otlp_grpc` as the exporter type. Both are equivalent and refer to the same OTLP gRPC exporter. The `otlp_grpc` alias makes the protocol explicit and helps distinguish it from the `otlphttp` exporter. + ## Getting Started The following settings are required: @@ -46,6 +48,11 @@ exporters: endpoint: otelcol2:4317 tls: insecure: true + # Alternative: using the otlp_grpc alias + otlp_grpc: + endpoint: otelcol2:4317 + tls: + insecure: true ``` By default, `gzip` compression is enabled. See [compression comparison](../../config/configgrpc/README.md#compression-comparison) for details benchmark information. To disable, configure as follows: diff --git a/exporter/otlpexporter/factory.go b/exporter/otlpexporter/factory.go index 3d5cc851214..c389fb8f1ea 100644 --- a/exporter/otlpexporter/factory.go +++ b/exporter/otlpexporter/factory.go @@ -31,6 +31,41 @@ func NewFactory() exporter.Factory { ) } +// NewFactoryWithAlias creates a factory for OTLP exporter with the otlp_grpc alias type and wraps the original factory. +func NewFactoryWithAlias() exporter.Factory { + originalFactory := NewFactory().(xexporter.Factory) + aliasType := component.MustNewType("otlp_grpc") + + // Helper function to adjust the ID from otlp_grpc to otlp while preserving the name + adjustID := func(id component.ID) component.ID { + if id.Name() == "" { + return component.NewID(metadata.Type) + } + return component.NewIDWithName(metadata.Type, id.Name()) + } + + return xexporter.NewFactory( + aliasType, + createDefaultConfig, + xexporter.WithTraces(func(ctx context.Context, set exporter.Settings, cfg component.Config) (exporter.Traces, error) { + set.ID = adjustID(set.ID) // transform otlp_grpc to otlp + return originalFactory.CreateTraces(ctx, set, cfg) + }, metadata.TracesStability), + xexporter.WithMetrics(func(ctx context.Context, set exporter.Settings, cfg component.Config) (exporter.Metrics, error) { + set.ID = adjustID(set.ID) + return originalFactory.CreateMetrics(ctx, set, cfg) + }, metadata.MetricsStability), + xexporter.WithLogs(func(ctx context.Context, set exporter.Settings, cfg component.Config) (exporter.Logs, error) { + set.ID = adjustID(set.ID) + return originalFactory.CreateLogs(ctx, set, cfg) + }, metadata.LogsStability), + xexporter.WithProfiles(func(ctx context.Context, set exporter.Settings, cfg component.Config) (xexporter.Profiles, error) { + set.ID = adjustID(set.ID) + return originalFactory.CreateProfiles(ctx, set, cfg) + }, metadata.ProfilesStability), + ) +} + func createDefaultConfig() component.Config { clientCfg := configgrpc.NewDefaultClientConfig() // Default to gzip compression diff --git a/exporter/otlpexporter/factory_test.go b/exporter/otlpexporter/factory_test.go index 0a8e82d22fd..06a10a4d1cf 100644 --- a/exporter/otlpexporter/factory_test.go +++ b/exporter/otlpexporter/factory_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/component/componenttest" "go.opentelemetry.io/collector/config/configcompression" "go.opentelemetry.io/collector/config/configgrpc" @@ -339,3 +340,107 @@ func TestCreateProfiles(t *testing.T) { }) } } + +func TestNewFactoryWithAlias(t *testing.T) { + factory := NewFactoryWithAlias() + assert.NotNil(t, factory) + + // Verify the factory type is otlp_grpc + assert.Equal(t, component.MustNewType("otlp_grpc"), factory.Type()) + + // Verify it can create default config + cfg := factory.CreateDefaultConfig() + assert.NotNil(t, cfg, "failed to create default config") + require.NoError(t, componenttest.CheckConfigStruct(cfg)) + + // Verify stability levels match the original factory + originalFactory := NewFactory() + assert.Equal(t, originalFactory.TracesStability(), factory.TracesStability()) + assert.Equal(t, originalFactory.MetricsStability(), factory.MetricsStability()) + assert.Equal(t, originalFactory.LogsStability(), factory.LogsStability()) +} + +func TestAliasFactoryCreateMetrics(t *testing.T) { + factory := NewFactoryWithAlias() + cfg := factory.CreateDefaultConfig().(*Config) + cfg.ClientConfig.Endpoint = testutil.GetAvailableLocalAddress(t) + + // Use the alias type for settings + set := exportertest.NewNopSettings(factory.Type()) + oexp, err := factory.CreateMetrics(context.Background(), set, cfg) + require.NoError(t, err) + require.NotNil(t, oexp) +} + +func TestAliasFactoryCreateTraces(t *testing.T) { + factory := NewFactoryWithAlias() + cfg := factory.CreateDefaultConfig().(*Config) + cfg.ClientConfig.Endpoint = testutil.GetAvailableLocalAddress(t) + + // Use the alias type for settings + set := exportertest.NewNopSettings(factory.Type()) + oexp, err := factory.CreateTraces(context.Background(), set, cfg) + require.NoError(t, err) + require.NotNil(t, oexp) +} + +func TestAliasFactoryCreateLogs(t *testing.T) { + factory := NewFactoryWithAlias() + cfg := factory.CreateDefaultConfig().(*Config) + cfg.ClientConfig.Endpoint = testutil.GetAvailableLocalAddress(t) + + // Use the alias type for settings + set := exportertest.NewNopSettings(factory.Type()) + oexp, err := factory.CreateLogs(context.Background(), set, cfg) + require.NoError(t, err) + require.NotNil(t, oexp) +} + +func TestAliasFactoryCreateProfiles(t *testing.T) { + factory := NewFactoryWithAlias() + cfg := factory.CreateDefaultConfig().(*Config) + cfg.ClientConfig.Endpoint = testutil.GetAvailableLocalAddress(t) + + // Use the alias type for settings + set := exportertest.NewNopSettings(factory.Type()) + xFactory, ok := factory.(xexporter.Factory) + require.True(t, ok) + oexp, err := xFactory.CreateProfiles(context.Background(), set, cfg) + require.NoError(t, err) + require.NotNil(t, oexp) +} + +func TestAliasFactoryWithNamedComponent(t *testing.T) { + factory := NewFactoryWithAlias() + cfg := factory.CreateDefaultConfig().(*Config) + cfg.ClientConfig.Endpoint = testutil.GetAvailableLocalAddress(t) + + // Test with a named component (e.g., otlp_grpc/myname) + aliasType := component.MustNewType("otlp_grpc") + componentID := component.NewIDWithName(aliasType, "myname") + set := exportertest.NewNopSettings(componentID.Type()) + set.ID = componentID + + oexp, err := factory.CreateMetrics(context.Background(), set, cfg) + require.NoError(t, err) + require.NotNil(t, oexp) +} + +// TestAliasFactoryUnnamedID covers the branch: return component.NewID(metadata.Type) +// when id.Name() == "" +func TestAliasFactoryUnnamedID(t *testing.T) { + factory := NewFactoryWithAlias() + cfg := factory.CreateDefaultConfig().(*Config) + cfg.ClientConfig.Endpoint = testutil.GetAvailableLocalAddress(t) + + // Create an unnamed component ID (no name) to trigger: return component.NewID(metadata.Type) + aliasType := component.MustNewType("otlp_grpc") + unnamedID := component.NewID(aliasType) // ID without a name + set := exportertest.NewNopSettings(aliasType) + set.ID = unnamedID // Override to use unnamed ID + + // Test with traces to cover the adjustID branch + oexp, err := factory.CreateTraces(context.Background(), set, cfg) + require.NoError(t, err) + require.NotNil(t, oexp) +}