From 00c022df33de54adc4feabd49eab045be7438576 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraci=20Paix=C3=A3o=20Kr=C3=B6hling?= Date: Mon, 27 May 2024 12:18:58 +0200 Subject: [PATCH 1/4] [cmd/telemetrygen] Add TraceID and SpanID support for logs in tracegen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Juraci Paixão Kröhling --- cmd/telemetrygen/internal/logs/config.go | 4 ++++ cmd/telemetrygen/internal/logs/logs.go | 2 ++ cmd/telemetrygen/internal/logs/worker.go | 22 +++++++++++++++++ cmd/telemetrygen/internal/logs/worker_test.go | 24 +++++++++++++++++++ 4 files changed, 52 insertions(+) diff --git a/cmd/telemetrygen/internal/logs/config.go b/cmd/telemetrygen/internal/logs/config.go index 24d334b1acaa..3f1478031036 100644 --- a/cmd/telemetrygen/internal/logs/config.go +++ b/cmd/telemetrygen/internal/logs/config.go @@ -16,6 +16,8 @@ type Config struct { Body string SeverityText string SeverityNumber int32 + TraceID string + SpanID string } // Flags registers config flags. @@ -28,4 +30,6 @@ func (c *Config) Flags(fs *pflag.FlagSet) { fs.StringVar(&c.Body, "body", "the message", "Body of the log") fs.StringVar(&c.SeverityText, "severity-text", "Info", "Severity text of the log") fs.Int32Var(&c.SeverityNumber, "severity-number", 9, "Severity number of the log, range from 1 to 24 (inclusive)") + fs.StringVar(&c.TraceID, "trace-id", "", "TraceID of the log") + fs.StringVar(&c.SpanID, "span-id", "", "SpanID of the log") } diff --git a/cmd/telemetrygen/internal/logs/logs.go b/cmd/telemetrygen/internal/logs/logs.go index b4349021425f..040fdef59fa5 100644 --- a/cmd/telemetrygen/internal/logs/logs.go +++ b/cmd/telemetrygen/internal/logs/logs.go @@ -78,6 +78,8 @@ func Run(c *Config, exp exporter, logger *zap.Logger) error { wg: &wg, logger: logger.With(zap.Int("worker", i)), index: i, + traceID: c.TraceID, + spanID: c.SpanID, } go w.simulateLogs(res, exp, c.GetTelemetryAttributes()) diff --git a/cmd/telemetrygen/internal/logs/worker.go b/cmd/telemetrygen/internal/logs/worker.go index aa5cc0e346c2..245002971e96 100644 --- a/cmd/telemetrygen/internal/logs/worker.go +++ b/cmd/telemetrygen/internal/logs/worker.go @@ -5,6 +5,7 @@ package logs import ( "context" + "encoding/hex" "sync" "sync/atomic" "time" @@ -28,6 +29,8 @@ type worker struct { wg *sync.WaitGroup // notify when done logger *zap.Logger // logger index int // worker index + traceID string // traceID string + spanID string // spanID string } func (w worker) simulateLogs(res *resource.Resource, exporter exporter, telemetryAttributes []attribute.KeyValue) { @@ -41,6 +44,7 @@ func (w worker) simulateLogs(res *resource.Resource, exporter exporter, telemetr for _, attr := range attrs { nRes.Attributes().PutStr(string(attr.Key), attr.Value.AsString()) } + log := logs.ResourceLogs().At(0).ScopeLogs().AppendEmpty().LogRecords().AppendEmpty() log.Body().SetStr(w.body) log.SetTimestamp(pcommon.NewTimestampFromTime(time.Now())) @@ -51,6 +55,24 @@ func (w worker) simulateLogs(res *resource.Resource, exporter exporter, telemetr lattrs := log.Attributes() lattrs.PutStr("app", "server") + if w.traceID != "" { + b, err := hex.DecodeString(w.traceID) + if err != nil { + w.logger.Fatal("failed to create traceID byte array from the given traceID, make sure the traceID is a hex representation of a [16]byte, like: 'ae87dadd90e9935a4bc9660628efd569'", zap.Error(err)) + } + tid := pcommon.TraceID(b[:]) + log.SetTraceID(tid) + } + + if w.spanID != "" { + b, err := hex.DecodeString(w.spanID) + if err != nil { + w.logger.Fatal("failed to create spanID byte array from the given spanID, make sure the spanID is a hex representation of a [8]byte, like: '5828fa4960140870'", zap.Error(err)) + } + sid := pcommon.SpanID(b[:]) + log.SetSpanID(sid) + } + for i, attr := range telemetryAttributes { lattrs.PutStr(string(attr.Key), telemetryAttributes[i].Value.AsString()) } diff --git a/cmd/telemetrygen/internal/logs/worker_test.go b/cmd/telemetrygen/internal/logs/worker_test.go index 99e41ef18c0c..68f9eb9d5198 100644 --- a/cmd/telemetrygen/internal/logs/worker_test.go +++ b/cmd/telemetrygen/internal/logs/worker_test.go @@ -187,6 +187,30 @@ func TestLogsWithMultipleTelemetryAttributes(t *testing.T) { } } +func TestLogsWithTraceIDAndSpanID(t *testing.T) { + qty := 1 + cfg := configWithOneAttribute(qty, "custom body") + cfg.TraceID = "ae87dadd90e9935a4bc9660628efd569" + cfg.SpanID = "5828fa4960140870" + + exp := &mockExporter{} + + // test + logger, _ := zap.NewDevelopment() + require.NoError(t, Run(cfg, exp, logger)) + + // verify + require.Len(t, exp.logs, qty) + for _, log := range exp.logs { + rlogs := log.ResourceLogs() + for i := 0; i < rlogs.Len(); i++ { + log := rlogs.At(i).ScopeLogs().At(0).LogRecords().At(0) + assert.Equal(t, "ae87dadd90e9935a4bc9660628efd569", log.TraceID().String()) + assert.Equal(t, "5828fa4960140870", log.SpanID().String()) + } + } +} + func configWithNoAttributes(qty int, body string) *Config { return &Config{ Body: body, From d9e7c625bc8149e0fd2acb88747501abf5e54c6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraci=20Paix=C3=A3o=20Kr=C3=B6hling?= Date: Mon, 27 May 2024 17:14:48 +0200 Subject: [PATCH 2/4] move validation to more testable location MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Juraci Paixão Kröhling --- cmd/telemetrygen/internal/logs/logs.go | 39 +++++++++++++ cmd/telemetrygen/internal/logs/logs_test.go | 63 +++++++++++++++++++++ cmd/telemetrygen/internal/logs/worker.go | 16 +++--- 3 files changed, 110 insertions(+), 8 deletions(-) create mode 100644 cmd/telemetrygen/internal/logs/logs_test.go diff --git a/cmd/telemetrygen/internal/logs/logs.go b/cmd/telemetrygen/internal/logs/logs.go index 040fdef59fa5..43e4063f3107 100644 --- a/cmd/telemetrygen/internal/logs/logs.go +++ b/cmd/telemetrygen/internal/logs/logs.go @@ -4,6 +4,7 @@ package logs import ( + "encoding/hex" "fmt" "sync" "sync/atomic" @@ -18,6 +19,13 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen/internal/common" ) +var ( + errInvalidTraceIDLenght = fmt.Errorf("TraceID must be a 32 character hex string, like: 'ae87dadd90e9935a4bc9660628efd569'") + errInvalidSpanIDLenght = fmt.Errorf("SpanID must be a 16 character hex string, like: '5828fa4960140870'") + errInvalidTraceID = fmt.Errorf("failed to create traceID byte array from the given traceID, make sure the traceID is a hex representation of a [16]byte, like: 'ae87dadd90e9935a4bc9660628efd569'") + errInvalidSpanID = fmt.Errorf("failed to create SpanID byte array from the given SpanID, make sure the SpanID is a hex representation of a [8]byte, like: '5828fa4960140870'") +) + // Start starts the log telemetry generator func Start(cfg *Config) error { logger, err := common.CreateLogger(cfg.SkipSettingGRPCLogger) @@ -30,6 +38,11 @@ func Start(cfg *Config) error { return err } + if err = Validate(cfg); err != nil { + logger.Error("failed to validate the parameters for the test scenario.", zap.Error(err)) + return err + } + if err = Run(cfg, e, logger); err != nil { logger.Error("failed to execute the test scenario.", zap.Error(err)) return err @@ -92,6 +105,32 @@ func Run(c *Config, exp exporter, logger *zap.Logger) error { return nil } +// Validate validates the test scenario parameters. +func Validate(c *Config) error { + if c.TraceID != "" { + if len(c.TraceID) != 32 { + return errInvalidTraceIDLenght + } + + _, err := hex.DecodeString(c.TraceID) + if err != nil { + return errInvalidTraceID + } + } + + if c.SpanID != "" { + if len(c.SpanID) != 16 { + return errInvalidSpanIDLenght + } + _, err := hex.DecodeString(c.SpanID) + if err != nil { + return errInvalidSpanID + } + } + + return nil +} + func parseSeverity(severityText string, severityNumber int32) (string, plog.SeverityNumber, error) { // severityNumber must range in [1,24] if severityNumber <= 0 || severityNumber >= 25 { diff --git a/cmd/telemetrygen/internal/logs/logs_test.go b/cmd/telemetrygen/internal/logs/logs_test.go new file mode 100644 index 000000000000..d128e4786f6b --- /dev/null +++ b/cmd/telemetrygen/internal/logs/logs_test.go @@ -0,0 +1,63 @@ +package logs + +import ( + "testing" +) + +func TestValidate(t *testing.T) { + tests := []struct { + name string + config *Config + expected error + }{ + { + name: "ValidConfig", + config: &Config{ + TraceID: "ae87dadd90e9935a4bc9660628efd569", + SpanID: "5828fa4960140870", + }, + expected: nil, + }, + { + name: "InvalidInvalidTraceIDLenght", + config: &Config{ + TraceID: "invalid-length", + SpanID: "5828fa4960140870", + }, + expected: errInvalidTraceIDLenght, + }, + { + name: "InvalidInvalidSpanIDLenght", + config: &Config{ + TraceID: "ae87dadd90e9935a4bc9660628efd569", + SpanID: "invalid-length", + }, + expected: errInvalidSpanIDLenght, + }, + { + name: "InvalidTraceID", + config: &Config{ + TraceID: "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz", + SpanID: "5828fa4960140870", + }, + expected: errInvalidTraceID, + }, + { + name: "InvalidSpanID", + config: &Config{ + TraceID: "ae87dadd90e9935a4bc9660628efd569", + SpanID: "zzzzzzzzzzzzzzzz", + }, + expected: errInvalidSpanID, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := Validate(tt.config) + if err != tt.expected { + t.Errorf("unexpected error, got: %v, want: %v", err, tt.expected) + } + }) + } +} diff --git a/cmd/telemetrygen/internal/logs/worker.go b/cmd/telemetrygen/internal/logs/worker.go index 245002971e96..893277b21d32 100644 --- a/cmd/telemetrygen/internal/logs/worker.go +++ b/cmd/telemetrygen/internal/logs/worker.go @@ -56,19 +56,19 @@ func (w worker) simulateLogs(res *resource.Resource, exporter exporter, telemetr lattrs.PutStr("app", "server") if w.traceID != "" { - b, err := hex.DecodeString(w.traceID) - if err != nil { - w.logger.Fatal("failed to create traceID byte array from the given traceID, make sure the traceID is a hex representation of a [16]byte, like: 'ae87dadd90e9935a4bc9660628efd569'", zap.Error(err)) - } + // we checked this for errors in the Validate function + // nolint: errcheck + b, _ := hex.DecodeString(w.traceID) + tid := pcommon.TraceID(b[:]) log.SetTraceID(tid) } if w.spanID != "" { - b, err := hex.DecodeString(w.spanID) - if err != nil { - w.logger.Fatal("failed to create spanID byte array from the given spanID, make sure the spanID is a hex representation of a [8]byte, like: '5828fa4960140870'", zap.Error(err)) - } + // we checked this for errors in the Validate function + // nolint: errcheck + b, _ := hex.DecodeString(w.spanID) + sid := pcommon.SpanID(b[:]) log.SetSpanID(sid) } From a237bf7c46021d5eab393fe9924e2af925d34d55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraci=20Paix=C3=A3o=20Kr=C3=B6hling?= Date: Mon, 27 May 2024 17:31:20 +0200 Subject: [PATCH 3/4] changelog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Juraci Paixão Kröhling --- .../add-traceid-spanid-telemetrygen.yaml | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .chloggen/add-traceid-spanid-telemetrygen.yaml diff --git a/.chloggen/add-traceid-spanid-telemetrygen.yaml b/.chloggen/add-traceid-spanid-telemetrygen.yaml new file mode 100644 index 000000000000..b6ede10e26b3 --- /dev/null +++ b/.chloggen/add-traceid-spanid-telemetrygen.yaml @@ -0,0 +1,27 @@ +# 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. filelogreceiver) +component: cmd/telemetrygen + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Add support for specifying trace ID and span ID in telemetrygen for logs + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [33234] + +# (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: + +# 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: [] From 736e02b40d146433bc4575450d5836592fe87e7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraci=20Paix=C3=A3o=20Kr=C3=B6hling?= Date: Tue, 28 May 2024 08:50:33 +0200 Subject: [PATCH 4/4] move validate to config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Juraci Paixão Kröhling --- cmd/telemetrygen/internal/logs/config.go | 28 ++++++++++++++++++ .../logs/{logs_test.go => config_test.go} | 11 ++++--- cmd/telemetrygen/internal/logs/logs.go | 29 +------------------ cmd/telemetrygen/internal/logs/worker.go | 4 +-- 4 files changed, 38 insertions(+), 34 deletions(-) rename cmd/telemetrygen/internal/logs/{logs_test.go => config_test.go} (85%) diff --git a/cmd/telemetrygen/internal/logs/config.go b/cmd/telemetrygen/internal/logs/config.go index 3f1478031036..323026d573ec 100644 --- a/cmd/telemetrygen/internal/logs/config.go +++ b/cmd/telemetrygen/internal/logs/config.go @@ -4,6 +4,8 @@ package logs import ( + "encoding/hex" + "github.com/spf13/pflag" "github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen/internal/common" @@ -33,3 +35,29 @@ func (c *Config) Flags(fs *pflag.FlagSet) { fs.StringVar(&c.TraceID, "trace-id", "", "TraceID of the log") fs.StringVar(&c.SpanID, "span-id", "", "SpanID of the log") } + +// Validate validates the test scenario parameters. +func (c *Config) Validate() error { + if c.TraceID != "" { + if len(c.TraceID) != 32 { + return errInvalidTraceIDLenght + } + + _, err := hex.DecodeString(c.TraceID) + if err != nil { + return errInvalidTraceID + } + } + + if c.SpanID != "" { + if len(c.SpanID) != 16 { + return errInvalidSpanIDLenght + } + _, err := hex.DecodeString(c.SpanID) + if err != nil { + return errInvalidSpanID + } + } + + return nil +} diff --git a/cmd/telemetrygen/internal/logs/logs_test.go b/cmd/telemetrygen/internal/logs/config_test.go similarity index 85% rename from cmd/telemetrygen/internal/logs/logs_test.go rename to cmd/telemetrygen/internal/logs/config_test.go index d128e4786f6b..dc8e2361a1eb 100644 --- a/cmd/telemetrygen/internal/logs/logs_test.go +++ b/cmd/telemetrygen/internal/logs/config_test.go @@ -1,7 +1,12 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + package logs import ( "testing" + + "github.com/stretchr/testify/assert" ) func TestValidate(t *testing.T) { @@ -54,10 +59,8 @@ func TestValidate(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - err := Validate(tt.config) - if err != tt.expected { - t.Errorf("unexpected error, got: %v, want: %v", err, tt.expected) - } + err := tt.config.Validate() + assert.ErrorIs(t, err, tt.expected) }) } } diff --git a/cmd/telemetrygen/internal/logs/logs.go b/cmd/telemetrygen/internal/logs/logs.go index 43e4063f3107..c33f7c622529 100644 --- a/cmd/telemetrygen/internal/logs/logs.go +++ b/cmd/telemetrygen/internal/logs/logs.go @@ -4,7 +4,6 @@ package logs import ( - "encoding/hex" "fmt" "sync" "sync/atomic" @@ -38,7 +37,7 @@ func Start(cfg *Config) error { return err } - if err = Validate(cfg); err != nil { + if err = cfg.Validate(); err != nil { logger.Error("failed to validate the parameters for the test scenario.", zap.Error(err)) return err } @@ -105,32 +104,6 @@ func Run(c *Config, exp exporter, logger *zap.Logger) error { return nil } -// Validate validates the test scenario parameters. -func Validate(c *Config) error { - if c.TraceID != "" { - if len(c.TraceID) != 32 { - return errInvalidTraceIDLenght - } - - _, err := hex.DecodeString(c.TraceID) - if err != nil { - return errInvalidTraceID - } - } - - if c.SpanID != "" { - if len(c.SpanID) != 16 { - return errInvalidSpanIDLenght - } - _, err := hex.DecodeString(c.SpanID) - if err != nil { - return errInvalidSpanID - } - } - - return nil -} - func parseSeverity(severityText string, severityNumber int32) (string, plog.SeverityNumber, error) { // severityNumber must range in [1,24] if severityNumber <= 0 || severityNumber >= 25 { diff --git a/cmd/telemetrygen/internal/logs/worker.go b/cmd/telemetrygen/internal/logs/worker.go index 893277b21d32..118a4784f443 100644 --- a/cmd/telemetrygen/internal/logs/worker.go +++ b/cmd/telemetrygen/internal/logs/worker.go @@ -60,7 +60,7 @@ func (w worker) simulateLogs(res *resource.Resource, exporter exporter, telemetr // nolint: errcheck b, _ := hex.DecodeString(w.traceID) - tid := pcommon.TraceID(b[:]) + tid := pcommon.TraceID(b) log.SetTraceID(tid) } @@ -69,7 +69,7 @@ func (w worker) simulateLogs(res *resource.Resource, exporter exporter, telemetr // nolint: errcheck b, _ := hex.DecodeString(w.spanID) - sid := pcommon.SpanID(b[:]) + sid := pcommon.SpanID(b) log.SetSpanID(sid) }