Skip to content

Commit 411655a

Browse files
committed
[pkg/stanza] Add 'strip_ansi_escape_codes' operator
1 parent 5d0a771 commit 411655a

File tree

10 files changed

+525
-0
lines changed

10 files changed

+525
-0
lines changed
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: 'enhancement'
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
7+
component: pkg/stanza
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Add 'strip_ansi_escape_codes' operator
11+
12+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
13+
issues: [37443]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext:
19+
20+
# If your change doesn't affect end users or the exported elements of any package,
21+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
22+
# Optional: The change log or logs in which this entry should be included.
23+
# e.g. '[user]' or '[user, api]'
24+
# Include 'user' if the change is relevant to end users.
25+
# Include 'api' if there is a change to a library API.
26+
# Default: '[user]'
27+
change_logs: []

pkg/stanza/adapter/register.go

+1
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,6 @@ import (
2929
_ "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza/operator/transformer/remove"
3030
_ "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza/operator/transformer/retain"
3131
_ "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza/operator/transformer/router"
32+
_ "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza/operator/transformer/stripansiescapecodes"
3233
_ "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza/operator/transformer/unquote"
3334
)

pkg/stanza/docs/operators/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,6 @@ General purpose:
4444
- [remove](./remove.md)
4545
- [retain](./retain.md)
4646
- [router](./router.md)
47+
- [strip_ansi_escape_codes](./strip_ansi_escape_codes.md)
4748
- [unquote](./unquote.md)
4849
- [assign_keys](./assign_keys.md)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
## `strip_ansi_escape_codes` operator
2+
3+
The `strip_ansi_escape_codes` operator removes ANSI escape sequences (such as color codes) from a string.
4+
5+
### Configuration Fields
6+
7+
| Field | Default | Description |
8+
| --- | --- | --- |
9+
| `id` | `strip_ansi_escape_codes` | A unique identifier for the operator. |
10+
| `output` | Next in pipeline | The connected operator(s) that will receive all outbound entries. |
11+
| `field` | required | The [field](../types/field.md) to strip. Must be a string. |
12+
| `on_error` | `send` | The behavior of the operator if it encounters an error. See [on_error](../types/on_error.md). |
13+
| `if` | | An [expression](../types/expression.md) that, when set, will be evaluated to determine whether this operator should be used for the given entry. This allows you to do easy conditional parsing without branching logic with routers. |
14+
15+
Right now the operator only removes the ANSI "Control Sequence Introducer (CSI)" escape codes starting with `ESC [`.
16+
17+
### Example Configurations:
18+
19+
<hr>
20+
21+
Remove all color escape codes from the body
22+
```yaml
23+
- type: strip_ansi_escape_codes
24+
field: body
25+
```
26+
27+
<table>
28+
<tr><td> Input Entry </td> <td> Output Entry </td></tr>
29+
<tr>
30+
<td>
31+
32+
```json
33+
{
34+
"resource": { },
35+
"attributes": { },
36+
"body": "\x1b[31mred\x1b[0m"
37+
}
38+
```
39+
40+
</td>
41+
<td>
42+
43+
```json
44+
{
45+
"resource": { },
46+
"attributes": { },
47+
"body": "red"
48+
}
49+
```
50+
51+
</td>
52+
</tr>
53+
</table>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package stripansiescapecodes // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza/operator/transformer/stripansiescapecodes"
5+
6+
import (
7+
"go.opentelemetry.io/collector/component"
8+
9+
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza/entry"
10+
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza/operator"
11+
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza/operator/helper"
12+
)
13+
14+
const operatorType = "strip_ansi_escape_codes"
15+
16+
func init() {
17+
operator.Register(operatorType, func() operator.Builder { return NewConfig() })
18+
}
19+
20+
// NewConfig creates a new strip_ansi_escape_codes config with default values
21+
func NewConfig() *Config {
22+
return NewConfigWithID(operatorType)
23+
}
24+
25+
// NewConfigWithID creates a new strip_ansi_escape_codes config with default values
26+
func NewConfigWithID(operatorID string) *Config {
27+
return &Config{
28+
TransformerConfig: helper.NewTransformerConfig(operatorID, operatorType),
29+
}
30+
}
31+
32+
// Config is the configuration of an strip_ansi_escape_codes operator.
33+
type Config struct {
34+
helper.TransformerConfig `mapstructure:",squash"`
35+
Field entry.Field `mapstructure:"field"`
36+
}
37+
38+
// Build will build an strip_ansi_escape_codes operator.
39+
func (c Config) Build(set component.TelemetrySettings) (operator.Operator, error) {
40+
transformerOperator, err := c.TransformerConfig.Build(set)
41+
if err != nil {
42+
return nil, err
43+
}
44+
45+
return &Transformer{
46+
TransformerOperator: transformerOperator,
47+
field: c.Field,
48+
}, nil
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
package stripansiescapecodes
4+
5+
import (
6+
"path/filepath"
7+
"testing"
8+
9+
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza/entry"
10+
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza/operator/operatortest"
11+
)
12+
13+
// test unmarshalling of values into config struct
14+
func TestUnmarshal(t *testing.T) {
15+
operatortest.ConfigUnmarshalTests{
16+
DefaultConfig: NewConfig(),
17+
TestsFile: filepath.Join(".", "testdata", "config.yaml"),
18+
Tests: []operatortest.ConfigUnmarshalTest{
19+
{
20+
Name: "strip_ansi_escape_codes_body",
21+
Expect: func() *Config {
22+
cfg := NewConfig()
23+
cfg.Field = entry.NewBodyField("nested")
24+
return cfg
25+
}(),
26+
},
27+
{
28+
Name: "strip_ansi_escape_codes_single_attribute",
29+
Expect: func() *Config {
30+
cfg := NewConfig()
31+
cfg.Field = entry.NewAttributeField("key")
32+
return cfg
33+
}(),
34+
},
35+
{
36+
Name: "strip_ansi_escape_codes_single_resource",
37+
Expect: func() *Config {
38+
cfg := NewConfig()
39+
cfg.Field = entry.NewResourceField("key")
40+
return cfg
41+
}(),
42+
},
43+
{
44+
Name: "strip_ansi_escape_codes_nested_body",
45+
Expect: func() *Config {
46+
cfg := NewConfig()
47+
cfg.Field = entry.NewBodyField("one", "two")
48+
return cfg
49+
}(),
50+
},
51+
{
52+
Name: "strip_ansi_escape_codes_nested_attribute",
53+
Expect: func() *Config {
54+
cfg := NewConfig()
55+
cfg.Field = entry.NewAttributeField("one", "two")
56+
return cfg
57+
}(),
58+
},
59+
{
60+
Name: "strip_ansi_escape_codes_nested_resource",
61+
Expect: func() *Config {
62+
cfg := NewConfig()
63+
cfg.Field = entry.NewResourceField("one", "two")
64+
return cfg
65+
}(),
66+
},
67+
},
68+
}.Run(t)
69+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package stripansiescapecodes
5+
6+
import (
7+
"testing"
8+
9+
"go.uber.org/goleak"
10+
)
11+
12+
func TestMain(m *testing.M) {
13+
goleak.VerifyTestMain(m)
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
strip_ansi_escape_codes_body:
2+
type: strip_ansi_escape_codes
3+
field: body.nested
4+
strip_ansi_escape_codes_nested_attribute:
5+
type: strip_ansi_escape_codes
6+
field: attributes.one.two
7+
strip_ansi_escape_codes_nested_body:
8+
type: strip_ansi_escape_codes
9+
field: body.one.two
10+
strip_ansi_escape_codes_nested_resource:
11+
type: strip_ansi_escape_codes
12+
field: resource.one.two
13+
strip_ansi_escape_codes_single_attribute:
14+
type: strip_ansi_escape_codes
15+
field: attributes.key
16+
strip_ansi_escape_codes_single_resource:
17+
type: strip_ansi_escape_codes
18+
field: resource.key
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package stripansiescapecodes // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza/operator/transformer/stripansiescapecodes"
5+
6+
import (
7+
"context"
8+
"fmt"
9+
"regexp"
10+
11+
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza/entry"
12+
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza/operator/helper"
13+
)
14+
15+
var ansiCsiEscapeRegex = regexp.MustCompile(`\x1B\[[\x30-\x3F]*[\x20-\x2F]*[\x40-\x7E]`)
16+
17+
// Transformer is an operator that will remove ANSI Escape Codes from a string.
18+
type Transformer struct {
19+
helper.TransformerOperator
20+
field entry.Field
21+
}
22+
23+
// Process will remove ANSI Escape Codes from a string
24+
func (t *Transformer) Process(ctx context.Context, entry *entry.Entry) error {
25+
return t.ProcessWith(ctx, entry, t.strip)
26+
}
27+
28+
// strip will remove ANSI Escape Codes from a string
29+
func (t *Transformer) strip(e *entry.Entry) error {
30+
value, ok := t.field.Get(e)
31+
if !ok {
32+
return nil
33+
}
34+
35+
switch v := value.(type) {
36+
case string:
37+
s := ansiCsiEscapeRegex.ReplaceAllString(v, "")
38+
return t.field.Set(e, s)
39+
default:
40+
return fmt.Errorf("type %T cannot be stripped", value)
41+
}
42+
}

0 commit comments

Comments
 (0)