Skip to content

Commit 0898525

Browse files
bacherflevan-bradley
authored andcommitted
[processor/resourcedetection] Add Dynatrace resource detector (open-telemetry#37766)
<!--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 the Dynatrace detector to the resource detection processor. The Dynatrace detector loads resource information from the `dt_host_metadata.properties` file which is located in the `/var/lib/dynatrace/enrichment` (on *nix systems) or `%ProgramData%\dynatrace\enrichment` (on Windows) directories. If present in the file, the following attributes will be added: - `dt.entity.host` - `host.name` The Dynatrace detector does not require any additional configuration, other than being added to the list of detectors. <!-- Issue number (e.g. open-telemetry#1234) or full URL to issue, if applicable. --> #### Link to tracking issue Fixes open-telemetry#37577 <!--Describe what testing was performed and which tests were added.--> #### Testing Added unit tests <!--Describe the documentation added.--> #### Documentation Added a description of the detector to the readme <!--Please delete paragraphs that you did not use before submitting.--> --------- Signed-off-by: Florian Bacher <[email protected]> Co-authored-by: Evan Bradley <[email protected]>
1 parent a7db268 commit 0898525

File tree

17 files changed

+352
-1
lines changed

17 files changed

+352
-1
lines changed
Lines changed: 27 additions & 0 deletions
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: resourcedetectionprocessor
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: add the Dynatrace detector to the resource detection processor
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: [37577]
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: []

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ processor/probabilisticsamplerprocessor/ @open-telemetry
192192
processor/redactionprocessor/ @open-telemetry/collector-contrib-approvers @dmitryax @mx-psi @TylerHelmuth
193193
processor/remotetapprocessor/ @open-telemetry/collector-contrib-approvers @atoulme @jaronoff97
194194
processor/resourcedetectionprocessor/ @open-telemetry/collector-contrib-approvers @Aneurysm9 @dashpole
195+
processor/resourcedetectionprocessor/internal/dynatrace/ @open-telemetry/collector-contrib-approvers @bacherfl @evan-bradley
195196
processor/resourceprocessor/ @open-telemetry/collector-contrib-approvers @dmitryax
196197
processor/routingprocessor/ @open-telemetry/collector-contrib-approvers @jpkrohling
197198
processor/schemaprocessor/ @open-telemetry/collector-contrib-approvers @MovieStoreGuy @ankitpatel96

.github/ISSUE_TEMPLATE/bug_report.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ body:
196196
- processor/remotetap
197197
- processor/resource
198198
- processor/resourcedetection
199+
- processor/resourcedetection/internal/dynatrace
199200
- processor/routing
200201
- processor/schema
201202
- processor/span

.github/ISSUE_TEMPLATE/feature_request.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ body:
190190
- processor/remotetap
191191
- processor/resource
192192
- processor/resourcedetection
193+
- processor/resourcedetection/internal/dynatrace
193194
- processor/routing
194195
- processor/schema
195196
- processor/span

.github/ISSUE_TEMPLATE/other.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ body:
190190
- processor/remotetap
191191
- processor/resource
192192
- processor/resourcedetection
193+
- processor/resourcedetection/internal/dynatrace
193194
- processor/routing
194195
- processor/schema
195196
- processor/span

.github/ISSUE_TEMPLATE/unmaintained.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ body:
195195
- processor/remotetap
196196
- processor/resource
197197
- processor/resourcedetection
198+
- processor/resourcedetection/internal/dynatrace
198199
- processor/routing
199200
- processor/schema
200201
- processor/span

processor/resourcedetectionprocessor/README.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -582,10 +582,29 @@ processors:
582582

583583
See: [TLS Configuration Settings](https://github.com/open-telemetry/opentelemetry-collector/blob/main/config/configtls/README.md) for the full set of available options.
584584

585+
### Dynatrace
586+
587+
Loads resource information from the `dt_host_metadata.properties` file which is located in
588+
the `/var/lib/dynatrace/enrichment` (on *nix systems) or `%ProgramData%\dynatrace\enrichment` (on Windows) directories.
589+
If present in the file, the following attributes will be added:
590+
591+
- `dt.entity.host`
592+
- `host.name`
593+
594+
The Dynatrace detector does not require any additional configuration, other than being added to the list of detectors.
595+
596+
Example:
597+
598+
```yaml
599+
processors:
600+
resourcedetection/dynatrace:
601+
detectors: [dynatrace]
602+
```
603+
585604
## Configuration
586605

587606
```yaml
588-
# a list of resource detectors to run, valid options are: "env", "system", "gcp", "ec2", "ecs", "elastic_beanstalk", "eks", "lambda", "azure", "heroku", "openshift"
607+
# a list of resource detectors to run, valid options are: "env", "system", "gcp", "ec2", "ecs", "elastic_beanstalk", "eks", "lambda", "azure", "heroku", "openshift", "dynatrace"
589608
detectors: [ <string> ]
590609
# determines if existing resource attributes should be overridden or preserved, defaults to true
591610
override: <bool>

processor/resourcedetectionprocessor/doc.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
//go:generate mdatagen internal/system/metadata.yaml
1818
//go:generate mdatagen internal/k8snode/metadata.yaml
1919
//go:generate mdatagen internal/kubeadm/metadata.yaml
20+
//go:generate mdatagen internal/dynatrace/metadata.yaml
2021

2122
// package resourcedetectionprocessor implements a processor
2223
// which can be used to detect resource information from the host,

processor/resourcedetectionprocessor/factory.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor/internal/azure/aks"
2929
"github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor/internal/consul"
3030
"github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor/internal/docker"
31+
"github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor/internal/dynatrace"
3132
"github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor/internal/env"
3233
"github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor/internal/gcp"
3334
"github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor/internal/heroku"
@@ -68,6 +69,7 @@ func NewFactory() processor.Factory {
6869
openshift.TypeStr: openshift.NewDetector,
6970
k8snode.TypeStr: k8snode.NewDetector,
7071
kubeadm.TypeStr: kubeadm.NewDetector,
72+
dynatrace.TypeStr: dynatrace.NewDetector,
7173
})
7274

7375
f := &factory{
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[comment]: <> (Code generated by mdatagen. DO NOT EDIT.)
2+
3+
# dynatracedetector
4+
5+
## Resource Attributes
6+
7+
| Name | Description | Values | Enabled |
8+
| ---- | ----------- | ------ | ------- |
9+
| dt.entity.host | The Dynatrace host entity | Any Str | true |
10+
| host.name | The hostname | Any Str | true |
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
// Package dynatrace provides a detector that loads resource information from
5+
// the dt_host_metadata.properties file which is located in
6+
// the /var/lib/dynatrace/enrichment (on *nix systems) and %ProgramData%\dynatrace\enrichment
7+
// (on Windows) directories.
8+
9+
package dynatrace // import "github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor/internal/dynatrace"
10+
import (
11+
"bufio"
12+
"context"
13+
"os"
14+
"path/filepath"
15+
"runtime"
16+
"slices"
17+
"strings"
18+
19+
"go.opentelemetry.io/collector/pdata/pcommon"
20+
"go.opentelemetry.io/collector/processor"
21+
"go.uber.org/zap"
22+
23+
"github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor/internal"
24+
)
25+
26+
const TypeStr = "dynatrace"
27+
28+
const dtHostMetadataProperties = "dt_host_metadata.properties"
29+
30+
var dtHostProperties = []string{"dt.entity.host", "host.name"}
31+
32+
type Detector struct {
33+
enrichmentDirectory string
34+
logger *zap.Logger
35+
}
36+
37+
func NewDetector(set processor.Settings, _ internal.DetectorConfig) (internal.Detector, error) {
38+
enrichmentDir := "/var/lib/dynatrace/enrichment"
39+
if runtime.GOOS == "windows" {
40+
// Windows default is "%ProgramData%\dynatrace\enrichment"
41+
// If the ProgramData environment variable is not set,
42+
// it falls back to C:\ProgramData
43+
programDataDir := os.Getenv("ProgramData")
44+
if programDataDir == "" {
45+
programDataDir = `C:\ProgramData`
46+
}
47+
48+
enrichmentDir = filepath.Join(programDataDir, "dynatrace", "enrichment")
49+
}
50+
51+
return &Detector{
52+
enrichmentDirectory: enrichmentDir,
53+
logger: set.Logger,
54+
}, nil
55+
}
56+
57+
func (d Detector) Detect(_ context.Context) (pcommon.Resource, string, error) {
58+
res := pcommon.NewResource()
59+
60+
if err := d.readPropertiesFile(res.Attributes()); err != nil {
61+
return res, "", err
62+
}
63+
return res, "", nil
64+
}
65+
66+
func (d Detector) readPropertiesFile(attributes pcommon.Map) error {
67+
filePath := filepath.Join(d.enrichmentDirectory, dtHostMetadataProperties)
68+
69+
file, err := os.Open(filePath)
70+
if err != nil {
71+
if os.IsNotExist(err) {
72+
return nil
73+
}
74+
return err
75+
}
76+
77+
defer func() {
78+
_ = file.Close()
79+
}()
80+
81+
scanner := bufio.NewScanner(file)
82+
83+
for scanner.Scan() {
84+
line := scanner.Text()
85+
// split by the first "=" character. If there is another "=" afterward, this will be part of the value
86+
split := strings.SplitN(line, "=", 2)
87+
if len(split) != 2 {
88+
d.logger.Warn("Skipping line as it does not match the expected format of '<key>=<value>", zap.String("line", line))
89+
continue
90+
}
91+
key, value := split[0], split[1]
92+
93+
if key != "" && value != "" {
94+
if !slices.Contains(dtHostProperties, key) {
95+
continue
96+
}
97+
attributes.PutStr(strings.TrimSpace(key), strings.TrimSpace(value))
98+
}
99+
}
100+
101+
return scanner.Err()
102+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package dynatrace
5+
6+
import (
7+
"context"
8+
"os"
9+
"path/filepath"
10+
"runtime"
11+
"testing"
12+
13+
"github.com/stretchr/testify/require"
14+
"go.opentelemetry.io/collector/component"
15+
"go.opentelemetry.io/collector/processor"
16+
"go.uber.org/zap"
17+
)
18+
19+
const testPropertiesFile = `
20+
dt.entity.host=my-host-from-properties
21+
host.name=my-host-from-properties
22+
dt.entity.host_group=my-host-group-from-properties
23+
dt.foo=bar
24+
invalid-entry
25+
`
26+
27+
func TestDetectorNewDetector(t *testing.T) {
28+
d, err := NewDetector(processor.Settings{}, nil)
29+
30+
require.NoError(t, err)
31+
require.NotNil(t, d)
32+
33+
if runtime.GOOS == "windows" {
34+
t.Setenv("ProgramData", "C:\\ProgramData")
35+
require.Equal(t, "C:\\ProgramData\\dynatrace\\enrichment", d.(*Detector).enrichmentDirectory)
36+
} else {
37+
require.Equal(t, "/var/lib/dynatrace/enrichment", d.(*Detector).enrichmentDirectory)
38+
}
39+
}
40+
41+
func TestDetector_DetectFromProperties(t *testing.T) {
42+
d, err := NewDetector(processor.Settings{
43+
TelemetrySettings: component.TelemetrySettings{
44+
Logger: zap.NewNop(),
45+
},
46+
}, nil)
47+
48+
require.NoError(t, err)
49+
50+
tempDir := t.TempDir()
51+
52+
require.NoError(t, createTestFile(tempDir, dtHostMetadataProperties, testPropertiesFile))
53+
d.(*Detector).enrichmentDirectory = tempDir
54+
55+
resource, _, err := d.Detect(context.Background())
56+
57+
require.NoError(t, err)
58+
require.NotNil(t, resource)
59+
require.Equal(t, 2, resource.Attributes().Len())
60+
61+
get, ok := resource.Attributes().Get("dt.entity.host")
62+
require.True(t, ok)
63+
require.Equal(t, "my-host-from-properties", get.Str())
64+
65+
get, ok = resource.Attributes().Get("host.name")
66+
require.True(t, ok)
67+
require.Equal(t, "my-host-from-properties", get.Str())
68+
69+
// verify that we do not take any additional properties
70+
_, ok = resource.Attributes().Get("dt.entity.host_group")
71+
require.False(t, ok)
72+
}
73+
74+
func TestDetector_DetectNoFileAvailable(t *testing.T) {
75+
d, err := NewDetector(processor.Settings{
76+
TelemetrySettings: component.TelemetrySettings{
77+
Logger: zap.NewNop(),
78+
},
79+
}, nil)
80+
81+
require.NoError(t, err)
82+
83+
tempDir := t.TempDir()
84+
85+
d.(*Detector).enrichmentDirectory = tempDir
86+
87+
resource, _, err := d.Detect(context.Background())
88+
89+
require.NoError(t, err)
90+
require.NotNil(t, resource)
91+
require.Equal(t, 0, resource.Attributes().Len())
92+
}
93+
94+
func createTestFile(directory, name, content string) error {
95+
return os.WriteFile(filepath.Join(directory, name), []byte(content), 0o600)
96+
}

processor/resourcedetectionprocessor/internal/dynatrace/generated_component_test.go

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

processor/resourcedetectionprocessor/internal/dynatrace/generated_package_test.go

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

processor/resourcedetectionprocessor/internal/dynatrace/internal/metadata/generated_status.go

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)