Skip to content

Commit d4cdec7

Browse files
axwRoryCrispin
authored andcommitted
cmd/telemetrygen: add HTTP export for logs (open-telemetry#29078)
**Description:** Closes open-telemetry#18867 **Testing:** Ran opentelemetry-collector locally with debug exporter, then used telemetrygen with `--otlp-http` with and without `--otlp-insecure`. **Documentation:** None
1 parent d821a3b commit d4cdec7

File tree

5 files changed

+127
-36
lines changed

5 files changed

+127
-36
lines changed

.chloggen/telemetrygen-logs-http.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: cmd/telemetrygen
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Add support for --otlp-http for telemetrygen logs
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: [18867]
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]

cmd/telemetrygen/config_test.go

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

1212
// TestConfig_HTTPPath verifies that the HTTPPath configuration defaults are correctly set for each sub-command.
1313
func TestConfig_HTTPPath(t *testing.T) {
14-
t.Run("LogsConfigEmptyDefaultUrlPath", func(t *testing.T) {
15-
assert.Equal(t, "", logsCfg.HTTPPath)
14+
t.Run("LogsConfigValidDefaultUrlPath", func(t *testing.T) {
15+
assert.Equal(t, "/v1/logs", logsCfg.HTTPPath)
1616
})
1717

1818
t.Run("MetricsConfigValidDefaultUrlPath", func(t *testing.T) {

cmd/telemetrygen/internal/logs/config.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ type Config struct {
1919
// Flags registers config flags.
2020
func (c *Config) Flags(fs *pflag.FlagSet) {
2121
c.CommonFlags(fs)
22+
23+
fs.StringVar(&c.HTTPPath, "otlp-http-url-path", "/v1/logs", "Which URL path to write to")
24+
2225
fs.IntVar(&c.NumLogs, "logs", 1, "Number of logs to generate in each worker (ignored if duration is provided)")
2326
fs.StringVar(&c.Body, "body", "the message", "Body of the log")
2427
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package logs
5+
6+
import (
7+
"bytes"
8+
"context"
9+
"fmt"
10+
"io"
11+
"net/http"
12+
13+
"go.opentelemetry.io/collector/pdata/plog"
14+
"go.opentelemetry.io/collector/pdata/plog/plogotlp"
15+
"google.golang.org/grpc"
16+
"google.golang.org/grpc/credentials/insecure"
17+
)
18+
19+
type exporter interface {
20+
export(plog.Logs) error
21+
}
22+
23+
func newExporter(ctx context.Context, cfg *Config) (exporter, error) {
24+
if cfg.UseHTTP {
25+
return &httpClientExporter{
26+
client: http.DefaultClient,
27+
cfg: cfg,
28+
}, nil
29+
}
30+
31+
if !cfg.Insecure {
32+
return nil, fmt.Errorf("'telemetrygen logs' only supports insecure gRPC")
33+
}
34+
// only support grpc in insecure mode
35+
clientConn, err := grpc.DialContext(ctx, cfg.Endpoint(), grpc.WithTransportCredentials(insecure.NewCredentials()))
36+
if err != nil {
37+
return nil, err
38+
}
39+
return &gRPCClientExporter{client: plogotlp.NewGRPCClient(clientConn)}, nil
40+
}
41+
42+
type gRPCClientExporter struct {
43+
client plogotlp.GRPCClient
44+
}
45+
46+
func (e *gRPCClientExporter) export(logs plog.Logs) error {
47+
req := plogotlp.NewExportRequestFromLogs(logs)
48+
if _, err := e.client.Export(context.Background(), req); err != nil {
49+
return err
50+
}
51+
return nil
52+
}
53+
54+
type httpClientExporter struct {
55+
client *http.Client
56+
cfg *Config
57+
}
58+
59+
func (e *httpClientExporter) export(logs plog.Logs) error {
60+
scheme := "https"
61+
if e.cfg.Insecure {
62+
scheme = "http"
63+
}
64+
path := e.cfg.HTTPPath
65+
url := fmt.Sprintf("%s://%s%s", scheme, e.cfg.Endpoint(), path)
66+
67+
req := plogotlp.NewExportRequestFromLogs(logs)
68+
body, err := req.MarshalProto()
69+
if err != nil {
70+
return fmt.Errorf("failed to marshal logs to protobuf: %w", err)
71+
}
72+
73+
httpReq, err := http.NewRequestWithContext(context.Background(), "POST", url, bytes.NewReader(body))
74+
if err != nil {
75+
return fmt.Errorf("failed to create logs HTTP request: %w", err)
76+
}
77+
for k, v := range e.cfg.Headers {
78+
httpReq.Header.Set(k, v)
79+
}
80+
httpReq.Header.Set("Content-Type", "application/x-protobuf")
81+
resp, err := e.client.Do(httpReq)
82+
if err != nil {
83+
return fmt.Errorf("failed to execute logs HTTP request: %w", err)
84+
}
85+
defer resp.Body.Close()
86+
if resp.StatusCode != http.StatusOK {
87+
var respData bytes.Buffer
88+
_, _ = io.Copy(&respData, resp.Body)
89+
return fmt.Errorf("log request failed with status %s (%s)", resp.Status, respData.String())
90+
}
91+
92+
return nil
93+
}

cmd/telemetrygen/internal/logs/logs.go

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,59 +10,27 @@ import (
1010
"sync/atomic"
1111
"time"
1212

13-
"go.opentelemetry.io/collector/pdata/plog"
14-
"go.opentelemetry.io/collector/pdata/plog/plogotlp"
1513
"go.opentelemetry.io/otel/sdk/resource"
1614
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
1715
"go.uber.org/zap"
1816
"golang.org/x/time/rate"
19-
"google.golang.org/grpc"
20-
"google.golang.org/grpc/credentials/insecure"
2117

2218
"github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen/internal/common"
2319
)
2420

25-
type exporter interface {
26-
export(plog.Logs) error
27-
}
28-
29-
type gRPCClientExporter struct {
30-
client plogotlp.GRPCClient
31-
}
32-
33-
func (e *gRPCClientExporter) export(logs plog.Logs) error {
34-
req := plogotlp.NewExportRequestFromLogs(logs)
35-
if _, err := e.client.Export(context.Background(), req); err != nil {
36-
return err
37-
}
38-
return nil
39-
}
40-
4121
// Start starts the log telemetry generator
4222
func Start(cfg *Config) error {
4323
logger, err := common.CreateLogger(cfg.SkipSettingGRPCLogger)
4424
if err != nil {
4525
return err
4626
}
4727

48-
if cfg.UseHTTP {
49-
return fmt.Errorf("http is not supported by 'telemetrygen logs'")
50-
}
51-
52-
if !cfg.Insecure {
53-
return fmt.Errorf("'telemetrygen logs' only supports insecure gRPC")
54-
}
55-
56-
// only support grpc in insecure mode
57-
clientConn, err := grpc.DialContext(context.TODO(), cfg.Endpoint(), grpc.WithTransportCredentials(insecure.NewCredentials()))
28+
e, err := newExporter(context.Background(), cfg)
5829
if err != nil {
5930
return err
6031
}
61-
exporter := &gRPCClientExporter{
62-
client: plogotlp.NewGRPCClient(clientConn),
63-
}
6432

65-
if err = Run(cfg, exporter, logger); err != nil {
33+
if err = Run(cfg, e, logger); err != nil {
6634
logger.Error("failed to stop the exporter", zap.Error(err))
6735
return err
6836
}

0 commit comments

Comments
 (0)