Skip to content

Commit

Permalink
[cmd/telemetrygen] Better defaults for HTTP exporter mode (open-telem…
Browse files Browse the repository at this point in the history
…etry#27007)

**Description:**

- Refactored and moved `otlp-http-url-path` flag from the common flags
for each command
    - Added flag to the `traces` command with the default `/v1/traces`
    - Added flag to the `metrics` command with the default `/v1/metrics`
    - Flag was not used for the `logs` command so no longer exposed
- Handle changing the default endpoint based on the communication mode
(gRPC or HTTP)
- Flag `--otlp-endpoint` now defaults to empty string and targets a new
attribute `CustomEndpoint` on `common.Config`
- Added the `Endpoint()` getter function to the `common.Config` struct
for retrieving the correct endpoint
- When `CustomEndpoint` is empty then either `DefaultGRPCEndpoint` or
`DefaultHTTPEndpoint` are chosen based upon the value of
`config.UseHTTP`
- **Misc**: Split out the creation of trace/metric exporters into
standalone factory functions with docstrings available in `exporters.go`
in both modules
- **Misc**: Removed the "default value is" from the descriptions of some
flags as it was repeated information

**Link to tracking Issue:** open-telemetry#26999

**Testing:** 

Added new set of unit tests to cover the addition of the
`config.Endpoint()` getter.

Running `telemetrygen traces --otlp-http --otlp-insecure` now correctly
sends traces using HTTP to the default HTTP endpoint.

**Documentation:** 

No documentation added/changed but updated some command flag
descriptions.

---------

Co-authored-by: Alex Boten <aboten@lightstep.com>
  • Loading branch information
jmsnll and Alex Boten committed Nov 12, 2023
1 parent f108fb8 commit 6fea892
Show file tree
Hide file tree
Showing 12 changed files with 237 additions and 56 deletions.
29 changes: 29 additions & 0 deletions .chloggen/fix_default-http-exporter-behaviour.yaml
Original file line number Diff line number Diff line change
@@ -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: bug_fix

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: telemetrygen

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: better defaults for http exporter mode

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [26999]

# (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 url path default is now correct for both traces and metrics
- when not provided, the endpoint is automatically set to target a local gRPC or HTTP endpoint depending on the communication mode selected
# If your change doesn't affect end users or the exported elements of any package,
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
# 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]
25 changes: 25 additions & 0 deletions cmd/telemetrygen/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package main

import (
"testing"

"github.com/stretchr/testify/assert"
)

// TestConfig_HTTPPath verifies that the HTTPPath configuration defaults are correctly set for each sub-command.
func TestConfig_HTTPPath(t *testing.T) {
t.Run("LogsConfigEmptyDefaultUrlPath", func(t *testing.T) {
assert.Equal(t, "", logsCfg.HTTPPath)
})

t.Run("MetricsConfigValidDefaultUrlPath", func(t *testing.T) {
assert.Equal(t, "/v1/metrics", metricsCfg.HTTPPath)
})

t.Run("TracesConfigValidDefaultUrlPath", func(t *testing.T) {
assert.Equal(t, "/v1/traces", tracesCfg.HTTPPath)
})
}
24 changes: 20 additions & 4 deletions cmd/telemetrygen/internal/common/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ var (
errDoubleQuotesOTLPAttributes = fmt.Errorf("value should be a string wrapped in double quotes")
)

const (
defaultGRPCEndpoint = "localhost:4317"
defaultHTTPEndpoint = "localhost:4318"
)

type KeyValue map[string]string

var _ pflag.Value = (*KeyValue)(nil)
Expand Down Expand Up @@ -51,7 +56,7 @@ type Config struct {
SkipSettingGRPCLogger bool

// OTLP config
Endpoint string
CustomEndpoint string
Insecure bool
UseHTTP bool
HTTPPath string
Expand All @@ -60,6 +65,18 @@ type Config struct {
TelemetryAttributes KeyValue
}

// Endpoint returns the appropriate endpoint URL based on the selected communication mode (gRPC or HTTP)
// or custom endpoint provided in the configuration.
func (c *Config) Endpoint() string {
if c.CustomEndpoint != "" {
return c.CustomEndpoint
}
if c.UseHTTP {
return defaultHTTPEndpoint
}
return defaultGRPCEndpoint
}

func (c *Config) GetAttributes() []attribute.KeyValue {
var attributes []attribute.KeyValue

Expand Down Expand Up @@ -87,12 +104,11 @@ func (c *Config) CommonFlags(fs *pflag.FlagSet) {
fs.IntVar(&c.WorkerCount, "workers", 1, "Number of workers (goroutines) to run")
fs.Int64Var(&c.Rate, "rate", 0, "Approximately how many metrics per second each worker should generate. Zero means no throttling.")
fs.DurationVar(&c.TotalDuration, "duration", 0, "For how long to run the test")
fs.DurationVar(&c.ReportingInterval, "interval", 1*time.Second, "Reporting interval (default 1 second)")
fs.DurationVar(&c.ReportingInterval, "interval", 1*time.Second, "Reporting interval")

fs.StringVar(&c.Endpoint, "otlp-endpoint", "localhost:4317", "Target to which the exporter is going to send metrics.")
fs.StringVar(&c.CustomEndpoint, "otlp-endpoint", "", "Destination endpoint for exporting logs, metrics and traces")
fs.BoolVar(&c.Insecure, "otlp-insecure", false, "Whether to enable client transport security for the exporter's grpc or http connection")
fs.BoolVar(&c.UseHTTP, "otlp-http", false, "Whether to use HTTP exporter rather than a gRPC one")
fs.StringVar(&c.HTTPPath, "otlp-http-url-path", "/v1/metrics", "Which URL path to write to (default /v1/metrics)")

// custom headers
c.Headers = make(map[string]string)
Expand Down
51 changes: 51 additions & 0 deletions cmd/telemetrygen/internal/common/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,54 @@ func TestKeyValueSet(t *testing.T) {
})
}
}

func TestEndpoint(t *testing.T) {
tests := []struct {
name string
endpoint string
http bool
expected string
}{
{
"default-no-http",
"",
false,
defaultGRPCEndpoint,
},
{
"default-with-http",
"",
true,
defaultHTTPEndpoint,
},
{
"custom-endpoint-no-http",
"collector:4317",
false,
"collector:4317",
},
{
"custom-endpoint-with-http",
"collector:4317",
true,
"collector:4317",
},
{
"wrong-custom-endpoint-with-http",
defaultGRPCEndpoint,
true,
defaultGRPCEndpoint,
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
cfg := &Config{
CustomEndpoint: tc.endpoint,
UseHTTP: tc.http,
}

assert.Equal(t, tc.expected, cfg.Endpoint())
})
}
}
2 changes: 1 addition & 1 deletion cmd/telemetrygen/internal/logs/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func Start(cfg *Config) error {
}

// only support grpc in insecure mode
clientConn, err := grpc.DialContext(context.TODO(), cfg.Endpoint, grpc.WithTransportCredentials(insecure.NewCredentials()))
clientConn, err := grpc.DialContext(context.TODO(), cfg.Endpoint(), grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return err
}
Expand Down
3 changes: 3 additions & 0 deletions cmd/telemetrygen/internal/metrics/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ func (c *Config) Flags(fs *pflag.FlagSet) {
c.MetricType = metricTypeGauge

c.CommonFlags(fs)

fs.StringVar(&c.HTTPPath, "otlp-http-url-path", "/v1/metrics", "Which URL path to write to")

fs.Var(&c.MetricType, "metric-type", "Metric type enum. must be one of 'Gauge' or 'Sum'")
fs.IntVar(&c.NumMetrics, "metrics", 1, "Number of metrics to generate in each worker (ignored if duration is provided)")
}
50 changes: 50 additions & 0 deletions cmd/telemetrygen/internal/metrics/exporter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package metrics

import (
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp"
"google.golang.org/grpc"
)

// grpcExporterOptions creates the configuration options for a gRPC-based OTLP metric exporter.
// It configures the exporter with the provided endpoint, connection security settings, and headers.
func grpcExporterOptions(cfg *Config) []otlpmetricgrpc.Option {
grpcExpOpt := []otlpmetricgrpc.Option{
otlpmetricgrpc.WithEndpoint(cfg.Endpoint()),
otlpmetricgrpc.WithDialOption(
grpc.WithBlock(),
),
}

if cfg.Insecure {
grpcExpOpt = append(grpcExpOpt, otlpmetricgrpc.WithInsecure())
}

if len(cfg.Headers) > 0 {
grpcExpOpt = append(grpcExpOpt, otlpmetricgrpc.WithHeaders(cfg.Headers))
}

return grpcExpOpt
}

// httpExporterOptions creates the configuration options for an HTTP-based OTLP metric exporter.
// It configures the exporter with the provided endpoint, URL path, connection security settings, and headers.
func httpExporterOptions(cfg *Config) []otlpmetrichttp.Option {
httpExpOpt := []otlpmetrichttp.Option{
otlpmetrichttp.WithEndpoint(cfg.Endpoint()),
otlpmetrichttp.WithURLPath(cfg.HTTPPath),
}

if cfg.Insecure {
httpExpOpt = append(httpExpOpt, otlpmetrichttp.WithInsecure())
}

if len(cfg.Headers) > 0 {
httpExpOpt = append(httpExpOpt, otlpmetrichttp.WithHeaders(cfg.Headers))
}

return httpExpOpt
}
27 changes: 2 additions & 25 deletions cmd/telemetrygen/internal/metrics/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import (
"go.opentelemetry.io/otel/sdk/resource"
"go.uber.org/zap"
"golang.org/x/time/rate"
"google.golang.org/grpc"

"github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen/internal/common"
)
Expand All @@ -30,35 +29,13 @@ func Start(cfg *Config) error {
}
logger.Info("starting the metrics generator with configuration", zap.Any("config", cfg))

grpcExpOpt := []otlpmetricgrpc.Option{
otlpmetricgrpc.WithEndpoint(cfg.Endpoint),
otlpmetricgrpc.WithDialOption(
grpc.WithBlock(),
),
}

httpExpOpt := []otlpmetrichttp.Option{
otlpmetrichttp.WithEndpoint(cfg.Endpoint),
otlpmetrichttp.WithURLPath(cfg.HTTPPath),
}

if cfg.Insecure {
grpcExpOpt = append(grpcExpOpt, otlpmetricgrpc.WithInsecure())
httpExpOpt = append(httpExpOpt, otlpmetrichttp.WithInsecure())
}

if len(cfg.Headers) > 0 {
grpcExpOpt = append(grpcExpOpt, otlpmetricgrpc.WithHeaders(cfg.Headers))
httpExpOpt = append(httpExpOpt, otlpmetrichttp.WithHeaders(cfg.Headers))
}

var exp sdkmetric.Exporter
if cfg.UseHTTP {
logger.Info("starting HTTP exporter")
exp, err = otlpmetrichttp.New(context.Background(), httpExpOpt...)
exp, err = otlpmetrichttp.New(context.Background(), httpExporterOptions(cfg)...)
} else {
logger.Info("starting gRPC exporter")
exp, err = otlpmetricgrpc.New(context.Background(), grpcExpOpt...)
exp, err = otlpmetricgrpc.New(context.Background(), grpcExporterOptions(cfg)...)
}

if err != nil {
Expand Down
3 changes: 3 additions & 0 deletions cmd/telemetrygen/internal/traces/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ type Config struct {
// Flags registers config flags.
func (c *Config) Flags(fs *pflag.FlagSet) {
c.CommonFlags(fs)

fs.StringVar(&c.HTTPPath, "otlp-http-url-path", "/v1/traces", "Which URL path to write to")

fs.IntVar(&c.NumTraces, "traces", 1, "Number of traces to generate in each worker (ignored if duration is provided)")
fs.BoolVar(&c.PropagateContext, "marshal", false, "Whether to marshal trace context via HTTP headers")
fs.StringVar(&c.ServiceName, "service", "telemetrygen", "Service name to use")
Expand Down
50 changes: 50 additions & 0 deletions cmd/telemetrygen/internal/traces/exporter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package traces

import (
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"google.golang.org/grpc"
)

// grpcExporterOptions creates the configuration options for a gRPC-based OTLP trace exporter.
// It configures the exporter with the provided endpoint, connection security settings, and headers.
func grpcExporterOptions(cfg *Config) []otlptracegrpc.Option {
grpcExpOpt := []otlptracegrpc.Option{
otlptracegrpc.WithEndpoint(cfg.Endpoint()),
otlptracegrpc.WithDialOption(
grpc.WithBlock(),
),
}

if cfg.Insecure {
grpcExpOpt = append(grpcExpOpt, otlptracegrpc.WithInsecure())
}

if len(cfg.Headers) > 0 {
grpcExpOpt = append(grpcExpOpt, otlptracegrpc.WithHeaders(cfg.Headers))
}

return grpcExpOpt
}

// httpExporterOptions creates the configuration options for an HTTP-based OTLP trace exporter.
// It configures the exporter with the provided endpoint, URL path, connection security settings, and headers.
func httpExporterOptions(cfg *Config) []otlptracehttp.Option {
httpExpOpt := []otlptracehttp.Option{
otlptracehttp.WithEndpoint(cfg.Endpoint()),
otlptracehttp.WithURLPath(cfg.HTTPPath),
}

if cfg.Insecure {
httpExpOpt = append(httpExpOpt, otlptracehttp.WithInsecure())
}

if len(cfg.Headers) > 0 {
httpExpOpt = append(httpExpOpt, otlptracehttp.WithHeaders(cfg.Headers))
}

return httpExpOpt
}
27 changes: 2 additions & 25 deletions cmd/telemetrygen/internal/traces/traces.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import (
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
"go.uber.org/zap"
"golang.org/x/time/rate"
"google.golang.org/grpc"

"github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen/internal/common"
)
Expand All @@ -33,35 +32,13 @@ func Start(cfg *Config) error {
return err
}

grpcExpOpt := []otlptracegrpc.Option{
otlptracegrpc.WithEndpoint(cfg.Endpoint),
otlptracegrpc.WithDialOption(
grpc.WithBlock(),
),
}

httpExpOpt := []otlptracehttp.Option{
otlptracehttp.WithEndpoint(cfg.Endpoint),
otlptracehttp.WithURLPath(cfg.HTTPPath),
}

if cfg.Insecure {
grpcExpOpt = append(grpcExpOpt, otlptracegrpc.WithInsecure())
httpExpOpt = append(httpExpOpt, otlptracehttp.WithInsecure())
}

if len(cfg.Headers) > 0 {
grpcExpOpt = append(grpcExpOpt, otlptracegrpc.WithHeaders(cfg.Headers))
httpExpOpt = append(httpExpOpt, otlptracehttp.WithHeaders(cfg.Headers))
}

var exp *otlptrace.Exporter
if cfg.UseHTTP {
logger.Info("starting HTTP exporter")
exp, err = otlptracehttp.New(context.Background(), httpExpOpt...)
exp, err = otlptracehttp.New(context.Background(), httpExporterOptions(cfg)...)
} else {
logger.Info("starting gRPC exporter")
exp, err = otlptracegrpc.New(context.Background(), grpcExpOpt...)
exp, err = otlptracegrpc.New(context.Background(), grpcExporterOptions(cfg)...)
}

if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion cmd/telemetrygen/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func TestGenerateTraces(t *testing.T) {
Rate: 10,
TotalDuration: 10 * time.Second,
ReportingInterval: 10,
Endpoint: endpoint,
CustomEndpoint: endpoint,
Insecure: true,
UseHTTP: false,
Headers: nil,
Expand Down

0 comments on commit 6fea892

Please sign in to comment.