├── .chloggen ├── TEMPLATE.yaml ├── jade-guiton-dd_fix-cumul-to-delta-initial-value.yaml ├── mackjmr_send-host-metadata-new-host.yaml └── mackjmr_support-host-aliases.yaml ├── .copyright-overrides.yml ├── .github ├── CODEOWNERS ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yaml └── workflows │ ├── changelog.yml │ ├── check_links_config.json │ └── test.yaml ├── .gitignore ├── .golangci.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── LICENSE-3rdparty.csv ├── Makefile ├── NOTICE ├── README.md ├── docs └── dev │ └── release.md ├── internal ├── scripts │ ├── apidiff-compare.sh │ └── apidiff-generate.sh └── tools │ ├── doc.go │ ├── generate-license-file │ ├── findheader.go │ ├── go.mod │ ├── go.sum │ ├── main.go │ └── override.go │ ├── go.mod │ ├── go.sum │ └── tools.go ├── pkg ├── inframetadata │ ├── doc.go │ ├── go.mod │ ├── go.sum │ ├── gohai │ │ ├── gohai.go │ │ ├── internal │ │ │ └── gohaitest │ │ │ │ ├── go.mod │ │ │ │ ├── go.sum │ │ │ │ └── gohai_test.go │ │ └── processes.go │ ├── internal │ │ ├── hostmap │ │ │ ├── constants.go │ │ │ ├── errors.go │ │ │ ├── hostmap.go │ │ │ ├── hostmap_test.go │ │ │ ├── tags.go │ │ │ └── tags_test.go │ │ └── testutils │ │ │ └── resource.go │ ├── payload │ │ └── payload.go │ ├── reporter.go │ └── reporter_test.go ├── internal │ └── sketchtest │ │ ├── go.mod │ │ ├── go.sum │ │ ├── quantile.go │ │ └── quantile_test.go ├── otlp │ ├── attributes │ │ ├── attributes.go │ │ ├── attributes_test.go │ │ ├── azure │ │ │ ├── azure.go │ │ │ └── azure_test.go │ │ ├── ec2 │ │ │ ├── ec2.go │ │ │ └── ec2_test.go │ │ ├── gateway_usage.go │ │ ├── gateway_usage_test.go │ │ ├── gcp │ │ │ ├── gcp.go │ │ │ └── gcp_test.go │ │ ├── go.mod │ │ ├── go.sum │ │ ├── internal │ │ │ └── testutils │ │ │ │ └── test_utils.go │ │ ├── process.go │ │ ├── process_test.go │ │ ├── source.go │ │ ├── source │ │ │ └── source_provider.go │ │ ├── source_test.go │ │ ├── system.go │ │ ├── system_test.go │ │ ├── translator.go │ │ └── translator_test.go │ ├── logs │ │ ├── doc.go │ │ ├── go.mod │ │ ├── go.sum │ │ ├── transform.go │ │ ├── transform_test.go │ │ └── translator.go │ └── metrics │ │ ├── config.go │ │ ├── consumer.go │ │ ├── dimensions.go │ │ ├── dimensions_test.go │ │ ├── exponential_histograms_translator.go │ │ ├── go.mod │ │ ├── go.sum │ │ ├── histograms_test.go │ │ ├── internal │ │ ├── instrumentationlibrary │ │ │ ├── metadata.go │ │ │ └── metadata_test.go │ │ ├── instrumentationscope │ │ │ ├── metadata.go │ │ │ └── metadata_test.go │ │ └── utils │ │ │ ├── tags.go │ │ │ └── tags_test.go │ │ ├── kafka_mapping.go │ │ ├── metrics_remapping.go │ │ ├── metrics_remapping_test.go │ │ ├── metrics_translator.go │ │ ├── metrics_translator_benchmark_test.go │ │ ├── metrics_translator_test.go │ │ ├── mixed_metrics_test.go │ │ ├── nan_metrics_test.go │ │ ├── origin.go │ │ ├── origin_test.go │ │ ├── runtime_metric_mappings.go │ │ ├── sketches_test.go │ │ ├── statspayload.go │ │ ├── statspayload_test.go │ │ ├── summary_test.go │ │ ├── testdata │ │ ├── datadogdata │ │ │ ├── histogram │ │ │ │ ├── empty-cumulative-exponential.json │ │ │ │ ├── empty-delta-exponential.json │ │ │ │ ├── empty-delta-no-min-max.json │ │ │ │ ├── empty-delta-with-min-max.json │ │ │ │ ├── simple-cumulative_counters-cs.json │ │ │ │ ├── simple-cumulative_counters-nocs.json │ │ │ │ ├── simple-cumulative_dist-cs.json │ │ │ │ ├── simple-cumulative_dist-nocs.json │ │ │ │ ├── simple-cumulative_nobuckets-cs.json │ │ │ │ ├── simple-delta-min-max_dist-nocs.json │ │ │ │ ├── simple-delta_counters-cs.json │ │ │ │ ├── simple-delta_counters-nocs.json │ │ │ │ ├── simple-delta_dist-cs.json │ │ │ │ ├── simple-delta_dist-nocs.json │ │ │ │ ├── simple-delta_nobuckets-cs.json │ │ │ │ ├── simple-exponential.json │ │ │ │ ├── simple-exponential_all.json │ │ │ │ ├── simple-exponential_cs-both-tags.json │ │ │ │ ├── simple-exponential_cs-ilmd-tags.json │ │ │ │ ├── simple-exponential_cs.json │ │ │ │ ├── simple-exponential_ilmd-tags.json │ │ │ │ ├── simple-exponential_ismd-tags.json │ │ │ │ ├── simple-exponential_res-ilmd-tags.json │ │ │ │ ├── simple-exponential_res-tags.json │ │ │ │ ├── single-bucket-delta-no-min-max.json │ │ │ │ ├── single-bucket-delta-with-min-max.json │ │ │ │ ├── static-cumulative_dist-cs.json │ │ │ │ └── zero-delta_dist-cs.json │ │ │ ├── mixed │ │ │ │ ├── simple.json │ │ │ │ ├── simple_all.json │ │ │ │ ├── simple_cs-both-tags.json │ │ │ │ ├── simple_cs-ilmd-tags.json │ │ │ │ ├── simple_cs.json │ │ │ │ ├── simple_drop.json │ │ │ │ ├── simple_ilmd-tags.json │ │ │ │ ├── simple_ismd-tags.json │ │ │ │ ├── simple_keep.json │ │ │ │ ├── simple_res-ilmd-tags.json │ │ │ │ └── simple_res-tags.json │ │ │ ├── origin │ │ │ │ └── origin.json │ │ │ ├── source │ │ │ │ └── simple.json │ │ │ └── summary │ │ │ │ ├── simple_summary-with-quantile.json │ │ │ │ ├── simple_summary.json │ │ │ │ ├── simple_summary_cumsum-keep.json │ │ │ │ ├── with-attributes-quantile_summary.json │ │ │ │ └── with-attributes_summary.json │ │ └── otlpdata │ │ │ ├── histogram │ │ │ ├── empty-cumulative-exponential.json │ │ │ ├── empty-delta-exponential.json │ │ │ ├── empty-delta-no-min-max.json │ │ │ ├── empty-delta-with-min-max.json │ │ │ ├── simple-cumulative.json │ │ │ ├── simple-delta-min-max.json │ │ │ ├── simple-delta.json │ │ │ ├── simple-exponential.json │ │ │ ├── single-bucket-delta-no-min-max.json │ │ │ ├── single-bucket-delta-with-min-max.json │ │ │ ├── static-cumulative.json │ │ │ └── zero-delta.json │ │ │ ├── mixed │ │ │ └── simple.json │ │ │ ├── origin │ │ │ └── origin.json │ │ │ ├── source │ │ │ └── simple.json │ │ │ └── summary │ │ │ ├── simple.json │ │ │ └── with-attributes.json │ │ ├── testhelper_test.go │ │ ├── ttlcache.go │ │ └── ttlcache_test.go └── quantile │ ├── agent.go │ ├── agent_test.go │ ├── bin.go │ ├── bin_test.go │ ├── config.go │ ├── config_test.go │ ├── ddsketch.go │ ├── ddsketch_test.go │ ├── go.mod │ ├── go.sum │ ├── key.go │ ├── main_test.go │ ├── pool.go │ ├── print.go │ ├── print_test.go │ ├── sparse.go │ ├── sparse_test.go │ ├── store.go │ ├── store_test.go │ ├── summary │ ├── equal.go │ ├── equal_test.go │ ├── summary.go │ └── summary_test.go │ └── test_helper.go └── versions.yaml /.chloggen/TEMPLATE.yaml: -------------------------------------------------------------------------------- 1 | # One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' 2 | change_type: 3 | 4 | # The name of the component (e.g. pkg/quantile) 5 | component: 6 | 7 | # A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). 8 | note: 9 | 10 | # The PR related to this change 11 | issues: [] 12 | 13 | # (Optional) One or more lines of additional information to render under the primary note. 14 | # These lines will be padded with 2 spaces and then inserted directly into the document. 15 | # Use pipe (|) for multiline entries. 16 | subtext: 17 | -------------------------------------------------------------------------------- /.chloggen/jade-guiton-dd_fix-cumul-to-delta-initial-value.yaml: -------------------------------------------------------------------------------- 1 | # One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' 2 | change_type: 'bug_fix' 3 | 4 | # The name of the component (e.g. pkg/quantile) 5 | component: pkg/otlp/metrics 6 | 7 | # A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). 8 | note: Fix automatic intial point dropping when converting cumulative monotonic sum metrics 9 | 10 | # The PR related to this change 11 | issues: [654] 12 | 13 | # (Optional) One or more lines of additional information to render under the primary note. 14 | # These lines will be padded with 2 spaces and then inserted directly into the document. 15 | # Use pipe (|) for multiline entries. 16 | subtext: | 17 | This may fix issues with large spikes on Agent/Collector restarts. 18 | -------------------------------------------------------------------------------- /.chloggen/mackjmr_send-host-metadata-new-host.yaml: -------------------------------------------------------------------------------- 1 | # One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' 2 | change_type: bug_fix 3 | 4 | # The name of the component (e.g. pkg/quantile) 5 | component: pkg/infradata 6 | 7 | # A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). 8 | note: Send host meta on new hosts. 9 | 10 | # The PR related to this change 11 | issues: [652] 12 | 13 | # (Optional) One or more lines of additional information to render under the primary note. 14 | # These lines will be padded with 2 spaces and then inserted directly into the document. 15 | # Use pipe (|) for multiline entries. 16 | subtext: 17 | -------------------------------------------------------------------------------- /.chloggen/mackjmr_support-host-aliases.yaml: -------------------------------------------------------------------------------- 1 | # One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' 2 | change_type: enhancement 3 | 4 | # The name of the component (e.g. pkg/quantile) 5 | component: pkg/inframetadata 6 | 7 | # A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). 8 | note: Add support for host aliasing via resource attribute datadog.host.aliases 9 | 10 | # The PR related to this change 11 | issues: [661] 12 | 13 | # (Optional) One or more lines of additional information to render under the primary note. 14 | # These lines will be padded with 2 spaces and then inserted directly into the document. 15 | # Use pipe (|) for multiline entries. 16 | subtext: 17 | -------------------------------------------------------------------------------- /.copyright-overrides.yml: -------------------------------------------------------------------------------- 1 | # this file contains overrides for the automatic determination of copyright 2 | # implemented in tasks/licenses.py. The table keys are the golang package name 3 | # or pattern, and the value is either a copyright owner or a list of owners. 4 | # 5 | # Package patterns may end with a `*`. When multiple patterns match, the longer 6 | # is preferred. 7 | # 8 | # Copyright statements should reflect the copyright exactly as it is shown on 9 | # the original component. 10 | 11 | go.opentelemetry.io/*: Copyright The OpenTelemetry Authors 12 | contrib.go.opencensus.io/*: Copyright 2017, OpenCensus Authors 13 | google.golang.org/genproto/*: Copyright 2020 Google LLC 14 | github.com/DataDog/opentelemetry-mapping-go/*: Copyright [2023-Present] Datadog, Inc. 15 | github.com/modern-go/concurrent: Copyright (c) 2018 Tao Wen 16 | github.com/modern-go/reflect2: Copyright (c) 2018 Tao Wen 17 | github.com/go-logr/logr: "Copyright 2019 The logr Authors." 18 | github.com/go-logr/stdr: "Copyright 2019 The logr Authors." 19 | github.com/golang/groupcache: Copyright 2012 Google Inc. 20 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/about-codeowners/ for syntax 2 | 3 | * @DataDog/opentelemetry 4 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### What does this PR do? 2 | 3 | 8 | 9 | ### Motivation 10 | 11 | 15 | 16 | -------------------------------------------------------------------------------- /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gomod 4 | directories: 5 | - /pkg/otlp/metrics 6 | - /pkg/otlp/attributes 7 | - /pkg/quantile 8 | - /pkg/internal/sketchtest 9 | - /pkg/inframetadata 10 | - /pkg/otlp/logs 11 | labels: 12 | - dependencies 13 | schedule: 14 | interval: weekly 15 | ignore: 16 | # Ignore internal modules 17 | - dependency-name: github.com/DataDog/opentelemetry-mapping-go/* 18 | open-pull-requests-limit: 100 19 | - package-ecosystem: gomod 20 | directory: /internal/tools 21 | labels: 22 | - dependencies 23 | schedule: 24 | interval: monthly 25 | - package-ecosystem: gomod 26 | directory: /internal/tools/generate-license-file 27 | labels: 28 | - dependencies 29 | schedule: 30 | interval: monthly 31 | - package-ecosystem: "github-actions" 32 | directory: "/" 33 | labels: 34 | - dependencies 35 | schedule: 36 | interval: "monthly" 37 | -------------------------------------------------------------------------------- /.github/workflows/check_links_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "ignorePatterns": [ 3 | { 4 | "pattern": "http(s)?://\\d+\\.\\d+\\.\\d+\\.\\d+" 5 | }, 6 | { 7 | "pattern": "http(s)?://localhost" 8 | }, 9 | { 10 | "pattern": "http(s)?://example.com" 11 | }, 12 | { 13 | "pattern": "^#" 14 | } 15 | ], 16 | "aliveStatusCodes": [429, 200] 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | 17 | # IDE files 18 | .idea/ 19 | 20 | # OS files 21 | .DS_Store 22 | 23 | # VS code files 24 | .vscode/ 25 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | issues: 2 | # Do not limit the number of issues per linter. 3 | max-issues-per-linter: 0 4 | 5 | # Do not limit the number of times a same issue is reported. 6 | max-same-issues: 0 7 | exclude-rules: 8 | # Exclude some linters from running on tests files. 9 | - path: '(.+)_test\.go' 10 | linters: 11 | - errcheck 12 | - text: "appendAssign" 13 | linters: gocritic 14 | 15 | linters: 16 | disable-all: true 17 | enable: 18 | - bodyclose 19 | - depguard 20 | - errcheck 21 | - errorlint 22 | - copyloopvar 23 | - gofmt 24 | - govet 25 | - ineffassign 26 | - misspell 27 | - revive 28 | - staticcheck 29 | - usetesting 30 | - unconvert 31 | - unparam 32 | - unused 33 | 34 | linters-settings: 35 | depguard: 36 | include-go-root: true 37 | packages-with-error-message: 38 | - sync/atomic: "Use go.uber.org/atomic instead" 39 | rules: 40 | main: 41 | files: 42 | - $all 43 | allow: 44 | - $gostd 45 | - go.opentelemetry.io/otel 46 | - go.opentelemetry.io/collector 47 | - github.com/open-telemetry/opentelemetry-collector-contrib 48 | - github.com/DataDog/datadog-agent 49 | - github.com/DataDog/datadog-api-client-go 50 | - github.com/DataDog/gohai 51 | - github.com/DataDog/opentelemetry-mapping-go 52 | - github.com/DataDog/sketches-go 53 | - github.com/dustin/go-humanize 54 | - github.com/golang 55 | - github.com/lightstep/go-expohisto 56 | - github.com/patrickmn/go-cache 57 | - github.com/stretchr/testify 58 | - google.golang.org/protobuf/proto 59 | - golang.org/x/exp 60 | - gopkg.in/yaml.v3 61 | - go.uber.org 62 | 63 | staticcheck: 64 | go: "1.23" 65 | checks: ["all", 66 | "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022", # These ones are disabled by default on staticcheck 67 | "-ST1013", # Use HTTP code enums instead of integers 68 | "-SA1019", "-SA4011", "-SA4031" # Disabling these to re-enable golanci-lint default tests 69 | ] 70 | gofmt: 71 | simplify: true 72 | 73 | govet: 74 | check-shadowing: true 75 | 76 | revive: 77 | rules: 78 | - name: package-comments 79 | disabled: true 80 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | All Go modules in this repository are officially supported for usage in Datadog products only. 4 | 5 | ## Submitting issues 6 | 7 | This repository contains Go modules used in the implementation of the Datadog Agent and the OpenTelemetry Collector Datadog components. Whenever possible, prefer reporting the issue directly on the upstream issue trackers instead. 8 | 9 | ## Pull requests 10 | 11 | To submit a pull request on this repository, we assume you have installed a supported Go compiler and the `make` utility. Additional tooling can be installed by running `make install-tools`. 12 | 13 | When submitting a pull request, take into account the following guidelines: 14 | 15 | - Ensure your contribution is properly tested and that tests pass locally. 16 | - Open your PR against the `main` branch. 17 | - If your PR results in user-facing changes, add a changelog note with `make chlog-new`. 18 | 19 | ## Releasing a new version 20 | 21 | See [docs/dev/release.md][1] for instructions on how to do a new release. 22 | 23 | [1]: docs/dev/release.md 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL = /bin/bash 2 | TOOLS_MOD_DIR := $(CURDIR)/internal/tools 3 | GOTEST_OPT?= -race -timeout 2400s 4 | 5 | .PHONY: install-tools 6 | install-tools: 7 | cd $(TOOLS_MOD_DIR) && go install go.opentelemetry.io/build-tools/chloggen 8 | cd $(TOOLS_MOD_DIR) && go install go.opentelemetry.io/build-tools/multimod 9 | cd $(TOOLS_MOD_DIR) && go install github.com/frapposelli/wwhrd 10 | cd $(TOOLS_MOD_DIR) && go install github.com/golangci/golangci-lint/cmd/golangci-lint 11 | cd $(TOOLS_MOD_DIR) && go install golang.org/x/exp/cmd/apidiff@v0.0.0-20250106191152-7588d65b2ba8 12 | cd $(TOOLS_MOD_DIR) && go install gotest.tools/gotestsum 13 | cd $(TOOLS_MOD_DIR)/generate-license-file && go install . 14 | 15 | FILENAME?=$(shell git branch --show-current).yaml 16 | .PHONY: chlog-new 17 | chlog-new: 18 | chloggen new --filename $(FILENAME) 19 | 20 | .PHONY: chlog-validate 21 | chlog-validate: 22 | chloggen validate 23 | 24 | .PHONY: chlog-preview 25 | chlog-preview: 26 | chloggen update --dry 27 | 28 | GOMODULES := $(shell find . -type f -name "go.mod" -exec dirname {} \; | sort | egrep '^./' ) 29 | 30 | .PHONY: $(GOMODULES) 31 | $(GOMODULES): 32 | @echo "Running '$(CMD)' in module '$@'" 33 | cd $@ && $(CMD) 34 | 35 | # Run CMD for all modules 36 | .PHONY: for-all 37 | for-all: $(GOMODULES) 38 | 39 | # Tidy go.mod/go.sum for all modules 40 | .PHONY: tidy 41 | tidy: 42 | @$(MAKE) for-all CMD="go mod tidy -compat=1.23" 43 | 44 | # Format code for all modules 45 | .PHONY: fmt 46 | fmt: 47 | @$(MAKE) for-all CMD="gofmt -w -s ./" 48 | 49 | # Run unit test suite for all modules 50 | .PHONY: test 51 | test: 52 | @$(MAKE) for-all CMD="gotestsum -- $(GOTEST_OPT) ./..." 53 | 54 | .PHONY: test-junit 55 | test-junit: 56 | mkdir -p $(TOOLS_MOD_DIR)/junit 57 | COUNT=0 58 | set -e; for mod in $(GOMODULES); do \ 59 | COUNT=$$((COUNT+1)); \ 60 | cd $$mod && gotestsum --junitfile $(CURDIR)/testresults/$$COUNT-junit.xml -- $(GOTEST_OPT) -coverprofile=coverage.cover -covermode=atomic ./... && cd -; \ 61 | done 62 | 63 | # Run linters for all modules 64 | # Use 'make lint OPTS="--fix"' to autofix issues. 65 | .PHONY: lint 66 | lint: 67 | @$(MAKE) for-all CMD="golangci-lint run ./... $(OPTS)" 68 | 69 | # Generate licenses file for compliance. 70 | .PHONY: gen-licenses 71 | gen-licenses: 72 | generate-license-file 73 | 74 | # Do PR for preparing a release 75 | .PHONY: prerelease 76 | prerelease: 77 | multimod verify && multimod prerelease -m pkgs 78 | 79 | # Push tags 80 | .PHONY: push-tags 81 | push-tags: 82 | multimod verify 83 | set -e; for tag in `multimod tag -m pkgs -c HEAD --print-tags | grep -v "Using" `; do \ 84 | echo "pushing tag $${tag}"; \ 85 | git push git@github.com:DataDog/opentelemetry-mapping-go.git $${tag}; \ 86 | done; 87 | 88 | APIHEADERS := internal/apidiff-data 89 | 90 | .PHONY: apidiff-generate 91 | apidiff-generate: 92 | set -e; for mod in $(GOMODULES); do \ 93 | ./internal/scripts/apidiff-generate.sh $$mod $(APIHEADERS); \ 94 | done 95 | 96 | .PHONY: apidiff-compare 97 | apidiff-compare: 98 | set -e; for mod in $(GOMODULES); do \ 99 | ./internal/scripts/apidiff-compare.sh $$mod $(APIHEADERS); \ 100 | done 101 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Datadog opentelemetry-mapping-go 2 | Copyright [2023-Present] Datadog, Inc. 3 | 4 | This product includes software developed at Datadog ( 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # opentelemetry-mapping-go 2 | 3 | This repository contains Go modules that implement [OpenTelemetry][1]-to-Datadog mapping for all telemetry signals as well as for semantic conventions. 4 | 5 | These modules are used internally by Datadog in the [Datadog Agent OTLP ingest][2] and [OpenTelemetry Collector Datadog Exporter][3] implementations as well as related features, to ensure a consistent mapping between the two formats on all Datadog products. If building a new Datadog product that accepts telemetry in the [OTLP format][5], use the modules on this repository to convert to the Datadog public API format. 6 | 7 | ## Getting started 8 | 9 | To get started contributing, clone this repository locally and check [CONTRIBUTING.md][4] for instructions on how to set up your development environment and send patches. You will need a supported Go compiler and the `make` utility for local testing and development. 10 | 11 | [1]: https://opentelemetry.io 12 | [2]: https://docs.datadoghq.com/opentelemetry/otlp_ingest_in_the_agent 13 | [3]: https://docs.datadoghq.com/opentelemetry/otel_collector_datadog_exporter 14 | [4]: CONTRIBUTING.md 15 | [5]: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/otlp.md 16 | -------------------------------------------------------------------------------- /docs/dev/release.md: -------------------------------------------------------------------------------- 1 | # How to release a new version of all modules 2 | 3 | On a release, all modules will be tagged with the same version. We use [`multimod`][1] for releases. 4 | As a prerequisite, run `make install-tools` to ensure the necessary tooling is available. 5 | 6 | To make a new release, follow these steps: 7 | 8 | 0. Make sure CI passes on [main](https://github.com/DataDog/opentelemetry-mapping-go/actions/workflows/test.yaml?query=branch%3Amain) and there are no release blockers. 9 | 1. Choose the new version number, `${VERSION}`. We follow semantic versioning and are currently doing `v0.x.y` releases. 10 | 2. Checkout to a new branch. 11 | 3. Update the version number on `versions.yaml` and commit the changes. 12 | 4. Run `chloggen update -v ${VERSION}` to update the changelog and commit the changes. 13 | 5. Run `make prerelease` and checkout to the branch created by this step. Open a PR to `main` from this branch and get it merged. 14 | 6. Checkout and pull the main branch locally. Run `git show HEAD` and make sure that it points to the commit from the previously merged PR. 15 | 7. Run `make push-tags` to push the tags. 16 | 8. Check that the new version is available on the [Github repository](https://github.com/DataDog/opentelemetry-mapping-go/tags). 17 | 18 | ## What to do if something goes wrong 19 | 20 | If something goes wrong, it is important that you **do not remove or modify a tag once it has been pushed to Github**. 21 | 22 | Instead, follow these steps: 23 | 24 | 1. Add a [`retract` directive][2] to all affected `go.mod` files, open a PR and commit it. 25 | 2. Fix the issue(s) in the release and commit the changes. 26 | 3. Follow the usual release process. 27 | 28 | This will make tooling and bots ignore the retracted version. 29 | If a project has already updated to the new version, you may want to notify them directly. 30 | 31 | [1]: https://github.com/open-telemetry/opentelemetry-go-build-tools/tree/main/multimod 32 | [2]: https://go.dev/ref/mod#go-mod-file-retract 33 | -------------------------------------------------------------------------------- /internal/scripts/apidiff-compare.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Usage: apidiff-compare.sh 3 | # 4 | # Compares current API with saved API in gcexport data files 5 | # for all non-internal packages in the module. 6 | set -eo pipefail 7 | 8 | pushd "$1" > /dev/null 9 | for pkg in $(go list ./...); do 10 | if [[ "$pkg" =~ .*"/internal/".* ]]; then 11 | # Internal packages don't have breaking changes 12 | continue 13 | fi 14 | GCEXPORT_DIR=$2/$pkg 15 | changes=$(apidiff "$GCEXPORT_DIR/apidiff.state" "$pkg") 16 | if [[ -n "$changes" ]]; then 17 | echo "Changes for $pkg:" 18 | echo "$changes" 19 | fi 20 | 21 | # Search for "Incompatible changes:" header 22 | if [[ "$changes" =~ .*"Incompatible changes:".* ]]; then 23 | echo "Incompatible changes found!" 24 | exit 1 25 | fi 26 | done 27 | popd > /dev/null 28 | -------------------------------------------------------------------------------- /internal/scripts/apidiff-generate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Usage: apidiff-generate.sh 3 | # 4 | # Generates apidiff-compatible gcexport data files 5 | # for all non-internal packages in the module. 6 | 7 | set -eo pipefail 8 | 9 | pushd "$1" > /dev/null 10 | for pkg in $(go list ./...); do 11 | if [[ "$pkg" =~ .*"/internal/".* ]]; then 12 | # Internal packages don't have breaking changes 13 | continue 14 | fi 15 | 16 | # Write gcexport data into $2//apidiff.state 17 | OUT_DIR=$2/$pkg 18 | mkdir -p "$OUT_DIR" 19 | apidiff -w "$OUT_DIR/apidiff.state" "$pkg" 20 | done 21 | popd > /dev/null 22 | -------------------------------------------------------------------------------- /internal/tools/doc.go: -------------------------------------------------------------------------------- 1 | // Unless explicitly stated otherwise all files in this repository are licensed 2 | // under the Apache License Version 2.0. 3 | // This product includes software developed at Datadog (https://www.datadoghq.com/). 4 | // Copyright 2023-present Datadog, Inc. 5 | 6 | // package tools is an auxiliary package for tracking tooling version. 7 | package tools 8 | -------------------------------------------------------------------------------- /internal/tools/generate-license-file/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/DataDog/opentelemetry-mapping-go/internal/tools/generate-license-file 2 | 3 | go 1.23 4 | 5 | require gopkg.in/yaml.v3 v3.0.1 6 | -------------------------------------------------------------------------------- /internal/tools/generate-license-file/go.sum: -------------------------------------------------------------------------------- 1 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 2 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 3 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 4 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 5 | -------------------------------------------------------------------------------- /internal/tools/generate-license-file/override.go: -------------------------------------------------------------------------------- 1 | // Unless explicitly stated otherwise all files in this repository are licensed 2 | // under the Apache License Version 2.0. 3 | // This product includes software developed at Datadog (https://www.datadoghq.com/). 4 | // Copyright 2023-present Datadog, Inc. 5 | 6 | package main 7 | 8 | import ( 9 | "fmt" 10 | "os" 11 | "path/filepath" 12 | 13 | "gopkg.in/yaml.v3" 14 | ) 15 | 16 | type CopyrightOverride struct { 17 | dependencies map[string]string 18 | } 19 | 20 | const ( 21 | overrideFilePath = ".copyright-overrides.yml" 22 | ) 23 | 24 | var globalOverrides *CopyrightOverride 25 | 26 | func init() { 27 | var err error 28 | if globalOverrides, err = NewOverrideFromFile(overrideFilePath); err != nil { 29 | panic(fmt.Sprintf("Failed to load overrides: %s", err)) 30 | } 31 | } 32 | 33 | func (c *CopyrightOverride) CopyrightNotice(dependency string) ([]string, bool) { 34 | for pattern, notice := range c.dependencies { 35 | if ok, err := filepath.Match(pattern, dependency); err != nil { 36 | // Bad pattern, should never happen 37 | panic(err) 38 | } else if ok { 39 | return []string{notice}, true 40 | } 41 | } 42 | return nil, false 43 | } 44 | 45 | func NewOverrideFromFile(path string) (*CopyrightOverride, error) { 46 | //nolint:gosec // (G304) Path is fixed. 47 | data, err := os.ReadFile(path) 48 | if err != nil { 49 | return nil, fmt.Errorf("failed to read %q: %w", path, err) 50 | } 51 | 52 | overrides := &CopyrightOverride{ 53 | dependencies: map[string]string{}, 54 | } 55 | 56 | if err = yaml.Unmarshal(data, &overrides.dependencies); err != nil { 57 | return nil, fmt.Errorf("failed to unmarshal %q: %w", path, err) 58 | } 59 | 60 | return overrides, nil 61 | } 62 | -------------------------------------------------------------------------------- /internal/tools/tools.go: -------------------------------------------------------------------------------- 1 | // Unless explicitly stated otherwise all files in this repository are licensed 2 | // under the Apache License Version 2.0. 3 | // This product includes software developed at Datadog (https://www.datadoghq.com/). 4 | // Copyright 2023-present Datadog, Inc. 5 | 6 | //go:build tools 7 | // +build tools 8 | 9 | package tools 10 | 11 | // These imports are used to track test and build tool dependencies. 12 | // This is the currently recommended approach: https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module 13 | 14 | import ( 15 | _ "github.com/frapposelli/wwhrd" 16 | _ "github.com/golangci/golangci-lint/cmd/golangci-lint" 17 | _ "go.opentelemetry.io/build-tools/chloggen" 18 | _ "go.opentelemetry.io/build-tools/multimod" 19 | _ "golang.org/x/exp/cmd/apidiff" 20 | _ "gotest.tools/gotestsum" 21 | ) 22 | -------------------------------------------------------------------------------- /pkg/inframetadata/doc.go: -------------------------------------------------------------------------------- 1 | // Unless explicitly stated otherwise all files in this repository are licensed 2 | // under the Apache License Version 2.0. 3 | // This product includes software developed at Datadog (https://www.datadoghq.com/). 4 | // Copyright 2023-present Datadog, Inc. 5 | 6 | // package inframetadata handles host metadata and infrastructure list related features. It stores the host metadata and gohai payload definitions as well as the `Reporter` implementation. 7 | // 8 | // A `Reporter` keeps a `HostMap` (a map of hostnames to host metadata payloads) and periodically clears it out and reports the information using a `Pusher` 9 | // 10 | // The `Reporter` has three public methods: 11 | // - The `Run() error` and `Stop()` methods manage its lifecycle 12 | // - The `ConsumeResource(pcommon.Resource) (bool, error)` method ingests resources, updates host metadata payloads, and reports whether any changes or errors occurred during processing. 13 | // 14 | // Internally, the `Reporter` manages a `HostMap`, which has two public methods: 15 | // - The `Update(host string, resource pcommon.Resource) (changed bool, err error)` method updates a hosts information and reports whether any changes or errors occurred during processing. 16 | // - The `Extract() map[string]payloads.HostMetadata` method clears out the `HostMap` and returns a copy of its internal information. 17 | package inframetadata 18 | -------------------------------------------------------------------------------- /pkg/inframetadata/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/DataDog/opentelemetry-mapping-go/pkg/inframetadata 2 | 3 | go 1.23.0 4 | 5 | require ( 6 | github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.27.1 7 | github.com/stretchr/testify v1.10.0 8 | go.opentelemetry.io/collector/pdata v1.32.0 9 | go.opentelemetry.io/collector/semconv v0.126.0 10 | go.uber.org/zap v1.27.0 11 | ) 12 | 13 | require ( 14 | github.com/davecgh/go-spew v1.1.1 // indirect 15 | github.com/go-logr/logr v1.4.2 // indirect 16 | github.com/go-logr/stdr v1.2.2 // indirect 17 | github.com/gogo/protobuf v1.3.2 // indirect 18 | github.com/google/uuid v1.6.0 // indirect 19 | github.com/hashicorp/go-version v1.7.0 // indirect 20 | github.com/json-iterator/go v1.1.12 // indirect 21 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 22 | github.com/modern-go/reflect2 v1.0.2 // indirect 23 | github.com/pmezard/go-difflib v1.0.0 // indirect 24 | go.opentelemetry.io/auto/sdk v1.1.0 // indirect 25 | go.opentelemetry.io/collector/component v1.32.0 // indirect 26 | go.opentelemetry.io/collector/featuregate v1.32.0 // indirect 27 | go.opentelemetry.io/collector/internal/telemetry v0.126.0 // indirect 28 | go.opentelemetry.io/contrib/bridges/otelzap v0.10.0 // indirect 29 | go.opentelemetry.io/otel v1.36.0 // indirect 30 | go.opentelemetry.io/otel/log v0.11.0 // indirect 31 | go.opentelemetry.io/otel/metric v1.36.0 // indirect 32 | go.opentelemetry.io/otel/sdk v1.36.0 // indirect 33 | go.opentelemetry.io/otel/trace v1.36.0 // indirect 34 | go.uber.org/multierr v1.11.0 // indirect 35 | golang.org/x/net v0.39.0 // indirect 36 | golang.org/x/sys v0.33.0 // indirect 37 | golang.org/x/text v0.24.0 // indirect 38 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect 39 | google.golang.org/grpc v1.72.0 // indirect 40 | google.golang.org/protobuf v1.36.6 // indirect 41 | gopkg.in/yaml.v3 v3.0.1 // indirect 42 | ) 43 | 44 | replace github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes => ../otlp/attributes 45 | -------------------------------------------------------------------------------- /pkg/inframetadata/gohai/internal/gohaitest/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/DataDog/opentelemetry-mapping-go/pkg/inframetadata/gohai/internal/gohaitest 2 | 3 | go 1.23.0 4 | 5 | require ( 6 | github.com/DataDog/gohai v0.0.0-20230524154621-4316413895ee 7 | github.com/DataDog/opentelemetry-mapping-go/pkg/inframetadata v0.27.1 8 | github.com/stretchr/testify v1.10.0 9 | ) 10 | 11 | require ( 12 | github.com/cihub/seelog v0.0.0-20151216151435-d2c6e5aa9fbf // indirect 13 | github.com/davecgh/go-spew v1.1.1 // indirect 14 | github.com/pmezard/go-difflib v1.0.0 // indirect 15 | golang.org/x/sys v0.33.0 // indirect 16 | gopkg.in/yaml.v3 v3.0.1 // indirect 17 | ) 18 | 19 | replace github.com/DataDog/opentelemetry-mapping-go/pkg/inframetadata => ../../../ 20 | -------------------------------------------------------------------------------- /pkg/inframetadata/gohai/internal/gohaitest/go.sum: -------------------------------------------------------------------------------- 1 | github.com/DataDog/gohai v0.0.0-20230524154621-4316413895ee h1:tXibLZk3G6HncIFJKaNItsdzcrk4YqILNDZlXPTNt4k= 2 | github.com/DataDog/gohai v0.0.0-20230524154621-4316413895ee/go.mod h1:nTot/Iy0kW16bXgXr6blEc8gFeAS7vTqYlhAxh+dbc0= 3 | github.com/cihub/seelog v0.0.0-20151216151435-d2c6e5aa9fbf h1:XI2tOTCBqEnMyN2j1yPBI07yQHeywUSCEf8YWqf0oKw= 4 | github.com/cihub/seelog v0.0.0-20151216151435-d2c6e5aa9fbf/go.mod h1:9d6lWj8KzO/fd/NrVaLscBKmPigpZpn5YawRPw+e3Yo= 5 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 7 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 9 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 10 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 11 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= 12 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 13 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 14 | github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= 15 | github.com/shirou/gopsutil/v3 v3.22.12/go.mod h1:Xd7P1kwZcp5VW52+9XsirIKd/BROzbb2wdX3Kqlz9uI= 16 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 17 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 18 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 19 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 20 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 21 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 22 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 23 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 24 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 25 | github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= 26 | github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= 27 | github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= 28 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 29 | golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 30 | golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 31 | golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 32 | golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= 33 | golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 34 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 35 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 36 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 37 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 38 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 39 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 40 | -------------------------------------------------------------------------------- /pkg/inframetadata/gohai/internal/gohaitest/gohai_test.go: -------------------------------------------------------------------------------- 1 | // Copyright The OpenTelemetry Authors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package gohaitest 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/DataDog/gohai/cpu" 10 | "github.com/DataDog/gohai/filesystem" 11 | "github.com/DataDog/gohai/memory" 12 | "github.com/DataDog/gohai/network" 13 | "github.com/DataDog/gohai/platform" 14 | "github.com/DataDog/opentelemetry-mapping-go/pkg/inframetadata/gohai" 15 | "github.com/stretchr/testify/require" 16 | ) 17 | 18 | func TestGohaiHasTheRightTypes(t *testing.T) { 19 | res := new(gohai.Gohai) 20 | p, err := new(cpu.Cpu).Collect() 21 | require.NoError(t, err) 22 | res.CPU = p.(map[string]string) 23 | require.NotNil(t, res.CPU) 24 | 25 | p, err = new(filesystem.FileSystem).Collect() 26 | require.NoError(t, err) 27 | res.FileSystem = p.([]any) 28 | require.NotNil(t, res.FileSystem) 29 | 30 | p, err = new(memory.Memory).Collect() 31 | require.NoError(t, err) 32 | res.Memory = p.(map[string]string) 33 | require.NotNil(t, res.Memory) 34 | 35 | p, err = new(network.Network).Collect() 36 | require.NoError(t, err) 37 | res.Network = p.(map[string]any) 38 | require.NotNil(t, res.Network) 39 | 40 | p, err = new(platform.Platform).Collect() 41 | require.NoError(t, err) 42 | res.Platform = p.(map[string]string) 43 | require.NotNil(t, res.Platform) 44 | } 45 | -------------------------------------------------------------------------------- /pkg/inframetadata/gohai/processes.go: -------------------------------------------------------------------------------- 1 | // Copyright The OpenTelemetry Authors 2 | // SPDX-License-Identifier: Apache-2.0 3 | // Original sources of this file: 4 | // - https://github.com/DataDog/datadog-agent/blob/dfab82/pkg/metadata/internal/resources/payload.go 5 | // 6 | // This file defines the Gohai metadata payload for resources. This includes information about processes running in the system. 7 | 8 | package gohai 9 | 10 | // ProcessesPayload handles the JSON unmarshalling 11 | type ProcessesPayload struct { 12 | Processes map[string]interface{} `json:"processes"` 13 | Meta map[string]string `json:"meta"` 14 | } 15 | -------------------------------------------------------------------------------- /pkg/inframetadata/internal/hostmap/errors.go: -------------------------------------------------------------------------------- 1 | // Unless explicitly stated otherwise all files in this repository are licensed 2 | // under the Apache License Version 2.0. 3 | // This product includes software developed at Datadog (https://www.datadoghq.com/). 4 | // Copyright 2023-present Datadog, Inc. 5 | 6 | package hostmap 7 | 8 | import ( 9 | "fmt" 10 | 11 | "go.opentelemetry.io/collector/pdata/pcommon" 12 | ) 13 | 14 | var _ error = mismatchedTypeErr{} 15 | 16 | type mismatchedTypeErr struct { 17 | name string 18 | actualType pcommon.ValueType 19 | expectedType pcommon.ValueType 20 | } 21 | 22 | func mismatchErr(name string, actualType, expectedType pcommon.ValueType) error { 23 | return mismatchedTypeErr{ 24 | name: name, 25 | actualType: actualType, 26 | expectedType: expectedType, 27 | } 28 | } 29 | 30 | func (e mismatchedTypeErr) Error() string { 31 | return fmt.Sprintf("%q has type %q, expected type %q instead", e.name, e.actualType, e.expectedType) 32 | } 33 | -------------------------------------------------------------------------------- /pkg/inframetadata/internal/hostmap/tags.go: -------------------------------------------------------------------------------- 1 | // Unless explicitly stated otherwise all files in this repository are licensed 2 | // under the Apache License Version 2.0. 3 | // This product includes software developed at Datadog (https://www.datadoghq.com/). 4 | // Copyright 2023-present Datadog, Inc. 5 | 6 | package hostmap 7 | 8 | import ( 9 | "errors" 10 | "fmt" 11 | "sort" 12 | "strings" 13 | 14 | "go.opentelemetry.io/collector/pdata/pcommon" 15 | conventions "go.opentelemetry.io/collector/semconv/v1.21.0" 16 | ) 17 | 18 | const ( 19 | hostTagPrefix = "datadog.host.tag." 20 | hostAliasAttribute = "datadog.host.aliases" 21 | ) 22 | 23 | var hostTagMapping = map[string]string{ 24 | conventions.AttributeDeploymentEnvironment: "env", 25 | conventions.AttributeK8SClusterName: "cluster_name", 26 | conventions.AttributeCloudProvider: "cloud_provider", 27 | conventions.AttributeCloudRegion: "region", 28 | conventions.AttributeCloudAvailabilityZone: "zone", 29 | 30 | // TODO(OTEL-1766): import of semconv 1.27.0 is blocked on Go1.22 support 31 | "deployment.environment.name": "env", 32 | } 33 | 34 | // assertStringValue returns the string value of the given value, or an error if the value is not a string. 35 | func assertStringValue(name string, v pcommon.Value) (string, error) { 36 | if v.Type() != pcommon.ValueTypeStr { 37 | return "", mismatchErr(name, v.Type(), pcommon.ValueTypeStr) 38 | } 39 | return v.Str(), nil 40 | } 41 | 42 | // getHostTags returns a slice of tags from the given map. 43 | func getHostTags(m pcommon.Map) ([]string, error) { 44 | var tags []string 45 | var err error 46 | m.Range(func(k string, v pcommon.Value) bool { 47 | if strings.HasPrefix(k, hostTagPrefix) { // User-defined tags 48 | if str, err2 := assertStringValue(k, v); err2 != nil { 49 | err = errors.Join(err, err2) 50 | } else if str == "" { 51 | err = errors.Join(err, fmt.Errorf("attribute %q has empty string value, expected non-empty string", k)) 52 | } else { 53 | tags = append(tags, k[len(hostTagPrefix):]+":"+str) 54 | } 55 | } else if mappedKey, ok := hostTagMapping[k]; ok { // Well-known tags 56 | if str, err2 := assertStringValue(k, v); err2 != nil { 57 | err = errors.Join(err, err2) 58 | } else { 59 | tags = append(tags, mappedKey+":"+str) 60 | } 61 | } 62 | return true 63 | }) 64 | 65 | // Allow for comparison of tags 66 | sort.Strings(tags) 67 | return tags, err 68 | } 69 | 70 | // getHostAliases tries to get a host aliases from attribute datadog.host.aliases 71 | func getHostAliases(attrs pcommon.Map) []string { 72 | var hostAliases []string 73 | attrs.Range(func(k string, v pcommon.Value) bool { 74 | if k == hostAliasAttribute { 75 | if v.Type() != pcommon.ValueTypeSlice { 76 | return false 77 | } 78 | hostAliasesAny := v.Slice().AsRaw() 79 | for _, hostAlias := range hostAliasesAny { 80 | if hostAliasStr, ok := hostAlias.(string); ok { 81 | hostAliases = append(hostAliases, hostAliasStr) 82 | } 83 | } 84 | return false 85 | } 86 | return true 87 | }) 88 | return hostAliases 89 | } 90 | -------------------------------------------------------------------------------- /pkg/inframetadata/internal/hostmap/tags_test.go: -------------------------------------------------------------------------------- 1 | // Unless explicitly stated otherwise all files in this repository are licensed 2 | // under the Apache License Version 2.0. 3 | // This product includes software developed at Datadog (https://www.datadoghq.com/). 4 | // Copyright 2023-present Datadog, Inc. 5 | 6 | package hostmap 7 | 8 | import ( 9 | "testing" 10 | 11 | "github.com/stretchr/testify/assert" 12 | "go.opentelemetry.io/collector/pdata/pcommon" 13 | ) 14 | 15 | func TestGetHostAliases(t *testing.T) { 16 | var uninitializedSlice []string 17 | tests := []struct { 18 | name string 19 | attrs func() pcommon.Map 20 | expected []string 21 | }{ 22 | { 23 | name: "no attributes", 24 | attrs: func() pcommon.Map { 25 | return pcommon.NewMap() 26 | }, 27 | expected: uninitializedSlice, 28 | }, 29 | { 30 | name: "attribute not present", 31 | attrs: func() pcommon.Map { 32 | m := pcommon.NewMap() 33 | m.PutStr("some.other.key", "value") 34 | return m 35 | }, 36 | expected: uninitializedSlice, 37 | }, 38 | { 39 | name: "host aliases present", 40 | attrs: func() pcommon.Map { 41 | m := pcommon.NewMap() 42 | slice := m.PutEmptySlice(hostAliasAttribute) 43 | slice.AppendEmpty().SetStr("alias1") 44 | slice.AppendEmpty().SetStr("alias2") 45 | return m 46 | }, 47 | expected: []string{"alias1", "alias2"}, 48 | }, 49 | { 50 | name: "host aliases present but with mixed types", 51 | attrs: func() pcommon.Map { 52 | m := pcommon.NewMap() 53 | slice := m.PutEmptySlice(hostAliasAttribute) 54 | slice.AppendEmpty().SetStr("alias1") 55 | slice.AppendEmpty().SetInt(123) // invalid, will be skipped 56 | slice.AppendEmpty().SetStr("alias2") 57 | return m 58 | }, 59 | expected: []string{"alias1", "alias2"}, 60 | }, 61 | { 62 | name: "wrong type, no panic", 63 | attrs: func() pcommon.Map { 64 | m := pcommon.NewMap() 65 | m.PutStr(hostAliasAttribute, "alias") 66 | return m 67 | }, 68 | expected: uninitializedSlice, 69 | }, 70 | { 71 | name: "empty slice", 72 | attrs: func() pcommon.Map { 73 | m := pcommon.NewMap() 74 | m.PutEmptySlice(hostAliasAttribute) 75 | return m 76 | }, 77 | expected: uninitializedSlice, 78 | }, 79 | { 80 | name: "non initialized slice", 81 | attrs: func() pcommon.Map { 82 | var nonInitializedSlice []string 83 | m := pcommon.NewMap() 84 | m.FromRaw(map[string]any{hostAliasAttribute: nonInitializedSlice}) 85 | return m 86 | }, 87 | expected: uninitializedSlice, 88 | }, 89 | } 90 | 91 | for _, tt := range tests { 92 | t.Run(tt.name, func(t *testing.T) { 93 | result := getHostAliases(tt.attrs()) 94 | assert.Equal(t, tt.expected, result) 95 | }) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /pkg/inframetadata/internal/testutils/resource.go: -------------------------------------------------------------------------------- 1 | // Unless explicitly stated otherwise all files in this repository are licensed 2 | // under the Apache License Version 2.0. 3 | // This product includes software developed at Datadog (https://www.datadoghq.com/). 4 | // Copyright 2023-present Datadog, Inc. 5 | 6 | package testutils 7 | 8 | import ( 9 | "testing" 10 | 11 | "github.com/stretchr/testify/require" 12 | 13 | "go.opentelemetry.io/collector/pdata/pcommon" 14 | ) 15 | 16 | // ResourceFromMap builds a resource with resource attributes set to the provided map. 17 | func NewResourceFromMap(t *testing.T, m map[string]any) pcommon.Resource { 18 | res := pcommon.NewResource() 19 | err := res.Attributes().FromRaw(m) 20 | require.NoError(t, err) 21 | return res 22 | } 23 | -------------------------------------------------------------------------------- /pkg/inframetadata/payload/payload.go: -------------------------------------------------------------------------------- 1 | // Copyright The OpenTelemetry Authors 2 | // SPDX-License-Identifier: Apache-2.0 3 | // Original sources for this file: 4 | // - https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/a5cdd2/exporter/datadogexporter/internal/hostmetadata/metadata.go 5 | // - https://github.com/DataDog/datadog-agent/blob/ab37437/pkg/metadata/host/payload.go 6 | // 7 | // This file defines the host metadata payload. This payload fills in information about the hosts in the Datadog's infrastructure list page. 8 | 9 | package payload 10 | 11 | import ( 12 | "github.com/DataDog/opentelemetry-mapping-go/pkg/inframetadata/gohai" 13 | ) 14 | 15 | // HostMetadata includes metadata about the host tags, 16 | // host aliases and identifies the host as an OpenTelemetry host 17 | type HostMetadata struct { 18 | // Meta includes metadata about the host. 19 | Meta *Meta `json:"meta"` 20 | 21 | // InternalHostname is the canonical hostname. 22 | // We always set its value to be the same as Meta.Hostname. 23 | // See Meta.Hostname docstring for more details. 24 | InternalHostname string `json:"internalHostname"` 25 | 26 | // Version is the OpenTelemetry Collector version. 27 | // This is used for correctly identifying the Collector in the backend, 28 | // and for telemetry purposes. 29 | Version string `json:"otel_version"` 30 | 31 | // Flavor is always set to "opentelemetry-collector". 32 | // It is used for telemetry purposes in the backend. 33 | Flavor string `json:"agent-flavor"` 34 | 35 | // Tags includes the host tags 36 | Tags *HostTags `json:"host-tags"` 37 | 38 | // Payload contains inventory of system information provided by gohai 39 | // this is embedded because of special serialization requirements 40 | // the field `gohai` is JSON-formatted string 41 | gohai.Payload 42 | 43 | // Processes contains the process payload devired by gohai 44 | // Because of legacy reasons this is called resources in datadog intake 45 | Processes *gohai.ProcessesPayload `json:"resources"` 46 | } 47 | 48 | // HostTags are the host tags. 49 | // Currently only system (configuration) tags are considered. 50 | type HostTags struct { 51 | // OTel are host tags set in the configuration 52 | OTel []string `json:"otel,omitempty"` 53 | 54 | // GCP are Google Cloud Platform tags 55 | GCP []string `json:"google cloud platform,omitempty"` 56 | } 57 | 58 | // Meta includes metadata about the host aliases 59 | type Meta struct { 60 | // InstanceID is the EC2 instance id the Collector is running on, if available 61 | InstanceID string `json:"instance-id,omitempty"` 62 | 63 | // EC2Hostname is the hostname from the EC2 metadata API 64 | EC2Hostname string `json:"ec2-hostname,omitempty"` 65 | 66 | // Hostname is (typically) the canonical hostname. 67 | // See https://github.com/DataDog/dogweb/blob/a8f5d94/prozess/sobotka/transform.py#L991-L1105 68 | // for more details on how the canonical hostname resolution logic works. 69 | Hostname string `json:"hostname"` 70 | 71 | // SocketHostname is the OS hostname 72 | SocketHostname string `json:"socket-hostname,omitempty"` 73 | 74 | // SocketFqdn is the FQDN hostname 75 | SocketFqdn string `json:"socket-fqdn,omitempty"` 76 | 77 | // HostAliases are other available host names 78 | HostAliases []string `json:"host_aliases,omitempty"` 79 | } 80 | 81 | // NewEmpty creates a new HostMetadata with empty fields. 82 | // Pointer fields are initialized to empty structs. 83 | // All other fields are initialized to their zero value. 84 | func NewEmpty() HostMetadata { 85 | return HostMetadata{ 86 | Meta: &Meta{}, 87 | Tags: &HostTags{}, 88 | Payload: gohai.NewEmpty(), 89 | Processes: &gohai.ProcessesPayload{}, 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /pkg/inframetadata/reporter_test.go: -------------------------------------------------------------------------------- 1 | // Unless explicitly stated otherwise all files in this repository are licensed 2 | // under the Apache License Version 2.0. 3 | // This product includes software developed at Datadog (https://www.datadoghq.com/). 4 | // Copyright 2023-present Datadog, Inc. 5 | 6 | package inframetadata 7 | 8 | import ( 9 | "context" 10 | "fmt" 11 | "testing" 12 | "time" 13 | 14 | "github.com/stretchr/testify/assert" 15 | "github.com/stretchr/testify/require" 16 | conventions "go.opentelemetry.io/collector/semconv/v1.18.0" 17 | "go.uber.org/zap" 18 | "go.uber.org/zap/zapcore" 19 | "go.uber.org/zap/zaptest/observer" 20 | 21 | "github.com/DataDog/opentelemetry-mapping-go/pkg/inframetadata/internal/testutils" 22 | "github.com/DataDog/opentelemetry-mapping-go/pkg/inframetadata/payload" 23 | ) 24 | 25 | func TestHasHostMetadata(t *testing.T) { 26 | tests := []struct { 27 | name string 28 | attrs map[string]any 29 | ok bool 30 | err string 31 | }{ 32 | { 33 | name: "wrong type for datadog.host.use_as_metadata", 34 | attrs: map[string]any{ 35 | AttributeDatadogHostUseAsMetadata: "a string", 36 | }, 37 | err: "\"datadog.host.use_as_metadata\" has type \"Str\", expected \"Bool\"", 38 | }, 39 | { 40 | name: "no datadog.host.use_as_metadata", 41 | attrs: map[string]any{}, 42 | ok: shouldUseByDefault, 43 | }, 44 | { 45 | name: "datadog.host.use_as_metadata = true", 46 | attrs: map[string]any{ 47 | AttributeDatadogHostUseAsMetadata: true, 48 | }, 49 | ok: true, 50 | }, 51 | { 52 | name: "datadog.host.use_as_metadata = false", 53 | attrs: map[string]any{ 54 | AttributeDatadogHostUseAsMetadata: false, 55 | }, 56 | ok: false, 57 | }, 58 | } 59 | 60 | for _, tt := range tests { 61 | t.Run(tt.name, func(t *testing.T) { 62 | ok, err := hasHostMetadata(testutils.NewResourceFromMap(t, tt.attrs)) 63 | if tt.err != "" { 64 | assert.EqualError(t, err, tt.err) 65 | } else { 66 | assert.NoError(t, err) 67 | assert.Equal(t, tt.ok, ok) 68 | } 69 | }) 70 | } 71 | 72 | } 73 | 74 | var _ Pusher = (*pusher)(nil) 75 | 76 | type pusher struct { 77 | md payload.HostMetadata 78 | ch chan struct{} 79 | } 80 | 81 | func (p *pusher) Push(_ context.Context, md payload.HostMetadata) error { 82 | p.md = md 83 | close(p.ch) 84 | return fmt.Errorf("network error") 85 | } 86 | 87 | func TestRun(t *testing.T) { 88 | p := &pusher{ch: make(chan struct{})} 89 | core, observed := observer.New(zapcore.WarnLevel) 90 | r, err := NewReporter(zap.New(core), p, 50*time.Millisecond) 91 | require.NoError(t, err) 92 | 93 | ch := make(chan struct{}) 94 | go func() { 95 | require.NoError(t, r.Run(context.Background())) 96 | close(ch) 97 | }() 98 | 99 | err = r.ConsumeResource(testutils.NewResourceFromMap(t, map[string]any{ 100 | AttributeDatadogHostUseAsMetadata: true, 101 | conventions.AttributeCloudProvider: conventions.AttributeCloudProviderAWS, 102 | conventions.AttributeHostID: "host-1-hostid", 103 | conventions.AttributeHostName: "host-1-hostname", 104 | conventions.AttributeOSDescription: true, 105 | conventions.AttributeHostArch: conventions.AttributeHostArchAMD64, 106 | })) 107 | assert.EqualError(t, err, "\"os.description\" has type \"Bool\", expected type \"Str\" instead") 108 | 109 | err = r.ConsumeResource(testutils.NewResourceFromMap(t, map[string]any{})) 110 | assert.NoError(t, err) 111 | 112 | // wait until Push has been called once before stopping 113 | <-p.ch 114 | r.Stop() 115 | // wait until Reporter has stopped 116 | <-ch 117 | assert.Equal(t, p.md.Meta.Hostname, "host-1-hostid") 118 | assert.Contains(t, p.md.Tags.OTel, "cloud_provider:aws") 119 | logs := observed.AllUntimed() 120 | assert.Len(t, logs, 1) 121 | assert.Equal(t, logs[0].Message, "Failed to send host metadata") 122 | } 123 | -------------------------------------------------------------------------------- /pkg/internal/sketchtest/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/DataDog/opentelemetry-mapping-go/pkg/internal/sketchtest 2 | 3 | go 1.23 4 | 5 | require github.com/stretchr/testify v1.10.0 6 | 7 | require ( 8 | github.com/davecgh/go-spew v1.1.1 // indirect 9 | github.com/pmezard/go-difflib v1.0.0 // indirect 10 | gopkg.in/yaml.v3 v3.0.1 // indirect 11 | ) 12 | 13 | retract v0.4.0 // see #107 14 | -------------------------------------------------------------------------------- /pkg/internal/sketchtest/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 5 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 6 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 7 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 8 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 9 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 10 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 11 | -------------------------------------------------------------------------------- /pkg/internal/sketchtest/quantile_test.go: -------------------------------------------------------------------------------- 1 | package sketchtest 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestQuantileCDFInverses(t *testing.T) { 10 | for _, tt := range []struct { 11 | name string 12 | cdf CDF 13 | qf QuantileFunction 14 | }{ 15 | { 16 | name: "Exponential(lambda=1/100)", 17 | cdf: ExponentialCDF(1 / 100.0), 18 | qf: ExponentialQ(1 / 100.0), 19 | }, 20 | { 21 | name: "Exponential(lambda=1/200)", 22 | cdf: ExponentialCDF(1 / 200.0), 23 | qf: ExponentialQ(1 / 200.0), 24 | }, 25 | { 26 | name: "Normal(0, 1)", 27 | cdf: NormalCDF(0, 1), 28 | qf: NormalQ(0, 1), 29 | }, 30 | { 31 | name: "Normal(-3, 10)", 32 | cdf: NormalCDF(-3, 10), 33 | qf: NormalQ(-3, 10), 34 | }, 35 | { 36 | name: "Truncated normal (0, 1e-3)", 37 | cdf: TruncateCDF(-8, 8, NormalCDF(0, 1e-3)), 38 | qf: TruncateQ(-8, 8, NormalQ(0, 1e-3), NormalCDF(0, 1e-3)), 39 | }, 40 | } { 41 | for i := 0; i <= 100; i++ { 42 | assert.InDelta(t, tt.cdf(tt.qf(float64(i)/100.0)), float64(i)/100.0, 1e-15) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /pkg/otlp/attributes/azure/azure.go: -------------------------------------------------------------------------------- 1 | // Copyright The OpenTelemetry Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package azure 16 | 17 | import ( 18 | "strings" 19 | 20 | "go.opentelemetry.io/collector/pdata/pcommon" 21 | conventions "go.opentelemetry.io/collector/semconv/v1.6.1" 22 | ) 23 | 24 | const ( 25 | // AttributeResourceGroupName is the Azure resource group name attribute 26 | AttributeResourceGroupName = "azure.resourcegroup.name" 27 | ) 28 | 29 | // HostInfo has the Azure host information 30 | type HostInfo struct { 31 | HostAliases []string 32 | } 33 | 34 | // HostnameFromAttrs gets the Azure hostname from attributes. 35 | func HostnameFromAttrs(attrs pcommon.Map) (string, bool) { 36 | if vmID, ok := attrs.Get(conventions.AttributeHostID); ok { 37 | return vmID.Str(), true 38 | } 39 | 40 | if hostname, ok := attrs.Get(conventions.AttributeHostName); ok { 41 | return hostname.Str(), true 42 | } 43 | 44 | return "", false 45 | } 46 | 47 | // ClusterNameFromAttributes gets the Azure cluster name from attributes 48 | func ClusterNameFromAttributes(attrs pcommon.Map) (string, bool) { 49 | // Get cluster name from resource group from pkg/util/cloudprovider/azure:GetClusterName 50 | if resourceGroup, ok := attrs.Get(AttributeResourceGroupName); ok { 51 | splitAll := strings.Split(resourceGroup.Str(), "_") 52 | if len(splitAll) < 4 || strings.ToLower(splitAll[0]) != "mc" { 53 | return "", false // Failed to parse 54 | } 55 | return splitAll[len(splitAll)-2], true 56 | } 57 | 58 | return "", false 59 | } 60 | -------------------------------------------------------------------------------- /pkg/otlp/attributes/azure/azure_test.go: -------------------------------------------------------------------------------- 1 | // Copyright The OpenTelemetry Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package azure 15 | 16 | import ( 17 | "testing" 18 | 19 | "github.com/stretchr/testify/assert" 20 | "go.opentelemetry.io/collector/pdata/pcommon" 21 | conventions "go.opentelemetry.io/collector/semconv/v1.6.1" 22 | 23 | "github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes/internal/testutils" 24 | ) 25 | 26 | var ( 27 | testVMID = "02aab8a4-74ef-476e-8182-f6d2ba4166a6" 28 | testHostname = "test-hostname" 29 | testAttrs = testutils.NewAttributeMap(map[string]string{ 30 | conventions.AttributeCloudProvider: conventions.AttributeCloudProviderAzure, 31 | conventions.AttributeHostName: testHostname, 32 | conventions.AttributeCloudRegion: "location", 33 | conventions.AttributeHostID: testVMID, 34 | conventions.AttributeCloudAccountID: "subscriptionID", 35 | AttributeResourceGroupName: "resourceGroup", 36 | }) 37 | testEmpty = testutils.NewAttributeMap(map[string]string{}) 38 | ) 39 | 40 | func TestClusterNameFromAttributes(t *testing.T) { 41 | cluster, ok := ClusterNameFromAttributes(testutils.NewAttributeMap(map[string]string{ 42 | AttributeResourceGroupName: "MC_aks-kenafeh_aks-kenafeh-eu_westeurope", 43 | })) 44 | assert.True(t, ok) 45 | assert.Equal(t, cluster, "aks-kenafeh-eu") 46 | 47 | cluster, ok = ClusterNameFromAttributes(testutils.NewAttributeMap(map[string]string{ 48 | AttributeResourceGroupName: "mc_foo-bar-aks-k8s-rg_foo-bar-aks-k8s_westeurope", 49 | })) 50 | assert.True(t, ok) 51 | assert.Equal(t, cluster, "foo-bar-aks-k8s") 52 | 53 | _, ok = ClusterNameFromAttributes(testutils.NewAttributeMap(map[string]string{ 54 | AttributeResourceGroupName: "unexpected-resource-group-name-format", 55 | })) 56 | assert.False(t, ok) 57 | 58 | _, ok = ClusterNameFromAttributes(testutils.NewAttributeMap(map[string]string{})) 59 | assert.False(t, ok) 60 | } 61 | 62 | func TestHostnameFromAttrs(t *testing.T) { 63 | tests := []struct { 64 | name string 65 | attrs pcommon.Map 66 | 67 | ok bool 68 | hostname string 69 | }{ 70 | { 71 | name: "all attributes", 72 | attrs: testAttrs, 73 | 74 | ok: true, 75 | hostname: testVMID, 76 | }, 77 | { 78 | name: "no host id", 79 | attrs: testutils.NewAttributeMap(map[string]string{ 80 | conventions.AttributeCloudProvider: conventions.AttributeCloudProviderAzure, 81 | conventions.AttributeHostName: testHostname, 82 | }), 83 | ok: true, 84 | hostname: testHostname, 85 | }, 86 | { 87 | name: "empty", 88 | attrs: testEmpty, 89 | }, 90 | } 91 | 92 | for _, testInstance := range tests { 93 | t.Run(testInstance.name, func(t *testing.T) { 94 | hostname, ok := HostnameFromAttrs(testInstance.attrs) 95 | 96 | assert.Equal(t, testInstance.ok, ok) 97 | if testInstance.ok || ok { 98 | assert.Equal(t, testInstance.hostname, hostname) 99 | } 100 | }) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /pkg/otlp/attributes/ec2/ec2.go: -------------------------------------------------------------------------------- 1 | // Copyright OpenTelemetry Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package ec2 16 | 17 | import ( 18 | "fmt" 19 | "strings" 20 | 21 | "go.opentelemetry.io/collector/pdata/pcommon" 22 | conventions "go.opentelemetry.io/collector/semconv/v1.6.1" 23 | ) 24 | 25 | var ( 26 | defaultPrefixes = [3]string{"ip-", "domu", "ec2amaz-"} 27 | ec2TagPrefix = "ec2.tag." 28 | clusterTagPrefix = ec2TagPrefix + "kubernetes.io/cluster/" 29 | ) 30 | 31 | // HostInfo holds the EC2 host information. 32 | type HostInfo struct { 33 | InstanceID string 34 | EC2Hostname string 35 | EC2Tags []string 36 | } 37 | 38 | // isDefaultHostname checks if a hostname is an EC2 default 39 | func isDefaultHostname(hostname string) bool { 40 | for _, val := range defaultPrefixes { 41 | if strings.HasPrefix(hostname, val) { 42 | return true 43 | } 44 | } 45 | 46 | return false 47 | } 48 | 49 | // HostnameFromAttrs gets a valid hostname from labels 50 | // if available 51 | func HostnameFromAttrs(attrs pcommon.Map) (string, bool) { 52 | if hostID, ok := attrs.Get(conventions.AttributeHostID); ok { 53 | return hostID.Str(), true 54 | } 55 | 56 | return "", false 57 | } 58 | 59 | // HostInfoFromAttributes gets EC2 host info from attributes following 60 | // OpenTelemetry semantic conventions 61 | func HostInfoFromAttributes(attrs pcommon.Map) (hostInfo *HostInfo) { 62 | hostInfo = &HostInfo{} 63 | 64 | if hostID, ok := attrs.Get(conventions.AttributeHostID); ok { 65 | hostInfo.InstanceID = hostID.Str() 66 | } 67 | 68 | if hostName, ok := attrs.Get(conventions.AttributeHostName); ok { 69 | hostInfo.EC2Hostname = hostName.Str() 70 | } 71 | 72 | attrs.Range(func(k string, v pcommon.Value) bool { 73 | if strings.HasPrefix(k, ec2TagPrefix) { 74 | tag := fmt.Sprintf("%s:%s", strings.TrimPrefix(k, ec2TagPrefix), v.Str()) 75 | hostInfo.EC2Tags = append(hostInfo.EC2Tags, tag) 76 | } 77 | return true 78 | }) 79 | 80 | return 81 | } 82 | 83 | // ClusterNameFromAttributes gets the AWS cluster name from attributes 84 | func ClusterNameFromAttributes(attrs pcommon.Map) (clusterName string, ok bool) { 85 | // Get cluster name from tag keys 86 | // https://github.com/DataDog/datadog-agent/blob/1c94b11/pkg/util/ec2/ec2.go#L238 87 | attrs.Range(func(k string, _ pcommon.Value) bool { 88 | if strings.HasPrefix(k, clusterTagPrefix) { 89 | clusterName = strings.Split(k, "/")[2] 90 | ok = true 91 | } 92 | return true 93 | }) 94 | return 95 | } 96 | -------------------------------------------------------------------------------- /pkg/otlp/attributes/ec2/ec2_test.go: -------------------------------------------------------------------------------- 1 | // Copyright OpenTelemetry Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package ec2 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/stretchr/testify/assert" 21 | conventions "go.opentelemetry.io/collector/semconv/v1.6.1" 22 | 23 | "github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes/internal/testutils" 24 | ) 25 | 26 | const ( 27 | testIP = "ip-12-34-56-78.us-west-2.compute.internal" 28 | testDomu = "domu-12-34-56-78.us-west-2.compute.internal" 29 | testEC2 = "ec2amaz-12-34-56-78.us-west-2.compute.internal" 30 | customHost = "custom-hostname" 31 | testInstanceID = "i-0123456789" 32 | ) 33 | 34 | func TestDefaultHostname(t *testing.T) { 35 | assert.True(t, isDefaultHostname(testIP)) 36 | assert.True(t, isDefaultHostname(testDomu)) 37 | assert.True(t, isDefaultHostname(testEC2)) 38 | assert.False(t, isDefaultHostname(customHost)) 39 | } 40 | 41 | func TestHostnameFromAttrs(t *testing.T) { 42 | t.Run("host id", func(t *testing.T) { 43 | attrs := testutils.NewAttributeMap(map[string]string{ 44 | conventions.AttributeCloudProvider: conventions.AttributeCloudProviderAWS, 45 | conventions.AttributeHostID: testInstanceID, 46 | conventions.AttributeHostName: testIP, 47 | }) 48 | hostname, ok := HostnameFromAttrs(attrs) 49 | assert.True(t, ok) 50 | assert.Equal(t, hostname, testInstanceID) 51 | }) 52 | 53 | t.Run("no host id", func(t *testing.T) { 54 | attrs := testutils.NewAttributeMap(map[string]string{ 55 | conventions.AttributeCloudProvider: conventions.AttributeCloudProviderAWS, 56 | conventions.AttributeHostName: testIP, 57 | }) 58 | hostname, ok := HostnameFromAttrs(attrs) 59 | assert.False(t, ok) 60 | assert.Equal(t, hostname, "") 61 | }) 62 | } 63 | 64 | func TestHostInfoFromAttributes(t *testing.T) { 65 | attrs := testutils.NewAttributeMap(map[string]string{ 66 | conventions.AttributeCloudProvider: conventions.AttributeCloudProviderAWS, 67 | conventions.AttributeHostID: testInstanceID, 68 | conventions.AttributeHostName: testIP, 69 | "ec2.tag.tag1": "val1", 70 | "ec2.tag.tag2": "val2", 71 | "ignored": "ignored", 72 | }) 73 | 74 | hostInfo := HostInfoFromAttributes(attrs) 75 | 76 | assert.Equal(t, hostInfo.InstanceID, testInstanceID) 77 | assert.Equal(t, hostInfo.EC2Hostname, testIP) 78 | assert.ElementsMatch(t, hostInfo.EC2Tags, []string{"tag1:val1", "tag2:val2"}) 79 | } 80 | 81 | func TestClusterNameFromAttributes(t *testing.T) { 82 | cluster, ok := ClusterNameFromAttributes(testutils.NewAttributeMap(map[string]string{ 83 | "ec2.tag.kubernetes.io/cluster/clustername": "dummy_value", 84 | })) 85 | assert.True(t, ok) 86 | assert.Equal(t, cluster, "clustername") 87 | 88 | _, ok = ClusterNameFromAttributes(testutils.NewAttributeMap(map[string]string{})) 89 | assert.False(t, ok) 90 | } 91 | -------------------------------------------------------------------------------- /pkg/otlp/attributes/gateway_usage.go: -------------------------------------------------------------------------------- 1 | // Copyright OpenTelemetry Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package attributes 16 | 17 | import "sync" 18 | 19 | // GatewayUsage is a HostFromAttributesHandler that detects if the setup is a gateway. 20 | // If two attributes have different hostnames, then we consider the setup is a gateway. 21 | type GatewayUsage struct { 22 | firstHostname string 23 | gatewayUsage bool 24 | m sync.Mutex 25 | } 26 | 27 | var _ HostFromAttributesHandler = (*GatewayUsage)(nil) 28 | 29 | // NewGatewayUsage returns a new GatewayUsage. 30 | // If two attributes have different hostnames, then we consider the setup is a gateway. 31 | func NewGatewayUsage() *GatewayUsage { 32 | return &GatewayUsage{} 33 | } 34 | 35 | // OnHost implements HostFromAttributesHandler. 36 | func (g *GatewayUsage) OnHost(host string) { 37 | g.m.Lock() 38 | defer g.m.Unlock() 39 | if g.firstHostname == "" { 40 | g.firstHostname = host 41 | } else if g.firstHostname != host { 42 | g.gatewayUsage = true 43 | } 44 | } 45 | 46 | // GatewayUsage returns true if the GatewayUsage was detected. 47 | func (g *GatewayUsage) GatewayUsage() bool { 48 | g.m.Lock() 49 | defer g.m.Unlock() 50 | return g.gatewayUsage 51 | } 52 | 53 | // Gauge returns 1 if the GatewayUsage was detected, 0 otherwise. 54 | func (g *GatewayUsage) Gauge() float64 { 55 | if g.GatewayUsage() { 56 | return 1 57 | } 58 | return 0 59 | } 60 | -------------------------------------------------------------------------------- /pkg/otlp/attributes/gateway_usage_test.go: -------------------------------------------------------------------------------- 1 | // Copyright OpenTelemetry Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package attributes 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/stretchr/testify/require" 21 | ) 22 | 23 | func TestGatewayUsage_OnHost(t *testing.T) { 24 | g := NewGatewayUsage() 25 | g.OnHost("host1") 26 | require.False(t, g.GatewayUsage()) 27 | 28 | g.OnHost("host1") 29 | require.False(t, g.GatewayUsage()) 30 | require.Equal(t, float64(0), g.Gauge()) 31 | 32 | g.OnHost("host2") 33 | require.True(t, g.GatewayUsage()) 34 | require.Equal(t, float64(1), g.Gauge()) 35 | } 36 | -------------------------------------------------------------------------------- /pkg/otlp/attributes/gcp/gcp.go: -------------------------------------------------------------------------------- 1 | // Copyright The OpenTelemetry Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package gcp 16 | 17 | import ( 18 | "fmt" 19 | "strings" 20 | 21 | "go.opentelemetry.io/collector/pdata/pcommon" 22 | conventions "go.opentelemetry.io/collector/semconv/v1.6.1" 23 | ) 24 | 25 | // HostInfo holds the GCP host information. 26 | type HostInfo struct { 27 | HostAliases []string 28 | GCPTags []string 29 | } 30 | 31 | // HostnameFromAttrs gets the GCP Integration hostname from attributes 32 | // if available. 33 | func HostnameFromAttrs(attrs pcommon.Map) (string, bool) { 34 | hostName, ok := attrs.Get(conventions.AttributeHostName) 35 | if !ok { 36 | // We need the hostname. 37 | return "", false 38 | } 39 | 40 | name := hostName.Str() 41 | if strings.Count(name, ".") >= 3 { 42 | // Unless the host.name attribute has been tampered with, use the same logic as the Agent to 43 | // extract the hostname: https://github.com/DataDog/datadog-agent/blob/7.36.0/pkg/util/cloudproviders/gce/gce.go#L106 44 | name = strings.SplitN(name, ".", 2)[0] 45 | } 46 | 47 | cloudAccount, ok := attrs.Get(conventions.AttributeCloudAccountID) 48 | if !ok { 49 | // We need the project ID. 50 | return "", false 51 | } 52 | 53 | alias := fmt.Sprintf("%s.%s", name, cloudAccount.Str()) 54 | return alias, true 55 | } 56 | 57 | // HostInfoFromAttrs gets GCP host info from attributes following 58 | // OpenTelemetry semantic conventions 59 | func HostInfoFromAttrs(attrs pcommon.Map) (hostInfo *HostInfo) { 60 | hostInfo = &HostInfo{} 61 | 62 | if hostID, ok := attrs.Get(conventions.AttributeHostID); ok { 63 | hostInfo.GCPTags = append(hostInfo.GCPTags, fmt.Sprintf("instance-id:%s", hostID.Str())) 64 | } 65 | 66 | if cloudZone, ok := attrs.Get(conventions.AttributeCloudAvailabilityZone); ok { 67 | hostInfo.GCPTags = append(hostInfo.GCPTags, fmt.Sprintf("zone:%s", cloudZone.Str())) 68 | } 69 | 70 | if hostType, ok := attrs.Get(conventions.AttributeHostType); ok { 71 | hostInfo.GCPTags = append(hostInfo.GCPTags, fmt.Sprintf("instance-type:%s", hostType.Str())) 72 | } 73 | 74 | if cloudAccount, ok := attrs.Get(conventions.AttributeCloudAccountID); ok { 75 | hostInfo.GCPTags = append(hostInfo.GCPTags, fmt.Sprintf("project:%s", cloudAccount.Str())) 76 | } 77 | 78 | return 79 | } 80 | -------------------------------------------------------------------------------- /pkg/otlp/attributes/gcp/gcp_test.go: -------------------------------------------------------------------------------- 1 | // Copyright The OpenTelemetry Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package gcp 15 | 16 | import ( 17 | "fmt" 18 | "testing" 19 | 20 | "github.com/stretchr/testify/assert" 21 | "go.opentelemetry.io/collector/pdata/pcommon" 22 | conventions "go.opentelemetry.io/collector/semconv/v1.6.1" 23 | 24 | "github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes/internal/testutils" 25 | ) 26 | 27 | const ( 28 | testShortHostname = "hostname" 29 | testHostID = "hostID" 30 | testCloudZone = "zone" 31 | testHostType = "machineType" 32 | testCloudAccount = "projectID" 33 | testHostname = testShortHostname + ".c." + testCloudAccount + ".internal" 34 | testBadHostname = "badhostname" 35 | ) 36 | 37 | var ( 38 | testFullMap = testutils.NewAttributeMap(map[string]string{ 39 | conventions.AttributeCloudProvider: conventions.AttributeCloudProviderGCP, 40 | conventions.AttributeHostID: testHostID, 41 | conventions.AttributeHostName: testHostname, 42 | conventions.AttributeCloudAvailabilityZone: testCloudZone, 43 | conventions.AttributeHostType: testHostType, 44 | conventions.AttributeCloudAccountID: testCloudAccount, 45 | }) 46 | 47 | testFullBadMap = testutils.NewAttributeMap(map[string]string{ 48 | conventions.AttributeCloudProvider: conventions.AttributeCloudProviderGCP, 49 | conventions.AttributeHostID: testHostID, 50 | conventions.AttributeHostName: testBadHostname, 51 | conventions.AttributeCloudAvailabilityZone: testCloudZone, 52 | conventions.AttributeHostType: testHostType, 53 | conventions.AttributeCloudAccountID: testCloudAccount, 54 | }) 55 | 56 | testGCPIntegrationHostname = fmt.Sprintf("%s.%s", testShortHostname, testCloudAccount) 57 | testGCPIntegrationBadHostname = fmt.Sprintf("%s.%s", testBadHostname, testCloudAccount) 58 | ) 59 | 60 | func TestInfoFromAttrs(t *testing.T) { 61 | tags := []string{"instance-id:hostID", "zone:zone", "instance-type:machineType", "project:projectID"} 62 | tests := []struct { 63 | name string 64 | attrs pcommon.Map 65 | 66 | ok bool 67 | hostname string 68 | hostAliases []string 69 | gcpTags []string 70 | }{ 71 | { 72 | name: "no hostname", 73 | attrs: testutils.NewAttributeMap(map[string]string{}), 74 | }, 75 | { 76 | name: "hostname", 77 | attrs: testFullMap, 78 | ok: true, 79 | hostname: testGCPIntegrationHostname, 80 | gcpTags: tags, 81 | }, 82 | { 83 | name: "bad hostname", 84 | attrs: testFullBadMap, 85 | ok: true, 86 | hostname: testGCPIntegrationBadHostname, 87 | gcpTags: tags, 88 | }, 89 | } 90 | 91 | for _, testInstance := range tests { 92 | t.Run(testInstance.name, func(t *testing.T) { 93 | hostname, ok := HostnameFromAttrs(testInstance.attrs) 94 | assert.Equal(t, testInstance.ok, ok) 95 | assert.Equal(t, testInstance.hostname, hostname) 96 | 97 | hostInfo := HostInfoFromAttrs(testInstance.attrs) 98 | assert.ElementsMatch(t, testInstance.hostAliases, hostInfo.HostAliases) 99 | assert.ElementsMatch(t, testInstance.gcpTags, hostInfo.GCPTags) 100 | }) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /pkg/otlp/attributes/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes 2 | 3 | go 1.23.0 4 | 5 | require ( 6 | github.com/stretchr/testify v1.10.0 7 | go.opentelemetry.io/collector/component v1.32.0 8 | go.opentelemetry.io/collector/component/componenttest v0.126.0 9 | go.opentelemetry.io/collector/pdata v1.32.0 10 | go.opentelemetry.io/collector/semconv v0.126.0 11 | go.opentelemetry.io/otel v1.36.0 12 | go.opentelemetry.io/otel/metric v1.36.0 13 | go.opentelemetry.io/otel/sdk/metric v1.36.0 14 | ) 15 | 16 | require ( 17 | github.com/davecgh/go-spew v1.1.1 // indirect 18 | github.com/go-logr/logr v1.4.2 // indirect 19 | github.com/go-logr/stdr v1.2.2 // indirect 20 | github.com/gogo/protobuf v1.3.2 // indirect 21 | github.com/google/uuid v1.6.0 // indirect 22 | github.com/hashicorp/go-version v1.7.0 // indirect 23 | github.com/pmezard/go-difflib v1.0.0 // indirect 24 | go.opentelemetry.io/auto/sdk v1.1.0 // indirect 25 | go.opentelemetry.io/collector/featuregate v1.32.0 // indirect 26 | go.opentelemetry.io/collector/internal/telemetry v0.126.0 // indirect 27 | go.opentelemetry.io/contrib/bridges/otelzap v0.10.0 // indirect 28 | go.opentelemetry.io/otel/log v0.11.0 // indirect 29 | go.opentelemetry.io/otel/sdk v1.36.0 // indirect 30 | go.opentelemetry.io/otel/trace v1.36.0 // indirect 31 | go.uber.org/multierr v1.11.0 // indirect 32 | go.uber.org/zap v1.27.0 // indirect 33 | golang.org/x/net v0.39.0 // indirect 34 | golang.org/x/sys v0.33.0 // indirect 35 | golang.org/x/text v0.24.0 // indirect 36 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect 37 | google.golang.org/grpc v1.72.0 // indirect 38 | google.golang.org/protobuf v1.36.6 // indirect 39 | gopkg.in/yaml.v3 v3.0.1 // indirect 40 | ) 41 | 42 | retract v0.4.0 // see #107 43 | -------------------------------------------------------------------------------- /pkg/otlp/attributes/internal/testutils/test_utils.go: -------------------------------------------------------------------------------- 1 | // Unless explicitly stated otherwise all files in this repository are licensed 2 | // under the Apache License Version 2.0. 3 | // This product includes software developed at Datadog (https://www.datadoghq.com/). 4 | // Copyright 2016-present Datadog, Inc. 5 | 6 | package testutils 7 | 8 | import ( 9 | "go.opentelemetry.io/collector/pdata/pcommon" 10 | ) 11 | 12 | func fillAttributeMap(attrs pcommon.Map, mp map[string]string) { 13 | attrs.Clear() 14 | attrs.EnsureCapacity(len(mp)) 15 | for k, v := range mp { 16 | attrs.PutStr(k, v) 17 | } 18 | } 19 | 20 | // NewAttributeMap creates a new attribute map (string only) 21 | // from a Go map 22 | func NewAttributeMap(mp map[string]string) pcommon.Map { 23 | attrs := pcommon.NewMap() 24 | fillAttributeMap(attrs, mp) 25 | return attrs 26 | } 27 | -------------------------------------------------------------------------------- /pkg/otlp/attributes/process.go: -------------------------------------------------------------------------------- 1 | // Copyright The OpenTelemetry Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package attributes 16 | 17 | import ( 18 | "fmt" 19 | 20 | conventions "go.opentelemetry.io/collector/semconv/v1.6.1" 21 | ) 22 | 23 | type processAttributes struct { 24 | ExecutableName string 25 | ExecutablePath string 26 | Command string 27 | CommandLine string 28 | PID int64 29 | Owner string 30 | } 31 | 32 | func (pattrs *processAttributes) extractTags() []string { 33 | tags := make([]string, 0, 1) 34 | 35 | // According to OTel conventions: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/process.md, 36 | // a process can be defined by any of the 4 following attributes: process.executable.name, process.executable.path, process.command or process.command_line 37 | // (process.command_args isn't in the current attribute conventions: https://github.com/open-telemetry/opentelemetry-collector/blob/ecb27f49d4e26ae42d82e6ea18d57b08e252452d/model/semconv/opentelemetry.go#L58-L63) 38 | // We go through them, and add the first available one as a tag to identify the process. 39 | // We don't want to add all of them to avoid unnecessarily increasing the number of tags attached to a metric. 40 | 41 | // TODO: check if this order should be changed. 42 | switch { 43 | case pattrs.ExecutableName != "": // otelcol 44 | tags = append(tags, fmt.Sprintf("%s:%s", conventions.AttributeProcessExecutableName, pattrs.ExecutableName)) 45 | case pattrs.ExecutablePath != "": // /usr/bin/cmd/otelcol 46 | tags = append(tags, fmt.Sprintf("%s:%s", conventions.AttributeProcessExecutablePath, pattrs.ExecutablePath)) 47 | case pattrs.Command != "": // cmd/otelcol 48 | tags = append(tags, fmt.Sprintf("%s:%s", conventions.AttributeProcessCommand, pattrs.Command)) 49 | case pattrs.CommandLine != "": // cmd/otelcol --config="/path/to/config.yaml" 50 | tags = append(tags, fmt.Sprintf("%s:%s", conventions.AttributeProcessCommandLine, pattrs.CommandLine)) 51 | } 52 | 53 | // For now, we don't care about the process ID nor the process owner. 54 | 55 | return tags 56 | } 57 | -------------------------------------------------------------------------------- /pkg/otlp/attributes/process_test.go: -------------------------------------------------------------------------------- 1 | // Copyright The OpenTelemetry Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package attributes 16 | 17 | import ( 18 | "fmt" 19 | "testing" 20 | 21 | "github.com/stretchr/testify/assert" 22 | conventions "go.opentelemetry.io/collector/semconv/v1.6.1" 23 | ) 24 | 25 | func TestProcessExtractTags(t *testing.T) { 26 | pattrs := processAttributes{ 27 | ExecutableName: "otelcol", 28 | ExecutablePath: "/usr/bin/cmd/otelcol", 29 | Command: "cmd/otelcol", 30 | CommandLine: "cmd/otelcol --config=\"/path/to/config.yaml\"", 31 | PID: 1, 32 | Owner: "root", 33 | } 34 | 35 | assert.Equal(t, []string{ 36 | fmt.Sprintf("%s:%s", conventions.AttributeProcessExecutableName, "otelcol"), 37 | }, pattrs.extractTags()) 38 | 39 | pattrs = processAttributes{ 40 | ExecutablePath: "/usr/bin/cmd/otelcol", 41 | Command: "cmd/otelcol", 42 | CommandLine: "cmd/otelcol --config=\"/path/to/config.yaml\"", 43 | PID: 1, 44 | Owner: "root", 45 | } 46 | 47 | assert.Equal(t, []string{ 48 | fmt.Sprintf("%s:%s", conventions.AttributeProcessExecutablePath, "/usr/bin/cmd/otelcol"), 49 | }, pattrs.extractTags()) 50 | 51 | pattrs = processAttributes{ 52 | Command: "cmd/otelcol", 53 | CommandLine: "cmd/otelcol --config=\"/path/to/config.yaml\"", 54 | PID: 1, 55 | Owner: "root", 56 | } 57 | 58 | assert.Equal(t, []string{ 59 | fmt.Sprintf("%s:%s", conventions.AttributeProcessCommand, "cmd/otelcol"), 60 | }, pattrs.extractTags()) 61 | 62 | pattrs = processAttributes{ 63 | CommandLine: "cmd/otelcol --config=\"/path/to/config.yaml\"", 64 | PID: 1, 65 | Owner: "root", 66 | } 67 | 68 | assert.Equal(t, []string{ 69 | fmt.Sprintf("%s:%s", conventions.AttributeProcessCommandLine, "cmd/otelcol --config=\"/path/to/config.yaml\""), 70 | }, pattrs.extractTags()) 71 | } 72 | 73 | func TestProcessExtractTagsEmpty(t *testing.T) { 74 | pattrs := processAttributes{} 75 | 76 | assert.Equal(t, []string{}, pattrs.extractTags()) 77 | } 78 | -------------------------------------------------------------------------------- /pkg/otlp/attributes/source/source_provider.go: -------------------------------------------------------------------------------- 1 | // Copyright The OpenTelemetry Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package source 16 | 17 | import ( 18 | "context" 19 | "fmt" 20 | ) 21 | 22 | // Kind of source 23 | type Kind string 24 | 25 | const ( 26 | // InvalidKind is an invalid kind. It is the zero value of Kind. 27 | InvalidKind Kind = "" 28 | // HostnameKind is a host source. 29 | HostnameKind Kind = "host" 30 | // AWSECSFargateKind is a serverless source on AWS ECS Fargate. 31 | AWSECSFargateKind Kind = "task_arn" 32 | ) 33 | 34 | // Source represents a telemetry source. 35 | type Source struct { 36 | // Kind of source (serverless v. host). 37 | Kind Kind 38 | // Identifier that uniquely determines the source. 39 | Identifier string 40 | } 41 | 42 | // Tag associated to a source. 43 | func (s *Source) Tag() string { 44 | return fmt.Sprintf("%s:%s", s.Kind, s.Identifier) 45 | } 46 | 47 | // Provider identifies a source. 48 | type Provider interface { 49 | // Source gets the source from the current context. 50 | Source(ctx context.Context) (Source, error) 51 | } 52 | -------------------------------------------------------------------------------- /pkg/otlp/attributes/system.go: -------------------------------------------------------------------------------- 1 | // Copyright The OpenTelemetry Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package attributes 16 | 17 | import ( 18 | "fmt" 19 | 20 | conventions "go.opentelemetry.io/collector/semconv/v1.6.1" 21 | ) 22 | 23 | type systemAttributes struct { 24 | OSType string 25 | } 26 | 27 | func (sattrs *systemAttributes) extractTags() []string { 28 | tags := make([]string, 0, 1) 29 | 30 | // Add OS type, eg. WINDOWS, LINUX, etc. 31 | if sattrs.OSType != "" { 32 | tags = append(tags, fmt.Sprintf("%s:%s", conventions.AttributeOSType, sattrs.OSType)) 33 | } 34 | 35 | return tags 36 | } 37 | -------------------------------------------------------------------------------- /pkg/otlp/attributes/system_test.go: -------------------------------------------------------------------------------- 1 | // Copyright The OpenTelemetry Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package attributes 16 | 17 | import ( 18 | "fmt" 19 | "testing" 20 | 21 | "github.com/stretchr/testify/assert" 22 | conventions "go.opentelemetry.io/collector/semconv/v1.6.1" 23 | ) 24 | 25 | func TestSystemExtractTags(t *testing.T) { 26 | sattrs := systemAttributes{ 27 | OSType: "windows", 28 | } 29 | 30 | assert.Equal(t, []string{ 31 | fmt.Sprintf("%s:%s", conventions.AttributeOSType, "windows"), 32 | }, sattrs.extractTags()) 33 | } 34 | 35 | func TestSystemExtractTagsEmpty(t *testing.T) { 36 | sattrs := systemAttributes{} 37 | 38 | assert.Equal(t, []string{}, sattrs.extractTags()) 39 | } 40 | -------------------------------------------------------------------------------- /pkg/otlp/attributes/translator.go: -------------------------------------------------------------------------------- 1 | // Copyright OpenTelemetry Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package attributes 16 | 17 | import ( 18 | "context" 19 | "fmt" 20 | 21 | "go.opentelemetry.io/collector/component" 22 | "go.opentelemetry.io/collector/pdata/pcommon" 23 | "go.opentelemetry.io/otel/attribute" 24 | "go.opentelemetry.io/otel/metric" 25 | 26 | "github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes/source" 27 | ) 28 | 29 | const missingSourceMetricName string = "datadog.otlp_translator.resources.missing_source" 30 | 31 | // Translator of attributes. 32 | type Translator struct { 33 | missingSources metric.Int64Counter 34 | } 35 | 36 | // NewTranslator returns a new attributes translator. 37 | func NewTranslator(set component.TelemetrySettings) (*Translator, error) { 38 | meter := set.MeterProvider.Meter("github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes") 39 | missingSources, err := meter.Int64Counter( 40 | missingSourceMetricName, 41 | metric.WithDescription("OTLP resources that are missing a source (e.g. hostname)"), 42 | metric.WithUnit("{resource}"), 43 | ) 44 | if err != nil { 45 | return nil, fmt.Errorf("failed to build missing source counter: %w", err) 46 | } 47 | 48 | return &Translator{ 49 | missingSources: missingSources, 50 | }, nil 51 | } 52 | 53 | // ResourceToSource gets a telemetry signal source from its resource attributes. 54 | func (p *Translator) ResourceToSource(ctx context.Context, res pcommon.Resource, set attribute.Set, hostFromAttributesHandler HostFromAttributesHandler) (source.Source, bool) { 55 | src, ok := SourceFromAttrs(res.Attributes(), hostFromAttributesHandler) 56 | if !ok { 57 | p.missingSources.Add(ctx, 1, metric.WithAttributeSet(set)) 58 | } 59 | 60 | return src, ok 61 | } 62 | 63 | // AttributesToSource gets a telemetry signal source from a set of attributes. 64 | // As opposed to ResourceToSource, this does not keep track of failed requests. 65 | // 66 | // NOTE: This method SHOULD NOT generally be used: it is only used in the logs implementation 67 | // because of a fallback logic that will be removed. The attributes detected are resource attributes, 68 | // not attributes from a telemetry signal. 69 | func (p *Translator) AttributesToSource(_ context.Context, attrs pcommon.Map) (source.Source, bool) { 70 | return SourceFromAttrs(attrs, nil) 71 | } 72 | -------------------------------------------------------------------------------- /pkg/otlp/attributes/translator_test.go: -------------------------------------------------------------------------------- 1 | // Unless explicitly stated otherwise all files in this repository are licensed 2 | // under the Apache License Version 2.0. 3 | // This product includes software developed at Datadog (https://www.datadoghq.com/). 4 | // Copyright 2022-present Datadog, Inc. 5 | 6 | package attributes 7 | 8 | import ( 9 | "context" 10 | "testing" 11 | 12 | "github.com/stretchr/testify/assert" 13 | "github.com/stretchr/testify/require" 14 | "go.opentelemetry.io/collector/component/componenttest" 15 | "go.opentelemetry.io/collector/pdata/pcommon" 16 | "go.opentelemetry.io/otel/attribute" 17 | sdkmetric "go.opentelemetry.io/otel/sdk/metric" 18 | "go.opentelemetry.io/otel/sdk/metric/metricdata" 19 | ) 20 | 21 | // deltaSelector sets delta aggregation temporality for monotonic counters and histograms. 22 | func deltaSelector(kind sdkmetric.InstrumentKind) metricdata.Temporality { 23 | switch kind { 24 | case sdkmetric.InstrumentKindCounter, 25 | sdkmetric.InstrumentKindHistogram, 26 | sdkmetric.InstrumentKindObservableGauge, 27 | sdkmetric.InstrumentKindObservableCounter: 28 | return metricdata.DeltaTemporality 29 | case sdkmetric.InstrumentKindUpDownCounter, 30 | sdkmetric.InstrumentKindObservableUpDownCounter: 31 | return metricdata.CumulativeTemporality 32 | } 33 | panic("unknown instrument kind") 34 | } 35 | 36 | // AssertHasSumMetric asserts that an OTLP metrics payload has 37 | // a single sum metric with a single datapoint and with the given name and value. 38 | func AssertHasSumMetric[N int64 | float64](t *testing.T, rm *metricdata.ResourceMetrics, name string, value int64) { 39 | var found bool 40 | for _, scopeMetric := range rm.ScopeMetrics { 41 | for _, metric := range scopeMetric.Metrics { 42 | if metric.Name == name { 43 | if !found { 44 | assert.Len(t, metric.Data.(metricdata.Sum[N]).DataPoints, 1) 45 | assert.Equal(t, value, metric.Data.(metricdata.Sum[N]).DataPoints[0].Value) 46 | found = true 47 | } else { 48 | assert.Fail(t, "metric %s found more than once", name) 49 | } 50 | } 51 | } 52 | } 53 | 54 | assert.True(t, found, "metric %s not found", name) 55 | } 56 | 57 | func TestInternalTelemetryMetrics(t *testing.T) { 58 | set := componenttest.NewNopTelemetrySettings() 59 | reader := sdkmetric.NewManualReader(sdkmetric.WithTemporalitySelector(deltaSelector)) 60 | set.MeterProvider = sdkmetric.NewMeterProvider(sdkmetric.WithReader(reader)) 61 | translator, err := NewTranslator(set) 62 | require.NoError(t, err) 63 | 64 | resWithHostname := pcommon.NewResource() 65 | resWithHostname.Attributes().FromRaw(map[string]any{ 66 | "datadog.host.name": "testhost", 67 | }) 68 | 69 | resWithoutHostname := pcommon.NewResource() 70 | 71 | attributeSet := attribute.NewSet(attribute.String("signal", "test")) 72 | 73 | _, _ = translator.ResourceToSource(context.Background(), resWithHostname, attributeSet, nil) 74 | _, _ = translator.ResourceToSource(context.Background(), resWithoutHostname, attributeSet, nil) 75 | _, _ = translator.ResourceToSource(context.Background(), resWithoutHostname, attributeSet, nil) 76 | 77 | rm := &metricdata.ResourceMetrics{} 78 | assert.NoError(t, reader.Collect(context.Background(), rm)) 79 | AssertHasSumMetric[int64](t, rm, missingSourceMetricName, 2) 80 | } 81 | -------------------------------------------------------------------------------- /pkg/otlp/logs/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright The OpenTelemetry Authors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // Package logs provides utils for transforming OTLP LogRecord to Datadog format 5 | package logs 6 | -------------------------------------------------------------------------------- /pkg/otlp/logs/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/logs 2 | 3 | go 1.23.0 4 | 5 | require ( 6 | github.com/DataDog/datadog-api-client-go/v2 v2.37.1 7 | github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.27.1 8 | github.com/stretchr/testify v1.10.0 9 | go.opentelemetry.io/collector/component v1.32.0 10 | go.opentelemetry.io/collector/component/componenttest v0.126.0 11 | go.opentelemetry.io/collector/pdata v1.32.0 12 | go.opentelemetry.io/collector/semconv v0.126.0 13 | go.opentelemetry.io/otel v1.36.0 14 | go.uber.org/zap v1.27.0 15 | ) 16 | 17 | require ( 18 | github.com/DataDog/zstd v1.5.6 // indirect 19 | github.com/davecgh/go-spew v1.1.1 // indirect 20 | github.com/go-logr/logr v1.4.2 // indirect 21 | github.com/go-logr/stdr v1.2.2 // indirect 22 | github.com/goccy/go-json v0.10.4 // indirect 23 | github.com/gogo/protobuf v1.3.2 // indirect 24 | github.com/google/uuid v1.6.0 // indirect 25 | github.com/hashicorp/go-version v1.7.0 // indirect 26 | github.com/json-iterator/go v1.1.12 // indirect 27 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 28 | github.com/modern-go/reflect2 v1.0.2 // indirect 29 | github.com/pmezard/go-difflib v1.0.0 // indirect 30 | go.opentelemetry.io/auto/sdk v1.1.0 // indirect 31 | go.opentelemetry.io/collector/featuregate v1.32.0 // indirect 32 | go.opentelemetry.io/collector/internal/telemetry v0.126.0 // indirect 33 | go.opentelemetry.io/contrib/bridges/otelzap v0.10.0 // indirect 34 | go.opentelemetry.io/otel/log v0.11.0 // indirect 35 | go.opentelemetry.io/otel/metric v1.36.0 // indirect 36 | go.opentelemetry.io/otel/sdk v1.36.0 // indirect 37 | go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect 38 | go.opentelemetry.io/otel/trace v1.36.0 // indirect 39 | go.uber.org/multierr v1.11.0 // indirect 40 | golang.org/x/net v0.39.0 // indirect 41 | golang.org/x/oauth2 v0.26.0 // indirect 42 | golang.org/x/sys v0.33.0 // indirect 43 | golang.org/x/text v0.24.0 // indirect 44 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect 45 | google.golang.org/grpc v1.72.0 // indirect 46 | google.golang.org/protobuf v1.36.6 // indirect 47 | gopkg.in/yaml.v3 v3.0.1 // indirect 48 | ) 49 | 50 | replace github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes => ../attributes 51 | 52 | retract v0.4.0 // see #107 53 | -------------------------------------------------------------------------------- /pkg/otlp/metrics/consumer.go: -------------------------------------------------------------------------------- 1 | // Copyright The OpenTelemetry Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package metrics 16 | 17 | import ( 18 | "context" 19 | "encoding" 20 | "fmt" 21 | 22 | pb "github.com/DataDog/datadog-agent/pkg/proto/pbgo/trace" 23 | "github.com/DataDog/opentelemetry-mapping-go/pkg/quantile" 24 | ) 25 | 26 | // DataType is a timeseries-style metric type. 27 | type DataType int 28 | 29 | var _ encoding.TextUnmarshaler = (*DataType)(nil) 30 | var _ encoding.TextMarshaler = Gauge 31 | 32 | const ( 33 | // Gauge is the Datadog Gauge metric type. 34 | Gauge DataType = iota 35 | // Count is the Datadog Count metric type. 36 | Count 37 | ) 38 | 39 | // UnmarshalText implements encoding.TextUnmarshaler. 40 | func (t *DataType) UnmarshalText(text []byte) error { 41 | switch string(text) { 42 | case "gauge": 43 | *t = Gauge 44 | case "count": 45 | *t = Count 46 | default: 47 | return fmt.Errorf("invalid metric data type %q", text) 48 | } 49 | return nil 50 | } 51 | 52 | // MarshalText implements encoding.TextMarshaler. 53 | func (t DataType) MarshalText() ([]byte, error) { 54 | switch t { 55 | case Gauge: 56 | return []byte("gauge"), nil 57 | case Count: 58 | return []byte("count"), nil 59 | } 60 | 61 | return nil, fmt.Errorf("invalid metric data type %d", t) 62 | } 63 | 64 | // TimeSeriesConsumer is timeseries consumer. 65 | type TimeSeriesConsumer interface { 66 | // ConsumeTimeSeries consumes a timeseries-style metric. 67 | ConsumeTimeSeries( 68 | ctx context.Context, 69 | dimensions *Dimensions, 70 | typ DataType, 71 | timestamp uint64, 72 | value float64, 73 | ) 74 | } 75 | 76 | // SketchConsumer is a pkg/quantile sketch consumer. 77 | type SketchConsumer interface { 78 | // ConsumeSketch consumes a pkg/quantile-style sketch. 79 | ConsumeSketch( 80 | ctx context.Context, 81 | dimensions *Dimensions, 82 | timestamp uint64, 83 | sketch *quantile.Sketch, 84 | ) 85 | } 86 | 87 | // Consumer is a metrics consumer. 88 | type Consumer interface { 89 | TimeSeriesConsumer 90 | SketchConsumer 91 | } 92 | 93 | // Deprecated: use WithStatsOut instead 94 | // APMStatsConsumer implementations are able to consume APM Stats generated by 95 | // a Translator. 96 | type APMStatsConsumer interface { 97 | // ConsumeAPMStats consumes the given StatsPayload. 98 | ConsumeAPMStats(*pb.ClientStatsPayload) 99 | } 100 | 101 | // HostConsumer is a hostname consumer. 102 | // It is an optional interface that can be implemented by a Consumer. 103 | type HostConsumer interface { 104 | // ConsumeHost consumes a hostname. 105 | ConsumeHost(host string) 106 | } 107 | 108 | // TagsConsumer is a tags consumer. 109 | // It is an optional interface that can be implemented by a Consumer. 110 | // Consumed tags are used for running metrics, and should represent 111 | // some resource running a Collector (e.g. Fargate task). 112 | type TagsConsumer interface { 113 | // ConsumeTag consumes a tag 114 | ConsumeTag(tag string) 115 | } 116 | -------------------------------------------------------------------------------- /pkg/otlp/metrics/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/metrics 2 | 3 | go 1.23.0 4 | 5 | require ( 6 | github.com/DataDog/datadog-agent/pkg/proto v0.68.0-devel 7 | github.com/DataDog/opentelemetry-mapping-go/pkg/internal/sketchtest v0.27.1 8 | github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.27.1 9 | github.com/DataDog/opentelemetry-mapping-go/pkg/quantile v0.27.1 10 | github.com/DataDog/sketches-go v1.4.7 11 | github.com/golang/protobuf v1.5.4 12 | github.com/lightstep/go-expohisto v1.0.0 13 | github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.126.0 14 | github.com/patrickmn/go-cache v2.1.0+incompatible 15 | github.com/stretchr/testify v1.10.0 16 | go.opentelemetry.io/collector/component v1.32.0 17 | go.opentelemetry.io/collector/component/componenttest v0.126.0 18 | go.opentelemetry.io/collector/pdata v1.32.0 19 | go.opentelemetry.io/otel v1.36.0 20 | go.uber.org/zap v1.27.0 21 | golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 22 | google.golang.org/protobuf v1.36.6 23 | ) 24 | 25 | require ( 26 | github.com/cespare/xxhash/v2 v2.3.0 // indirect 27 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 28 | github.com/dustin/go-humanize v1.0.1 // indirect 29 | github.com/go-logr/logr v1.4.2 // indirect 30 | github.com/go-logr/stdr v1.2.2 // indirect 31 | github.com/gogo/protobuf v1.3.2 // indirect 32 | github.com/google/uuid v1.6.0 // indirect 33 | github.com/hashicorp/go-version v1.7.0 // indirect 34 | github.com/json-iterator/go v1.1.12 // indirect 35 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 36 | github.com/modern-go/reflect2 v1.0.2 // indirect 37 | github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.126.0 // indirect 38 | github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect 39 | github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect 40 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect 41 | github.com/tinylib/msgp v1.2.5 // indirect 42 | go.opentelemetry.io/auto/sdk v1.1.0 // indirect 43 | go.opentelemetry.io/collector/featuregate v1.32.0 // indirect 44 | go.opentelemetry.io/collector/internal/telemetry v0.126.0 // indirect 45 | go.opentelemetry.io/collector/semconv v0.126.0 // indirect 46 | go.opentelemetry.io/contrib/bridges/otelzap v0.10.0 // indirect 47 | go.opentelemetry.io/otel/log v0.11.0 // indirect 48 | go.opentelemetry.io/otel/metric v1.36.0 // indirect 49 | go.opentelemetry.io/otel/sdk v1.36.0 // indirect 50 | go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect 51 | go.opentelemetry.io/otel/trace v1.36.0 // indirect 52 | go.uber.org/multierr v1.11.0 // indirect 53 | golang.org/x/net v0.39.0 // indirect 54 | golang.org/x/sys v0.33.0 // indirect 55 | golang.org/x/text v0.24.0 // indirect 56 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250425173222-7b384671a197 // indirect 57 | google.golang.org/grpc v1.72.0 // indirect 58 | gopkg.in/yaml.v3 v3.0.1 // indirect 59 | ) 60 | 61 | replace ( 62 | github.com/DataDog/opentelemetry-mapping-go/pkg/internal/sketchtest => ../../internal/sketchtest 63 | github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes => ../attributes 64 | github.com/DataDog/opentelemetry-mapping-go/pkg/quantile => ../../quantile 65 | ) 66 | 67 | retract v0.4.0 // see #107 68 | -------------------------------------------------------------------------------- /pkg/otlp/metrics/internal/instrumentationlibrary/metadata.go: -------------------------------------------------------------------------------- 1 | // Copyright The OpenTelemetry Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package instrumentationlibrary 16 | 17 | import ( 18 | "go.opentelemetry.io/collector/pdata/pcommon" 19 | 20 | "github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/metrics/internal/utils" 21 | ) 22 | 23 | const ( 24 | instrumentationLibraryTag = "instrumentation_library" 25 | instrumentationLibraryVersionTag = "instrumentation_library_version" 26 | ) 27 | 28 | // TagsFromInstrumentationLibraryMetadata takes the name and version of 29 | // the instrumentation library and converts them to Datadog tags. 30 | func TagsFromInstrumentationLibraryMetadata(il pcommon.InstrumentationScope) []string { 31 | return []string{ 32 | utils.FormatKeyValueTag(instrumentationLibraryTag, il.Name()), 33 | utils.FormatKeyValueTag(instrumentationLibraryVersionTag, il.Version()), 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /pkg/otlp/metrics/internal/instrumentationlibrary/metadata_test.go: -------------------------------------------------------------------------------- 1 | // Copyright The OpenTelemetry Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package instrumentationlibrary 16 | 17 | import ( 18 | "fmt" 19 | "testing" 20 | 21 | "github.com/stretchr/testify/assert" 22 | "go.opentelemetry.io/collector/pdata/pcommon" 23 | ) 24 | 25 | func TestTagsFromInstrumentationLibraryMetadata(t *testing.T) { 26 | tests := []struct { 27 | name string 28 | version string 29 | expectedTags []string 30 | }{ 31 | {"test-il", "1.0.0", []string{fmt.Sprintf("%s:%s", instrumentationLibraryTag, "test-il"), fmt.Sprintf("%s:%s", instrumentationLibraryVersionTag, "1.0.0")}}, 32 | {"test-il", "", []string{fmt.Sprintf("%s:%s", instrumentationLibraryTag, "test-il"), fmt.Sprintf("%s:%s", instrumentationLibraryVersionTag, "n/a")}}, 33 | {"", "1.0.0", []string{fmt.Sprintf("%s:%s", instrumentationLibraryTag, "n/a"), fmt.Sprintf("%s:%s", instrumentationLibraryVersionTag, "1.0.0")}}, 34 | {"", "", []string{fmt.Sprintf("%s:%s", instrumentationLibraryTag, "n/a"), fmt.Sprintf("%s:%s", instrumentationLibraryVersionTag, "n/a")}}, 35 | } 36 | 37 | for _, testInstance := range tests { 38 | il := pcommon.NewInstrumentationScope() 39 | il.SetName(testInstance.name) 40 | il.SetVersion(testInstance.version) 41 | tags := TagsFromInstrumentationLibraryMetadata(il) 42 | 43 | assert.ElementsMatch(t, testInstance.expectedTags, tags) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /pkg/otlp/metrics/internal/instrumentationscope/metadata.go: -------------------------------------------------------------------------------- 1 | // Copyright The OpenTelemetry Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package instrumentationscope 16 | 17 | import ( 18 | "go.opentelemetry.io/collector/pdata/pcommon" 19 | 20 | "github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/metrics/internal/utils" 21 | ) 22 | 23 | const ( 24 | instrumentationScopeTag = "instrumentation_scope" 25 | instrumentationScopeVersionTag = "instrumentation_scope_version" 26 | ) 27 | 28 | // TagsFromInstrumentationScopeMetadata takes the name, version, and attributes of 29 | // the instrumentation scope and converts them to Datadog tags. 30 | func TagsFromInstrumentationScopeMetadata(il pcommon.InstrumentationScope) []string { 31 | tags := make([]string, 0, 2+il.Attributes().Len()) 32 | tags = append(tags, utils.FormatKeyValueTag(instrumentationScopeTag, il.Name())) 33 | tags = append(tags, utils.FormatKeyValueTag(instrumentationScopeVersionTag, il.Version())) 34 | for k, v := range il.Attributes().Range { 35 | tags = append(tags, utils.FormatKeyValueTag(k, v.AsString())) 36 | } 37 | return tags 38 | } 39 | -------------------------------------------------------------------------------- /pkg/otlp/metrics/internal/instrumentationscope/metadata_test.go: -------------------------------------------------------------------------------- 1 | // Copyright The OpenTelemetry Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package instrumentationscope 16 | 17 | import ( 18 | "fmt" 19 | "testing" 20 | 21 | "github.com/stretchr/testify/assert" 22 | "go.opentelemetry.io/collector/pdata/pcommon" 23 | ) 24 | 25 | func TestTagsFromInstrumentationScopeMetadata(t *testing.T) { 26 | tests := []struct { 27 | name string 28 | version string 29 | attrs map[string]string 30 | expectedTags []string 31 | }{ 32 | { 33 | "test-il", "1.0.0", 34 | nil, 35 | []string{fmt.Sprintf("%s:%s", instrumentationScopeTag, "test-il"), fmt.Sprintf("%s:%s", instrumentationScopeVersionTag, "1.0.0")}, 36 | }, 37 | { 38 | "test-il", "", 39 | nil, 40 | []string{fmt.Sprintf("%s:%s", instrumentationScopeTag, "test-il"), fmt.Sprintf("%s:%s", instrumentationScopeVersionTag, "n/a")}, 41 | }, 42 | { 43 | "", "1.0.0", 44 | nil, 45 | []string{fmt.Sprintf("%s:%s", instrumentationScopeTag, "n/a"), fmt.Sprintf("%s:%s", instrumentationScopeVersionTag, "1.0.0")}, 46 | }, 47 | { 48 | "", "", 49 | nil, 50 | []string{fmt.Sprintf("%s:%s", instrumentationScopeTag, "n/a"), fmt.Sprintf("%s:%s", instrumentationScopeVersionTag, "n/a")}, 51 | }, 52 | { 53 | "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc", "0.60.0", 54 | map[string]string{ 55 | "otelcol.component.id": "otlp", 56 | "otelcol.component.kind": "Receiver", 57 | }, 58 | []string{ 59 | fmt.Sprintf("%s:%s", instrumentationScopeTag, "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"), 60 | fmt.Sprintf("%s:%s", instrumentationScopeVersionTag, "0.60.0"), 61 | "otelcol.component.id:otlp", 62 | "otelcol.component.kind:Receiver", 63 | }, 64 | }, 65 | } 66 | 67 | for _, testInstance := range tests { 68 | il := pcommon.NewInstrumentationScope() 69 | il.SetName(testInstance.name) 70 | il.SetVersion(testInstance.version) 71 | for k, v := range testInstance.attrs { 72 | il.Attributes().PutStr(k, v) 73 | } 74 | tags := TagsFromInstrumentationScopeMetadata(il) 75 | 76 | assert.ElementsMatch(t, testInstance.expectedTags, tags) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /pkg/otlp/metrics/internal/utils/tags.go: -------------------------------------------------------------------------------- 1 | // Copyright The OpenTelemetry Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package utils 16 | 17 | import ( 18 | "fmt" 19 | ) 20 | 21 | // FormatKeyValueTag takes a key-value pair, and creates a tag string out of it 22 | // Tags can't end with ":" so we replace empty values with "n/a" 23 | func FormatKeyValueTag(key, value string) string { 24 | if value == "" { 25 | value = "n/a" 26 | } 27 | return fmt.Sprintf("%s:%s", key, value) 28 | } 29 | -------------------------------------------------------------------------------- /pkg/otlp/metrics/internal/utils/tags_test.go: -------------------------------------------------------------------------------- 1 | // Copyright The OpenTelemetry Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package utils 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/stretchr/testify/assert" 21 | ) 22 | 23 | func TestFormatKeyValueTag(t *testing.T) { 24 | tests := []struct { 25 | key string 26 | value string 27 | expectedTag string 28 | }{ 29 | {"a.test.tag", "a.test.value", "a.test.tag:a.test.value"}, 30 | {"a.test.tag", "", "a.test.tag:n/a"}, 31 | } 32 | 33 | for _, testInstance := range tests { 34 | assert.Equal(t, testInstance.expectedTag, FormatKeyValueTag(testInstance.key, testInstance.value)) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /pkg/otlp/metrics/origin_test.go: -------------------------------------------------------------------------------- 1 | // Copyright The OpenTelemetry Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package metrics 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/stretchr/testify/assert" 21 | ) 22 | 23 | func TestOriginProductDetailFromScopeName(t *testing.T) { 24 | tests := []struct { 25 | scopeName string 26 | expected OriginProductDetail 27 | }{ 28 | { 29 | scopeName: "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/notsupportedreceiver", 30 | expected: OriginProductDetailUnknown, 31 | }, 32 | { 33 | scopeName: "otelcol/kubeletstatsreceiver", 34 | expected: OriginProductDetailUnknown, 35 | }, 36 | { 37 | scopeName: "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/kubeletstatsreceiver", 38 | expected: OriginProductDetailKubeletStatsReceiver, 39 | }, 40 | { 41 | scopeName: "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/memory", 42 | expected: OriginProductDetailHostMetricsReceiver, 43 | }, 44 | { 45 | scopeName: "go.opentelemetry.io/otel/metric/example", 46 | expected: OriginProductDetailUnknown, 47 | }, 48 | } 49 | for _, tt := range tests { 50 | t.Run(tt.scopeName, func(t *testing.T) { 51 | service := originProductDetailFromScopeName(tt.scopeName) 52 | assert.Equal(t, tt.expected, service) 53 | }) 54 | } 55 | } 56 | 57 | func TestOriginFull(t *testing.T) { 58 | translator := NewTestTranslator(t, WithOriginProduct(OriginProduct(42))) 59 | AssertTranslatorMap(t, translator, 60 | "testdata/otlpdata/origin/origin.json", 61 | "testdata/datadogdata/origin/origin.json", 62 | ) 63 | } 64 | -------------------------------------------------------------------------------- /pkg/otlp/metrics/statspayload.go: -------------------------------------------------------------------------------- 1 | // Copyright The OpenTelemetry Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package metrics 16 | 17 | import ( 18 | pb "github.com/DataDog/datadog-agent/pkg/proto/pbgo/trace" 19 | "github.com/golang/protobuf/proto" 20 | "go.opentelemetry.io/collector/pdata/pmetric" 21 | "go.uber.org/zap" 22 | ) 23 | 24 | // keyStatsPayload is the key for the stats payload in the attributes map. 25 | // This is used as Metric name and Attribute key. 26 | const keyStatsPayload = "dd.internal.stats.payload" 27 | 28 | // StatsToMetrics converts a StatsPayload to a pdata.Metrics 29 | func (t *Translator) StatsToMetrics(sp *pb.StatsPayload) (pmetric.Metrics, error) { 30 | bytes, err := proto.Marshal(sp) 31 | if err != nil { 32 | t.logger.Error("Failed to marshal stats payload", zap.Error(err)) 33 | return pmetric.NewMetrics(), err 34 | } 35 | mmx := pmetric.NewMetrics() 36 | rmx := mmx.ResourceMetrics().AppendEmpty() 37 | smx := rmx.ScopeMetrics().AppendEmpty() 38 | mslice := smx.Metrics() 39 | mx := mslice.AppendEmpty() 40 | mx.SetName(keyStatsPayload) 41 | sum := mx.SetEmptySum() 42 | sum.SetIsMonotonic(false) 43 | dp := sum.DataPoints().AppendEmpty() 44 | byteSlice := dp.Attributes().PutEmptyBytes(keyStatsPayload) 45 | byteSlice.Append(bytes...) 46 | return mmx, nil 47 | } 48 | -------------------------------------------------------------------------------- /pkg/otlp/metrics/summary_test.go: -------------------------------------------------------------------------------- 1 | // Unless explicitly stated otherwise all files in this repository are licensed 2 | // under the Apache License Version 2.0. 3 | // This product includes software developed at Datadog (https://www.datadoghq.com/). 4 | // Copyright 2022-present Datadog, Inc. 5 | 6 | package metrics 7 | 8 | import ( 9 | "testing" 10 | ) 11 | 12 | func TestSummaryMetrics(t *testing.T) { 13 | tests := []struct { 14 | name string 15 | otlpfile string 16 | ddogfile string 17 | options []TranslatorOption 18 | }{ 19 | { 20 | name: "summary", 21 | otlpfile: "testdata/otlpdata/summary/simple.json", 22 | ddogfile: "testdata/datadogdata/summary/simple_summary.json", 23 | options: []TranslatorOption{WithFallbackSourceProvider(testProvider("fallbackHostname"))}, 24 | }, 25 | { 26 | name: "summary-cumsum-keep", 27 | otlpfile: "testdata/otlpdata/summary/simple.json", 28 | ddogfile: "testdata/datadogdata/summary/simple_summary_cumsum-keep.json", 29 | options: []TranslatorOption{ 30 | WithFallbackSourceProvider(testProvider("fallbackHostname")), 31 | WithInitialCumulMonoValueMode(InitialCumulMonoValueModeKeep), 32 | }, 33 | }, 34 | { 35 | name: "summary-with-quantiles", 36 | otlpfile: "testdata/otlpdata/summary/simple.json", 37 | ddogfile: "testdata/datadogdata/summary/simple_summary-with-quantile.json", 38 | options: []TranslatorOption{ 39 | WithFallbackSourceProvider(testProvider("fallbackHostname")), 40 | WithQuantiles(), 41 | }, 42 | }, 43 | { 44 | name: "summary-with-attributes", 45 | otlpfile: "testdata/otlpdata/summary/with-attributes.json", 46 | ddogfile: "testdata/datadogdata/summary/with-attributes_summary.json", 47 | options: []TranslatorOption{WithFallbackSourceProvider(testProvider("fallbackHostname"))}, 48 | }, 49 | { 50 | name: "summary-with-attributes-quantiles", 51 | otlpfile: "testdata/otlpdata/summary/with-attributes.json", 52 | ddogfile: "testdata/datadogdata/summary/with-attributes-quantile_summary.json", 53 | options: []TranslatorOption{ 54 | WithFallbackSourceProvider(testProvider("fallbackHostname")), 55 | WithQuantiles(), 56 | }, 57 | }, 58 | } 59 | 60 | for _, testinstance := range tests { 61 | t.Run(testinstance.name, func(t *testing.T) { 62 | options := append( 63 | []TranslatorOption{WithOriginProduct(OriginProductDatadogAgent)}, 64 | testinstance.options..., 65 | ) 66 | translator := NewTestTranslator(t, options...) 67 | AssertTranslatorMap(t, translator, testinstance.otlpfile, testinstance.ddogfile) 68 | }) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/histogram/empty-cumulative-exponential.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/histogram/empty-delta-exponential.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/histogram/empty-delta-no-min-max.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": [ 3 | { 4 | "Name": "doubleHist.empty.test", 5 | "Tags": [ 6 | "attribute_tag:attribute_value" 7 | ], 8 | "Host": "hostname", 9 | "OriginID": "", 10 | "OriginProduct": 10, 11 | "OriginSubProduct": 17, 12 | "OriginProductDetail": 0, 13 | "Timestamp": 1667560641226420924, 14 | "Summary": { 15 | "Min": -9.941854089121368e-10, 16 | "Max": -9.941854089121368e-10, 17 | "Sum": 3.141592653589793, 18 | "Avg": 0.15707963267948966, 19 | "Cnt": 20 20 | }, 21 | "Keys": [ 22 | -1 23 | ], 24 | "Counts": [ 25 | 20 26 | ] 27 | } 28 | ], 29 | "TimeSeries": null 30 | } -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/histogram/empty-delta-with-min-max.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": [ 3 | { 4 | "Name": "doubleHist.empty.minmax.test", 5 | "Tags": [ 6 | "attribute_tag:attribute_value" 7 | ], 8 | "Host": "hostname", 9 | "OriginID": "", 10 | "OriginProduct": 10, 11 | "OriginSubProduct": 17, 12 | "OriginProductDetail": 0, 13 | "Timestamp": 1667560641226420924, 14 | "Summary": { 15 | "Min": 100, 16 | "Max": 101, 17 | "Sum": 3.141592653589793, 18 | "Avg": 0.15707963267948966, 19 | "Cnt": 20 20 | }, 21 | "Keys": [ 22 | 1635 23 | ], 24 | "Counts": [ 25 | 20 26 | ] 27 | } 28 | ], 29 | "TimeSeries": null 30 | } -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/histogram/simple-cumulative_counters-cs.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": null, 3 | "TimeSeries": [ 4 | { 5 | "Name": "doubleHist.test.count", 6 | "Tags": [], 7 | "Host": "hostname", 8 | "OriginID": "", 9 | "OriginProduct": 10, 10 | "OriginSubProduct": 17, 11 | "OriginProductDetail": 0, 12 | "Type": "count", 13 | "Timestamp": 1667560641226420925, 14 | "Value": 30 15 | }, 16 | { 17 | "Name": "doubleHist.test.sum", 18 | "Tags": [], 19 | "Host": "hostname", 20 | "OriginID": "", 21 | "OriginProduct": 10, 22 | "OriginSubProduct": 17, 23 | "OriginProductDetail": 0, 24 | "Type": "count", 25 | "Timestamp": 1667560641226420925, 26 | "Value": 20 27 | }, 28 | { 29 | "Name": "doubleHist.test.bucket", 30 | "Tags": [ 31 | "lower_bound:-inf", 32 | "upper_bound:0" 33 | ], 34 | "Host": "hostname", 35 | "OriginID": "", 36 | "OriginProduct": 10, 37 | "OriginSubProduct": 17, 38 | "OriginProductDetail": 0, 39 | "Type": "count", 40 | "Timestamp": 1667560641226420925, 41 | "Value": 11 42 | }, 43 | { 44 | "Name": "doubleHist.test.bucket", 45 | "Tags": [ 46 | "lower_bound:0", 47 | "upper_bound:inf" 48 | ], 49 | "Host": "hostname", 50 | "OriginID": "", 51 | "OriginProduct": 10, 52 | "OriginSubProduct": 17, 53 | "OriginProductDetail": 0, 54 | "Type": "count", 55 | "Timestamp": 1667560641226420925, 56 | "Value": 19 57 | } 58 | ] 59 | } -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/histogram/simple-cumulative_counters-nocs.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": null, 3 | "TimeSeries": [ 4 | { 5 | "Name": "doubleHist.test.bucket", 6 | "Tags": [ 7 | "lower_bound:-inf", 8 | "upper_bound:0" 9 | ], 10 | "Host": "hostname", 11 | "OriginID": "", 12 | "OriginProduct": 10, 13 | "OriginSubProduct": 17, 14 | "OriginProductDetail": 0, 15 | "Type": "count", 16 | "Timestamp": 1667560641226420925, 17 | "Value": 11 18 | }, 19 | { 20 | "Name": "doubleHist.test.bucket", 21 | "Tags": [ 22 | "lower_bound:0", 23 | "upper_bound:inf" 24 | ], 25 | "Host": "hostname", 26 | "OriginID": "", 27 | "OriginProduct": 10, 28 | "OriginSubProduct": 17, 29 | "OriginProductDetail": 0, 30 | "Type": "count", 31 | "Timestamp": 1667560641226420925, 32 | "Value": 19 33 | } 34 | ] 35 | } -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/histogram/simple-cumulative_dist-cs.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": [ 3 | { 4 | "Name": "doubleHist.test", 5 | "Tags": [], 6 | "Host": "hostname", 7 | "OriginID": "", 8 | "OriginProduct": 10, 9 | "OriginSubProduct": 17, 10 | "OriginProductDetail": 0, 11 | "Timestamp": 1667560641226420925, 12 | "Summary": { 13 | "Min": -11, 14 | "Max": 0, 15 | "Sum": 20, 16 | "Avg": 0.6666666666666666, 17 | "Cnt": 30 18 | }, 19 | "Keys": [ 20 | 0 21 | ], 22 | "Counts": [ 23 | 30 24 | ] 25 | } 26 | ], 27 | "TimeSeries": [ 28 | { 29 | "Name": "doubleHist.test.count", 30 | "Tags": [], 31 | "Host": "hostname", 32 | "OriginID": "", 33 | "OriginProduct": 10, 34 | "OriginSubProduct": 17, 35 | "OriginProductDetail": 0, 36 | "Type": "count", 37 | "Timestamp": 1667560641226420925, 38 | "Value": 30 39 | }, 40 | { 41 | "Name": "doubleHist.test.sum", 42 | "Tags": [], 43 | "Host": "hostname", 44 | "OriginID": "", 45 | "OriginProduct": 10, 46 | "OriginSubProduct": 17, 47 | "OriginProductDetail": 0, 48 | "Type": "count", 49 | "Timestamp": 1667560641226420925, 50 | "Value": 20 51 | } 52 | ] 53 | } -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/histogram/simple-cumulative_dist-nocs.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": [ 3 | { 4 | "Name": "doubleHist.test", 5 | "Tags": [], 6 | "Host": "hostname", 7 | "OriginID": "", 8 | "OriginProduct": 10, 9 | "OriginSubProduct": 17, 10 | "OriginProductDetail": 0, 11 | "Timestamp": 1667560641226420925, 12 | "Summary": { 13 | "Min": -11, 14 | "Max": 0, 15 | "Sum": 20, 16 | "Avg": 0.6666666666666666, 17 | "Cnt": 30 18 | }, 19 | "Keys": [ 20 | 0 21 | ], 22 | "Counts": [ 23 | 30 24 | ] 25 | } 26 | ], 27 | "TimeSeries": null 28 | } -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/histogram/simple-cumulative_nobuckets-cs.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": null, 3 | "TimeSeries": [ 4 | { 5 | "Name": "doubleHist.test.count", 6 | "Tags": [], 7 | "Host": "hostname", 8 | "OriginID": "", 9 | "OriginProduct": 10, 10 | "OriginSubProduct": 17, 11 | "OriginProductDetail": 0, 12 | "Type": "count", 13 | "Timestamp": 1667560641226420925, 14 | "Value": 30 15 | }, 16 | { 17 | "Name": "doubleHist.test.sum", 18 | "Tags": [], 19 | "Host": "hostname", 20 | "OriginID": "", 21 | "OriginProduct": 10, 22 | "OriginSubProduct": 17, 23 | "OriginProductDetail": 0, 24 | "Type": "count", 25 | "Timestamp": 1667560641226420925, 26 | "Value": 20 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/histogram/simple-delta-min-max_dist-nocs.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": [ 3 | { 4 | "Name": "doubleHist.test.minmax", 5 | "Tags": [ 6 | "attribute_tag:attribute_value" 7 | ], 8 | "Host": "hostname", 9 | "OriginID": "", 10 | "OriginProduct": 10, 11 | "OriginSubProduct": 17, 12 | "OriginProductDetail": 0, 13 | "Timestamp": 1667560641226420924, 14 | "Summary": { 15 | "Min": -100, 16 | "Max": 99.95733062532716, 17 | "Sum": 3.141592653589793, 18 | "Avg": 0.15707963267948966, 19 | "Cnt": 20 20 | }, 21 | "Keys": [ 22 | -1635, 23 | -1590, 24 | 0, 25 | 1453, 26 | 1498, 27 | 1524, 28 | 1543, 29 | 1558, 30 | 1570, 31 | 1580, 32 | 1589, 33 | 1597, 34 | 1604, 35 | 1610, 36 | 1616, 37 | 1621, 38 | 1626, 39 | 1631, 40 | 1635 41 | ], 42 | "Counts": [ 43 | 1, 44 | 1, 45 | 1, 46 | 1, 47 | 1, 48 | 1, 49 | 1, 50 | 1, 51 | 1, 52 | 1, 53 | 1, 54 | 1, 55 | 1, 56 | 1, 57 | 1, 58 | 1, 59 | 1, 60 | 1, 61 | 2 62 | ] 63 | } 64 | ], 65 | "TimeSeries": null 66 | } -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/histogram/simple-delta_counters-cs.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": null, 3 | "TimeSeries": [ 4 | { 5 | "Name": "doubleHist.test.count", 6 | "Tags": [ 7 | "attribute_tag:attribute_value" 8 | ], 9 | "Host": "hostname", 10 | "OriginID": "", 11 | "OriginProduct": 10, 12 | "OriginSubProduct": 17, 13 | "OriginProductDetail": 0, 14 | "Type": "count", 15 | "Timestamp": 1667560641226420924, 16 | "Value": 20 17 | }, 18 | { 19 | "Name": "doubleHist.test.sum", 20 | "Tags": [ 21 | "attribute_tag:attribute_value" 22 | ], 23 | "Host": "hostname", 24 | "OriginID": "", 25 | "OriginProduct": 10, 26 | "OriginSubProduct": 17, 27 | "OriginProductDetail": 0, 28 | "Type": "count", 29 | "Timestamp": 1667560641226420924, 30 | "Value": 3.141592653589793 31 | }, 32 | { 33 | "Name": "doubleHist.test.min", 34 | "Tags": [ 35 | "attribute_tag:attribute_value" 36 | ], 37 | "Host": "hostname", 38 | "OriginID": "", 39 | "OriginProduct": 10, 40 | "OriginSubProduct": 17, 41 | "OriginProductDetail": 0, 42 | "Type": "gauge", 43 | "Timestamp": 1667560641226420924, 44 | "Value": -100 45 | }, 46 | { 47 | "Name": "doubleHist.test.max", 48 | "Tags": [ 49 | "attribute_tag:attribute_value" 50 | ], 51 | "Host": "hostname", 52 | "OriginID": "", 53 | "OriginProduct": 10, 54 | "OriginSubProduct": 17, 55 | "OriginProductDetail": 0, 56 | "Type": "gauge", 57 | "Timestamp": 1667560641226420924, 58 | "Value": 100 59 | }, 60 | { 61 | "Name": "doubleHist.test.bucket", 62 | "Tags": [ 63 | "lower_bound:-inf", 64 | "upper_bound:0", 65 | "attribute_tag:attribute_value" 66 | ], 67 | "Host": "hostname", 68 | "OriginID": "", 69 | "OriginProduct": 10, 70 | "OriginSubProduct": 17, 71 | "OriginProductDetail": 0, 72 | "Type": "count", 73 | "Timestamp": 1667560641226420924, 74 | "Value": 2 75 | }, 76 | { 77 | "Name": "doubleHist.test.bucket", 78 | "Tags": [ 79 | "lower_bound:0", 80 | "upper_bound:inf", 81 | "attribute_tag:attribute_value" 82 | ], 83 | "Host": "hostname", 84 | "OriginID": "", 85 | "OriginProduct": 10, 86 | "OriginSubProduct": 17, 87 | "OriginProductDetail": 0, 88 | "Type": "count", 89 | "Timestamp": 1667560641226420924, 90 | "Value": 18 91 | } 92 | ] 93 | } -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/histogram/simple-delta_counters-nocs.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": null, 3 | "TimeSeries": [ 4 | { 5 | "Name": "doubleHist.test.bucket", 6 | "Tags": [ 7 | "lower_bound:-inf", 8 | "upper_bound:0", 9 | "attribute_tag:attribute_value" 10 | ], 11 | "Host": "hostname", 12 | "OriginID": "", 13 | "OriginProduct": 10, 14 | "OriginSubProduct": 17, 15 | "OriginProductDetail": 0, 16 | "Type": "count", 17 | "Timestamp": 1667560641226420924, 18 | "Value": 2 19 | }, 20 | { 21 | "Name": "doubleHist.test.bucket", 22 | "Tags": [ 23 | "lower_bound:0", 24 | "upper_bound:inf", 25 | "attribute_tag:attribute_value" 26 | ], 27 | "Host": "hostname", 28 | "OriginID": "", 29 | "OriginProduct": 10, 30 | "OriginSubProduct": 17, 31 | "OriginProductDetail": 0, 32 | "Type": "count", 33 | "Timestamp": 1667560641226420924, 34 | "Value": 18 35 | } 36 | ] 37 | } -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/histogram/simple-delta_dist-cs.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": [ 3 | { 4 | "Name": "doubleHist.test", 5 | "Tags": [ 6 | "attribute_tag:attribute_value" 7 | ], 8 | "Host": "hostname", 9 | "OriginID": "", 10 | "OriginProduct": 10, 11 | "OriginSubProduct": 17, 12 | "OriginProductDetail": 0, 13 | "Timestamp": 1667560641226420924, 14 | "Summary": { 15 | "Min": -100, 16 | "Max": 100, 17 | "Sum": 3.141592653589793, 18 | "Avg": 0.15707963267948966, 19 | "Cnt": 20 20 | }, 21 | "Keys": [ 22 | 0 23 | ], 24 | "Counts": [ 25 | 20 26 | ] 27 | } 28 | ], 29 | "TimeSeries": [ 30 | { 31 | "Name": "doubleHist.test.count", 32 | "Tags": [ 33 | "attribute_tag:attribute_value" 34 | ], 35 | "Host": "hostname", 36 | "OriginID": "", 37 | "OriginProduct": 10, 38 | "OriginSubProduct": 17, 39 | "OriginProductDetail": 0, 40 | "Type": "count", 41 | "Timestamp": 1667560641226420924, 42 | "Value": 20 43 | }, 44 | { 45 | "Name": "doubleHist.test.sum", 46 | "Tags": [ 47 | "attribute_tag:attribute_value" 48 | ], 49 | "Host": "hostname", 50 | "OriginID": "", 51 | "OriginProduct": 10, 52 | "OriginSubProduct": 17, 53 | "OriginProductDetail": 0, 54 | "Type": "count", 55 | "Timestamp": 1667560641226420924, 56 | "Value": 3.141592653589793 57 | }, 58 | { 59 | "Name": "doubleHist.test.min", 60 | "Tags": [ 61 | "attribute_tag:attribute_value" 62 | ], 63 | "Host": "hostname", 64 | "OriginID": "", 65 | "OriginProduct": 10, 66 | "OriginSubProduct": 17, 67 | "OriginProductDetail": 0, 68 | "Type": "gauge", 69 | "Timestamp": 1667560641226420924, 70 | "Value": -100 71 | }, 72 | { 73 | "Name": "doubleHist.test.max", 74 | "Tags": [ 75 | "attribute_tag:attribute_value" 76 | ], 77 | "Host": "hostname", 78 | "OriginID": "", 79 | "OriginProduct": 10, 80 | "OriginSubProduct": 17, 81 | "OriginProductDetail": 0, 82 | "Type": "gauge", 83 | "Timestamp": 1667560641226420924, 84 | "Value": 100 85 | } 86 | ] 87 | } -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/histogram/simple-delta_dist-nocs.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": [ 3 | { 4 | "Name": "doubleHist.test", 5 | "Tags": [ 6 | "attribute_tag:attribute_value" 7 | ], 8 | "Host": "hostname", 9 | "OriginID": "", 10 | "OriginProduct": 10, 11 | "OriginSubProduct": 17, 12 | "OriginProductDetail": 0, 13 | "Timestamp": 1667560641226420924, 14 | "Summary": { 15 | "Min": -100, 16 | "Max": 100, 17 | "Sum": 3.141592653589793, 18 | "Avg": 0.15707963267948966, 19 | "Cnt": 20 20 | }, 21 | "Keys": [ 22 | 0 23 | ], 24 | "Counts": [ 25 | 20 26 | ] 27 | } 28 | ], 29 | "TimeSeries": null 30 | } -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/histogram/simple-delta_nobuckets-cs.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": null, 3 | "TimeSeries": [ 4 | { 5 | "Name": "doubleHist.test.count", 6 | "Tags": [ 7 | "attribute_tag:attribute_value" 8 | ], 9 | "Host": "hostname", 10 | "OriginID": "", 11 | "OriginProduct": 10, 12 | "OriginSubProduct": 17, 13 | "OriginProductDetail": 0, 14 | "Type": "count", 15 | "Timestamp": 1667560641226420924, 16 | "Value": 20 17 | }, 18 | { 19 | "Name": "doubleHist.test.sum", 20 | "Tags": [ 21 | "attribute_tag:attribute_value" 22 | ], 23 | "Host": "hostname", 24 | "OriginID": "", 25 | "OriginProduct": 10, 26 | "OriginSubProduct": 17, 27 | "OriginProductDetail": 0, 28 | "Type": "count", 29 | "Timestamp": 1667560641226420924, 30 | "Value": 3.141592653589793 31 | }, 32 | { 33 | "Name": "doubleHist.test.min", 34 | "Tags": [ 35 | "attribute_tag:attribute_value" 36 | ], 37 | "Host": "hostname", 38 | "OriginID": "", 39 | "OriginProduct": 10, 40 | "OriginSubProduct": 17, 41 | "OriginProductDetail": 0, 42 | "Type": "gauge", 43 | "Timestamp": 1667560641226420924, 44 | "Value": -100 45 | }, 46 | { 47 | "Name": "doubleHist.test.max", 48 | "Tags": [ 49 | "attribute_tag:attribute_value" 50 | ], 51 | "Host": "hostname", 52 | "OriginID": "", 53 | "OriginProduct": 10, 54 | "OriginSubProduct": 17, 55 | "OriginProductDetail": 0, 56 | "Type": "gauge", 57 | "Timestamp": 1667560641226420924, 58 | "Value": 100 59 | } 60 | ] 61 | } 62 | -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/histogram/simple-exponential.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": [ 3 | { 4 | "Name": "double.exponential.delta.histogram", 5 | "Tags": [ 6 | "custom_attribute:custom_value", 7 | "deployment.environment:dev" 8 | ], 9 | "Host": "res-hostname", 10 | "OriginID": "", 11 | "OriginProduct": 10, 12 | "OriginSubProduct": 17, 13 | "OriginProductDetail": 0, 14 | "Timestamp": 1667560641226420924, 15 | "Summary": { 16 | "Min": -100000, 17 | "Max": 100000, 18 | "Sum": 3.141592653589793, 19 | "Avg": 0.10471975511965977, 20 | "Cnt": 30 21 | }, 22 | "Keys": [ 23 | -1341, 24 | -1340, 25 | -1339, 26 | 0, 27 | 1340, 28 | 1341, 29 | 1342, 30 | 1343, 31 | 1344 32 | ], 33 | "Counts": [ 34 | 6, 35 | 4, 36 | 0, 37 | 10, 38 | 1, 39 | 1, 40 | 2, 41 | 3, 42 | 3 43 | ] 44 | } 45 | ], 46 | "TimeSeries": null 47 | } -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/histogram/simple-exponential_all.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": [ 3 | { 4 | "Name": "double.exponential.delta.histogram", 5 | "Tags": [ 6 | "custom_attribute:custom_value", 7 | "deployment.environment:dev", 8 | "instrumentation_scope:foo", 9 | "instrumentation_scope_version:1.0.0" 10 | ], 11 | "Host": "res-hostname", 12 | "OriginID": "", 13 | "OriginProduct": 10, 14 | "OriginSubProduct": 17, 15 | "OriginProductDetail": 0, 16 | "Timestamp": 1667560641226420924, 17 | "Summary": { 18 | "Min": -100000, 19 | "Max": 100000, 20 | "Sum": 3.141592653589793, 21 | "Avg": 0.10471975511965977, 22 | "Cnt": 30 23 | }, 24 | "Keys": [ 25 | -1341, 26 | -1340, 27 | -1339, 28 | 0, 29 | 1340, 30 | 1341, 31 | 1342, 32 | 1343, 33 | 1344 34 | ], 35 | "Counts": [ 36 | 6, 37 | 4, 38 | 0, 39 | 10, 40 | 1, 41 | 1, 42 | 2, 43 | 3, 44 | 3 45 | ] 46 | } 47 | ], 48 | "TimeSeries": [ 49 | { 50 | "Name": "double.exponential.delta.histogram.count", 51 | "Tags": [ 52 | "custom_attribute:custom_value", 53 | "deployment.environment:dev", 54 | "instrumentation_scope:foo", 55 | "instrumentation_scope_version:1.0.0" 56 | ], 57 | "Host": "res-hostname", 58 | "OriginID": "", 59 | "OriginProduct": 10, 60 | "OriginSubProduct": 17, 61 | "OriginProductDetail": 0, 62 | "Type": "count", 63 | "Timestamp": 1667560641226420924, 64 | "Value": 30 65 | }, 66 | { 67 | "Name": "double.exponential.delta.histogram.sum", 68 | "Tags": [ 69 | "custom_attribute:custom_value", 70 | "deployment.environment:dev", 71 | "instrumentation_scope:foo", 72 | "instrumentation_scope_version:1.0.0" 73 | ], 74 | "Host": "res-hostname", 75 | "OriginID": "", 76 | "OriginProduct": 10, 77 | "OriginSubProduct": 17, 78 | "OriginProductDetail": 0, 79 | "Type": "count", 80 | "Timestamp": 1667560641226420924, 81 | "Value": 3.141592653589793 82 | }, 83 | { 84 | "Name": "double.exponential.delta.histogram.min", 85 | "Tags": [ 86 | "custom_attribute:custom_value", 87 | "deployment.environment:dev", 88 | "instrumentation_scope:foo", 89 | "instrumentation_scope_version:1.0.0" 90 | ], 91 | "Host": "res-hostname", 92 | "OriginID": "", 93 | "OriginProduct": 10, 94 | "OriginSubProduct": 17, 95 | "OriginProductDetail": 0, 96 | "Type": "gauge", 97 | "Timestamp": 1667560641226420924, 98 | "Value": -100000 99 | }, 100 | { 101 | "Name": "double.exponential.delta.histogram.max", 102 | "Tags": [ 103 | "custom_attribute:custom_value", 104 | "deployment.environment:dev", 105 | "instrumentation_scope:foo", 106 | "instrumentation_scope_version:1.0.0" 107 | ], 108 | "Host": "res-hostname", 109 | "OriginID": "", 110 | "OriginProduct": 10, 111 | "OriginSubProduct": 17, 112 | "OriginProductDetail": 0, 113 | "Type": "gauge", 114 | "Timestamp": 1667560641226420924, 115 | "Value": 100000 116 | } 117 | ] 118 | } -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/histogram/simple-exponential_cs-both-tags.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": [ 3 | { 4 | "Name": "double.exponential.delta.histogram", 5 | "Tags": [ 6 | "custom_attribute:custom_value", 7 | "deployment.environment:dev", 8 | "instrumentation_library:foo", 9 | "instrumentation_library_version:1.0.0" 10 | ], 11 | "Host": "res-hostname", 12 | "OriginID": "", 13 | "OriginProduct": 10, 14 | "OriginSubProduct": 17, 15 | "OriginProductDetail": 0, 16 | "Timestamp": 1667560641226420924, 17 | "Summary": { 18 | "Min": -100000, 19 | "Max": 100000, 20 | "Sum": 3.141592653589793, 21 | "Avg": 0.10471975511965977, 22 | "Cnt": 30 23 | }, 24 | "Keys": [ 25 | -1341, 26 | -1340, 27 | -1339, 28 | 0, 29 | 1340, 30 | 1341, 31 | 1342, 32 | 1343, 33 | 1344 34 | ], 35 | "Counts": [ 36 | 6, 37 | 4, 38 | 0, 39 | 10, 40 | 1, 41 | 1, 42 | 2, 43 | 3, 44 | 3 45 | ] 46 | } 47 | ], 48 | "TimeSeries": [ 49 | { 50 | "Name": "double.exponential.delta.histogram.count", 51 | "Tags": [ 52 | "custom_attribute:custom_value", 53 | "deployment.environment:dev", 54 | "instrumentation_library:foo", 55 | "instrumentation_library_version:1.0.0" 56 | ], 57 | "Host": "res-hostname", 58 | "OriginID": "", 59 | "OriginProduct": 10, 60 | "OriginSubProduct": 17, 61 | "OriginProductDetail": 0, 62 | "Type": "count", 63 | "Timestamp": 1667560641226420924, 64 | "Value": 30 65 | }, 66 | { 67 | "Name": "double.exponential.delta.histogram.sum", 68 | "Tags": [ 69 | "custom_attribute:custom_value", 70 | "deployment.environment:dev", 71 | "instrumentation_library:foo", 72 | "instrumentation_library_version:1.0.0" 73 | ], 74 | "Host": "res-hostname", 75 | "OriginID": "", 76 | "OriginProduct": 10, 77 | "OriginSubProduct": 17, 78 | "OriginProductDetail": 0, 79 | "Type": "count", 80 | "Timestamp": 1667560641226420924, 81 | "Value": 3.141592653589793 82 | }, 83 | { 84 | "Name": "double.exponential.delta.histogram.min", 85 | "Tags": [ 86 | "custom_attribute:custom_value", 87 | "deployment.environment:dev", 88 | "instrumentation_library:foo", 89 | "instrumentation_library_version:1.0.0" 90 | ], 91 | "Host": "res-hostname", 92 | "OriginID": "", 93 | "OriginProduct": 10, 94 | "OriginSubProduct": 17, 95 | "OriginProductDetail": 0, 96 | "Type": "gauge", 97 | "Timestamp": 1667560641226420924, 98 | "Value": -100000 99 | }, 100 | { 101 | "Name": "double.exponential.delta.histogram.max", 102 | "Tags": [ 103 | "custom_attribute:custom_value", 104 | "deployment.environment:dev", 105 | "instrumentation_library:foo", 106 | "instrumentation_library_version:1.0.0" 107 | ], 108 | "Host": "res-hostname", 109 | "OriginID": "", 110 | "OriginProduct": 10, 111 | "OriginSubProduct": 17, 112 | "OriginProductDetail": 0, 113 | "Type": "gauge", 114 | "Timestamp": 1667560641226420924, 115 | "Value": 100000 116 | } 117 | ] 118 | } -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/histogram/simple-exponential_cs-ilmd-tags.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": [ 3 | { 4 | "Name": "double.exponential.delta.histogram", 5 | "Tags": [ 6 | "custom_attribute:custom_value", 7 | "deployment.environment:dev", 8 | "instrumentation_library:foo", 9 | "instrumentation_library_version:1.0.0" 10 | ], 11 | "Host": "res-hostname", 12 | "OriginID": "", 13 | "OriginProduct": 10, 14 | "OriginSubProduct": 17, 15 | "OriginProductDetail": 0, 16 | "Timestamp": 1667560641226420924, 17 | "Summary": { 18 | "Min": -100000, 19 | "Max": 100000, 20 | "Sum": 3.141592653589793, 21 | "Avg": 0.10471975511965977, 22 | "Cnt": 30 23 | }, 24 | "Keys": [ 25 | -1341, 26 | -1340, 27 | -1339, 28 | 0, 29 | 1340, 30 | 1341, 31 | 1342, 32 | 1343, 33 | 1344 34 | ], 35 | "Counts": [ 36 | 6, 37 | 4, 38 | 0, 39 | 10, 40 | 1, 41 | 1, 42 | 2, 43 | 3, 44 | 3 45 | ] 46 | } 47 | ], 48 | "TimeSeries": [ 49 | { 50 | "Name": "double.exponential.delta.histogram.count", 51 | "Tags": [ 52 | "custom_attribute:custom_value", 53 | "deployment.environment:dev", 54 | "instrumentation_library:foo", 55 | "instrumentation_library_version:1.0.0" 56 | ], 57 | "Host": "res-hostname", 58 | "OriginID": "", 59 | "OriginProduct": 10, 60 | "OriginSubProduct": 17, 61 | "OriginProductDetail": 0, 62 | "Type": "count", 63 | "Timestamp": 1667560641226420924, 64 | "Value": 30 65 | }, 66 | { 67 | "Name": "double.exponential.delta.histogram.sum", 68 | "Tags": [ 69 | "custom_attribute:custom_value", 70 | "deployment.environment:dev", 71 | "instrumentation_library:foo", 72 | "instrumentation_library_version:1.0.0" 73 | ], 74 | "Host": "res-hostname", 75 | "OriginID": "", 76 | "OriginProduct": 10, 77 | "OriginSubProduct": 17, 78 | "OriginProductDetail": 0, 79 | "Type": "count", 80 | "Timestamp": 1667560641226420924, 81 | "Value": 3.141592653589793 82 | }, 83 | { 84 | "Name": "double.exponential.delta.histogram.min", 85 | "Tags": [ 86 | "custom_attribute:custom_value", 87 | "deployment.environment:dev", 88 | "instrumentation_library:foo", 89 | "instrumentation_library_version:1.0.0" 90 | ], 91 | "Host": "res-hostname", 92 | "OriginID": "", 93 | "OriginProduct": 10, 94 | "OriginSubProduct": 17, 95 | "OriginProductDetail": 0, 96 | "Type": "gauge", 97 | "Timestamp": 1667560641226420924, 98 | "Value": -100000 99 | }, 100 | { 101 | "Name": "double.exponential.delta.histogram.max", 102 | "Tags": [ 103 | "custom_attribute:custom_value", 104 | "deployment.environment:dev", 105 | "instrumentation_library:foo", 106 | "instrumentation_library_version:1.0.0" 107 | ], 108 | "Host": "res-hostname", 109 | "OriginID": "", 110 | "OriginProduct": 10, 111 | "OriginSubProduct": 17, 112 | "OriginProductDetail": 0, 113 | "Type": "gauge", 114 | "Timestamp": 1667560641226420924, 115 | "Value": 100000 116 | } 117 | ] 118 | } -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/histogram/simple-exponential_cs.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": [ 3 | { 4 | "Name": "double.exponential.delta.histogram", 5 | "Tags": [ 6 | "custom_attribute:custom_value", 7 | "deployment.environment:dev" 8 | ], 9 | "Host": "res-hostname", 10 | "OriginID": "", 11 | "OriginProduct": 10, 12 | "OriginSubProduct": 17, 13 | "OriginProductDetail": 0, 14 | "Timestamp": 1667560641226420924, 15 | "Summary": { 16 | "Min": -100000, 17 | "Max": 100000, 18 | "Sum": 3.141592653589793, 19 | "Avg": 0.10471975511965977, 20 | "Cnt": 30 21 | }, 22 | "Keys": [ 23 | -1341, 24 | -1340, 25 | -1339, 26 | 0, 27 | 1340, 28 | 1341, 29 | 1342, 30 | 1343, 31 | 1344 32 | ], 33 | "Counts": [ 34 | 6, 35 | 4, 36 | 0, 37 | 10, 38 | 1, 39 | 1, 40 | 2, 41 | 3, 42 | 3 43 | ] 44 | } 45 | ], 46 | "TimeSeries": [ 47 | { 48 | "Name": "double.exponential.delta.histogram.count", 49 | "Tags": [ 50 | "custom_attribute:custom_value", 51 | "deployment.environment:dev" 52 | ], 53 | "Host": "res-hostname", 54 | "OriginID": "", 55 | "OriginProduct": 10, 56 | "OriginSubProduct": 17, 57 | "OriginProductDetail": 0, 58 | "Type": "count", 59 | "Timestamp": 1667560641226420924, 60 | "Value": 30 61 | }, 62 | { 63 | "Name": "double.exponential.delta.histogram.sum", 64 | "Tags": [ 65 | "custom_attribute:custom_value", 66 | "deployment.environment:dev" 67 | ], 68 | "Host": "res-hostname", 69 | "OriginID": "", 70 | "OriginProduct": 10, 71 | "OriginSubProduct": 17, 72 | "OriginProductDetail": 0, 73 | "Type": "count", 74 | "Timestamp": 1667560641226420924, 75 | "Value": 3.141592653589793 76 | }, 77 | { 78 | "Name": "double.exponential.delta.histogram.min", 79 | "Tags": [ 80 | "custom_attribute:custom_value", 81 | "deployment.environment:dev" 82 | ], 83 | "Host": "res-hostname", 84 | "OriginID": "", 85 | "OriginProduct": 10, 86 | "OriginSubProduct": 17, 87 | "OriginProductDetail": 0, 88 | "Type": "gauge", 89 | "Timestamp": 1667560641226420924, 90 | "Value": -100000 91 | }, 92 | { 93 | "Name": "double.exponential.delta.histogram.max", 94 | "Tags": [ 95 | "custom_attribute:custom_value", 96 | "deployment.environment:dev" 97 | ], 98 | "Host": "res-hostname", 99 | "OriginID": "", 100 | "OriginProduct": 10, 101 | "OriginSubProduct": 17, 102 | "OriginProductDetail": 0, 103 | "Type": "gauge", 104 | "Timestamp": 1667560641226420924, 105 | "Value": 100000 106 | } 107 | ] 108 | } -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/histogram/simple-exponential_ilmd-tags.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": [ 3 | { 4 | "Name": "double.exponential.delta.histogram", 5 | "Tags": [ 6 | "custom_attribute:custom_value", 7 | "deployment.environment:dev", 8 | "instrumentation_library:foo", 9 | "instrumentation_library_version:1.0.0" 10 | ], 11 | "Host": "res-hostname", 12 | "OriginID": "", 13 | "OriginProduct": 10, 14 | "OriginSubProduct": 17, 15 | "OriginProductDetail": 0, 16 | "Timestamp": 1667560641226420924, 17 | "Summary": { 18 | "Min": -100000, 19 | "Max": 100000, 20 | "Sum": 3.141592653589793, 21 | "Avg": 0.10471975511965977, 22 | "Cnt": 30 23 | }, 24 | "Keys": [ 25 | -1341, 26 | -1340, 27 | -1339, 28 | 0, 29 | 1340, 30 | 1341, 31 | 1342, 32 | 1343, 33 | 1344 34 | ], 35 | "Counts": [ 36 | 6, 37 | 4, 38 | 0, 39 | 10, 40 | 1, 41 | 1, 42 | 2, 43 | 3, 44 | 3 45 | ] 46 | } 47 | ], 48 | "TimeSeries": null 49 | } -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/histogram/simple-exponential_ismd-tags.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": [ 3 | { 4 | "Name": "double.exponential.delta.histogram", 5 | "Tags": [ 6 | "custom_attribute:custom_value", 7 | "deployment.environment:dev", 8 | "instrumentation_scope:foo", 9 | "instrumentation_scope_version:1.0.0" 10 | ], 11 | "Host": "res-hostname", 12 | "OriginID": "", 13 | "OriginProduct": 10, 14 | "OriginSubProduct": 17, 15 | "OriginProductDetail": 0, 16 | "Timestamp": 1667560641226420924, 17 | "Summary": { 18 | "Min": -100000, 19 | "Max": 100000, 20 | "Sum": 3.141592653589793, 21 | "Avg": 0.10471975511965977, 22 | "Cnt": 30 23 | }, 24 | "Keys": [ 25 | -1341, 26 | -1340, 27 | -1339, 28 | 0, 29 | 1340, 30 | 1341, 31 | 1342, 32 | 1343, 33 | 1344 34 | ], 35 | "Counts": [ 36 | 6, 37 | 4, 38 | 0, 39 | 10, 40 | 1, 41 | 1, 42 | 2, 43 | 3, 44 | 3 45 | ] 46 | } 47 | ], 48 | "TimeSeries": null 49 | } -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/histogram/simple-exponential_res-ilmd-tags.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": [ 3 | { 4 | "Name": "double.exponential.delta.histogram", 5 | "Tags": [ 6 | "custom_attribute:custom_value", 7 | "deployment.environment:dev", 8 | "instrumentation_library:foo", 9 | "instrumentation_library_version:1.0.0" 10 | ], 11 | "Host": "res-hostname", 12 | "OriginID": "", 13 | "OriginProduct": 10, 14 | "OriginSubProduct": 17, 15 | "OriginProductDetail": 0, 16 | "Timestamp": 1667560641226420924, 17 | "Summary": { 18 | "Min": -100000, 19 | "Max": 100000, 20 | "Sum": 3.141592653589793, 21 | "Avg": 0.10471975511965977, 22 | "Cnt": 30 23 | }, 24 | "Keys": [ 25 | -1341, 26 | -1340, 27 | -1339, 28 | 0, 29 | 1340, 30 | 1341, 31 | 1342, 32 | 1343, 33 | 1344 34 | ], 35 | "Counts": [ 36 | 6, 37 | 4, 38 | 0, 39 | 10, 40 | 1, 41 | 1, 42 | 2, 43 | 3, 44 | 3 45 | ] 46 | } 47 | ], 48 | "TimeSeries": null 49 | } -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/histogram/simple-exponential_res-tags.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": [ 3 | { 4 | "Name": "double.exponential.delta.histogram", 5 | "Tags": [ 6 | "custom_attribute:custom_value", 7 | "deployment.environment:dev" 8 | ], 9 | "Host": "res-hostname", 10 | "OriginID": "", 11 | "OriginProduct": 10, 12 | "OriginSubProduct": 17, 13 | "OriginProductDetail": 0, 14 | "Timestamp": 1667560641226420924, 15 | "Summary": { 16 | "Min": -100000, 17 | "Max": 100000, 18 | "Sum": 3.141592653589793, 19 | "Avg": 0.10471975511965977, 20 | "Cnt": 30 21 | }, 22 | "Keys": [ 23 | -1341, 24 | -1340, 25 | -1339, 26 | 0, 27 | 1340, 28 | 1341, 29 | 1342, 30 | 1343, 31 | 1344 32 | ], 33 | "Counts": [ 34 | 6, 35 | 4, 36 | 0, 37 | 10, 38 | 1, 39 | 1, 40 | 2, 41 | 3, 42 | 3 43 | ] 44 | } 45 | ], 46 | "TimeSeries": null 47 | } -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/histogram/single-bucket-delta-no-min-max.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": [ 3 | { 4 | "Name": "doubleHist.singlebucket.test", 5 | "Tags": [ 6 | "attribute_tag:attribute_value" 7 | ], 8 | "Host": "hostname", 9 | "OriginID": "", 10 | "OriginProduct": 10, 11 | "OriginSubProduct": 17, 12 | "OriginProductDetail": 0, 13 | "Timestamp": 1667560641226420924, 14 | "Summary": { 15 | "Min": -9.941854089121368e-10, 16 | "Max": -9.941854089121368e-10, 17 | "Sum": 3.141592653589793, 18 | "Avg": 0.15707963267948966, 19 | "Cnt": 20 20 | }, 21 | "Keys": [ 22 | -1 23 | ], 24 | "Counts": [ 25 | 20 26 | ] 27 | } 28 | ], 29 | "TimeSeries": null 30 | } -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/histogram/single-bucket-delta-with-min-max.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": [ 3 | { 4 | "Name": "doubleHist.singlebucket.minmax.test", 5 | "Tags": [ 6 | "attribute_tag:attribute_value" 7 | ], 8 | "Host": "hostname", 9 | "OriginID": "", 10 | "OriginProduct": 10, 11 | "OriginSubProduct": 17, 12 | "OriginProductDetail": 0, 13 | "Timestamp": 1667560641226420924, 14 | "Summary": { 15 | "Min": 100, 16 | "Max": 101, 17 | "Sum": 3.141592653589793, 18 | "Avg": 0.15707963267948966, 19 | "Cnt": 20 20 | }, 21 | "Keys": [ 22 | -1 23 | ], 24 | "Counts": [ 25 | 20 26 | ] 27 | } 28 | ], 29 | "TimeSeries": null 30 | } -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/histogram/static-cumulative_dist-cs.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": null, 3 | "TimeSeries": [ 4 | { 5 | "Name": "doubleHist.test.count", 6 | "Tags": [], 7 | "Host": "hostname", 8 | "OriginID": "", 9 | "OriginProduct": 10, 10 | "OriginSubProduct": 17, 11 | "OriginProductDetail": 0, 12 | "Type": "count", 13 | "Timestamp": 1667560641226420925, 14 | "Value": 0 15 | }, 16 | { 17 | "Name": "doubleHist.test.sum", 18 | "Tags": [], 19 | "Host": "hostname", 20 | "OriginID": "", 21 | "OriginProduct": 10, 22 | "OriginSubProduct": 17, 23 | "OriginProductDetail": 0, 24 | "Type": "count", 25 | "Timestamp": 1667560641226420925, 26 | "Value": 0 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/histogram/zero-delta_dist-cs.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": null, 3 | "TimeSeries": [ 4 | { 5 | "Name": "doubleHist.test.count", 6 | "Tags": [ 7 | "attribute_tag:attribute_value" 8 | ], 9 | "Host": "hostname", 10 | "OriginID": "", 11 | "OriginProduct": 10, 12 | "OriginSubProduct": 17, 13 | "OriginProductDetail": 0, 14 | "Type": "count", 15 | "Timestamp": 1667560641226420924, 16 | "Value": 0 17 | }, 18 | { 19 | "Name": "doubleHist.test.sum", 20 | "Tags": [ 21 | "attribute_tag:attribute_value" 22 | ], 23 | "Host": "hostname", 24 | "OriginID": "", 25 | "OriginProduct": 10, 26 | "OriginSubProduct": 17, 27 | "OriginProductDetail": 0, 28 | "Type": "count", 29 | "Timestamp": 1667560641226420924, 30 | "Value": 0 31 | }, 32 | { 33 | "Name": "doubleHist.test.min", 34 | "Tags": [ 35 | "attribute_tag:attribute_value" 36 | ], 37 | "Host": "hostname", 38 | "OriginID": "", 39 | "OriginProduct": 10, 40 | "OriginSubProduct": 17, 41 | "OriginProductDetail": 0, 42 | "Type": "gauge", 43 | "Timestamp": 1667560641226420924, 44 | "Value": 0 45 | }, 46 | { 47 | "Name": "doubleHist.test.max", 48 | "Tags": [ 49 | "attribute_tag:attribute_value" 50 | ], 51 | "Host": "hostname", 52 | "OriginID": "", 53 | "OriginProduct": 10, 54 | "OriginSubProduct": 17, 55 | "OriginProductDetail": 0, 56 | "Type": "gauge", 57 | "Timestamp": 1667560641226420924, 58 | "Value": 0 59 | } 60 | ] 61 | } -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/origin/origin.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": null, 3 | "TimeSeries": [ 4 | { 5 | "Name": "unknown.receiver.metric", 6 | "Tags": [], 7 | "Host": "res-hostname", 8 | "OriginID": "", 9 | "OriginProduct": 42, 10 | "OriginSubProduct": 17, 11 | "OriginProductDetail": 0, 12 | "Type": "gauge", 13 | "Timestamp": 1667560641226420924, 14 | "Value": 1 15 | }, 16 | { 17 | "Name": "system.memory.limit", 18 | "Tags": [], 19 | "Host": "res-hostname", 20 | "OriginID": "", 21 | "OriginProduct": 42, 22 | "OriginSubProduct": 17, 23 | "OriginProductDetail": 224, 24 | "Type": "gauge", 25 | "Timestamp": 1667560641226420924, 26 | "Value": 1 27 | }, 28 | { 29 | "Name": "prometheus.receiver.metric", 30 | "Tags": [], 31 | "Host": "res-hostname", 32 | "OriginID": "", 33 | "OriginProduct": 42, 34 | "OriginSubProduct": 17, 35 | "OriginProductDetail": 238, 36 | "Type": "gauge", 37 | "Timestamp": 1667560641226420924, 38 | "Value": 1 39 | }, 40 | { 41 | "Name": "datadog.otlp_translator.resource.missing_source", 42 | "Tags": [], 43 | "Host": "res-hostname", 44 | "OriginID": "", 45 | "OriginProduct": 42, 46 | "OriginSubProduct": 17, 47 | "OriginProductDetail": 0, 48 | "Type": "gauge", 49 | "Timestamp": 1667560641226420924, 50 | "Value": 37 51 | } 52 | ] 53 | } -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/source/simple.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": null, 3 | "TimeSeries": [ 4 | { 5 | "Name": "with-source", 6 | "Tags": [], 7 | "Host": "res-hostname", 8 | "OriginID": "", 9 | "OriginProduct": 0, 10 | "OriginSubProduct": 17, 11 | "OriginProductDetail": 0, 12 | "Type": "gauge", 13 | "Timestamp": 1667560641226420924, 14 | "Value": 1 15 | }, 16 | { 17 | "Name": "with-source-2", 18 | "Tags": [], 19 | "Host": "res-hostname", 20 | "OriginID": "", 21 | "OriginProduct": 0, 22 | "OriginSubProduct": 17, 23 | "OriginProductDetail": 0, 24 | "Type": "gauge", 25 | "Timestamp": 1667560641226420924, 26 | "Value": 1 27 | }, 28 | { 29 | "Name": "missing-source-1", 30 | "Tags": [], 31 | "Host": "", 32 | "OriginID": "", 33 | "OriginProduct": 0, 34 | "OriginSubProduct": 17, 35 | "OriginProductDetail": 0, 36 | "Type": "gauge", 37 | "Timestamp": 1667560641226420924, 38 | "Value": 1 39 | }, 40 | { 41 | "Name": "missing-source-2", 42 | "Tags": [], 43 | "Host": "", 44 | "OriginID": "", 45 | "OriginProduct": 0, 46 | "OriginSubProduct": 17, 47 | "OriginProductDetail": 0, 48 | "Type": "gauge", 49 | "Timestamp": 1667560641226420924, 50 | "Value": 1 51 | }, 52 | { 53 | "Name": "missing-source-3", 54 | "Tags": [], 55 | "Host": "", 56 | "OriginID": "", 57 | "OriginProduct": 0, 58 | "OriginSubProduct": 17, 59 | "OriginProductDetail": 0, 60 | "Type": "gauge", 61 | "Timestamp": 1667560641226420924, 62 | "Value": 1 63 | }, 64 | { 65 | "Name": "missing-source-4", 66 | "Tags": [], 67 | "Host": "", 68 | "OriginID": "", 69 | "OriginProduct": 0, 70 | "OriginSubProduct": 17, 71 | "OriginProductDetail": 0, 72 | "Type": "gauge", 73 | "Timestamp": 1667560641226420924, 74 | "Value": 1 75 | }, 76 | { 77 | "Name": "datadog.otlp_translator.metrics.missing_source", 78 | "Tags": [], 79 | "Host": "", 80 | "OriginID": "", 81 | "OriginProduct": 0, 82 | "OriginSubProduct": 17, 83 | "OriginProductDetail": 0, 84 | "Type": "gauge", 85 | "Timestamp": 1667560641226420924, 86 | "Value": 37 87 | } 88 | ] 89 | } -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/summary/simple_summary-with-quantile.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": null, 3 | "TimeSeries": [ 4 | { 5 | "Name": "summary.example.count", 6 | "Tags": [], 7 | "Host": "hostname", 8 | "OriginID": "", 9 | "OriginProduct": 10, 10 | "OriginSubProduct": 17, 11 | "OriginProductDetail": 0, 12 | "Type": "count", 13 | "Timestamp": 1669802350921840070, 14 | "Value": 10 15 | }, 16 | { 17 | "Name": "summary.example.sum", 18 | "Tags": [], 19 | "Host": "hostname", 20 | "OriginID": "", 21 | "OriginProduct": 10, 22 | "OriginSubProduct": 17, 23 | "OriginProductDetail": 0, 24 | "Type": "count", 25 | "Timestamp": 1669802350921840070, 26 | "Value": 100 27 | }, 28 | { 29 | "Name": "summary.example.quantile", 30 | "Tags": [ 31 | "quantile:0" 32 | ], 33 | "Host": "hostname", 34 | "OriginID": "", 35 | "OriginProduct": 10, 36 | "OriginSubProduct": 17, 37 | "OriginProductDetail": 0, 38 | "Type": "gauge", 39 | "Timestamp": 1669802350921840070, 40 | "Value": 0 41 | }, 42 | { 43 | "Name": "summary.example.quantile", 44 | "Tags": [ 45 | "quantile:0.5" 46 | ], 47 | "Host": "hostname", 48 | "OriginID": "", 49 | "OriginProduct": 10, 50 | "OriginSubProduct": 17, 51 | "OriginProductDetail": 0, 52 | "Type": "gauge", 53 | "Timestamp": 1669802350921840070, 54 | "Value": 100 55 | }, 56 | { 57 | "Name": "summary.example.quantile", 58 | "Tags": [ 59 | "quantile:0.999" 60 | ], 61 | "Host": "hostname", 62 | "OriginID": "", 63 | "OriginProduct": 10, 64 | "OriginSubProduct": 17, 65 | "OriginProductDetail": 0, 66 | "Type": "gauge", 67 | "Timestamp": 1669802350921840070, 68 | "Value": 500 69 | }, 70 | { 71 | "Name": "summary.example.quantile", 72 | "Tags": [ 73 | "quantile:1.0" 74 | ], 75 | "Host": "hostname", 76 | "OriginID": "", 77 | "OriginProduct": 10, 78 | "OriginSubProduct": 17, 79 | "OriginProductDetail": 0, 80 | "Type": "gauge", 81 | "Timestamp": 1669802350921840070, 82 | "Value": 600 83 | } 84 | ] 85 | } 86 | -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/summary/simple_summary.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": null, 3 | "TimeSeries": [ 4 | { 5 | "Name": "summary.example.count", 6 | "Tags": [], 7 | "Host": "hostname", 8 | "OriginID": "", 9 | "OriginProduct": 10, 10 | "OriginSubProduct": 17, 11 | "OriginProductDetail": 0, 12 | "Type": "count", 13 | "Timestamp": 1669802350921840070, 14 | "Value": 10 15 | }, 16 | { 17 | "Name": "summary.example.sum", 18 | "Tags": [], 19 | "Host": "hostname", 20 | "OriginID": "", 21 | "OriginProduct": 10, 22 | "OriginSubProduct": 17, 23 | "OriginProductDetail": 0, 24 | "Type": "count", 25 | "Timestamp": 1669802350921840070, 26 | "Value": 100 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/summary/simple_summary_cumsum-keep.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": null, 3 | "TimeSeries": [ 4 | { 5 | "Name": "summary.example.count", 6 | "Tags": [], 7 | "Host": "hostname", 8 | "OriginID": "", 9 | "OriginProduct": 10, 10 | "OriginSubProduct": 17, 11 | "OriginProductDetail": 0, 12 | "Type": "count", 13 | "Timestamp": 1669802348455623075, 14 | "Value": 1 15 | }, 16 | { 17 | "Name": "summary.example.count", 18 | "Tags": [], 19 | "Host": "hostname", 20 | "OriginID": "", 21 | "OriginProduct": 10, 22 | "OriginSubProduct": 17, 23 | "OriginProductDetail": 0, 24 | "Type": "count", 25 | "Timestamp": 1669802350921840070, 26 | "Value": 10 27 | }, 28 | { 29 | "Name": "summary.example.sum", 30 | "Tags": [], 31 | "Host": "hostname", 32 | "OriginID": "", 33 | "OriginProduct": 10, 34 | "OriginSubProduct": 17, 35 | "OriginProductDetail": 0, 36 | "Type": "count", 37 | "Timestamp": 1669802350921840070, 38 | "Value": 100 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/summary/with-attributes-quantile_summary.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": null, 3 | "TimeSeries": [ 4 | { 5 | "Name": "summary.example.count", 6 | "Tags": [ 7 | "attribute_tag:attribute_value" 8 | ], 9 | "Host": "hostname", 10 | "OriginID": "", 11 | "OriginProduct": 10, 12 | "OriginSubProduct": 17, 13 | "OriginProductDetail": 0, 14 | "Type": "count", 15 | "Timestamp": 1669802350921840070, 16 | "Value": 10 17 | }, 18 | { 19 | "Name": "summary.example.sum", 20 | "Tags": [ 21 | "attribute_tag:attribute_value" 22 | ], 23 | "Host": "hostname", 24 | "OriginID": "", 25 | "OriginProduct": 10, 26 | "OriginSubProduct": 17, 27 | "OriginProductDetail": 0, 28 | "Type": "count", 29 | "Timestamp": 1669802350921840070, 30 | "Value": 100 31 | }, 32 | { 33 | "Name": "summary.example.quantile", 34 | "Tags": [ 35 | "quantile:0", 36 | "attribute_tag:attribute_value" 37 | ], 38 | "Host": "hostname", 39 | "OriginID": "", 40 | "OriginProduct": 10, 41 | "OriginSubProduct": 17, 42 | "OriginProductDetail": 0, 43 | "Type": "gauge", 44 | "Timestamp": 1669802350921840070, 45 | "Value": 0 46 | }, 47 | { 48 | "Name": "summary.example.quantile", 49 | "Tags": [ 50 | "quantile:0.5", 51 | "attribute_tag:attribute_value" 52 | ], 53 | "Host": "hostname", 54 | "OriginID": "", 55 | "OriginProduct": 10, 56 | "OriginSubProduct": 17, 57 | "OriginProductDetail": 0, 58 | "Type": "gauge", 59 | "Timestamp": 1669802350921840070, 60 | "Value": 100 61 | }, 62 | { 63 | "Name": "summary.example.quantile", 64 | "Tags": [ 65 | "quantile:0.999", 66 | "attribute_tag:attribute_value" 67 | ], 68 | "Host": "hostname", 69 | "OriginID": "", 70 | "OriginProduct": 10, 71 | "OriginSubProduct": 17, 72 | "OriginProductDetail": 0, 73 | "Type": "gauge", 74 | "Timestamp": 1669802350921840070, 75 | "Value": 500 76 | }, 77 | { 78 | "Name": "summary.example.quantile", 79 | "Tags": [ 80 | "quantile:1.0", 81 | "attribute_tag:attribute_value" 82 | ], 83 | "Host": "hostname", 84 | "OriginID": "", 85 | "OriginProduct": 10, 86 | "OriginSubProduct": 17, 87 | "OriginProductDetail": 0, 88 | "Type": "gauge", 89 | "Timestamp": 1669802350921840070, 90 | "Value": 600 91 | } 92 | ] 93 | } -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/datadogdata/summary/with-attributes_summary.json: -------------------------------------------------------------------------------- 1 | { 2 | "Sketches": null, 3 | "TimeSeries": [ 4 | { 5 | "Name": "summary.example.count", 6 | "Tags": [ 7 | "attribute_tag:attribute_value" 8 | ], 9 | "Host": "hostname", 10 | "OriginID": "", 11 | "OriginProduct": 10, 12 | "OriginSubProduct": 17, 13 | "OriginProductDetail": 0, 14 | "Type": "count", 15 | "Timestamp": 1669802350921840070, 16 | "Value": 10 17 | }, 18 | { 19 | "Name": "summary.example.sum", 20 | "Tags": [ 21 | "attribute_tag:attribute_value" 22 | ], 23 | "Host": "hostname", 24 | "OriginID": "", 25 | "OriginProduct": 10, 26 | "OriginSubProduct": 17, 27 | "OriginProductDetail": 0, 28 | "Type": "count", 29 | "Timestamp": 1669802350921840070, 30 | "Value": 100 31 | } 32 | ] 33 | } -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/otlpdata/histogram/empty-cumulative-exponential.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceMetrics":[ 3 | { 4 | "resource":{ 5 | 6 | }, 7 | "scopeMetrics":[ 8 | { 9 | "scope":{ 10 | 11 | }, 12 | "metrics":[ 13 | { 14 | "name": "empty.exponential.cumulative.histogram", 15 | "exponentialHistogram":{ 16 | "dataPoints":[ 17 | { 18 | "startTimeUnixNano":"1693305960229586125", 19 | "timeUnixNano":"1693305970229586125", 20 | "sum":0, 21 | "scale":4, 22 | "positive":{ 23 | 24 | }, 25 | "negative":{ 26 | 27 | } 28 | } 29 | ], 30 | "aggregationTemporality":2 31 | } 32 | } 33 | ] 34 | } 35 | ] 36 | } 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/otlpdata/histogram/empty-delta-exponential.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceMetrics":[ 3 | { 4 | "resource":{ 5 | 6 | }, 7 | "scopeMetrics":[ 8 | { 9 | "scope":{ 10 | 11 | }, 12 | "metrics":[ 13 | { 14 | "name": "empty.exponential.delta.histogram", 15 | "exponentialHistogram":{ 16 | "dataPoints":[ 17 | { 18 | "startTimeUnixNano":"1693305960229586125", 19 | "timeUnixNano":"1693305970229586125", 20 | "sum":0, 21 | "scale":4, 22 | "positive":{ 23 | 24 | }, 25 | "negative":{ 26 | 27 | } 28 | } 29 | ], 30 | "aggregationTemporality":1 31 | } 32 | } 33 | ] 34 | } 35 | ] 36 | } 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/otlpdata/histogram/empty-delta-no-min-max.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceMetrics": [ 3 | { 4 | "resource": { 5 | "attributes": [ 6 | { 7 | "key": "host.name", 8 | "value": { 9 | "stringValue": "hostname" 10 | } 11 | } 12 | ] 13 | }, 14 | "scopeMetrics": [ 15 | { 16 | "scope": {}, 17 | "metrics": [ 18 | { 19 | "name": "doubleHist.empty.test", 20 | "histogram": { 21 | "dataPoints": [ 22 | { 23 | "attributes": [ 24 | { 25 | "key": "attribute_tag", 26 | "value": { 27 | "stringValue": "attribute_value" 28 | } 29 | } 30 | ], 31 | "timeUnixNano": "1667560641226420924", 32 | "count": "20", 33 | "sum": 3.141592653589793 34 | } 35 | ], 36 | "aggregationTemporality": 1 37 | } 38 | } 39 | ] 40 | } 41 | ] 42 | } 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/otlpdata/histogram/empty-delta-with-min-max.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceMetrics": [ 3 | { 4 | "resource": { 5 | "attributes": [ 6 | { 7 | "key": "host.name", 8 | "value": { 9 | "stringValue": "hostname" 10 | } 11 | } 12 | ] 13 | }, 14 | "scopeMetrics": [ 15 | { 16 | "scope": {}, 17 | "metrics": [ 18 | { 19 | "name": "doubleHist.empty.minmax.test", 20 | "histogram": { 21 | "dataPoints": [ 22 | { 23 | "attributes": [ 24 | { 25 | "key": "attribute_tag", 26 | "value": { 27 | "stringValue": "attribute_value" 28 | } 29 | } 30 | ], 31 | "timeUnixNano": "1667560641226420924", 32 | "count": "20", 33 | "sum": 3.141592653589793, 34 | "min": 100, 35 | "max": 101 36 | } 37 | ], 38 | "aggregationTemporality": 1 39 | } 40 | } 41 | ] 42 | } 43 | ] 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/otlpdata/histogram/simple-cumulative.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceMetrics": [ 3 | { 4 | "resource": { 5 | "attributes": [ 6 | { 7 | "key": "host.name", 8 | "value": { 9 | "stringValue": "hostname" 10 | } 11 | } 12 | ] 13 | }, 14 | "scopeMetrics": [ 15 | { 16 | "scope": {}, 17 | "metrics": [ 18 | { 19 | "name": "doubleHist.test", 20 | "histogram": { 21 | "dataPoints": [ 22 | { 23 | "timeUnixNano": "1667560641226420924", 24 | "count": "20", 25 | "sum": 3.141592653589793, 26 | "min": -10, 27 | "max": 10, 28 | "bucketCounts": [ 29 | "2", 30 | "18" 31 | ], 32 | "explicitBounds": [ 33 | 0 34 | ] 35 | }, 36 | { 37 | "timeUnixNano": "1667560641226420925", 38 | "count": "50", 39 | "sum": 23.141592653589793, 40 | "min": -11, 41 | "max": 10, 42 | "bucketCounts": [ 43 | "13", 44 | "37" 45 | ], 46 | "explicitBounds": [ 47 | 0 48 | ] 49 | } 50 | ], 51 | "aggregationTemporality": 2 52 | } 53 | } 54 | ] 55 | } 56 | ] 57 | } 58 | ] 59 | } 60 | -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/otlpdata/histogram/simple-delta-min-max.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceMetrics": [ 3 | { 4 | "resource": { 5 | "attributes": [ 6 | { 7 | "key": "host.name", 8 | "value": { 9 | "stringValue": "hostname" 10 | } 11 | } 12 | ] 13 | }, 14 | "scopeMetrics": [ 15 | { 16 | "scope": {}, 17 | "metrics": [ 18 | { 19 | "name": "doubleHist.test.minmax", 20 | "histogram": { 21 | "dataPoints": [ 22 | { 23 | "attributes": [ 24 | { 25 | "key": "attribute_tag", 26 | "value": { 27 | "stringValue": "attribute_value" 28 | } 29 | } 30 | ], 31 | "timeUnixNano": "1667560641226420924", 32 | "count": "20", 33 | "sum": 3.141592653589793, 34 | "bucketCounts": [ 35 | "0", 36 | "2", 37 | "17", 38 | "1" 39 | ], 40 | "explicitBounds": [ 41 | -100, 0, 100 42 | ] 43 | } 44 | ], 45 | "aggregationTemporality": 1 46 | } 47 | } 48 | ] 49 | } 50 | ] 51 | } 52 | ] 53 | } 54 | -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/otlpdata/histogram/simple-delta.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceMetrics": [ 3 | { 4 | "resource": { 5 | "attributes": [ 6 | { 7 | "key": "host.name", 8 | "value": { 9 | "stringValue": "hostname" 10 | } 11 | } 12 | ] 13 | }, 14 | "scopeMetrics": [ 15 | { 16 | "scope": {}, 17 | "metrics": [ 18 | { 19 | "name": "doubleHist.test", 20 | "histogram": { 21 | "dataPoints": [ 22 | { 23 | "attributes": [ 24 | { 25 | "key": "attribute_tag", 26 | "value": { 27 | "stringValue": "attribute_value" 28 | } 29 | } 30 | ], 31 | "timeUnixNano": "1667560641226420924", 32 | "count": "20", 33 | "sum": 3.141592653589793, 34 | "bucketCounts": [ 35 | "2", 36 | "18" 37 | ], 38 | "explicitBounds": [ 39 | 0 40 | ], 41 | "min": -100, 42 | "max": 100 43 | } 44 | ], 45 | "aggregationTemporality": 1 46 | } 47 | } 48 | ] 49 | } 50 | ] 51 | } 52 | ] 53 | } -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/otlpdata/histogram/single-bucket-delta-no-min-max.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceMetrics": [ 3 | { 4 | "resource": { 5 | "attributes": [ 6 | { 7 | "key": "host.name", 8 | "value": { 9 | "stringValue": "hostname" 10 | } 11 | } 12 | ] 13 | }, 14 | "scopeMetrics": [ 15 | { 16 | "scope": {}, 17 | "metrics": [ 18 | { 19 | "name": "doubleHist.singlebucket.test", 20 | "histogram": { 21 | "dataPoints": [ 22 | { 23 | "attributes": [ 24 | { 25 | "key": "attribute_tag", 26 | "value": { 27 | "stringValue": "attribute_value" 28 | } 29 | } 30 | ], 31 | "timeUnixNano": "1667560641226420924", 32 | "count": "20", 33 | "sum": 3.141592653589793, 34 | "bucketCounts": [ 35 | "20" 36 | ] 37 | } 38 | ], 39 | "aggregationTemporality": 1 40 | } 41 | } 42 | ] 43 | } 44 | ] 45 | } 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/otlpdata/histogram/single-bucket-delta-with-min-max.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceMetrics": [ 3 | { 4 | "resource": { 5 | "attributes": [ 6 | { 7 | "key": "host.name", 8 | "value": { 9 | "stringValue": "hostname" 10 | } 11 | } 12 | ] 13 | }, 14 | "scopeMetrics": [ 15 | { 16 | "scope": {}, 17 | "metrics": [ 18 | { 19 | "name": "doubleHist.singlebucket.minmax.test", 20 | "histogram": { 21 | "dataPoints": [ 22 | { 23 | "attributes": [ 24 | { 25 | "key": "attribute_tag", 26 | "value": { 27 | "stringValue": "attribute_value" 28 | } 29 | } 30 | ], 31 | "timeUnixNano": "1667560641226420924", 32 | "count": "20", 33 | "sum": 3.141592653589793, 34 | "min": 100, 35 | "max": 101, 36 | "bucketCounts": [ 37 | "20" 38 | ] 39 | } 40 | ], 41 | "aggregationTemporality": 1 42 | } 43 | } 44 | ] 45 | } 46 | ] 47 | } 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/otlpdata/histogram/static-cumulative.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceMetrics": [ 3 | { 4 | "resource": { 5 | "attributes": [ 6 | { 7 | "key": "host.name", 8 | "value": { 9 | "stringValue": "hostname" 10 | } 11 | } 12 | ] 13 | }, 14 | "scopeMetrics": [ 15 | { 16 | "scope": {}, 17 | "metrics": [ 18 | { 19 | "name": "doubleHist.test", 20 | "histogram": { 21 | "dataPoints": [ 22 | { 23 | "timeUnixNano": "1667560641226420924", 24 | "count": "20", 25 | "sum": 3.141592653589793, 26 | "min": -10, 27 | "max": 10, 28 | "bucketCounts": [ 29 | "2", 30 | "18" 31 | ], 32 | "explicitBounds": [ 33 | 0 34 | ] 35 | }, 36 | { 37 | "timeUnixNano": "1667560641226420925", 38 | "count": "20", 39 | "sum": 3.141592653589793, 40 | "min": -10, 41 | "max": 10, 42 | "bucketCounts": [ 43 | "2", 44 | "18" 45 | ], 46 | "explicitBounds": [ 47 | 0 48 | ] 49 | } 50 | ], 51 | "aggregationTemporality": 2 52 | } 53 | } 54 | ] 55 | } 56 | ] 57 | } 58 | ] 59 | } 60 | -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/otlpdata/histogram/zero-delta.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceMetrics": [ 3 | { 4 | "resource": { 5 | "attributes": [ 6 | { 7 | "key": "host.name", 8 | "value": { 9 | "stringValue": "hostname" 10 | } 11 | } 12 | ] 13 | }, 14 | "scopeMetrics": [ 15 | { 16 | "scope": {}, 17 | "metrics": [ 18 | { 19 | "name": "doubleHist.test", 20 | "histogram": { 21 | "dataPoints": [ 22 | { 23 | "attributes": [ 24 | { 25 | "key": "attribute_tag", 26 | "value": { 27 | "stringValue": "attribute_value" 28 | } 29 | } 30 | ], 31 | "timeUnixNano": "1667560641226420924", 32 | "count": "0", 33 | "sum": 0, 34 | "bucketCounts": [ 35 | "0", 36 | "0" 37 | ], 38 | "explicitBounds": [ 39 | 0 40 | ], 41 | "min": 0, 42 | "max": 0 43 | } 44 | ], 45 | "aggregationTemporality": 1 46 | } 47 | } 48 | ] 49 | } 50 | ] 51 | } 52 | ] 53 | } 54 | -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/otlpdata/origin/origin.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceMetrics": [ 3 | { 4 | "resource": { 5 | "attributes": [ 6 | { 7 | "key": "datadog.host.name", 8 | "value": { 9 | "stringValue": "res-hostname" 10 | } 11 | } 12 | ] 13 | }, 14 | "scopeMetrics": [ 15 | { 16 | "scope": { 17 | "name": "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/unknownreceiver", 18 | "version": "0.91.0" 19 | }, 20 | "metrics": [ 21 | { 22 | "name": "unknown.receiver.metric", 23 | "gauge": { 24 | "dataPoints": [ 25 | { 26 | "attributes": [], 27 | "timeUnixNano": "1667560641226420924", 28 | "asInt": 1 29 | } 30 | ] 31 | } 32 | } 33 | ] 34 | }, 35 | { 36 | "scope": { 37 | "name": "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/memory", 38 | "version": "1.0.0" 39 | }, 40 | "metrics": [ 41 | { 42 | "name": "system.memory.limit", 43 | "gauge": { 44 | "dataPoints": [ 45 | { 46 | "attributes": [], 47 | "timeUnixNano": "1667560641226420924", 48 | "asInt": 1 49 | } 50 | ] 51 | } 52 | } 53 | ] 54 | }, 55 | { 56 | "scope": { 57 | "name": "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/prometheusreceiver", 58 | "version": "0.91.0" 59 | }, 60 | "metrics": [ 61 | { 62 | "name": "prometheus.receiver.metric", 63 | "gauge": { 64 | "dataPoints": [ 65 | { 66 | "attributes": [], 67 | "timeUnixNano": "1667560641226420924", 68 | "asInt": 1 69 | } 70 | ] 71 | } 72 | } 73 | ] 74 | }, 75 | { 76 | "scope": { 77 | "name": "github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/metrics" 78 | }, 79 | "metrics": [ 80 | { 81 | "name": "datadog.otlp_translator.resource.missing_source", 82 | "gauge": { 83 | "dataPoints": [ 84 | { 85 | "attributes": [], 86 | "timeUnixNano": "1667560641226420924", 87 | "asInt": 37 88 | } 89 | ] 90 | } 91 | } 92 | ] 93 | } 94 | ] 95 | } 96 | ] 97 | } 98 | -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/otlpdata/summary/simple.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceMetrics": [ 3 | { 4 | "resource": { 5 | "attributes": [ 6 | { 7 | "key": "host.name", 8 | "value": { 9 | "stringValue": "hostname" 10 | } 11 | } 12 | ] 13 | }, 14 | "scopeMetrics": [ 15 | { 16 | "scope": {}, 17 | "metrics": [ 18 | { 19 | "name": "summary.example", 20 | "summary": { 21 | "dataPoints": [ 22 | { 23 | "timeUnixNano": "1669802348455623075", 24 | "count": 1, 25 | "sum": 1, 26 | "quantileValues": [ 27 | ] 28 | } 29 | ] 30 | } 31 | }, 32 | { 33 | "name": "summary.example", 34 | "summary": { 35 | "dataPoints": [ 36 | { 37 | "timeUnixNano": "1669802350921840070", 38 | "count": 11, 39 | "sum": 101, 40 | "quantileValues": [ 41 | { 42 | "quantile": 0, 43 | "value": 0 44 | }, 45 | { 46 | "quantile": 0.5, 47 | "value": 100 48 | }, 49 | { 50 | "quantile": 0.999, 51 | "value": 500 52 | }, 53 | { 54 | "quantile": 1, 55 | "value": 600 56 | } 57 | ] 58 | } 59 | ] 60 | } 61 | } 62 | ] 63 | } 64 | ] 65 | } 66 | ] 67 | } 68 | -------------------------------------------------------------------------------- /pkg/otlp/metrics/testdata/otlpdata/summary/with-attributes.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceMetrics": [ 3 | { 4 | "resource": { 5 | "attributes": [ 6 | { 7 | "key": "host.name", 8 | "value": { 9 | "stringValue": "hostname" 10 | } 11 | } 12 | ] 13 | }, 14 | "scopeMetrics": [ 15 | { 16 | "scope": {}, 17 | "metrics": [ 18 | { 19 | "name": "summary.example", 20 | "summary": { 21 | "dataPoints": [ 22 | { 23 | "attributes": [ 24 | { 25 | "key": "attribute_tag", 26 | "value": { 27 | "stringValue": "attribute_value" 28 | } 29 | } 30 | ], 31 | "timeUnixNano": "1669802348455623075", 32 | "count": 1, 33 | "sum": 1, 34 | "quantileValues": [ 35 | ] 36 | } 37 | ] 38 | } 39 | }, 40 | { 41 | "name": "summary.example", 42 | "summary": { 43 | "dataPoints": [ 44 | { 45 | "attributes": [ 46 | { 47 | "key": "attribute_tag", 48 | "value": { 49 | "stringValue": "attribute_value" 50 | } 51 | } 52 | ], 53 | "timeUnixNano": "1669802350921840070", 54 | "count": 11, 55 | "sum": 101, 56 | "quantileValues": [ 57 | { 58 | "quantile": 0, 59 | "value": 0 60 | }, 61 | { 62 | "quantile": 0.5, 63 | "value": 100 64 | }, 65 | { 66 | "quantile": 0.999, 67 | "value": 500 68 | }, 69 | { 70 | "quantile": 1, 71 | "value": 600 72 | } 73 | ] 74 | } 75 | ] 76 | } 77 | } 78 | ] 79 | } 80 | ] 81 | } 82 | ] 83 | } 84 | -------------------------------------------------------------------------------- /pkg/quantile/agent.go: -------------------------------------------------------------------------------- 1 | // Unless explicitly stated otherwise all files in this repository are licensed 2 | // under the Apache License Version 2.0. 3 | // This product includes software developed at Datadog (https://www.datadoghq.com/). 4 | // Copyright 2016-present Datadog, Inc. 5 | 6 | package quantile 7 | 8 | const ( 9 | agentBufCap = 512 10 | ) 11 | 12 | var agentConfig = Default() 13 | 14 | // An Agent sketch is an insert optimized version of the sketch for use in the 15 | // datadog-agent. 16 | type Agent struct { 17 | Buf []Key 18 | CountBuf []KeyCount 19 | Sketch Sketch 20 | } 21 | 22 | // IsEmpty returns true if the sketch is empty 23 | func (a *Agent) IsEmpty() bool { 24 | return a.Sketch.Basic.Cnt == 0 && len(a.Buf) == 0 25 | } 26 | 27 | // Finish flushes any pending inserts and returns a deep copy of the sketch. 28 | func (a *Agent) Finish() *Sketch { 29 | a.flush() 30 | 31 | if a.IsEmpty() { 32 | return nil 33 | } 34 | 35 | return a.Sketch.Copy() 36 | } 37 | 38 | // flush buffered values into the sketch. 39 | func (a *Agent) flush() { 40 | if len(a.Buf) != 0 { 41 | a.Sketch.insert(agentConfig, a.Buf) 42 | a.Buf = nil 43 | } 44 | 45 | if len(a.CountBuf) != 0 { 46 | a.Sketch.insertCounts(agentConfig, a.CountBuf) 47 | a.CountBuf = nil 48 | } 49 | } 50 | 51 | // Reset the agent sketch to the empty state. 52 | func (a *Agent) Reset() { 53 | a.Sketch.Reset() 54 | a.Buf = nil // TODO: pool 55 | } 56 | 57 | // Insert v into the sketch. 58 | func (a *Agent) Insert(v float64, sampleRate float64) { 59 | k := agentConfig.key(v) 60 | // bounds enforcement 61 | if sampleRate <= 0 || sampleRate > 1 { 62 | sampleRate = 1 63 | } 64 | 65 | if sampleRate == 1 { 66 | a.Sketch.Basic.Insert(v) 67 | a.Buf = append(a.Buf, k) 68 | 69 | if len(a.Buf) < agentBufCap { 70 | return 71 | } 72 | } else { 73 | // use truncated 1 / sampleRate as count to match histograms 74 | n := 1 / sampleRate 75 | a.Sketch.Basic.InsertN(v, n) 76 | kc := KeyCount{ 77 | k: k, 78 | n: uint(n), 79 | } 80 | a.CountBuf = append(a.CountBuf, kc) 81 | } 82 | a.flush() 83 | } 84 | 85 | // InsertInterpolate linearly interpolates a count from the given lower to upper bounds 86 | func (a *Agent) InsertInterpolate(lower float64, upper float64, count uint) { 87 | keys := make([]Key, 0) 88 | for k := agentConfig.key(lower); k <= agentConfig.key(upper); k++ { 89 | keys = append(keys, k) 90 | } 91 | whatsLeft := int(count) 92 | distance := upper - lower 93 | startIdx := 0 94 | lowerB := agentConfig.binLow(keys[startIdx]) 95 | endIdx := 1 96 | var remainder float64 97 | for endIdx < len(keys) && whatsLeft > 0 { 98 | upperB := agentConfig.binLow(keys[endIdx]) 99 | // ((upperB - lowerB) / distance) is the ratio of the distance between the current buckets to the total distance 100 | // which tells us how much of the remaining value to put in this bucket 101 | fkn := ((upperB - lowerB) / distance) * float64(count) 102 | // only track the remainder if fkn is >1 because we designed this to not store a bunch of 0 count buckets 103 | if fkn > 1 { 104 | remainder += fkn - float64(int(fkn)) 105 | } 106 | kn := int(fkn) 107 | if remainder > 1 { 108 | kn++ 109 | remainder-- 110 | } 111 | if kn > 0 { 112 | // Guard against overflow at the end 113 | if kn > whatsLeft { 114 | kn = whatsLeft 115 | } 116 | a.Sketch.Basic.InsertN(lowerB, float64(kn)) 117 | a.CountBuf = append(a.CountBuf, KeyCount{k: keys[startIdx], n: uint(kn)}) 118 | whatsLeft -= kn 119 | startIdx = endIdx 120 | lowerB = upperB 121 | } 122 | endIdx++ 123 | } 124 | if whatsLeft > 0 { 125 | a.Sketch.Basic.InsertN(agentConfig.binLow(keys[startIdx]), float64(whatsLeft)) 126 | a.CountBuf = append(a.CountBuf, KeyCount{k: keys[startIdx], n: uint(whatsLeft)}) 127 | } 128 | a.flush() 129 | } 130 | -------------------------------------------------------------------------------- /pkg/quantile/bin.go: -------------------------------------------------------------------------------- 1 | // Unless explicitly stated otherwise all files in this repository are licensed 2 | // under the Apache License Version 2.0. 3 | // This product includes software developed at Datadog (https://www.datadoghq.com/). 4 | // Copyright 2016-present Datadog, Inc. 5 | 6 | package quantile 7 | 8 | import ( 9 | "math" 10 | "strings" 11 | ) 12 | 13 | const ( 14 | maxBinWidth = math.MaxUint16 15 | ) 16 | 17 | type bin struct { 18 | k Key 19 | n uint16 20 | } 21 | 22 | // incrSafe performs `b.n += by` safely handling overflows. When an overflow 23 | // occurs, we set b.n to it's max, and return the leftover amount to increment. 24 | func (b *bin) incrSafe(by int) int { 25 | next := by + int(b.n) 26 | 27 | if next > maxBinWidth { 28 | b.n = maxBinWidth 29 | return next - maxBinWidth 30 | } 31 | 32 | b.n = uint16(next) 33 | return 0 34 | } 35 | 36 | // appendSafe appends 1 or more bins with the given key safely handing overflow by 37 | // inserting multiple buckets when needed. 38 | // 39 | // (1) n <= maxBinWidth : 1 bin 40 | // (2) n > maxBinWidth : >1 bin 41 | func appendSafe(bins []bin, k Key, n int) []bin { 42 | if n <= maxBinWidth { 43 | return append(bins, bin{k: k, n: uint16(n)}) 44 | } 45 | 46 | // on overflow, insert multiple bins with the same key. 47 | // put full bins at end 48 | 49 | // TODO|PROD: Add validation func that sorts by key and then n (smaller bin first). 50 | r := uint16(n % maxBinWidth) 51 | if r != 0 { 52 | bins = append(bins, bin{k: k, n: r}) 53 | } 54 | 55 | for i := 0; i < n/maxBinWidth; i++ { 56 | bins = append(bins, bin{k: k, n: maxBinWidth}) 57 | } 58 | 59 | return bins 60 | } 61 | 62 | type binList []bin 63 | 64 | func (bins binList) nSum() int { 65 | s := 0 66 | for _, b := range bins { 67 | s += int(b.n) 68 | } 69 | return s 70 | } 71 | 72 | func (bins binList) Cap() int { 73 | return cap(bins) 74 | } 75 | 76 | func (bins binList) Len() int { 77 | return len(bins) 78 | } 79 | 80 | func (bins binList) ensureLen(newLen int) binList { 81 | for cap(bins) < newLen { 82 | bins = append(bins[:cap(bins)], bin{}) 83 | } 84 | 85 | return bins[:newLen] 86 | } 87 | 88 | func (bins binList) String() string { 89 | var w strings.Builder 90 | printBins(&w, bins, defaultBinPerLine) 91 | return w.String() 92 | } 93 | -------------------------------------------------------------------------------- /pkg/quantile/bin_test.go: -------------------------------------------------------------------------------- 1 | // Unless explicitly stated otherwise all files in this repository are licensed 2 | // under the Apache License Version 2.0. 3 | // This product includes software developed at Datadog (https://www.datadoghq.com/). 4 | // Copyright 2016-present Datadog, Inc. 5 | 6 | package quantile 7 | 8 | import ( 9 | "fmt" 10 | "strings" 11 | "testing" 12 | ) 13 | 14 | func TestBin_incrSafe(t *testing.T) { 15 | const maxn = maxBinWidth 16 | tests := []struct { 17 | n uint16 18 | by int 19 | wantN uint16 20 | wantOverflow int 21 | name string 22 | }{ 23 | {by: 1, wantN: 1}, 24 | {n: 1, by: 1, wantN: 2}, 25 | {n: maxn, by: 1, wantN: maxn, wantOverflow: 1}, 26 | {by: maxn, wantN: maxn}, 27 | {n: 1, by: maxn, wantN: maxn, wantOverflow: 1}, 28 | {n: 100, by: 3 * maxn, wantN: maxn, wantOverflow: 2*maxn + 100}, 29 | } 30 | 31 | for _, tt := range tests { 32 | t.Run(tt.name, func(t *testing.T) { 33 | 34 | var ( 35 | b = bin{n: tt.n} 36 | gotOverflow = b.incrSafe(tt.by) 37 | errs []string 38 | ok = true 39 | ) 40 | 41 | if tt.wantOverflow != gotOverflow { 42 | ok = false 43 | errs = append(errs, fmt.Sprintf("\toverflow: got %d, want %d", 44 | gotOverflow, tt.wantOverflow)) 45 | } 46 | 47 | if tt.wantN != b.n { 48 | ok = false 49 | errs = append(errs, fmt.Sprintf("\tn: got %d, want %d", 50 | b.n, tt.wantN)) 51 | } 52 | 53 | if ok { 54 | return 55 | } 56 | 57 | t.Errorf("Bin{n:%d}.tryIncr(%d) = %d\n%s", 58 | tt.n, tt.by, gotOverflow, strings.Join(errs, "\n")) 59 | 60 | }) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /pkg/quantile/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/DataDog/opentelemetry-mapping-go/pkg/quantile 2 | 3 | go 1.23 4 | 5 | require ( 6 | github.com/DataDog/opentelemetry-mapping-go/pkg/internal/sketchtest v0.27.1 7 | github.com/DataDog/sketches-go v1.4.7 8 | github.com/dustin/go-humanize v1.0.1 9 | github.com/stretchr/testify v1.10.0 10 | ) 11 | 12 | require ( 13 | github.com/davecgh/go-spew v1.1.1 // indirect 14 | github.com/pmezard/go-difflib v1.0.0 // indirect 15 | google.golang.org/protobuf v1.36.1 // indirect 16 | gopkg.in/yaml.v3 v3.0.1 // indirect 17 | ) 18 | 19 | replace github.com/DataDog/opentelemetry-mapping-go/pkg/internal/sketchtest => ../internal/sketchtest 20 | 21 | retract v0.4.0 // see #107 22 | -------------------------------------------------------------------------------- /pkg/quantile/go.sum: -------------------------------------------------------------------------------- 1 | github.com/DataDog/sketches-go v1.4.7 h1:eHs5/0i2Sdf20Zkj0udVFWuCrXGRFig2Dcfm5rtcTxc= 2 | github.com/DataDog/sketches-go v1.4.7/go.mod h1:eAmQ/EBmtSO+nQp7IZMZVRPT4BQTmIc5RZQ+deGlTPM= 3 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= 6 | github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= 7 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= 8 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 9 | github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= 10 | github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 11 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 12 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 13 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 14 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 15 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 16 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 17 | google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= 18 | google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= 19 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 20 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 21 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 22 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 23 | -------------------------------------------------------------------------------- /pkg/quantile/key.go: -------------------------------------------------------------------------------- 1 | // Unless explicitly stated otherwise all files in this repository are licensed 2 | // under the Apache License Version 2.0. 3 | // This product includes software developed at Datadog (https://www.datadoghq.com/). 4 | // Copyright 2016-present Datadog, Inc. 5 | 6 | package quantile 7 | 8 | import ( 9 | "fmt" 10 | ) 11 | 12 | const ( 13 | // TODO|DOC: Talk about why I choose a symmetrical number system 14 | uvinf = 1<<15 - 1 15 | uvneginf = -uvinf 16 | 17 | maxKey = uvinf - 1 // 1 spot for +/- inf 18 | ) 19 | 20 | // A Key represents a quantized version of a float64. See Config for more details 21 | type Key int16 22 | 23 | // A KeyCount represents a Key and an associated count 24 | type KeyCount struct { 25 | k Key 26 | n uint 27 | } 28 | 29 | // IsInf returns true if the key represents +/-Inf 30 | func (k Key) IsInf() bool { 31 | // TODO: bench http://graphics.stanford.edu/~seander/bithacks.html#IntegerAbs 32 | return k == uvinf || k == -uvneginf 33 | } 34 | 35 | func (k Key) String() string { 36 | switch k { 37 | case uvinf: 38 | return "+Inf" 39 | case uvneginf: 40 | return "-Inf" 41 | } 42 | 43 | return fmt.Sprintf("%d", k) 44 | } 45 | 46 | // InfKey returns the Key for +Inf if sign >= 0, -Inf if sign < 0. 47 | func InfKey(sign int) Key { 48 | if sign >= 0 { 49 | return uvinf 50 | } 51 | 52 | return uvneginf 53 | } 54 | -------------------------------------------------------------------------------- /pkg/quantile/pool.go: -------------------------------------------------------------------------------- 1 | // Unless explicitly stated otherwise all files in this repository are licensed 2 | // under the Apache License Version 2.0. 3 | // This product includes software developed at Datadog (https://www.datadoghq.com/). 4 | // Copyright 2016-present Datadog, Inc. 5 | 6 | package quantile 7 | 8 | import ( 9 | "sync" 10 | ) 11 | 12 | const ( 13 | defaultBinListSize = 2 * defaultBinLimit 14 | defaultKeyListSize = 256 15 | defaultOverflowListSize = 16 16 | ) 17 | 18 | var ( 19 | // TODO: multiple pools, one for each size class (like github.com/oxtoacart/bpool) 20 | binListPool = sync.Pool{ 21 | New: func() interface{} { 22 | a := make([]bin, 0, defaultBinListSize) 23 | return &a 24 | }, 25 | } 26 | 27 | keyListPool = sync.Pool{ 28 | New: func() interface{} { 29 | a := make([]Key, 0, defaultKeyListSize) 30 | return &a 31 | }, 32 | } 33 | 34 | overflowListPool = sync.Pool{ 35 | New: func() interface{} { 36 | a := make([]bin, 0, defaultOverflowListSize) 37 | return &a 38 | }, 39 | } 40 | ) 41 | 42 | func getBinList() []bin { 43 | a := *(binListPool.Get().(*[]bin)) 44 | return a[:0] 45 | } 46 | 47 | func putBinList(a []bin) { 48 | binListPool.Put(&a) 49 | } 50 | 51 | func getKeyList() []Key { 52 | a := *(keyListPool.Get().(*[]Key)) 53 | return a[:0] 54 | } 55 | 56 | func putKeyList(a []Key) { 57 | keyListPool.Put(&a) 58 | } 59 | 60 | func getOverflowList() []bin { 61 | a := *(overflowListPool.Get().(*[]bin)) 62 | return a[:0] 63 | } 64 | 65 | func putOverflowList(a []bin) { 66 | overflowListPool.Put(&a) 67 | } 68 | -------------------------------------------------------------------------------- /pkg/quantile/print.go: -------------------------------------------------------------------------------- 1 | // Unless explicitly stated otherwise all files in this repository are licensed 2 | // under the Apache License Version 2.0. 3 | // This product includes software developed at Datadog (https://www.datadoghq.com/). 4 | // Copyright 2016-present Datadog, Inc. 5 | 6 | package quantile 7 | 8 | import ( 9 | "fmt" 10 | "io" 11 | "strings" 12 | 13 | humanize "github.com/dustin/go-humanize" 14 | ) 15 | 16 | const ( 17 | // defaultBinPerLine controls how many bins we print per line. 18 | defaultBinPerLine = 32 19 | ) 20 | 21 | type memSized interface { 22 | // MemSize returns memory use in bytes: 23 | // used: uses len(bins) 24 | // allocated: uses cap(bins) 25 | MemSize() (used, allocated int) 26 | } 27 | 28 | // printBins pretty prints bins to w. 29 | // 30 | // output: 31 | // 32 | // :... 33 | func printBins(w io.Writer, bins []bin, maxPerLine int) { 34 | for i, b := range bins { 35 | prefix := "" 36 | 37 | if i != 0 { 38 | prefix = " " 39 | if maxPerLine > 0 && i%maxPerLine == 0 { 40 | prefix = "\n" 41 | } 42 | } 43 | 44 | fmt.Fprintf(w, "%s%d:%d", prefix, b.k, b.n) 45 | } 46 | } 47 | 48 | func printSketch(w io.Writer, s *Sketch, c *Config) { 49 | fmt.Fprintln(w, "sketch:") 50 | head := func(s string) { 51 | fmt.Fprintln(w, indent(s, 1)) 52 | } 53 | 54 | iprintf := func(format string, a ...interface{}) { 55 | fmt.Fprintf(w, indent(format, 2), a...) 56 | } 57 | 58 | // bins 59 | head("bins:") 60 | fmt.Fprintln(w, indent(s.bins.String(), 2)) 61 | 62 | // size 63 | head("size:") 64 | used, allocated := s.MemSize() 65 | iprintf("used=%s allocated=%s %3.2f%%", 66 | humanize.Bytes(uint64(used)), 67 | humanize.Bytes(uint64(allocated)), 68 | float64(used)/float64(allocated)*100, 69 | ) 70 | fmt.Fprint(w, "\n") 71 | iprintf("len=%d cap=%d", 72 | s.bins.Len(), s.bins.Cap()) 73 | fmt.Fprint(w, "\n") 74 | 75 | // stats 76 | head("stats:") 77 | fmt.Fprint(w, " ") 78 | for i, p := range []float64{1, 50, 75, 90, 95, 99} { 79 | if i != 0 { 80 | fmt.Fprint(w, " ") 81 | } 82 | fmt.Fprintf(w, "%02g=%.2f", p, s.Quantile(c, p/100)) 83 | } 84 | fmt.Fprint(w, "\n") 85 | fmt.Fprintln(w, indent(s.Basic.String(), 2)) 86 | } 87 | 88 | func indent(s string, level int) string { 89 | // TODO: this is bad 90 | space := strings.Repeat(" ", 2*level) 91 | out := strings.Replace(s, "\n", "\n"+space, -1) 92 | return space + strings.TrimSpace(out) 93 | } 94 | -------------------------------------------------------------------------------- /pkg/quantile/print_test.go: -------------------------------------------------------------------------------- 1 | // Unless explicitly stated otherwise all files in this repository are licensed 2 | // under the Apache License Version 2.0. 3 | // This product includes software developed at Datadog (https://www.datadoghq.com/). 4 | // Copyright 2016-present Datadog, Inc. 5 | 6 | package quantile 7 | 8 | import ( 9 | "fmt" 10 | "strings" 11 | "testing" 12 | 13 | "github.com/stretchr/testify/require" 14 | ) 15 | 16 | func TestPrintBins(t *testing.T) { 17 | mb := func(dsl string) []bin { 18 | s := ParseSketch(t, dsl) 19 | return s.bins 20 | } 21 | 22 | b10 := "0:1 1:1 2:1 3:1 4:1 5:1 6:1 7:1 8:1 9:1" 23 | 24 | for _, tt := range []struct { 25 | bins []bin 26 | w int 27 | exp string 28 | }{ 29 | { 30 | bins: mb(b10), 31 | exp: b10, 32 | }, 33 | { 34 | bins: mb(b10), 35 | exp: b10, 36 | w: 10, 37 | }, 38 | { 39 | bins: mb(b10), 40 | exp: strings.Replace(b10, " ", "\n", -1), 41 | w: 1, 42 | }, 43 | { 44 | bins: mb(b10), 45 | exp: "0:1 1:1\n2:1 3:1\n4:1 5:1\n6:1 7:1\n8:1 9:1", 46 | w: 2, 47 | }, 48 | { 49 | bins: mb(b10), 50 | exp: "0:1 1:1 2:1\n3:1 4:1 5:1\n6:1 7:1 8:1\n9:1", 51 | w: 3, 52 | }, 53 | { 54 | bins: mb(b10), 55 | exp: "0:1 1:1 2:1 3:1 4:1 5:1 6:1 7:1 8:1\n9:1", 56 | w: 9, 57 | }, 58 | } { 59 | name := fmt.Sprintf("w=%d", tt.w) 60 | t.Run(name, func(t *testing.T) { 61 | var b strings.Builder 62 | printBins(&b, tt.bins, tt.w) 63 | 64 | got := b.String() 65 | require.Equal(t, tt.exp, got) 66 | }) 67 | } 68 | 69 | t.Run("demo", func(t *testing.T) { 70 | var bins binList 71 | for i := 0; i < defaultBinPerLine*2+1; i++ { 72 | bins = append(bins, bin{k: Key(i), n: 1}) 73 | } 74 | 75 | s := bins.String() 76 | lines := strings.Split(s, "\n") 77 | require.Equal(t, []string{ 78 | "0:1 1:1 2:1 3:1 4:1 5:1 6:1 7:1 8:1 9:1 10:1 11:1 12:1 13:1 14:1 15:1 16:1 17:1 18:1 19:1 20:1 21:1 22:1 23:1 24:1 25:1 26:1 27:1 28:1 29:1 30:1 31:1", 79 | "32:1 33:1 34:1 35:1 36:1 37:1 38:1 39:1 40:1 41:1 42:1 43:1 44:1 45:1 46:1 47:1 48:1 49:1 50:1 51:1 52:1 53:1 54:1 55:1 56:1 57:1 58:1 59:1 60:1 61:1 62:1 63:1", 80 | "64:1", 81 | }, lines) 82 | 83 | }) 84 | } 85 | -------------------------------------------------------------------------------- /pkg/quantile/summary/equal.go: -------------------------------------------------------------------------------- 1 | // Unless explicitly stated otherwise all files in this repository are licensed 2 | // under the Apache License Version 2.0. 3 | // This product includes software developed at Datadog (https://www.datadoghq.com/). 4 | // Copyright 2016-present Datadog, Inc. 5 | 6 | package summary 7 | 8 | import ( 9 | "fmt" 10 | "math" 11 | ) 12 | 13 | const ( 14 | ulpLimit = 256 15 | ) 16 | 17 | // ulpDistance is the absolute difference in units of least precision. 18 | // 19 | // Special cases are (order of arguments doesn't matter): 20 | // 21 | // ulpDistance(NaN, b) = max 22 | // ulpDistance(+Inf, -Inf) = max 23 | // ulpDistance(+Inf, +Inf) = 0 24 | // ulpDistance(-Inf, -Inf) = 0 25 | func ulpDistance(a, b float64) uint64 { 26 | switch { 27 | case a == b: 28 | return 0 29 | case math.IsInf(a, 0) || math.IsInf(b, 0): 30 | return math.MaxUint64 31 | case math.IsNaN(a) || math.IsNaN(b): 32 | return math.MaxUint64 33 | case math.Signbit(a) != math.Signbit(b): 34 | return math.Float64bits(math.Abs(a)) + math.Float64bits(math.Abs(b)) 35 | } 36 | 37 | x, y := math.Float64bits(a), math.Float64bits(b) 38 | if x > y { 39 | return x - y 40 | } 41 | 42 | return y - x 43 | } 44 | 45 | func checkFloat64Equal(name string, a, e float64) error { 46 | ulp := ulpDistance(a, e) 47 | if ulp <= ulpLimit { 48 | return nil 49 | } 50 | 51 | return fmt.Errorf("%s: (act) %g != %g (exp) ❌ ulp=%d limit=%d", 52 | name, a, e, ulp, ulpLimit) 53 | 54 | } 55 | 56 | func checkIntEqual(name string, a, e int) error { 57 | if a != e { 58 | return fmt.Errorf("%s: (act) %v != %v (exp) ❌", name, a, e) 59 | } 60 | 61 | return nil 62 | } 63 | 64 | // CheckEqual returns an error if the summaries are not equal 65 | func CheckEqual(a, e Summary) error { 66 | if err := checkIntEqual("Count", int(a.Cnt), int(e.Cnt)); err != nil { 67 | return err 68 | } 69 | 70 | if err := checkFloat64Equal("Min", a.Min, e.Min); err != nil { 71 | return err 72 | } 73 | 74 | if err := checkFloat64Equal("Max", a.Max, e.Max); err != nil { 75 | return err 76 | } 77 | 78 | if err := checkFloat64Equal("Sum", a.Sum, e.Sum); err != nil { 79 | return err 80 | } 81 | 82 | return checkFloat64Equal("Avg", a.Avg, e.Avg) 83 | } 84 | -------------------------------------------------------------------------------- /pkg/quantile/summary/summary.go: -------------------------------------------------------------------------------- 1 | // Unless explicitly stated otherwise all files in this repository are licensed 2 | // under the Apache License Version 2.0. 3 | // This product includes software developed at Datadog (https://www.datadoghq.com/). 4 | // Copyright 2016-present Datadog, Inc. 5 | 6 | package summary 7 | 8 | import ( 9 | "fmt" 10 | ) 11 | 12 | // A Summary stores basic incremental stats. 13 | type Summary struct { 14 | // TODO: store min/max in the entries array of the summary. 15 | Min, Max, Sum, Avg float64 16 | 17 | // TODO: cnt is a duplicate of sketch.n. 18 | Cnt int64 19 | } 20 | 21 | // Reset the summary 22 | func (s *Summary) Reset() { 23 | *s = Summary{} 24 | } 25 | 26 | func (s *Summary) String() string { 27 | return fmt.Sprintf("min=%.4f max=%.4f avg=%.4f sum=%.4f cnt=%d", 28 | s.Min, s.Max, s.Avg, s.Sum, s.Cnt) 29 | } 30 | 31 | // InsertN is equivalent to calling Insert(v) n times (but faster). 32 | func (s *Summary) InsertN(v float64, n float64) { 33 | s.Merge(Summary{ 34 | Cnt: int64(n), 35 | Sum: n * v, 36 | Min: v, 37 | Max: v, 38 | Avg: v, 39 | }) 40 | } 41 | 42 | // Insert adds a single value to the summary. 43 | func (s *Summary) Insert(v float64) { 44 | if v > s.Max || s.Cnt == 0 { 45 | s.Max = v 46 | } 47 | 48 | if v < s.Min || s.Cnt == 0 { 49 | s.Min = v 50 | } 51 | 52 | s.Cnt++ 53 | s.Sum += v 54 | 55 | // incremental avg to reduce precision errors. 56 | s.Avg += (v - s.Avg) / float64(s.Cnt) 57 | } 58 | 59 | // Merge another summary into this one. 60 | func (s *Summary) Merge(o Summary) { 61 | switch { 62 | case s.Cnt == 0: 63 | *s = o 64 | return 65 | case o.Cnt == 0: 66 | return 67 | } 68 | 69 | if o.Max > s.Max { 70 | s.Max = o.Max 71 | } 72 | 73 | if o.Min < s.Min { 74 | s.Min = o.Min 75 | } 76 | 77 | s.Cnt += o.Cnt 78 | s.Sum += o.Sum 79 | 80 | // TODO: Is there a numerically stable way of doing this. 81 | // - When o.Avg and s.Avg are close in value, we lose precision. 82 | s.Avg = s.Avg + (o.Avg-s.Avg)*float64(o.Cnt)/float64(s.Cnt) 83 | } 84 | -------------------------------------------------------------------------------- /pkg/quantile/test_helper.go: -------------------------------------------------------------------------------- 1 | // Unless explicitly stated otherwise all files in this repository are licensed 2 | // under the Apache License Version 2.0. 3 | // This product includes software developed at Datadog (https://www.datadoghq.com/). 4 | // Copyright 2016-present Datadog, Inc. 5 | 6 | //go:build test 7 | // +build test 8 | 9 | package quantile 10 | 11 | import ( 12 | "math" 13 | ) 14 | 15 | func almostEqual(a, b, e float64) bool { 16 | return math.Abs((a-b)/a) <= e 17 | } 18 | 19 | // SketchesApproxEqual checks whether two SketchSeries are equal 20 | func SketchesApproxEqual(exp, act *Sketch, e float64) bool { 21 | 22 | if !almostEqual(exp.Basic.Sum, act.Basic.Sum, e) { 23 | return false 24 | } 25 | 26 | if !almostEqual(exp.Basic.Avg, act.Basic.Avg, e) { 27 | return false 28 | } 29 | 30 | if !almostEqual(exp.Basic.Max, act.Basic.Max, e) { 31 | return false 32 | } 33 | 34 | if !almostEqual(exp.Basic.Min, act.Basic.Min, e) { 35 | return false 36 | } 37 | 38 | if exp.Basic.Cnt != exp.Basic.Cnt { 39 | return false 40 | } 41 | 42 | if exp.count != act.count { 43 | return false 44 | } 45 | 46 | if len(exp.bins) != len(act.bins) { 47 | return false 48 | } 49 | 50 | for i := range exp.bins { 51 | if math.Abs(float64(act.bins[i].k-exp.bins[i].k)) > 1 { 52 | return false 53 | } 54 | 55 | if act.bins[i].n != exp.bins[i].n { 56 | return false 57 | } 58 | } 59 | 60 | return true 61 | } 62 | 63 | type tHelper interface { 64 | Helper() 65 | } 66 | -------------------------------------------------------------------------------- /versions.yaml: -------------------------------------------------------------------------------- 1 | # Unless explicitly stated otherwise all files in this repository are licensed 2 | # under the Apache License Version 2.0. 3 | # This product includes software developed at Datadog (https:#www.datadoghq.com/). 4 | # Copyright 2023-present Datadog, Inc. 5 | 6 | module-sets: 7 | pkgs: 8 | version: v0.27.1 9 | modules: 10 | - github.com/DataDog/opentelemetry-mapping-go/pkg/quantile 11 | - github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes 12 | - github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/logs 13 | - github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/metrics 14 | - github.com/DataDog/opentelemetry-mapping-go/pkg/internal/sketchtest 15 | - github.com/DataDog/opentelemetry-mapping-go/pkg/inframetadata 16 | excluded-modules: 17 | - github.com/DataDog/opentelemetry-mapping-go/internal/tools 18 | - github.com/DataDog/opentelemetry-mapping-go/internal/tools/generate-license-file 19 | - github.com/DataDog/opentelemetry-mapping-go/pkg/inframetadata/gohai/internal/gohaitest 20 | --------------------------------------------------------------------------------