Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TT-8183 Resources #20

Merged
merged 4 commits into from
Jul 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ tasks:
e2e-test:
desc: Run e2e tests scenarios with tracetest
cmds:
- tracetest test run -d ./e2e/basic/tests/example.yml -w -o pretty
- tracetest run test -f ./e2e/basic/tests/example.yml --required-gates test-specs -o pretty

e2e-stop:
desc: Stop e2e enviroment.
Expand All @@ -37,4 +37,4 @@ tasks:
cmds:
- task: e2e-setup
- task: e2e-test
- task: e2e-stop
- task: e2e-stop
11 changes: 10 additions & 1 deletion e2e/basic/basic.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,16 @@ func main() {

log.Println("Initializing OpenTelemetry at e2e-basic:", cfg.Endpoint)

provider, err := trace.NewProvider(trace.WithContext(ctx), trace.WithConfig(&cfg), trace.WithLogger(logrus.New()))
provider, err := trace.NewProvider(
trace.WithContext(ctx),
trace.WithConfig(&cfg),
trace.WithLogger(logrus.New()),
trace.WithServiceID("service-id-1"),
trace.WithServiceVersion("v1"),
trace.WithHostDetector(),
trace.WithContainerDetector(),
trace.WithProcessDetector(),
)
if err != nil {
log.Printf("error on otel provider init %s", err.Error())
return
Expand Down
93 changes: 93 additions & 0 deletions trace/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,96 @@ func WithContext(ctx context.Context) Option {
},
}
}

/*
WithServiceID sets the resource service.id for the tracer provider
This is useful to identify service instance on the trace resource.

Example

provider, err := trace.NewProvider(trace.WithServiceID("instance-id"))
if err != nil {
panic(err)
}
*/
func WithServiceID(id string) Option {
return &opts{
fn: func(tp *traceProvider) {
tp.resources.id = id
},
}
}

/*
WithServiceVersion sets the resource service.version for the tracer provider
This is useful to identify service version on the trace resource.

Example

provider, err := trace.NewProvider(trace.WithServiceVersion("v4.0.5"))
if err != nil {
panic(err)
}
*/
func WithServiceVersion(version string) Option {
return &opts{
fn: func(tp *traceProvider) {
tp.resources.version = version
},
}
}

/*
WithHostDetector adds attributes from the host to the configured resource.

Example

provider, err := trace.NewProvider(trace.WithHostDetector())
if err != nil {
panic(err)
}
*/
func WithHostDetector() Option {
return &opts{
fn: func(tp *traceProvider) {
tp.resources.withHost = true
},
}
}

/*
WithContainerDetector adds attributes from the container to the configured resource.

Example

provider, err := trace.NewProvider(trace.WithContainerDetector())
if err != nil {
panic(err)
}
*/
func WithContainerDetector() Option {
return &opts{
fn: func(tp *traceProvider) {
tp.resources.withContainer = true
},
}
}

/*
WithProcessDetector adds attributes from the process to the configured resource.

Example

provider, err := trace.NewProvider(trace.WithProcessDetector())
if err != nil {
panic(err)
}
*/

func WithProcessDetector() Option {
return &opts{
fn: func(tp *traceProvider) {
tp.resources.withProcess = true
},
}
}
35 changes: 35 additions & 0 deletions trace/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,38 @@ func Test_WithConfig(t *testing.T) {
assert.NotNil(t, tp.cfg)
assert.IsType(t, cfg, *tp.cfg)
}

func Test_WithServiceID(t *testing.T) {
tp := &traceProvider{}
WithServiceID("id1").apply(tp)

assert.Equal(t, "id1", tp.resources.id)
}

func Test_WithServiceVersion(t *testing.T) {
tp := &traceProvider{}
WithServiceVersion("v1").apply(tp)

assert.Equal(t, "v1", tp.resources.version)
}

func Test_WithHostDetector(t *testing.T) {
tp := &traceProvider{}
WithHostDetector().apply(tp)

assert.Equal(t, true, tp.resources.withHost)
}

func Test_WithContainerDetector(t *testing.T) {
tp := &traceProvider{}
WithContainerDetector().apply(tp)

assert.Equal(t, true, tp.resources.withContainer)
}

func Test_WithProcessDetector(t *testing.T) {
tp := &traceProvider{}
WithProcessDetector().apply(tp)

assert.Equal(t, true, tp.resources.withProcess)
}
4 changes: 3 additions & 1 deletion trace/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ type traceProvider struct {

ctx context.Context
providerType string

resources resourceConfig
}

/*
Expand Down Expand Up @@ -82,7 +84,7 @@ func NewProvider(opts ...Option) (Provider, error) {
}

// create the resource
resource, err := resourceFactory(provider.ctx, provider.cfg.ResourceName)
resource, err := resourceFactory(provider.ctx, provider.cfg.ResourceName, provider.resources)
if err != nil {
provider.logger.Error("failed to create exporter", err)
return provider, fmt.Errorf("failed to create resource: %w", err)
Expand Down
47 changes: 40 additions & 7 deletions trace/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,48 @@ package trace
import (
"context"

"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/sdk/resource"
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
)

func resourceFactory(ctx context.Context, resourceName string) (*resource.Resource, error) {
return resource.New(ctx,
resource.WithAttributes(
// the service name used to display traces in backends
semconv.ServiceNameKey.String(resourceName),
),
)
type resourceConfig struct {
id string
version string

withHost bool
withContainer bool
withProcess bool
}

func resourceFactory(ctx context.Context, resourceName string, cfg resourceConfig) (*resource.Resource, error) {
opts := []resource.Option{}

attrs := []attribute.KeyValue{
semconv.ServiceNameKey.String(resourceName),
}

if cfg.id != "" {
attrs = append(attrs, semconv.ServiceInstanceID(cfg.id))
}

if cfg.version != "" {
attrs = append(attrs, semconv.ServiceVersion(cfg.version))
}

opts = append(opts, resource.WithAttributes(attrs...))

if cfg.withContainer {
opts = append(opts, resource.WithContainer())
}

if cfg.withHost {
opts = append(opts, resource.WithHost())
}

if cfg.withProcess {
opts = append(opts, resource.WithProcess())
}

return resource.New(ctx, opts...)
}
94 changes: 76 additions & 18 deletions trace/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,93 @@ package trace

import (
"context"
"os"
"testing"

"github.com/stretchr/testify/assert"
"go.opentelemetry.io/otel/attribute"
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
)

func Test_ResourceFactory(t *testing.T) {
ctx := context.Background()
resourceName := "test-service"

res, err := resourceFactory(ctx, resourceName)

func TestResourceFactory(t *testing.T) {
currentHost, err := os.Hostname()
assert.Nil(t, err)

attrs := res.Attributes()

assert.Equal(t, res.Len(), 1)
testCases := []struct {
name string
resourceName string
cfg resourceConfig
expectedAttrs []attribute.KeyValue
expectedErr error
}{
{
name: "Test with default config",
resourceName: "testResource",
cfg: resourceConfig{},
expectedAttrs: []attribute.KeyValue{
semconv.ServiceNameKey.String("testResource"),
},
expectedErr: nil,
},
{
name: "Test with id and version",
resourceName: "testResource",
cfg: resourceConfig{
id: "123",
version: "1.0.0",
},
expectedAttrs: []attribute.KeyValue{
semconv.ServiceNameKey.String("testResource"),
semconv.ServiceInstanceID("123"),
semconv.ServiceVersion("1.0.0"),
},
expectedErr: nil,
},
{
name: "Test with host",
resourceName: "testResource",
cfg: resourceConfig{
withHost: true,
},
expectedAttrs: []attribute.KeyValue{
semconv.ServiceNameKey.String("testResource"),
semconv.HostName(currentHost),
},
expectedErr: nil,
},
{
// special scenario to unit test - we cannot see the container attrs here
name: "Test with container",
resourceName: "testResource",
cfg: resourceConfig{
withContainer: true,
},
expectedAttrs: []attribute.KeyValue{
semconv.ServiceNameKey.String("testResource"),
},
expectedErr: nil,
},
}

found := false
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
ctx := context.Background()
res, err := resourceFactory(ctx, tc.resourceName, tc.cfg)

for _, attr := range attrs {
if attr.Key == semconv.ServiceNameKey {
found = true
if tc.expectedErr != nil {
assert.Error(t, err)
assert.Equal(t, tc.expectedErr, err)
} else {
assert.NoError(t, err)
}

assert.Equal(t, resourceName, attr.Value.AsString())
if res != nil {
attrs := res.Attributes()

break
}
for _, expectedAttr := range tc.expectedAttrs {
assert.Contains(t, attrs, expectedAttr)
}
}
})
}

assert.True(t, found)
}
Loading