Skip to content
Open
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Greatly reduce the cost of recording metrics in `go.opentelemetry.io/otel/sdk/metric` using hashing for map keys. (#7175)
- Add experimental observability for the prometheus exporter in `go.opentelemetry.io/otel/exporters/prometheus`.
Check the `go.opentelemetry.io/otel/exporters/prometheus/internal/x` package documentation for more information. (#7345)
- Add experimental observability log exporter metrics in `go.opentelemetry.io/otel/exporters/stdout/stdoutlog`.
Check the `go.opentelemetry.io/otel/exporters/stdout/stdoutlog/internal/x` package documentation for more information. (#7351)

### Fixed

Expand Down
3 changes: 3 additions & 0 deletions exporters/stdout/stdoutlog/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@
// format for OpenTelemetry that is supported with any stability or
// compatibility guarantees. If these are needed features, please use the OTLP
// exporter instead.
//
// See [go.opentelemetry.io/otel/exporters/stdout/stdoutlog/internal/x] for information about
// the experimental features.
package stdoutlog // import "go.opentelemetry.io/otel/exporters/stdout/stdoutlog"
49 changes: 42 additions & 7 deletions exporters/stdout/stdoutlog/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,30 @@ import (
"encoding/json"
"sync/atomic"

"go.opentelemetry.io/otel/exporters/stdout/stdoutlog/internal"
"go.opentelemetry.io/otel/exporters/stdout/stdoutlog/internal/counter"
"go.opentelemetry.io/otel/exporters/stdout/stdoutlog/internal/observ"
"go.opentelemetry.io/otel/sdk/log"
)

// otelComponentType is a name identifying the type of the OpenTelemetry component.
const (
otelComponentType = "go.opentelemetry.io/otel/exporters/stdout/stdoutlog"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be defined in observ not here. It is only used there and passed from here. This is an unneeded coupling.


// Version is the current version of this instrumentation.
//
// This matches the version of the exporter.
Version = internal.Version
Comment on lines +21 to +24
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This adds to the public API of the package, it should not be exported.

It does not seem needed either. Using just internal.Version should be adequate.

)

var _ log.Exporter = &Exporter{}

// Exporter writes JSON-encoded log records to an [io.Writer] ([os.Stdout] by default).
// Exporter must be created with [New].
type Exporter struct {
encoder atomic.Pointer[json.Encoder]
timestamps bool
encoder atomic.Pointer[json.Encoder]
timestamps bool
instrumentation *observ.Instrumentation
}

// New creates an [Exporter].
Expand All @@ -34,29 +48,50 @@ func New(options ...Option) (*Exporter, error) {
}
e.encoder.Store(enc)

exporterID := counter.NextExporterID()
inst, err := observ.NewInstrumentation(otelComponentType, exporterID)
if err != nil {
return nil, err
}
e.instrumentation = inst

return &e, nil
}

// Export exports log records to writer.
func (e *Exporter) Export(ctx context.Context, records []log.Record) error {
if inst := e.instrumentation; inst != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just shorten the field name to inst if you want to do things like this.

done := inst.ExportLogs(ctx, len(records))
exported, err := e.exportRecords(ctx, records)
done(exported, err)
return err
}

_, err := e.exportRecords(ctx, records)
return err
Comment on lines +63 to +71
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be needed to be called in two different code paths. Use defer:

Suggested change
if inst := e.instrumentation; inst != nil {
done := inst.ExportLogs(ctx, len(records))
exported, err := e.exportRecords(ctx, records)
done(exported, err)
return err
}
_, err := e.exportRecords(ctx, records)
return err
var n int64
var err error
if inst := e.instrumentation; inst != nil {
done := inst.ExportLogs(ctx, len(records))
defer func() { done(n, err) }()
}
n, err = e.exportRecords(ctx, records)
return err

}

func (e *Exporter) exportRecords(ctx context.Context, records []log.Record) (int64, error) {
enc := e.encoder.Load()
if enc == nil {
return nil
return 0, nil
}

var exported int64
for _, record := range records {
// Honor context cancellation.
if err := ctx.Err(); err != nil {
return err
return exported, err
}

// Encode record, one by one.
recordJSON := e.newRecordJSON(record)
if err := enc.Encode(recordJSON); err != nil {
return err
return exported, err
}
exported++
}
return nil

return exported, nil
}

// Shutdown shuts down the Exporter.
Expand Down
Loading
Loading