Skip to content

Commit

Permalink
[debugexporter] Add support for profiles signal (#11494)
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 adds Profiles support to the `debugexporter` component.

<!-- Issue number if applicable -->
#### Link to tracking issue
Fixes #11155.

<!--Describe what testing was performed and which tests were added.-->
#### Testing
Added tests to all modified modules:
- `exporter/debugexporter/exporter_test.go`
- `exporter/debugexporter/factory_test.go`
- `exporter/debugexporter/internal/normal/profiles_test.go`
  • Loading branch information
julianocosta89 authored Oct 23, 2024
1 parent 57e59d1 commit cb19e1d
Show file tree
Hide file tree
Showing 11 changed files with 222 additions and 20 deletions.
25 changes: 25 additions & 0 deletions .chloggen/julianocosta89-debugexporter-profiles.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# 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. otlpreceiver)
component: debugexporter

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Add profiles support to debug exporter

# One or more tracking issues or pull requests related to the change
issues: [11155]

# (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:

# 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: [api]
2 changes: 1 addition & 1 deletion exporter/debugexporter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<!-- status autogenerated section -->
| Status | |
| ------------- |-----------|
| Stability | [development]: traces, metrics, logs |
| Stability | [development]: traces, metrics, logs, profiles |
| Distributions | [core], [contrib], [k8s] |
| Warnings | [Unstable Output Format](#warnings) |
| Issues | [![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector?query=is%3Aissue%20is%3Aopen%20label%3Aexporter%2Fdebug%20&label=open&color=orange&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector/issues?q=is%3Aopen+is%3Aissue+label%3Aexporter%2Fdebug) [![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector?query=is%3Aissue%20is%3Aclosed%20label%3Aexporter%2Fdebug%20&label=closed&color=blue&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector/issues?q=is%3Aclosed+is%3Aissue+label%3Aexporter%2Fdebug) |
Expand Down
43 changes: 33 additions & 10 deletions exporter/debugexporter/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,36 +13,42 @@ import (
"go.opentelemetry.io/collector/exporter/debugexporter/internal/otlptext"
"go.opentelemetry.io/collector/pdata/plog"
"go.opentelemetry.io/collector/pdata/pmetric"
"go.opentelemetry.io/collector/pdata/pprofile"
"go.opentelemetry.io/collector/pdata/ptrace"
)

type debugExporter struct {
verbosity configtelemetry.Level
logger *zap.Logger
logsMarshaler plog.Marshaler
metricsMarshaler pmetric.Marshaler
tracesMarshaler ptrace.Marshaler
verbosity configtelemetry.Level
logger *zap.Logger
logsMarshaler plog.Marshaler
metricsMarshaler pmetric.Marshaler
tracesMarshaler ptrace.Marshaler
profilesMarshaler pprofile.Marshaler
}

func newDebugExporter(logger *zap.Logger, verbosity configtelemetry.Level) *debugExporter {
var logsMarshaler plog.Marshaler
var metricsMarshaler pmetric.Marshaler
var tracesMarshaler ptrace.Marshaler
var profilesMarshaler pprofile.Marshaler
if verbosity == configtelemetry.LevelDetailed {
logsMarshaler = otlptext.NewTextLogsMarshaler()
metricsMarshaler = otlptext.NewTextMetricsMarshaler()
tracesMarshaler = otlptext.NewTextTracesMarshaler()
profilesMarshaler = otlptext.NewTextProfilesMarshaler()
} else {
logsMarshaler = normal.NewNormalLogsMarshaler()
metricsMarshaler = normal.NewNormalMetricsMarshaler()
tracesMarshaler = normal.NewNormalTracesMarshaler()
profilesMarshaler = normal.NewNormalProfilesMarshaler()
}
return &debugExporter{
verbosity: verbosity,
logger: logger,
logsMarshaler: logsMarshaler,
metricsMarshaler: metricsMarshaler,
tracesMarshaler: tracesMarshaler,
verbosity: verbosity,
logger: logger,
logsMarshaler: logsMarshaler,
metricsMarshaler: metricsMarshaler,
tracesMarshaler: tracesMarshaler,
profilesMarshaler: profilesMarshaler,
}
}

Expand Down Expand Up @@ -95,3 +101,20 @@ func (s *debugExporter) pushLogs(_ context.Context, ld plog.Logs) error {
s.logger.Info(string(buf))
return nil
}

func (s *debugExporter) pushProfiles(_ context.Context, pd pprofile.Profiles) error {
s.logger.Info("Profiles",
zap.Int("resource profiles", pd.ResourceProfiles().Len()),
zap.Int("sample records", pd.SampleCount()))

if s.verbosity == configtelemetry.LevelBasic {
return nil
}

buf, err := s.profilesMarshaler.MarshalProfiles(pd)
if err != nil {
return err
}
s.logger.Info(string(buf))
return nil
}
22 changes: 22 additions & 0 deletions exporter/debugexporter/exporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"go.opentelemetry.io/collector/exporter/exportertest"
"go.opentelemetry.io/collector/pdata/plog"
"go.opentelemetry.io/collector/pdata/pmetric"
"go.opentelemetry.io/collector/pdata/pprofile"
"go.opentelemetry.io/collector/pdata/ptrace"
"go.opentelemetry.io/collector/pdata/testdata"
)
Expand Down Expand Up @@ -68,6 +69,21 @@ func TestLogsNoErrors(t *testing.T) {
}
}

func TestProfilesNoErrors(t *testing.T) {
for _, tc := range createTestCases() {
t.Run(tc.name, func(t *testing.T) {
lle, err := createProfiles(context.Background(), exportertest.NewNopSettings(), createDefaultConfig())
require.NotNil(t, lle)
assert.NoError(t, err)

assert.NoError(t, lle.ConsumeProfiles(context.Background(), pprofile.NewProfiles()))
assert.NoError(t, lle.ConsumeProfiles(context.Background(), testdata.GenerateProfiles(10)))

assert.NoError(t, lle.Shutdown(context.Background()))
})
}
}

func TestErrors(t *testing.T) {
le := newDebugExporter(zaptest.NewLogger(t), configtelemetry.LevelDetailed)
require.NotNil(t, le)
Expand All @@ -76,9 +92,11 @@ func TestErrors(t *testing.T) {
le.tracesMarshaler = &errMarshaler{err: errWant}
le.metricsMarshaler = &errMarshaler{err: errWant}
le.logsMarshaler = &errMarshaler{err: errWant}
le.profilesMarshaler = &errMarshaler{err: errWant}
assert.Equal(t, errWant, le.pushTraces(context.Background(), ptrace.NewTraces()))
assert.Equal(t, errWant, le.pushMetrics(context.Background(), pmetric.NewMetrics()))
assert.Equal(t, errWant, le.pushLogs(context.Background(), plog.NewLogs()))
assert.Equal(t, errWant, le.pushProfiles(context.Background(), pprofile.NewProfiles()))
}

type testCase struct {
Expand Down Expand Up @@ -120,3 +138,7 @@ func (e errMarshaler) MarshalMetrics(pmetric.Metrics) ([]byte, error) {
func (e errMarshaler) MarshalTraces(ptrace.Traces) ([]byte, error) {
return nil, e.err
}

func (e errMarshaler) MarshalProfiles(pprofile.Profiles) ([]byte, error) {
return nil, e.err
}
23 changes: 19 additions & 4 deletions exporter/debugexporter/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import (
"go.opentelemetry.io/collector/exporter/debugexporter/internal/metadata"
"go.opentelemetry.io/collector/exporter/debugexporter/internal/otlptext"
"go.opentelemetry.io/collector/exporter/exporterhelper"
"go.opentelemetry.io/collector/exporter/exporterhelper/exporterhelperprofiles"
"go.opentelemetry.io/collector/exporter/exporterprofiles"
)

// The value of "type" key in configuration.
Expand All @@ -29,12 +31,13 @@ const (

// NewFactory creates a factory for Debug exporter
func NewFactory() exporter.Factory {
return exporter.NewFactory(
return exporterprofiles.NewFactory(
componentType,
createDefaultConfig,
exporter.WithTraces(createTraces, metadata.TracesStability),
exporter.WithMetrics(createMetrics, metadata.MetricsStability),
exporter.WithLogs(createLogs, metadata.LogsStability),
exporterprofiles.WithTraces(createTraces, metadata.TracesStability),
exporterprofiles.WithMetrics(createMetrics, metadata.MetricsStability),
exporterprofiles.WithLogs(createLogs, metadata.LogsStability),
exporterprofiles.WithProfiles(createProfiles, metadata.ProfilesStability),
)
}

Expand Down Expand Up @@ -83,6 +86,18 @@ func createLogs(ctx context.Context, set exporter.Settings, config component.Con
)
}

func createProfiles(ctx context.Context, set exporter.Settings, config component.Config) (exporterprofiles.Profiles, error) {
cfg := config.(*Config)
exporterLogger := createLogger(cfg, set.TelemetrySettings.Logger)
debug := newDebugExporter(exporterLogger, cfg.Verbosity)
return exporterhelperprofiles.NewProfilesExporter(ctx, set, config,
debug.pushProfiles,
exporterhelper.WithCapabilities(consumer.Capabilities{MutatesData: false}),
exporterhelper.WithTimeout(exporterhelper.TimeoutConfig{Timeout: 0}),
exporterhelper.WithShutdown(otlptext.LoggerSync(exporterLogger)),
)
}

func createLogger(cfg *Config, logger *zap.Logger) *zap.Logger {
var exporterLogger *zap.Logger
if cfg.UseInternalLogger {
Expand Down
10 changes: 10 additions & 0 deletions exporter/debugexporter/factory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/require"

"go.opentelemetry.io/collector/component/componenttest"
"go.opentelemetry.io/collector/exporter/exporterprofiles"
"go.opentelemetry.io/collector/exporter/exportertest"
)

Expand Down Expand Up @@ -47,3 +48,12 @@ func TestCreateLogs(t *testing.T) {
require.NoError(t, err)
assert.NotNil(t, te)
}

func TestCreateFactoryProfiles(t *testing.T) {
factory := NewFactory()
cfg := factory.CreateDefaultConfig()

te, err := factory.(exporterprofiles.Factory).CreateProfiles(context.Background(), exportertest.NewNopSettings(), cfg)
require.NoError(t, err)
assert.NotNil(t, te)
}
11 changes: 10 additions & 1 deletion exporter/debugexporter/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ require (
go.opentelemetry.io/collector/confmap v1.18.0
go.opentelemetry.io/collector/consumer v0.112.0
go.opentelemetry.io/collector/exporter v0.112.0
go.opentelemetry.io/collector/exporter/exporterhelper/exporterhelperprofiles v0.112.0
go.opentelemetry.io/collector/exporter/exporterprofiles v0.112.0
go.opentelemetry.io/collector/exporter/exportertest v0.112.0
go.opentelemetry.io/collector/pdata v1.18.0
go.opentelemetry.io/collector/pdata/pprofile v0.112.0
Expand Down Expand Up @@ -37,12 +39,13 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
go.opentelemetry.io/collector/config/configretry v1.18.0 // indirect
go.opentelemetry.io/collector/consumer/consumererror v0.112.0 // indirect
go.opentelemetry.io/collector/consumer/consumererror/consumererrorprofiles v0.112.0 // indirect
go.opentelemetry.io/collector/consumer/consumerprofiles v0.112.0 // indirect
go.opentelemetry.io/collector/consumer/consumertest v0.112.0 // indirect
go.opentelemetry.io/collector/exporter/exporterprofiles v0.112.0 // indirect
go.opentelemetry.io/collector/extension v0.112.0 // indirect
go.opentelemetry.io/collector/extension/experimental/storage v0.112.0 // indirect
go.opentelemetry.io/collector/pipeline v0.112.0 // indirect
go.opentelemetry.io/collector/pipeline/pipelineprofiles v0.112.0 // indirect
go.opentelemetry.io/collector/receiver v0.112.0 // indirect
go.opentelemetry.io/collector/receiver/receiverprofiles v0.112.0 // indirect
go.opentelemetry.io/otel v1.31.0 // indirect
Expand Down Expand Up @@ -83,6 +86,8 @@ replace go.opentelemetry.io/collector/config/configtelemetry => ../../config/con

replace go.opentelemetry.io/collector/config/configretry => ../../config/configretry

replace go.opentelemetry.io/collector/consumer/consumererror/consumererrorprofiles => ../../consumer/consumererror/consumererrorprofiles

replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles

replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest
Expand All @@ -91,8 +96,12 @@ replace go.opentelemetry.io/collector/receiver/receiverprofiles => ../../receive

replace go.opentelemetry.io/collector/exporter/exporterprofiles => ../exporterprofiles

replace go.opentelemetry.io/collector/exporter/exporterhelper/exporterhelperprofiles => ../exporterhelper/exporterhelperprofiles

replace go.opentelemetry.io/collector/pipeline => ../../pipeline

replace go.opentelemetry.io/collector/pipeline/pipelineprofiles => ../../pipeline/pipelineprofiles

replace go.opentelemetry.io/collector/exporter/exportertest => ../exportertest

replace go.opentelemetry.io/collector/consumer/consumererror => ../../consumer/consumererror
7 changes: 4 additions & 3 deletions exporter/debugexporter/internal/metadata/generated_status.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 49 additions & 0 deletions exporter/debugexporter/internal/normal/profiles.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package normal // import "go.opentelemetry.io/collector/exporter/debugexporter/internal/normal"

import (
"bytes"
"strconv"
"strings"

"go.opentelemetry.io/collector/pdata/pprofile"
)

type normalProfilesMarshaler struct{}

// Ensure normalProfilesMarshaller implements interface pprofile.Marshaler
var _ pprofile.Marshaler = normalProfilesMarshaler{}

// NewNormalProfilesMarshaler returns a pprofile.Marshaler for normal verbosity. It writes one line of text per log record
func NewNormalProfilesMarshaler() pprofile.Marshaler {
return normalProfilesMarshaler{}
}

func (normalProfilesMarshaler) MarshalProfiles(pd pprofile.Profiles) ([]byte, error) {
var buffer bytes.Buffer
for i := 0; i < pd.ResourceProfiles().Len(); i++ {
resourceProfiles := pd.ResourceProfiles().At(i)
for j := 0; j < resourceProfiles.ScopeProfiles().Len(); j++ {
scopeProfiles := resourceProfiles.ScopeProfiles().At(j)
for k := 0; k < scopeProfiles.Profiles().Len(); k++ {
profile := scopeProfiles.Profiles().At(k)

buffer.WriteString(profile.ProfileID().String())

buffer.WriteString(" samples=")
buffer.WriteString(strconv.Itoa(profile.Profile().Sample().Len()))

if profile.Attributes().Len() > 0 {
profileAttributes := writeAttributes(profile.Attributes())
buffer.WriteString(" ")
buffer.WriteString(strings.Join(profileAttributes, " "))
}

buffer.WriteString("\n")
}
}
}
return buffer.Bytes(), nil
}
48 changes: 48 additions & 0 deletions exporter/debugexporter/internal/normal/profiles_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package normal

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"go.opentelemetry.io/collector/pdata/pprofile"
)

func TestMarshalProfiles(t *testing.T) {
tests := []struct {
name string
input pprofile.Profiles
expected string
}{
{
name: "empty profile",
input: pprofile.NewProfiles(),
expected: "",
},
{
name: "one profile",
input: func() pprofile.Profiles {
profiles := pprofile.NewProfiles()
profile := profiles.ResourceProfiles().AppendEmpty().ScopeProfiles().AppendEmpty().Profiles().AppendEmpty()
profile.SetProfileID([16]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10})
profile.Profile().Sample().AppendEmpty()
profile.Profile().Sample().AppendEmpty()
profile.Attributes().PutStr("key1", "value1")
return profiles
}(),
expected: `0102030405060708090a0b0c0d0e0f10 samples=2 key1=value1
`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
output, err := NewNormalProfilesMarshaler().MarshalProfiles(tt.input)
require.NoError(t, err)
assert.Equal(t, tt.expected, string(output))
})
}
}
2 changes: 1 addition & 1 deletion exporter/debugexporter/metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ github_project: open-telemetry/opentelemetry-collector
status:
class: exporter
stability:
development: [traces, metrics, logs]
development: [traces, metrics, logs, profiles]
distributions: [core, contrib, k8s]
warnings: [Unstable Output Format]

0 comments on commit cb19e1d

Please sign in to comment.