Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

opentelemetry tracer: add Dynatrace resource detector #30824

Merged
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
syntax = "proto3";

package envoy.extensions.tracers.opentelemetry.resource_detectors.v3;

import "udpa/annotations/status.proto";

option java_package = "io.envoyproxy.envoy.extensions.tracers.opentelemetry.resource_detectors.v3";
option java_outer_classname = "DynatraceResourceDetectorProto";
option java_multiple_files = true;
option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/tracers/opentelemetry/resource_detectors/v3;resource_detectorsv3";
option (udpa.annotations.file_status).package_version_status = ACTIVE;

// [#protodoc-title: Dynatrace Resource Detector config]

// Configuration for the Dynatrace Resource Detector extension.
// The resource detector reads from the Dynatrace enrichment files
// and adds host/process related attributes to the OpenTelemetry resource.
//
// See:
//
// `Enrich ingested data with Dynatrace-specific dimensions <https://docs.dynatrace.com/docs/shortlink/enrichment-files>`_
//
// [#extension: envoy.tracers.opentelemetry.resource_detectors.dynatrace]
message DynatraceResourceDetectorConfig {
}
1 change: 1 addition & 0 deletions source/extensions/extensions_build_config.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ EXTENSIONS = {
#

"envoy.tracers.opentelemetry.resource_detectors.environment": "//source/extensions/tracers/opentelemetry/resource_detectors/environment:config",
"envoy.tracers.opentelemetry.resource_detectors.dynatrace": "//source/extensions/tracers/opentelemetry/resource_detectors/dynatrace:config",

#
# OpenTelemetry tracer samplers
Expand Down
7 changes: 7 additions & 0 deletions source/extensions/extensions_metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1697,3 +1697,10 @@ envoy.tracers.opentelemetry.resource_detectors.environment:
status: wip
type_urls:
- envoy.extensions.tracers.opentelemetry.resource_detectors.v3.EnvironmentResourceDetectorConfig
envoy.tracers.opentelemetry.resource_detectors.dynatrace:
categories:
- envoy.tracers.opentelemetry.resource_detectors
security_posture: unknown
status: wip
type_urls:
- envoy.extensions.tracers.opentelemetry.resource_detectors.v3.DynatraceResourceDetectorConfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
load(
"//bazel:envoy_build_system.bzl",
"envoy_cc_extension",
"envoy_cc_library",
"envoy_extension_package",
)

licenses(["notice"]) # Apache 2

envoy_extension_package()

envoy_cc_extension(
name = "config",
srcs = ["config.cc"],
hdrs = ["config.h"],
deps = [
":dynatrace_resource_detector_lib",
"//envoy/registry",
"//source/common/config:utility_lib",
"@envoy_api//envoy/extensions/tracers/opentelemetry/resource_detectors/v3:pkg_cc_proto",
],
)

envoy_cc_library(
name = "dynatrace_resource_detector_lib",
srcs = [
"dynatrace_metadata_file_reader.cc",
"dynatrace_resource_detector.cc",
],
hdrs = [
"dynatrace_metadata_file_reader.h",
"dynatrace_resource_detector.h",
],
deps = [
"//source/common/config:datasource_lib",
"//source/extensions/tracers/opentelemetry/resource_detectors:resource_detector_lib",
"@envoy_api//envoy/extensions/tracers/opentelemetry/resource_detectors/v3:pkg_cc_proto",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#include "source/extensions/tracers/opentelemetry/resource_detectors/dynatrace/config.h"

#include "envoy/extensions/tracers/opentelemetry/resource_detectors/v3/dynatrace_resource_detector.pb.h"
#include "envoy/extensions/tracers/opentelemetry/resource_detectors/v3/dynatrace_resource_detector.pb.validate.h"

#include "source/common/config/utility.h"
#include "source/extensions/tracers/opentelemetry/resource_detectors/dynatrace/dynatrace_metadata_file_reader.h"
#include "source/extensions/tracers/opentelemetry/resource_detectors/dynatrace/dynatrace_resource_detector.h"

namespace Envoy {
namespace Extensions {
namespace Tracers {
namespace OpenTelemetry {

ResourceDetectorPtr DynatraceResourceDetectorFactory::createResourceDetector(
const Protobuf::Message& message, Server::Configuration::TracerFactoryContext& context) {

auto mptr = Envoy::Config::Utility::translateAnyToFactoryConfig(
dynamic_cast<const ProtobufWkt::Any&>(message), context.messageValidationVisitor(), *this);

const auto& proto_config = MessageUtil::downcastAndValidate<
const envoy::extensions::tracers::opentelemetry::resource_detectors::v3::
DynatraceResourceDetectorConfig&>(*mptr, context.messageValidationVisitor());

DynatraceMetadataFileReaderPtr reader = std::make_unique<DynatraceMetadataFileReaderImpl>();
return std::make_unique<DynatraceResourceDetector>(proto_config, std::move(reader));
}

/**
* Static registration for the Dynatrace resource detector factory. @see RegisterFactory.
*/
REGISTER_FACTORY(DynatraceResourceDetectorFactory, ResourceDetectorFactory);

} // namespace OpenTelemetry
} // namespace Tracers
} // namespace Extensions
} // namespace Envoy
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#pragma once

#include <string>

#include "envoy/extensions/tracers/opentelemetry/resource_detectors/v3/dynatrace_resource_detector.pb.h"

#include "source/extensions/tracers/opentelemetry/resource_detectors/resource_detector.h"

namespace Envoy {
namespace Extensions {
namespace Tracers {
namespace OpenTelemetry {

/**
* Config registration for the Dynatrace resource detector. @see ResourceDetectorFactory.
*/
class DynatraceResourceDetectorFactory : public ResourceDetectorFactory {
public:
/**
* @brief Creates a Resource Detector that reads from the Dynatrace enrichment files.
*
* @see
* https://docs.dynatrace.com/docs/shortlink/enrichment-files#oneagent-virtual-files
*
* @param message The resource detector configuration.
* @param context The tracer factory context.
* @return ResourceDetectorPtr
*/
ResourceDetectorPtr
createResourceDetector(const Protobuf::Message& message,
Server::Configuration::TracerFactoryContext& context) override;

ProtobufTypes::MessagePtr createEmptyConfigProto() override {
return std::make_unique<envoy::extensions::tracers::opentelemetry::resource_detectors::v3::
DynatraceResourceDetectorConfig>();
}

std::string name() const override {
return "envoy.tracers.opentelemetry.resource_detectors.dynatrace";
}
};

DECLARE_FACTORY(DynatraceResourceDetectorFactory);

} // namespace OpenTelemetry
} // namespace Tracers
} // namespace Extensions
} // namespace Envoy
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#include "source/extensions/tracers/opentelemetry/resource_detectors/dynatrace/dynatrace_metadata_file_reader.h"

#include <fstream>
#include <iostream>
#include <sstream>
#include <string>

#include "envoy/common/exception.h"

#include "source/common/common/logger.h"

#include "absl/strings/str_cat.h"

namespace Envoy {
namespace Extensions {
namespace Tracers {
namespace OpenTelemetry {

namespace {

bool isIndirectionFile(const std::string& file_name) {
return absl::StartsWith(file_name, "dt_metadata_");
}

std::string readFile(const std::string& file_name) {
if (file_name.empty()) {
return "";
}

std::ifstream file(file_name);
lizan marked this conversation as resolved.
Show resolved Hide resolved
if (file.fail()) {
throw EnvoyException(absl::StrCat("Unable to read Dynatrace enrichment file: ", file_name));
}

std::stringstream file_string;
file_string << file.rdbuf();

return file_string.str();
}

} // namespace

std::string DynatraceMetadataFileReaderImpl::readEnrichmentFile(const std::string& file_name) {
if (const bool indirectionFile = isIndirectionFile(file_name); indirectionFile) {
joaopgrassi marked this conversation as resolved.
Show resolved Hide resolved
return readFile(readFile(file_name));
} else {
return readFile(file_name);
}
}

} // namespace OpenTelemetry
} // namespace Tracers
} // namespace Extensions
} // namespace Envoy
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#pragma once

#include <array>
#include <memory>
#include <string>

#include "envoy/common/pure.h"

#include "absl/strings/match.h"

namespace Envoy {
namespace Extensions {
namespace Tracers {
namespace OpenTelemetry {

/**
* @brief A file reader that reads the content of the Dynatrace metadata enrichment files.
* When OneAgent is monitoring your application, it provides access to the enrichment files.
* These files do not physically exists in your file system, but are provided by the OneAgent on
* demand. This allows obtaining not only information about the host, but also about process.
*
* @see
* https://docs.dynatrace.com/docs/shortlink/enrichment-files#oneagent-virtual-files
*
*/
class DynatraceMetadataFileReader {
public:
virtual ~DynatraceMetadataFileReader() = default;

/**
* @brief Reads the enrichment file and returns the enrichment metadata.
*
* @param file_name The file name.
* @return const std::string String (java-like properties) containing the enrichment metadata.
*/
virtual std::string readEnrichmentFile(const std::string& file_name) PURE;
};

using DynatraceMetadataFileReaderPtr = std::unique_ptr<DynatraceMetadataFileReader>;

class DynatraceMetadataFileReaderImpl : public DynatraceMetadataFileReader {
public:
std::string readEnrichmentFile(const std::string& file_name) override;
};

} // namespace OpenTelemetry
} // namespace Tracers
} // namespace Extensions
} // namespace Envoy
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#include "source/extensions/tracers/opentelemetry/resource_detectors/dynatrace/dynatrace_resource_detector.h"

#include <fstream>
#include <iostream>

namespace Envoy {
namespace Extensions {
namespace Tracers {
namespace OpenTelemetry {
namespace {

void addAttributes(const std::string& content, Resource& resource) {
for (const auto& line : StringUtil::splitToken(content, "\n", false, true)) {
const auto key_value = StringUtil::splitToken(line, "=");
if (key_value.size() != 2) {
continue;
}
resource.attributes_[std::string(key_value[0])] = std::string(key_value[1]);
}
}

} // namespace

Resource DynatraceResourceDetector::detect() {
Resource resource;
resource.schema_url_ = "";
int failure_count = 0;

for (const auto& file_name : DynatraceResourceDetector::dynatraceMetadataFiles()) {
TRY_NEEDS_AUDIT {
lizan marked this conversation as resolved.
Show resolved Hide resolved
std::string content = dynatrace_file_reader_->readEnrichmentFile(file_name);
if (content.empty()) {
failure_count++;
} else {
addAttributes(content, resource);
}
}
END_TRY catch (const EnvoyException&) { failure_count++; }
}

if (failure_count > 0) {
ENVOY_LOG(
warn,
"Dynatrace OpenTelemetry resource detector is configured but could not detect attributes. "
"Check the Dynatrace deployment status to ensure it is correctly deployed.");
}

return resource;
}

} // namespace OpenTelemetry
} // namespace Tracers
} // namespace Extensions
} // namespace Envoy
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#pragma once

#include <array>
#include <string>

#include "envoy/extensions/tracers/opentelemetry/resource_detectors/v3/dynatrace_resource_detector.pb.h"

#include "source/common/common/logger.h"
#include "source/extensions/tracers/opentelemetry/resource_detectors/dynatrace/dynatrace_metadata_file_reader.h"
#include "source/extensions/tracers/opentelemetry/resource_detectors/resource_detector.h"

namespace Envoy {
namespace Extensions {
namespace Tracers {
namespace OpenTelemetry {

/**
* @brief A resource detector that reads the content of the Dynatrace metadata enrichment files.
* When OneAgent is monitoring your application, it provides access to the enrichment files.
* These files do not physically exists in your file system, but are provided by the OneAgent on
* demand. This allows obtaining not only information about the host, but also about process.
*
* Dynatrace can be deployed in multiple ways and flavors, depending on the environment.
* The available Dynatrace enrichment files vary depending on how it is deployed. E.g. In k8s via
* the Dynatrace operator.
*
* Since the resource detector is not aware how Dynatrace is deployed, the detector attempts to
* read all Dynatrace enrichment files. In such cases, reading some of these files may fail but
* this is expected and does not classify as a problem with the detector. The detector may also not
* detect any attributes, for example when a Dynatrace deployment is not successful. In such cases,
* Envoy will be started with no enrichment but Dynatrace users have the means to find out which
* Dynatrace deployment is failing.
*
* @see
* https://docs.dynatrace.com/docs/shortlink/enrichment-files
*
*/
class DynatraceResourceDetector : public ResourceDetector, Logger::Loggable<Logger::Id::tracing> {
public:
DynatraceResourceDetector(const envoy::extensions::tracers::opentelemetry::resource_detectors::
v3::DynatraceResourceDetectorConfig& config,
DynatraceMetadataFileReaderPtr dynatrace_file_reader)
: config_(config), dynatrace_file_reader_(std::move(dynatrace_file_reader)) {}
Resource detect() override;

static const std::vector<std::string>& dynatraceMetadataFiles() {
CONSTRUCT_ON_FIRST_USE(std::vector<std::string>,
{
"dt_metadata_e617c525669e072eebe3d0f08212e8f2.properties",
"/var/lib/dynatrace/enrichment/dt_metadata.properties",
"/var/lib/dynatrace/enrichment/dt_host_metadata.properties",
});
joaopgrassi marked this conversation as resolved.
Show resolved Hide resolved
}

private:
const envoy::extensions::tracers::opentelemetry::resource_detectors::v3::
DynatraceResourceDetectorConfig config_;
DynatraceMetadataFileReaderPtr dynatrace_file_reader_;
};

} // namespace OpenTelemetry
} // namespace Tracers
} // namespace Extensions
} // namespace Envoy
Loading
Loading