Skip to content

Commit

Permalink
[receiver/libhoney] Libhoney receiver log signal (#36827)
Browse files Browse the repository at this point in the history
<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue.
Ex. Adding a feature - Explain what this achieves.-->
#### Description

This PR is the implementation for the logs signal related to the new
libhoney receiver.

<!-- Issue number (e.g. #1234) or full URL to issue, if applicable. -->
#### Link to tracking issue

#36693
  • Loading branch information
mterhar authored Dec 19, 2024
1 parent 44e4d59 commit 8058e0c
Show file tree
Hide file tree
Showing 16 changed files with 1,508 additions and 188 deletions.
27 changes: 27 additions & 0 deletions .chloggen/logs-for-libhoneyreceiver.yaml
Original file line number Diff line number Diff line change
@@ -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: libhoneyreceiver

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Implement log signal for libhoney receiver

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [36693]

# (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: [user]
29 changes: 15 additions & 14 deletions receiver/libhoneyreceiver/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,20 +45,21 @@ The following setting is required for refinery traffic since:
- "/1/batch"
include_metadata: true
auth_api: https://api.honeycomb.io
resources:
service_name: service_name
scopes:
library_name: library.name
library_version: library.version
attributes:
trace_id: trace_id
parent_id: parent_id
span_id: span_id
name: name
error: error
spankind: span.kind
durationFields:
- duration_ms
fields:
resources:
service_name: service_name
scopes:
library_name: library.name
library_version: library.version
attributes:
trace_id: trace_id
parent_id: parent_id
span_id: span_id
name: name
error: error
spankind: span.kind
durationFields:
- duration_ms
```
### Telemetry data types supported
Expand Down
34 changes: 9 additions & 25 deletions receiver/libhoneyreceiver/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,51 +11,35 @@ import (

"go.opentelemetry.io/collector/config/confighttp"
"go.opentelemetry.io/collector/confmap"

"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/libhoneyreceiver/internal/libhoneyevent"
)

// Config represents the receiver config settings within the collector's config.yaml
type Config struct {
HTTP *HTTPConfig `mapstructure:"http"`
AuthAPI string `mapstructure:"auth_api"`
Wrapper string `mapstructure:"wrapper"`
Resources ResourcesConfig `mapstructure:"resources"`
Scopes ScopesConfig `mapstructure:"scopes"`
Attributes AttributesConfig `mapstructure:"attributes"`
HTTP *HTTPConfig `mapstructure:"http"`
AuthAPI string `mapstructure:"auth_api"`
Wrapper string `mapstructure:"wrapper"`
FieldMapConfig libhoneyevent.FieldMapConfig `mapstructure:"fields"`
}

// HTTPConfig defines the configuration for the HTTP server receiving traces.
type HTTPConfig struct {
*confighttp.ServerConfig `mapstructure:",squash"`

// The URL path to receive traces on. If omitted "/" will be used.
TracesURLPaths []string `mapstructure:"traces_url_paths,omitempty"`
}

type ResourcesConfig struct {
ServiceName string `mapstructure:"service_name"`
}

type ScopesConfig struct {
LibraryName string `mapstructure:"library_name"`
LibraryVersion string `mapstructure:"library_version"`
}

type AttributesConfig struct {
TraceID string `mapstructure:"trace_id"`
ParentID string `mapstructure:"parent_id"`
SpanID string `mapstructure:"span_id"`
Name string `mapstructure:"name"`
Error string `mapstructure:"error"`
SpanKind string `mapstructure:"spankind"`
DurationFields []string `mapstructure:"durationFields"`
}

// Validate ensures the HTTP configuration is set.
func (cfg *Config) Validate() error {
if cfg.HTTP == nil {
return errors.New("must specify at least one protocol when using the arbitrary JSON receiver")
}
return nil
}

// Unmarshal unmarshals the configuration from the given configuration and then checks for errors.
func (cfg *Config) Unmarshal(conf *confmap.Conf) error {
// first load the config normally
err := conf.Unmarshal(cfg)
Expand Down
29 changes: 29 additions & 0 deletions receiver/libhoneyreceiver/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package libhoneyreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/libhoneyreceiver"

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/component/componenttest"
)

func TestCreateDefaultConfig(t *testing.T) {
factory := NewFactory()
cfg := factory.CreateDefaultConfig()
assert.NotNil(t, cfg, "failed to create default config")
assert.NoError(t, componenttest.CheckConfigStruct(cfg))

libhoneyCfg, ok := cfg.(*Config)
require.True(t, ok, "invalid Config type")

assert.Equal(t, "localhost:8080", libhoneyCfg.HTTP.Endpoint)
assert.Equal(t, []string{"/events", "/event", "/batch"}, libhoneyCfg.HTTP.TracesURLPaths)
assert.Equal(t, "", libhoneyCfg.AuthAPI)
assert.Equal(t, "service.name", libhoneyCfg.FieldMapConfig.Resources.ServiceName)
assert.Equal(t, "library.name", libhoneyCfg.FieldMapConfig.Scopes.LibraryName)
assert.Equal(t, []string{"duration_ms"}, libhoneyCfg.FieldMapConfig.Attributes.DurationFields)
}
125 changes: 125 additions & 0 deletions receiver/libhoneyreceiver/encoder/encoder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package encoder // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/libhoneyreceiver/encoder"

import (
"bytes"

"github.com/gogo/protobuf/jsonpb"
"go.opentelemetry.io/collector/pdata/plog/plogotlp"
"go.opentelemetry.io/collector/pdata/pmetric/pmetricotlp"
"go.opentelemetry.io/collector/pdata/ptrace/ptraceotlp"
spb "google.golang.org/genproto/googleapis/rpc/status"
)

const (
PbContentType = "application/x-protobuf"
JsonContentType = "application/json"
MsgpackContentType = "application/x-msgpack"
)

var (
JsEncoder = &JsonEncoder{}
JsonPbMarshaler = &jsonpb.Marshaler{}
MpEncoder = &msgpackEncoder{}
)

type Encoder interface {
UnmarshalTracesRequest(buf []byte) (ptraceotlp.ExportRequest, error)
UnmarshalMetricsRequest(buf []byte) (pmetricotlp.ExportRequest, error)
UnmarshalLogsRequest(buf []byte) (plogotlp.ExportRequest, error)

MarshalTracesResponse(ptraceotlp.ExportResponse) ([]byte, error)
MarshalMetricsResponse(pmetricotlp.ExportResponse) ([]byte, error)
MarshalLogsResponse(plogotlp.ExportResponse) ([]byte, error)

MarshalStatus(rsp *spb.Status) ([]byte, error)

ContentType() string
}

type JsonEncoder struct{}

func (JsonEncoder) UnmarshalTracesRequest(buf []byte) (ptraceotlp.ExportRequest, error) {
req := ptraceotlp.NewExportRequest()
err := req.UnmarshalJSON(buf)
return req, err
}

func (JsonEncoder) UnmarshalMetricsRequest(buf []byte) (pmetricotlp.ExportRequest, error) {
req := pmetricotlp.NewExportRequest()
err := req.UnmarshalJSON(buf)
return req, err
}

func (JsonEncoder) UnmarshalLogsRequest(buf []byte) (plogotlp.ExportRequest, error) {
req := plogotlp.NewExportRequest()
err := req.UnmarshalJSON(buf)
return req, err
}

func (JsonEncoder) MarshalTracesResponse(resp ptraceotlp.ExportResponse) ([]byte, error) {
return resp.MarshalJSON()
}

func (JsonEncoder) MarshalMetricsResponse(resp pmetricotlp.ExportResponse) ([]byte, error) {
return resp.MarshalJSON()
}

func (JsonEncoder) MarshalLogsResponse(resp plogotlp.ExportResponse) ([]byte, error) {
return resp.MarshalJSON()
}

func (JsonEncoder) MarshalStatus(resp *spb.Status) ([]byte, error) {
buf := new(bytes.Buffer)
err := JsonPbMarshaler.Marshal(buf, resp)
return buf.Bytes(), err
}

func (JsonEncoder) ContentType() string {
return JsonContentType
}

// messagepack responses seem to work in JSON so leaving this alone for now.
type msgpackEncoder struct{}

func (msgpackEncoder) UnmarshalTracesRequest(buf []byte) (ptraceotlp.ExportRequest, error) {
req := ptraceotlp.NewExportRequest()
err := req.UnmarshalJSON(buf)
return req, err
}

func (msgpackEncoder) UnmarshalMetricsRequest(buf []byte) (pmetricotlp.ExportRequest, error) {
req := pmetricotlp.NewExportRequest()
err := req.UnmarshalJSON(buf)
return req, err
}

func (msgpackEncoder) UnmarshalLogsRequest(buf []byte) (plogotlp.ExportRequest, error) {
req := plogotlp.NewExportRequest()
err := req.UnmarshalJSON(buf)
return req, err
}

func (msgpackEncoder) MarshalTracesResponse(resp ptraceotlp.ExportResponse) ([]byte, error) {
return resp.MarshalJSON()
}

func (msgpackEncoder) MarshalMetricsResponse(resp pmetricotlp.ExportResponse) ([]byte, error) {
return resp.MarshalJSON()
}

func (msgpackEncoder) MarshalLogsResponse(resp plogotlp.ExportResponse) ([]byte, error) {
return resp.MarshalJSON()
}

func (msgpackEncoder) MarshalStatus(resp *spb.Status) ([]byte, error) {
buf := new(bytes.Buffer)
err := JsonPbMarshaler.Marshal(buf, resp)
return buf.Bytes(), err
}

func (msgpackEncoder) ContentType() string {
return MsgpackContentType
}
33 changes: 18 additions & 15 deletions receiver/libhoneyreceiver/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"go.opentelemetry.io/collector/receiver"

"github.com/open-telemetry/opentelemetry-collector-contrib/internal/sharedcomponent"
"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/libhoneyreceiver/internal/libhoneyevent"
"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/libhoneyreceiver/internal/metadata"
)

Expand Down Expand Up @@ -44,21 +45,23 @@ func createDefaultConfig() component.Config {
TracesURLPaths: defaultTracesURLPaths,
},
AuthAPI: "",
Resources: ResourcesConfig{
ServiceName: "service.name",
},
Scopes: ScopesConfig{
LibraryName: "library.name",
LibraryVersion: "library.version",
},
Attributes: AttributesConfig{
TraceID: "trace.trace_id",
SpanID: "trace.span_id",
ParentID: "trace.parent_id",
Name: "name",
Error: "error",
SpanKind: "span.kind",
DurationFields: durationFieldsArr,
FieldMapConfig: libhoneyevent.FieldMapConfig{
Resources: libhoneyevent.ResourcesConfig{
ServiceName: "service.name",
},
Scopes: libhoneyevent.ScopesConfig{
LibraryName: "library.name",
LibraryVersion: "library.version",
},
Attributes: libhoneyevent.AttributesConfig{
TraceID: "trace.trace_id",
SpanID: "trace.span_id",
ParentID: "trace.parent_id",
Name: "name",
Error: "error",
SpanKind: "span.kind",
DurationFields: durationFieldsArr,
},
},
}
}
Expand Down
47 changes: 47 additions & 0 deletions receiver/libhoneyreceiver/factory_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package libhoneyreceiver

import (
"context"
"testing"

"github.com/stretchr/testify/assert"
"go.opentelemetry.io/collector/component/componenttest"
"go.opentelemetry.io/collector/consumer/consumertest"
"go.opentelemetry.io/collector/receiver/receivertest"

"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/libhoneyreceiver/internal/metadata"
)

func TestCreateTracesReceiver(t *testing.T) {
factory := NewFactory()
cfg := factory.CreateDefaultConfig()
set := receivertest.NewNopSettings()
tReceiver, err := factory.CreateTraces(context.Background(), set, cfg, consumertest.NewNop())

assert.NoError(t, err, "receiver creation failed")
assert.NotNil(t, tReceiver, "receiver creation failed")

assert.NoError(t, tReceiver.Start(context.Background(), componenttest.NewNopHost()))
assert.NoError(t, tReceiver.Shutdown(context.Background()))
}

func TestCreateLogsReceiver(t *testing.T) {
factory := NewFactory()
cfg := factory.CreateDefaultConfig()
set := receivertest.NewNopSettings()
lReceiver, err := factory.CreateLogs(context.Background(), set, cfg, consumertest.NewNop())

assert.NoError(t, err, "receiver creation failed")
assert.NotNil(t, lReceiver, "receiver creation failed")

assert.NoError(t, lReceiver.Start(context.Background(), componenttest.NewNopHost()))
assert.NoError(t, lReceiver.Shutdown(context.Background()))
}

func TestType(t *testing.T) {
factory := NewFactory()
assert.Equal(t, metadata.Type, factory.Type())
}
Loading

0 comments on commit 8058e0c

Please sign in to comment.