Skip to content

Commit

Permalink
[connector/exceptions] New component (#21090)
Browse files Browse the repository at this point in the history
**Description:** Added connector to create metrics from recorded
exceptions. The implementation
is heavily inspired from
[spanmetricsconnector](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/connector/spanmetricsconnector).

**Link to tracking Issue:** #17272  

**Documentation:** Added examples in README.

---------

Co-authored-by: Juraci Paixão Kröhling <juraci.github@kroehling.de>
  • Loading branch information
marctc and jpkrohling committed Jul 19, 2023
1 parent d715dab commit 9c470a0
Show file tree
Hide file tree
Showing 29 changed files with 1,921 additions and 7 deletions.
20 changes: 20 additions & 0 deletions .chloggen/exceptions_connector.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Use this changelog template to create an entry for release notes.
# If your change doesn't affect end users, such as a test fix or a tooling change,
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: new_component

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: exceptionsconnector

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: A connector that generate metrics and logs from recorded applications exceptions from spans

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

# (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:
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ cmd/telemetrygen/ @open-telemetry/collect
confmap/provider/s3provider/ @open-telemetry/collector-contrib-approvers @Aneurysm9

connector/countconnector/ @open-telemetry/collector-contrib-approvers @djaglowski @jpkrohling
connector/exceptionsconnector/ @open-telemetry/collector-contrib-approvers @jpkrohling
connector/routingconnector/ @open-telemetry/collector-contrib-approvers @jpkrohling @kovrus @mwear
connector/servicegraphconnector/ @open-telemetry/collector-contrib-approvers @jpkrohling @mapno
connector/spanmetricsconnector/ @open-telemetry/collector-contrib-approvers @albertteoh @kovrus
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ body:
- cmd/telemetrygen
- confmap/provider/s3provider
- connector/count
- connector/exceptions
- connector/routing
- connector/servicegraph
- connector/spanmetrics
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ body:
- cmd/telemetrygen
- confmap/provider/s3provider
- connector/count
- connector/exceptions
- connector/routing
- connector/servicegraph
- connector/spanmetrics
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/other.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ body:
- cmd/telemetrygen
- confmap/provider/s3provider
- connector/count
- connector/exceptions
- connector/routing
- connector/servicegraph
- connector/spanmetrics
Expand Down
10 changes: 5 additions & 5 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ updates:
schedule:
interval: "weekly"
day: "wednesday"
- package-ecosystem: "gomod"
directory: "/connector/exceptionsconnector"
schedule:
interval: "weekly"
day: "wednesday"
- package-ecosystem: "gomod"
directory: "/connector/routingconnector"
schedule:
Expand Down Expand Up @@ -1097,8 +1102,3 @@ updates:
schedule:
interval: "weekly"
day: "wednesday"
- package-ecosystem: "gomod"
directory: "/receiver/sshcheckreceiver"
schedule:
interval: "weekly"
day: "wednesday"
2 changes: 2 additions & 0 deletions cmd/otelcontribcol/builder-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ receivers:
connectors:
- gomod: go.opentelemetry.io/collector/connector/forwardconnector v0.81.0
- gomod: github.com/open-telemetry/opentelemetry-collector-contrib/connector/countconnector v0.81.0
- gomod: github.com/open-telemetry/opentelemetry-collector-contrib/connector/exceptionsconnector v0.81.0
- gomod: github.com/open-telemetry/opentelemetry-collector-contrib/connector/routingconnector v0.81.0
- gomod: github.com/open-telemetry/opentelemetry-collector-contrib/connector/servicegraphconnector v0.81.0
- gomod: github.com/open-telemetry/opentelemetry-collector-contrib/connector/spanmetricsconnector v0.81.0
Expand Down Expand Up @@ -412,6 +413,7 @@ replaces:
- github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest => ../../pkg/pdatatest
- github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil => ../../pkg/pdatautil
- github.com/open-telemetry/opentelemetry-collector-contrib/connector/countconnector => ../../connector/countconnector
- github.com/open-telemetry/opentelemetry-collector-contrib/connector/exceptionsconnector => ../../connector/exceptionsconnector
- github.com/open-telemetry/opentelemetry-collector-contrib/connector/routingconnector => ../../connector/routingconnector
- github.com/open-telemetry/opentelemetry-collector-contrib/connector/servicegraphconnector => ../../connector/servicegraphconnector
- github.com/open-telemetry/opentelemetry-collector-contrib/connector/spanmetricsconnector => ../../connector/spanmetricsconnector
Expand Down
2 changes: 2 additions & 0 deletions cmd/otelcontribcol/components.go

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

3 changes: 3 additions & 0 deletions cmd/otelcontribcol/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ go 1.19

require (
github.com/open-telemetry/opentelemetry-collector-contrib/connector/countconnector v0.81.0
github.com/open-telemetry/opentelemetry-collector-contrib/connector/exceptionsconnector v0.81.0
github.com/open-telemetry/opentelemetry-collector-contrib/connector/routingconnector v0.81.0
github.com/open-telemetry/opentelemetry-collector-contrib/connector/servicegraphconnector v0.81.0
github.com/open-telemetry/opentelemetry-collector-contrib/connector/spanmetricsconnector v0.81.0
Expand Down Expand Up @@ -1109,6 +1110,8 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil

replace github.com/open-telemetry/opentelemetry-collector-contrib/connector/countconnector => ../../connector/countconnector

replace github.com/open-telemetry/opentelemetry-collector-contrib/connector/exceptionsconnector => ../../connector/exceptionsconnector

replace github.com/open-telemetry/opentelemetry-collector-contrib/connector/routingconnector => ../../connector/routingconnector

replace github.com/open-telemetry/opentelemetry-collector-contrib/connector/servicegraphconnector => ../../connector/servicegraphconnector
Expand Down
1 change: 1 addition & 0 deletions connector/exceptionsconnector/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include ../../Makefile.Common
115 changes: 115 additions & 0 deletions connector/exceptionsconnector/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Exceptions Connector

<!-- status autogenerated section -->
| Status | |
| ------------- |-----------|
| Distributions | [contrib] |
| Issues | ![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aopen%20label%3Aconnector%2Fexceptions%20&label=open&color=orange&logo=opentelemetry) ![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aclosed%20label%3Aconnector%2Fexceptions%20&label=closed&color=blue&logo=opentelemetry) |

[development]: https://github.com/open-telemetry/opentelemetry-collector#development
[contrib]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-contrib

## Supported Pipeline Types

| [Exporter Pipeline Type] | [Receiver Pipeline Type] | [Stability Level] |
| ------------------------ | ------------------------ | ----------------- |
| traces | metrics | [development] |
| traces | logs | [development] |

[Exporter Pipeline Type]: https://github.com/open-telemetry/opentelemetry-collector/blob/main/connector/README.md#exporter-pipeline-type
[Receiver Pipeline Type]: https://github.com/open-telemetry/opentelemetry-collector/blob/main/connector/README.md#receiver-pipeline-type
[Stability Level]: https://github.com/open-telemetry/opentelemetry-collector#stability-levels
<!-- end autogenerated section -->

## Overview

Generate metrics and logs from recorded [application exceptions](https://github.com/open-telemetry/semantic-conventions/blob/main/docs/exceptions/exceptions-spans.md/) associated with spans.

Each **metric** and **log** will have _at least_ the following dimensions:
- Service name
- Span kind
- Status code

With the provided default config, each **metric** and **log** will also have the following dimensions:
- Exception message
- Exception type

Each log will additionally have the following attributes:
- Exception stacktrace
- HTTP attributes from spans starting with `http.`.

## Configurations

If you are not already familiar with connectors, you may find it helpful to first visit the [Connectors README].

The following settings can be optionally configured:
- `dimensions`: the list of dimensions to add together with the default dimensions defined above.

Each additional dimension is defined with a `name` which is looked up in the span's collection of attributes or resource attributes.

The provided default config includes `exception.type` and `exception.message` as additional dimensions.

## Examples

The following is a simple example usage of the `exceptions` connector.

```yaml
receivers:
nop:

exporters:
nop:

connectors:
exceptions:

service:
pipelines:
traces:
receivers: [nop]
exporters: [exceptions]
metrics:
receivers: [exceptions]
exporters: [nop]
logs:
receivers: [exceptions]
exporters: [nop]
```

The following is a more complex example usage of the `exceptions` connector using Prometheus and Loki as exporters.

```yaml
receivers:
otlp:
protocols:
grpc:
http:

exporters:
prometheusremotewrite:
endpoint: http://prometheus:9090/api/v1/write
loki:
endpoint: http://loki:3100/loki/api/v1/push

connectors:
exceptions:

service:
pipelines:
traces:
receivers: [otlp]
exporters: [exceptions]
metrics:
receivers: [exceptions]
exporters: [prometheusremotewrite]
logs:
receivers: [exceptions]
exporters: [loki]
```

The full list of settings exposed for this connector are documented [here](../../connector/exceptionsconnector/config.go).
### More Examples

For more example configuration covering various other use cases, please visit the [testdata directory](../../connector/exceptionsconnector/testdata).

[Connectors README]:https://github.com/open-telemetry/opentelemetry-collector/blob/main/connector/README.md
55 changes: 55 additions & 0 deletions connector/exceptionsconnector/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package exceptionsconnector // import "github.com/open-telemetry/opentelemetry-collector-contrib/connector/exceptionsconnector"

import (
"fmt"

"go.opentelemetry.io/collector/component"
)

// Dimension defines the dimension name and optional default value if the Dimension is missing from a span attribute.
type Dimension struct {
Name string `mapstructure:"name"`
Default *string `mapstructure:"default"`
}

// Config defines the configuration options for exceptionsconnector
type Config struct {
// Dimensions defines the list of additional dimensions on top of the provided:
// - service.name
// - span.kind
// - status.code
// The dimensions will be fetched from the span's attributes. Examples of some conventionally used attributes:
// https://github.com/open-telemetry/opentelemetry-collector/blob/main/model/semconv/opentelemetry.go.
Dimensions []Dimension `mapstructure:"dimensions"`
}

var _ component.ConfigValidator = (*Config)(nil)

// Validate checks if the connector configuration is valid
func (c Config) Validate() error {
err := validateDimensions(c.Dimensions)
if err != nil {
return err
}
return nil
}

// validateDimensions checks duplicates for reserved dimensions and additional dimensions.
func validateDimensions(dimensions []Dimension) error {
labelNames := make(map[string]struct{})
for _, key := range []string{serviceNameKey, spanKindKey, statusCodeKey} {
labelNames[key] = struct{}{}
}

for _, key := range dimensions {
if _, ok := labelNames[key.Name]; ok {
return fmt.Errorf("duplicate dimension name %q", key.Name)
}
labelNames[key.Name] = struct{}{}
}

return nil
}
100 changes: 100 additions & 0 deletions connector/exceptionsconnector/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package exceptionsconnector

import (
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/confmap/confmaptest"

"github.com/open-telemetry/opentelemetry-collector-contrib/connector/exceptionsconnector/internal/metadata"
)

func TestLoadConfig(t *testing.T) {
t.Parallel()

cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml"))
require.NoError(t, err)

tests := []struct {
id component.ID
expected component.Config
}{
{
id: component.NewIDWithName(metadata.Type, "default"),
expected: createDefaultConfig(),
},
{
id: component.NewIDWithName(metadata.Type, "full"),
expected: &Config{
Dimensions: []Dimension{
{Name: exceptionTypeKey},
{Name: exceptionMessageKey},
},
},
},
}

for _, tt := range tests {
t.Run(tt.id.String(), func(t *testing.T) {
factory := NewFactory()
cfg := factory.CreateDefaultConfig()

sub, err := cm.Sub(tt.id.String())
require.NoError(t, err)
err = component.UnmarshalConfig(sub, cfg)
assert.NoError(t, err)
assert.NoError(t, component.ValidateConfig(cfg))
assert.Equal(t, tt.expected, cfg)
})
}
}

func TestValidateDimensions(t *testing.T) {
for _, tc := range []struct {
name string
dimensions []Dimension
expectedErr string
}{
{
name: "no additional dimensions",
dimensions: []Dimension{},
},
{
name: "no duplicate dimensions",
dimensions: []Dimension{
{Name: "http.service_name"},
{Name: "http.status_code"},
},
},
{
name: "duplicate dimension with reserved labels",
dimensions: []Dimension{
{Name: "service.name"},
},
expectedErr: "duplicate dimension name \"service.name\"",
},
{
name: "duplicate additional dimensions",
dimensions: []Dimension{
{Name: "service_name"},
{Name: "service_name"},
},
expectedErr: "duplicate dimension name \"service_name\"",
},
} {
t.Run(tc.name, func(t *testing.T) {
err := validateDimensions(tc.dimensions)
if tc.expectedErr != "" {
assert.EqualError(t, err, tc.expectedErr)
} else {
assert.NoError(t, err)
}
})
}
}
Loading

0 comments on commit 9c470a0

Please sign in to comment.