Skip to content

Commit

Permalink
New otelcol.exporter.debug component
Browse files Browse the repository at this point in the history
  • Loading branch information
ptodev committed Jun 13, 2024
1 parent f998c10 commit c7e15cd
Show file tree
Hide file tree
Showing 13 changed files with 432 additions and 0 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ Main (unreleased)

- Added live debugging support for `prometheus.relabel`. (@wildum)

- A new `otelcol.exporter.debug` component for printing OTel telemetry from
other `otelcol` components to the console. (@BarunKGP)

### Enhancements

- (_Public preview_) Add native histogram support to `otelcol.receiver.prometheus`. (@wildum)
Expand Down
1 change: 1 addition & 0 deletions docs/sources/reference/compatibility/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ The following components, grouped by namespace, _export_ OpenTelemetry `otelcol.
- [otelcol.connector.spanlogs](../components/otelcol.connector.spanlogs)
- [otelcol.connector.spanmetrics](../components/otelcol.connector.spanmetrics)
- [otelcol.exporter.awss3](../components/otelcol.exporter.awss3)
- [otelcol.exporter.debug](../components/otelcol.exporter.debug)
- [otelcol.exporter.kafka](../components/otelcol.exporter.kafka)
- [otelcol.exporter.loadbalancing](../components/otelcol.exporter.loadbalancing)
- [otelcol.exporter.logging](../components/otelcol.exporter.logging)
Expand Down
146 changes: 146 additions & 0 deletions docs/sources/reference/components/otelcol.exporter.debug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
---
canonical: https://grafana.com/docs/alloy/latest/reference/components/otelcol.exporter.debug/
description: Learn about otelcol.exporter.debug
title: otelcol.exporter.debug
---

<span class="badge docs-labels__stage docs-labels__item">Experimental</span>

# otelcol.exporter.debug

`otelcol.exporter.debug` accepts telemetry data from other `otelcol` components and writes them to the console (stderr).
You can control the verbosity of the logs.

{{< admonition type="note" >}}
`otelcol.exporter.debug` is a wrapper over the upstream OpenTelemetry Collector `debug` exporter.
If necessary, bug reports or feature requests are redirected to the upstream repository.
{{< /admonition >}}

Multiple `otelcol.exporter.debug` components can be specified by giving them different labels.

## Usage

```river
otelcol.exporter.debug "LABEL" { }
```

## Arguments

`otelcol.exporter.debug` supports the following arguments:

Name | Type | Description | Default | Required
---- | ---- | ----------- | ------- | --------
`verbosity` | `string` | Verbosity of the generated logs. | `"normal"` | no
`sampling_initial` | `int` | Number of messages initially logged each second. | `2` | no
`sampling_thereafter` | `int` | Sampling rate after the initial messages are logged. | `500` | no

The `verbosity` argument must be one of:
* `"basic"`: A single-line summary of received data is logged to stderr, with a total count of telemetry records for every batch of received logs, metrics or traces.
* `"normal"`: Currently, this produces the same output as `"basic"` verbosity.
* `"detailed"`: All details of every telemetry record are logged to stderr, typically writing multiple lines for every telemetry record.

Example of `"basic"` and `"normal"` output:
```
ts=2024-06-13T11:24:13.782957Z level=info msg=TracesExporter component_path=/ component_id=otelcol.exporter.debug.default "resource spans": 1, spans: 2
```

Example of `"detailed"` output:
```
ts=2024-06-13T11:24:13.782957Z level=info msg=TracesExporter component_path=/ component_id=otelcol.exporter.debug.default "resource spans"=1 spans=2
ts=2024-06-13T11:24:13.783101Z level=info msg="ResourceSpans #0
Resource SchemaURL: https://opentelemetry.io/schemas/1.4.0
Resource attributes:
-> service.name: Str(telemetrygen)
ScopeSpans #0
ScopeSpans SchemaURL:
InstrumentationScope telemetrygen
Span #0
Trace ID : 3bde5d3ee82303571bba6e1136781fe4
Parent ID : 5e9dcf9bac4acc1f
ID : 2cf3ef2899aba35c
Name : okey-dokey
Kind : Server
Start time : 2023-11-11 04:49:03.509369393 +0000 UTC
End time : 2023-11-11 04:49:03.50949377 +0000 UTC
Status code : Unset
Status message :
Attributes:
-> net.peer.ip: Str(1.2.3.4)
-> peer.service: Str(telemetrygen-client)
Span #1
Trace ID : 3bde5d3ee82303571bba6e1136781fe4
Parent ID :
ID : 5e9dcf9bac4acc1f
Name : lets-go
Kind : Client
Start time : 2023-11-11 04:49:03.50935117 +0000 UTC
End time : 2023-11-11 04:49:03.50949377 +0000 UTC
Status code : Unset
Status message :
Attributes:
-> net.peer.ip: Str(1.2.3.4)
-> peer.service: Str(telemetrygen-server)
{"kind": "exporter", "data_type": "traces", "name": "debug"}"
```

Note that the above `"detailed"` example was edited to replace all instances of `\n` with a new line.

## Exported fields

The following fields are exported and can be referenced by other components:

Name | Type | Description
---- | ---- | -----------
`input` | `otelcol.Consumer` | A value that other components can use to send telemetry data to.

`input` accepts `otelcol.Consumer` data for any telemetry signal (metrics,
logs, or traces).

## Component health

`otelcol.exporter.debug` is only reported as unhealthy if given an invalid
configuration.

## Debug information

`otelcol.exporter.debug` does not expose any component-specific debug
information.

## Example

This example scrapes Prometheus UNIX metrics and writes them to the console:

```river
prometheus.exporter.unix "default" { }
prometheus.scrape "default" {
targets = prometheus.exporter.unix.default.targets
forward_to = [otelcol.receiver.prometheus.default.receiver]
}
otelcol.receiver.prometheus "default" {
output {
metrics = [otelcol.exporter.debug.default.input]
}
}
otelcol.exporter.debug "default" {
verbosity = "detailed"
sampling_initial = 1
sampling_thereafter = 1
}
```
<!-- START GENERATED COMPATIBLE COMPONENTS -->

## Compatible components

`otelcol.exporter.debug` has exports that can be consumed by the following components:

- Components that consume [OpenTelemetry `otelcol.Consumer`](../../compatibility/#opentelemetry-otelcolconsumer-consumers)

{{< admonition type="note" >}}
Connecting some components may not be sensible or components may require further configuration to make the connection work correctly.
Refer to the linked documentation for more details.
{{< /admonition >}}

<!-- END GENERATED COMPATIBLE COMPONENTS -->
7 changes: 7 additions & 0 deletions docs/sources/reference/components/otelcol.exporter.logging.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ title: otelcol.exporter.logging

# otelcol.exporter.logging

{{< admonition type="warning" >}}
The OpenTelemetry Collector `logging` exporter is deprecated.
It is due to be removed from the upstream Collector repository in September 2024.
`otelcol.exporter.logging` may also be removed in a future Alloy version.
Please use `otelcol.exporter.debug` instead.
{{< /admonition >}}

`otelcol.exporter.logging` accepts telemetry data from other `otelcol` components
and writes them to the console.

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ require (
go.opentelemetry.io/collector/connector v0.102.1
go.opentelemetry.io/collector/consumer v0.102.1
go.opentelemetry.io/collector/exporter v0.102.1
go.opentelemetry.io/collector/exporter/debugexporter v0.102.1
go.opentelemetry.io/collector/exporter/loggingexporter v0.102.1
go.opentelemetry.io/collector/exporter/otlpexporter v0.102.1
go.opentelemetry.io/collector/exporter/otlphttpexporter v0.102.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2306,6 +2306,8 @@ go.opentelemetry.io/collector/consumer v0.102.1 h1:0CkgHhxwx4lI/m+hWjh607xyjooW5
go.opentelemetry.io/collector/consumer v0.102.1/go.mod h1:HoXqmrRV13jLnP3/Gg3fYNdRkDPoO7UW58hKiLyFF60=
go.opentelemetry.io/collector/exporter v0.102.1 h1:4VURYgBNJscxfMhZWitzcwA1cig5a6pH0xZSpdECDnM=
go.opentelemetry.io/collector/exporter v0.102.1/go.mod h1:1pmNxvrvvbWDW6PiGObICdj0eOSGV4Fzwpm5QA1GU54=
go.opentelemetry.io/collector/exporter/debugexporter v0.102.1 h1:eATGZwBNmqUn8xRr7oGQhoegjeOOCdmtbYgziUoFMf8=
go.opentelemetry.io/collector/exporter/debugexporter v0.102.1/go.mod h1:k5rDZX5pV3DsXZzvI+sk7PKMUljtB7T25PPXAPGBjEs=
go.opentelemetry.io/collector/exporter/loggingexporter v0.102.1 h1:LblufdV22DxB5NZa66CGCQZjadYTVxT+O5NR9YjNQ9Y=
go.opentelemetry.io/collector/exporter/loggingexporter v0.102.1/go.mod h1:zmOEwiQlfvEHnakWNO1YFNubgWZvZee+5Wshuck5lZk=
go.opentelemetry.io/collector/exporter/otlpexporter v0.102.1 h1:bOXE7u1iy0SKwH2mnVyIMKkvFIR9bn9iIm1Cf/CJlZU=
Expand Down
1 change: 1 addition & 0 deletions internal/component/all/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import (
_ "github.com/grafana/alloy/internal/component/otelcol/connector/spanlogs" // Import otelcol.connector.spanlogs
_ "github.com/grafana/alloy/internal/component/otelcol/connector/spanmetrics" // Import otelcol.connector.spanmetrics
_ "github.com/grafana/alloy/internal/component/otelcol/exporter/awss3" // Import otelcol.exporter.awss3exporter
_ "github.com/grafana/alloy/internal/component/otelcol/exporter/debug" // Import otelcol.exporter.debug
_ "github.com/grafana/alloy/internal/component/otelcol/exporter/kafka" // Import otelcol.exporter.kafka
_ "github.com/grafana/alloy/internal/component/otelcol/exporter/loadbalancing" // Import otelcol.exporter.loadbalancing
_ "github.com/grafana/alloy/internal/component/otelcol/exporter/logging" // Import otelcol.exporter.logging
Expand Down
97 changes: 97 additions & 0 deletions internal/component/otelcol/exporter/debug/debug.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package debug

import (
"fmt"

"github.com/grafana/alloy/internal/component"
"github.com/grafana/alloy/internal/component/otelcol"
otelcolCfg "github.com/grafana/alloy/internal/component/otelcol/config"
"github.com/grafana/alloy/internal/component/otelcol/exporter"
"github.com/grafana/alloy/internal/featuregate"
otelcomponent "go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/config/configtelemetry"
"go.opentelemetry.io/collector/exporter/debugexporter"
otelextension "go.opentelemetry.io/collector/extension"
)

func init() {
component.Register(component.Registration{
Name: "otelcol.exporter.debug",
Stability: featuregate.StabilityExperimental,
Args: Arguments{},
Exports: otelcol.ConsumerExports{},

Build: func(opts component.Options, args component.Arguments) (component.Component, error) {
fact := debugexporter.NewFactory()
return exporter.New(opts, fact, args.(Arguments), exporter.TypeAll)
},
})
}

type Arguments struct {
Verbosity string `alloy:"verbosity,attr,optional"`
SamplingInitial int `alloy:"sampling_initial,attr,optional"`
SamplingThereafter int `alloy:"sampling_thereafter,attr,optional"`

// DebugMetrics configures component internal metrics. Optional.
DebugMetrics otelcolCfg.DebugMetricsArguments `alloy:"debug_metrics,block,optional"`
}

func (args Arguments) convertVerbosity() (configtelemetry.Level, error) {
var verbosity configtelemetry.Level
switch args.Verbosity {
case "basic":
verbosity = configtelemetry.LevelBasic
case "normal":
verbosity = configtelemetry.LevelNormal
case "detailed":
verbosity = configtelemetry.LevelDetailed
default:
// Invalid verbosity
// debugexporter only supports basic, normal and detailed levels
return verbosity, fmt.Errorf("invalid verbosity %q", args.Verbosity)
}

return verbosity, nil
}

var _ exporter.Arguments = Arguments{}

// SetToDefault implements river.Defaulter.
func (args *Arguments) SetToDefault() {
*args = Arguments{
Verbosity: "normal",
SamplingInitial: 2,
SamplingThereafter: 500,
}
args.DebugMetrics.SetToDefault()
}

// Convert implements exporter.Arguments.
func (args Arguments) Convert() (otelcomponent.Config, error) {
verbosity, err := args.convertVerbosity()
if err != nil {
return nil, fmt.Errorf("error in conversion to config arguments, %v", err)
}

return &debugexporter.Config{
Verbosity: verbosity,
SamplingInitial: args.SamplingInitial,
SamplingThereafter: args.SamplingThereafter,
}, nil
}

// Extensions implements exporter.Arguments.
func (args Arguments) Extensions() map[otelcomponent.ID]otelextension.Extension {
return nil
}

// Exporters implements exporter.Arguments.
func (args Arguments) Exporters() map[otelcomponent.DataType]map[otelcomponent.ID]otelcomponent.Component {
return nil
}

// DebugMetricsConfig implements receiver.Arguments.
func (args Arguments) DebugMetricsConfig() otelcolCfg.DebugMetricsArguments {
return args.DebugMetrics
}
79 changes: 79 additions & 0 deletions internal/component/otelcol/exporter/debug/debug_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package debug_test

import (
"fmt"
"testing"

"github.com/grafana/alloy/internal/component/otelcol/exporter/debug"
"github.com/grafana/alloy/syntax"
"github.com/stretchr/testify/require"
otelcomponent "go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/config/configtelemetry"
"go.opentelemetry.io/collector/exporter/debugexporter"
)

func Test(t *testing.T) {
tests := []struct {
testName string
args string
expectedReturn debugexporter.Config
errorMsg string
}{
{
testName: "defaultConfig",
args: ``,
expectedReturn: debugexporter.Config{
Verbosity: configtelemetry.LevelNormal,
SamplingInitial: 2,
SamplingThereafter: 500,
},
},

{
testName: "validConfig",
args: `
verbosity = "detailed"
sampling_initial = 5
sampling_thereafter = 20
`,
expectedReturn: debugexporter.Config{
Verbosity: configtelemetry.LevelDetailed,
SamplingInitial: 5,
SamplingThereafter: 20,
},
},

{
testName: "invalidConfig",
args: `
verbosity = "test"
sampling_initial = 5
sampling_thereafter = 20
`,
errorMsg: "error in conversion to config arguments",
},
}

for _, tc := range tests {
t.Run(tc.testName, func(t *testing.T) {
var args debug.Arguments
err := syntax.Unmarshal([]byte(tc.args), &args)
require.NoError(t, err)

actualPtr, err := args.Convert()
if tc.errorMsg != "" {
require.ErrorContains(t, err, tc.errorMsg)
return
}

require.NoError(t, err)

actual := actualPtr.(*debugexporter.Config)
fmt.Printf("Passed conversion")

require.NoError(t, otelcomponent.ValidateConfig(actual))

require.Equal(t, tc.expectedReturn, *actual)
})
}
}
Loading

0 comments on commit c7e15cd

Please sign in to comment.