Skip to content

Commit

Permalink
Add otlploghttp transform package (#5191)
Browse files Browse the repository at this point in the history
* Add otlploghttp transform pkg

* Rename TestTransformations to TestResourceLogs

* Add TestAttrTransforms

* Add TestLogAttrs

* Add TestSeverityNumber

* Update go mod

* Fix comments

* Apply feedback
  • Loading branch information
MrAlias authored Apr 12, 2024
1 parent 7da00d9 commit c971888
Show file tree
Hide file tree
Showing 8 changed files with 891 additions and 30 deletions.
4 changes: 2 additions & 2 deletions exporters/otlp/otlplog/otlploghttp/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import (
)

type client struct {
uploadLogs func(context.Context, *logpb.ResourceLogs) error
uploadLogs func(context.Context, []*logpb.ResourceLogs) error
}

func (c *client) UploadLogs(ctx context.Context, rl *logpb.ResourceLogs) error {
func (c *client) UploadLogs(ctx context.Context, rl []*logpb.ResourceLogs) error {
if c.uploadLogs != nil {
return c.uploadLogs(ctx, rl)
}
Expand Down
10 changes: 4 additions & 6 deletions exporters/otlp/otlplog/otlploghttp/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ package otlploghttp // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/o

import (
"context"
"errors"
"sync/atomic"

"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/internal/transform"
Expand Down Expand Up @@ -47,12 +46,11 @@ func (e *Exporter) Export(ctx context.Context, records []log.Record) error {
if e.stopped.Load() {
return nil
}
otlp, err := transformResourceLogs(records)
if otlp != nil {
// Best effort upload of transformable logs.
err = errors.Join(err, e.client.Load().UploadLogs(ctx, otlp))
otlp := transformResourceLogs(records)
if otlp == nil {
return nil
}
return err
return e.client.Load().UploadLogs(ctx, otlp)
}

// Shutdown shuts down the Exporter. Calls to Export or ForceFlush will perform
Expand Down
21 changes: 5 additions & 16 deletions exporters/otlp/otlplog/otlploghttp/exporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,45 +19,34 @@ import (
)

func TestExporterExportErrors(t *testing.T) {
var (
errUpload = errors.New("upload")
errTForm = errors.New("transform")
)

errUpload := errors.New("upload")
c := &client{
uploadLogs: func(context.Context, *logpb.ResourceLogs) error {
uploadLogs: func(context.Context, []*logpb.ResourceLogs) error {
return errUpload
},
}

orig := transformResourceLogs
transformResourceLogs = func(r []log.Record) (*logpb.ResourceLogs, error) {
return new(logpb.ResourceLogs), errTForm
}
t.Cleanup(func() { transformResourceLogs = orig })

e, err := newExporter(c, config{})
require.NoError(t, err, "New")

err = e.Export(context.Background(), make([]log.Record, 1))
assert.ErrorIs(t, err, errUpload)
assert.ErrorIs(t, err, errTForm)
}

func TestExporterExport(t *testing.T) {
var uploads int
c := &client{
uploadLogs: func(context.Context, *logpb.ResourceLogs) error {
uploadLogs: func(context.Context, []*logpb.ResourceLogs) error {
uploads++
return nil
},
}

orig := transformResourceLogs
var got []log.Record
transformResourceLogs = func(r []log.Record) (*logpb.ResourceLogs, error) {
transformResourceLogs = func(r []log.Record) []*logpb.ResourceLogs {
got = r
return new(logpb.ResourceLogs), nil
return make([]*logpb.ResourceLogs, 1)
}
t.Cleanup(func() { transformResourceLogs = orig })

Expand Down
6 changes: 3 additions & 3 deletions exporters/otlp/otlplog/otlploghttp/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ require (
github.com/cenkalti/backoff/v4 v4.3.0
github.com/stretchr/testify v1.9.0
go.opentelemetry.io/otel v1.25.0
go.opentelemetry.io/otel/log v0.0.1-alpha
go.opentelemetry.io/otel/sdk v1.24.0
go.opentelemetry.io/otel/sdk/log v0.0.0-20240403115316-6c6e1e7416e9
go.opentelemetry.io/otel/trace v1.25.0
go.opentelemetry.io/proto/otlp v1.1.0
)

Expand All @@ -15,10 +18,7 @@ require (
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
go.opentelemetry.io/otel/log v0.0.1-alpha // indirect
go.opentelemetry.io/otel/metric v1.25.0 // indirect
go.opentelemetry.io/otel/sdk v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.25.0 // indirect
golang.org/x/sys v0.19.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
183 changes: 183 additions & 0 deletions exporters/otlp/otlplog/otlploghttp/internal/transform/attr_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package transform

import (
"testing"

"github.com/stretchr/testify/assert"

"go.opentelemetry.io/otel/attribute"
cpb "go.opentelemetry.io/proto/otlp/common/v1"
)

var (
attrBool = attribute.Bool("bool", true)
attrBoolSlice = attribute.BoolSlice("bool slice", []bool{true, false})
attrInt = attribute.Int("int", 1)
attrIntSlice = attribute.IntSlice("int slice", []int{-1, 1})
attrInt64 = attribute.Int64("int64", 1)
attrInt64Slice = attribute.Int64Slice("int64 slice", []int64{-1, 1})
attrFloat64 = attribute.Float64("float64", 1)
attrFloat64Slice = attribute.Float64Slice("float64 slice", []float64{-1, 1})
attrString = attribute.String("string", "o")
attrStringSlice = attribute.StringSlice("string slice", []string{"o", "n"})
attrInvalid = attribute.KeyValue{
Key: attribute.Key("invalid"),
Value: attribute.Value{},
}

valBoolTrue = &cpb.AnyValue{Value: &cpb.AnyValue_BoolValue{BoolValue: true}}
valBoolFalse = &cpb.AnyValue{Value: &cpb.AnyValue_BoolValue{BoolValue: false}}
valBoolSlice = &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
Values: []*cpb.AnyValue{valBoolTrue, valBoolFalse},
},
}}
valIntOne = &cpb.AnyValue{Value: &cpb.AnyValue_IntValue{IntValue: 1}}
valIntNOne = &cpb.AnyValue{Value: &cpb.AnyValue_IntValue{IntValue: -1}}
valIntSlice = &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
Values: []*cpb.AnyValue{valIntNOne, valIntOne},
},
}}
valDblOne = &cpb.AnyValue{Value: &cpb.AnyValue_DoubleValue{DoubleValue: 1}}
valDblNOne = &cpb.AnyValue{Value: &cpb.AnyValue_DoubleValue{DoubleValue: -1}}
valDblSlice = &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
Values: []*cpb.AnyValue{valDblNOne, valDblOne},
},
}}
valStrO = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{StringValue: "o"}}
valStrN = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{StringValue: "n"}}
valStrSlice = &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
Values: []*cpb.AnyValue{valStrO, valStrN},
},
}}

kvBool = &cpb.KeyValue{Key: "bool", Value: valBoolTrue}
kvBoolSlice = &cpb.KeyValue{Key: "bool slice", Value: valBoolSlice}
kvInt = &cpb.KeyValue{Key: "int", Value: valIntOne}
kvIntSlice = &cpb.KeyValue{Key: "int slice", Value: valIntSlice}
kvInt64 = &cpb.KeyValue{Key: "int64", Value: valIntOne}
kvInt64Slice = &cpb.KeyValue{Key: "int64 slice", Value: valIntSlice}
kvFloat64 = &cpb.KeyValue{Key: "float64", Value: valDblOne}
kvFloat64Slice = &cpb.KeyValue{Key: "float64 slice", Value: valDblSlice}
kvString = &cpb.KeyValue{Key: "string", Value: valStrO}
kvStringSlice = &cpb.KeyValue{Key: "string slice", Value: valStrSlice}
kvInvalid = &cpb.KeyValue{
Key: "invalid",
Value: &cpb.AnyValue{
Value: &cpb.AnyValue_StringValue{StringValue: "INVALID"},
},
}
)

func TestAttrTransforms(t *testing.T) {
type attrTest struct {
name string
in []attribute.KeyValue
want []*cpb.KeyValue
}

for _, test := range []attrTest{
{"nil", nil, nil},
{"empty", []attribute.KeyValue{}, nil},
{
"invalid",
[]attribute.KeyValue{attrInvalid},
[]*cpb.KeyValue{kvInvalid},
},
{
"bool",
[]attribute.KeyValue{attrBool},
[]*cpb.KeyValue{kvBool},
},
{
"bool slice",
[]attribute.KeyValue{attrBoolSlice},
[]*cpb.KeyValue{kvBoolSlice},
},
{
"int",
[]attribute.KeyValue{attrInt},
[]*cpb.KeyValue{kvInt},
},
{
"int slice",
[]attribute.KeyValue{attrIntSlice},
[]*cpb.KeyValue{kvIntSlice},
},
{
"int64",
[]attribute.KeyValue{attrInt64},
[]*cpb.KeyValue{kvInt64},
},
{
"int64 slice",
[]attribute.KeyValue{attrInt64Slice},
[]*cpb.KeyValue{kvInt64Slice},
},
{
"float64",
[]attribute.KeyValue{attrFloat64},
[]*cpb.KeyValue{kvFloat64},
},
{
"float64 slice",
[]attribute.KeyValue{attrFloat64Slice},
[]*cpb.KeyValue{kvFloat64Slice},
},
{
"string",
[]attribute.KeyValue{attrString},
[]*cpb.KeyValue{kvString},
},
{
"string slice",
[]attribute.KeyValue{attrStringSlice},
[]*cpb.KeyValue{kvStringSlice},
},
{
"all",
[]attribute.KeyValue{
attrBool,
attrBoolSlice,
attrInt,
attrIntSlice,
attrInt64,
attrInt64Slice,
attrFloat64,
attrFloat64Slice,
attrString,
attrStringSlice,
attrInvalid,
},
[]*cpb.KeyValue{
kvBool,
kvBoolSlice,
kvInt,
kvIntSlice,
kvInt64,
kvInt64Slice,
kvFloat64,
kvFloat64Slice,
kvString,
kvStringSlice,
kvInvalid,
},
},
} {
t.Run(test.name, func(t *testing.T) {
t.Run("Attrs", func(t *testing.T) {
assert.ElementsMatch(t, test.want, Attrs(test.in))
})
t.Run("AttrIter", func(t *testing.T) {
s := attribute.NewSet(test.in...)
assert.ElementsMatch(t, test.want, AttrIter(s.Iter()))
})
})
}
}
Loading

0 comments on commit c971888

Please sign in to comment.