Skip to content

Commit

Permalink
feat: add support for exporting logs via wasitellog.Exporter (#51)
Browse files Browse the repository at this point in the history
Signed-off-by: Joonas Bergius <[email protected]>
  • Loading branch information
joonas authored Dec 9, 2024
1 parent ae49804 commit 3c11054
Show file tree
Hide file tree
Showing 11 changed files with 881 additions and 15 deletions.
12 changes: 7 additions & 5 deletions x/wasitel/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ module go.wasmcloud.dev/component/x/wasitel
go 1.23.2

require (
go.opentelemetry.io/otel v1.31.0
go.opentelemetry.io/otel/sdk v1.31.0
go.opentelemetry.io/otel/trace v1.31.0
go.opentelemetry.io/otel v1.32.0
go.opentelemetry.io/otel/log v0.8.0
go.opentelemetry.io/otel/sdk v1.32.0
go.opentelemetry.io/otel/sdk/log v0.8.0
go.opentelemetry.io/otel/trace v1.32.0
go.wasmcloud.dev/component v0.0.5
)

Expand All @@ -14,8 +16,8 @@ require (
github.com/go-logr/stdr v1.2.2 // indirect
github.com/google/uuid v1.6.0 // indirect
go.bytecodealliance.org v0.4.0 // indirect
go.opentelemetry.io/otel/metric v1.31.0 // indirect
golang.org/x/sys v0.26.0 // indirect
go.opentelemetry.io/otel/metric v1.32.0 // indirect
golang.org/x/sys v0.27.0 // indirect
)

replace go.wasmcloud.dev/component => ../../
24 changes: 14 additions & 10 deletions x/wasitel/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,19 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
go.bytecodealliance.org v0.4.0 h1:SRwgZIcXR54AmbJg9Y3AMgDlZlvD8dffteBYW+nCD3k=
go.bytecodealliance.org v0.4.0/go.mod h1:hkdjfgQ/bFZYUucnm9cn0Q8/SHO3iT0rzskYlkV4Jy0=
go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY=
go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE=
go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE=
go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY=
go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk=
go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0=
go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys=
go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
go.opentelemetry.io/otel/log v0.8.0 h1:egZ8vV5atrUWUbnSsHn6vB8R21G2wrKqNiDt3iWertk=
go.opentelemetry.io/otel/log v0.8.0/go.mod h1:M9qvDdUTRCopJcGRKg57+JSQ9LgLBrwwfC32epk5NX8=
go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
go.opentelemetry.io/otel/sdk/log v0.8.0 h1:zg7GUYXqxk1jnGF/dTdLPrK06xJdrXgqgFLnI4Crxvs=
go.opentelemetry.io/otel/sdk/log v0.8.0/go.mod h1:50iXr0UVwQrYS45KbruFrEt4LvAdCaWWgIrsN3ZQggo=
go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
72 changes: 72 additions & 0 deletions x/wasitel/wasitellog/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package wasitellog

import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"

"go.wasmcloud.dev/component/net/wasihttp"
"go.wasmcloud.dev/component/x/wasitel/wasitellog/internal/types"
)

type client struct {
config config
httpClient *http.Client
}

func newClient(opts ...Option) *client {
cfg := newConfig(opts...)

wasiTransport := &wasihttp.Transport{}
httpClient := &http.Client{Transport: wasiTransport}

return &client{
config: cfg,
httpClient: httpClient,
}
}

func (c *client) UploadLogs(ctx context.Context, logs []*types.ResourceLogs) error {
if len(logs) == 0 {
return nil
}

export := &types.ExportLogsServiceRequest{
ResourceLogs: logs,
}

body, err := json.Marshal(export)
if err != nil {
return fmt.Errorf("failed to serialize export request to JSON: %w", err)
}

u := c.getUrl()
req, err := http.NewRequest(http.MethodPost, u.String(), bytes.NewBuffer(body))
if err != nil {
return fmt.Errorf("failed to create request to %q: %w", u.String(), err)
}
req.Header.Set("Content-Type", "application/json")

_, err = c.httpClient.Do(req)
if err != nil {
return fmt.Errorf("failed to request %q: %w", u.String(), err)
}

return nil
}

func (c *client) getUrl() url.URL {
scheme := "http"
if !c.config.Insecure {
scheme = "https"
}
u := url.URL{
Scheme: scheme,
Host: c.config.Endpoint,
Path: c.config.Path,
}
return u
}
74 changes: 74 additions & 0 deletions x/wasitel/wasitellog/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package wasitellog

import (
"fmt"
"net/url"
)

const (
// DefaultPort is the default HTTP port of the collector.
DefaultPort uint16 = 4318
// DefaultHost is the host address the client will attempt
// connect to if no collector address is provided.
DefaultHost string = "localhost"
// DefaultPath is a default URL path for endpoint that receives logs
DefaultPath string = "/v1/logs"
)

type config struct {
Endpoint string
Insecure bool
Path string
}

func newConfig(opts ...Option) config {
cfg := config{
Insecure: true,
Endpoint: fmt.Sprintf("%s:%d", DefaultHost, DefaultPort),
Path: DefaultPath,
}
for _, opt := range opts {
cfg = opt.apply(cfg)
}
return cfg
}

type Option interface {
apply(config) config
}

func newWrappedOption(fn func(config) config) Option {
return &wrappedOption{fn: fn}
}

type wrappedOption struct {
fn func(config) config
}

func (o *wrappedOption) apply(cfg config) config {
return o.fn(cfg)
}

func WithEndpoint(endpoint string) Option {
return newWrappedOption(func(cfg config) config {
cfg.Endpoint = endpoint
return cfg
})
}

func WithEndpointURL(eu string) Option {
return newWrappedOption(func(cfg config) config {
u, err := url.Parse(eu)
if err != nil {
return cfg
}

cfg.Endpoint = u.Host
cfg.Path = u.Path
if u.Scheme != "https" {
cfg.Insecure = true
}

return cfg
})
}
64 changes: 64 additions & 0 deletions x/wasitel/wasitellog/exporter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package wasitellog

import (
"context"
"fmt"
"sync"

logsdk "go.opentelemetry.io/otel/sdk/log"
"go.wasmcloud.dev/component/x/wasitel/wasitellog/internal/convert"
)

func New(opts ...Option) (*Exporter, error) {
client := newClient(opts...)
return &Exporter{
client: client,
}, nil
}

var _ logsdk.Exporter = (*Exporter)(nil)

type Exporter struct {
client *client
stopped bool
stoppedMu sync.RWMutex
}

func (e *Exporter) Export(ctx context.Context, logs []logsdk.Record) error {
err := ctx.Err()
if err != nil {
return err
}

// Check whether the exporter has been told to Shutdown
e.stoppedMu.RLock()
stopped := e.stopped
e.stoppedMu.RUnlock()
if stopped {
return nil
}

// Check whether there's anything to export
converted := convert.ResourceLogs(logs)
if len(converted) == 0 {
return nil
}

err = e.client.UploadLogs(ctx, converted)
if err != nil {
return fmt.Errorf("failed to export spans: %w", err)
}
return nil
}

func (e *Exporter) Shutdown(ctx context.Context) error {
e.stoppedMu.Lock()
e.stopped = true
e.stoppedMu.Unlock()

return nil
}

func (e *Exporter) ForceFlush(ctx context.Context) error {
return nil
}
Loading

0 comments on commit 3c11054

Please sign in to comment.