Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions bazel/external/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ cc_library(
tags = ["skip_on_windows"],
deps = [
"@com_github_datadog_dd_opentracing_cpp//:dd_opentracing_cpp",
"@com_github_datadog_dd_trace_cpp//:dd_trace_cpp",
"@com_google_googletest//:gtest",
"@io_opentracing_cpp//:opentracing",
],
Expand Down
8 changes: 8 additions & 0 deletions bazel/repositories.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ def envoy_dependencies(skip_targets = []):
_com_github_circonus_labs_libcircllhist()
_com_github_cyan4973_xxhash()
_com_github_datadog_dd_opentracing_cpp()
_com_github_datadog_dd_trace_cpp()
_com_github_mirror_tclap()
_com_github_envoyproxy_sqlparser()
_com_github_fmtlib_fmt()
Expand Down Expand Up @@ -574,6 +575,13 @@ def _com_github_datadog_dd_opentracing_cpp():
actual = "@com_github_datadog_dd_opentracing_cpp//:dd_opentracing_cpp",
)

def _com_github_datadog_dd_trace_cpp():
external_http_archive("com_github_datadog_dd_trace_cpp")
native.bind(
name = "dd_trace_cpp",
actual = "@com_github_datadog_dd_trace_cpp//:dd_trace_cpp",
)

def _com_github_skyapm_cpp2sky():
external_http_archive(
name = "com_github_skyapm_cpp2sky",
Expand Down
15 changes: 15 additions & 0 deletions bazel/repository_locations.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,21 @@ REPOSITORY_LOCATIONS_SPEC = dict(
license = "Apache-2.0",
license_url = "https://github.com/DataDog/dd-opentracing-cpp/blob/v{version}/LICENSE",
),
com_github_datadog_dd_trace_cpp = dict(
project_name = "Datadog C++ Tracing Library",
project_desc = "Datadog distributed tracing for C++",
project_url = "https://github.com/DataDog/dd-trace-cpp",
version = "0.1.5",
sha256 = "d76c98109822d6c3e8deb335117766b67c2be636169e60e5c813c9075b721aa1",
strip_prefix = "dd-trace-cpp-{version}",
urls = ["https://github.com/DataDog/dd-trace-cpp/archive/v{version}.tar.gz"],
use_category = ["observability_ext"],
extensions = ["envoy.tracers.datadog"],
release_date = "2022-12-06",
cpe = "N/A",
license = "Apache-2.0",
license_url = "https://github.com/DataDog/dd-trace-cpp/blob/v{version}/LICENSE",
),
com_github_google_benchmark = dict(
project_name = "Benchmark",
project_desc = "Library to benchmark code snippets",
Expand Down
12 changes: 11 additions & 1 deletion source/extensions/tracers/datadog/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,21 @@ envoy_cc_library(
name = "datadog_tracer_lib",
srcs = [
"datadog_tracer_impl.cc",
"time_util.cc",
],
hdrs = [
"datadog_tracer_impl.h",
"time_util.h",
],
copts = [
# Make sure that headers included from dd_trace_cpp use Abseil
# equivalents of std::string_view and std::optional.
"-DDD_USE_ABSEIL_FOR_ENVOY",
],
external_deps = [
"dd_opentracing_cpp",
"dd_trace_cpp",
],
external_deps = ["dd_opentracing_cpp"],
deps = [
"//source/common/config:utility_lib",
"//source/common/http:async_client_utility_lib",
Expand Down
26 changes: 26 additions & 0 deletions source/extensions/tracers/datadog/time_util.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#include "source/extensions/tracers/datadog/time_util.h"

#include <chrono>

namespace Envoy {
namespace Extensions {
namespace Tracers {
namespace Datadog {

datadog::tracing::TimePoint estimateTime(SystemTime wall) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are we not plumbing the MonotonicTime (or maybe just the TimeSource into here?

Copy link
Copy Markdown
Contributor Author

@dgoffredo dgoffredo Jan 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The goal is to implement the interfaces defined in https://github.com/envoyproxy/envoy/blob/main/envoy/tracing/trace_driver.h.

There are two time points in those interfaces: Span::spawnChild and Driver::startSpan. Both take SystemTime only, without a monotonic counterpart.

If I were redesigning the interface, I would have spawnChild and startSpan take both a SystemTime and a MonotonicTime, and additionally have finishSpan take a MonotonicTime (or both, but at least the MonotonicTime).

I thought it would be inappropriate to propose to change the interface and everything that implements it.

What do you think?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fine to push forward with this but we should also try to correct the API to get the best results. It's not that hard to change these APIs; there's probably not that much fallout, particularly if you just plumb through some additional fields and don't remove the SystemTime.

return estimateTime(wall, datadog::tracing::default_clock);
}

datadog::tracing::TimePoint estimateTime(SystemTime wall, const datadog::tracing::Clock& clock) {
datadog::tracing::TimePoint point = clock();
if (point.wall > wall) {
point.tick -= point.wall - wall;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we also verify that the delta we are subtracting is less than point.tick?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

point.tick is a time point, while point.wall - wall is a duration, so no.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got it, thanks!

}
point.wall = wall;
return point;
}

} // namespace Datadog
} // namespace Tracers
} // namespace Extensions
} // namespace Envoy
45 changes: 45 additions & 0 deletions source/extensions/tracers/datadog/time_util.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#pragma once

/**
* This file contains functions related to time points and durations.
*
* Envoy has a time type that contains both a system time point and a steady
* ("monotonic") time point. However, only the system time is exposed to the
* tracing subsystem. This may be remedied in the future, but for now we work
* with the system time.
*
* This is problematic for the Datadog core tracing library (dd-trace-cpp),
* because it uses the steady time to calculate the duration of a span
* (end.tick - begin.tick). So, we need to get a steady clock time from a given system
* clock time. The scheme is to measure the current system/steady time,
* compare the system part with the given system time, and then adjust the
* measured steady time accordingly. This is correct if the system clock has not
* been adjusted since the given system time was measured. It's incorrect
* otherwise, hence only an estimate. This conversion is performed by the
* estimateTime function.
*/

#include <datadog/clock.h>

#include "envoy/common/time.h"

namespace Envoy {
namespace Extensions {
namespace Tracers {
namespace Datadog {

/**
* Convert a specified system \p time to a datadog time point, estimating the
steady portion of the result by examining the current time as measured by
the optionally specified \p clock and comparing it with the given \p time.
* @param time system time to convert from
* @param clock datadog clock used to measure the current time (default clock if omitted)
* @return datadog time point whose steady portion is estimated from the given \p time.
*/
datadog::tracing::TimePoint estimateTime(SystemTime time);
datadog::tracing::TimePoint estimateTime(SystemTime time, const datadog::tracing::Clock& clock);

} // namespace Datadog
} // namespace Tracers
} // namespace Extensions
} // namespace Envoy
9 changes: 9 additions & 0 deletions test/extensions/tracers/datadog/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,17 @@ envoy_extension_cc_test(
name = "datadog_tracer_impl_test",
srcs = [
"datadog_tracer_impl_test.cc",
"time_util_test.cc",
],
copts = [
# Make sure that headers included from dd_trace_cpp use Abseil
# equivalents of std::string_view and std::optional.
"-DDD_USE_ABSEIL_FOR_ENVOY",
],
extension_names = ["envoy.tracers.datadog"],
external_deps = [
"dd_trace_cpp",
],
# TODO(wrowe): envoy_extension_ rules don't currently exclude windows extensions
tags = ["skip_on_windows"],
deps = [
Expand Down
72 changes: 72 additions & 0 deletions test/extensions/tracers/datadog/time_util_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#include <datadog/clock.h>

#include "envoy/common/time.h"

#include "source/extensions/tracers/datadog/time_util.h"

#include "gtest/gtest.h"

namespace Envoy {
namespace Extensions {
namespace Tracers {
namespace Datadog {
namespace {

TEST(DatadogTracerTimeUtilTest, EstimateTime) {
// Concerns:
//
// - If the current system time is after the specified time (likely case),
// then the resulting steady time point is set back accordingly.
// - If the current system time is before the specified time (rare case),
// then the resulting steady time point is whatever the clock returns.

// Modify `now` to change the value returned by `clock`.
datadog::tracing::TimePoint now;
datadog::tracing::Clock clock = [&]() { return now; };

// A little time has elapsed since the SystemTime was measured. The
// resulting steady time point should be set back by the difference.
datadog::tracing::TimePoint clock_result = datadog::tracing::default_clock();
SystemTime argument = clock_result.wall;
clock_result.wall += std::chrono::microseconds(100);
now = clock_result;
datadog::tracing::TimePoint result = estimateTime(argument, clock);
EXPECT_EQ(result.wall, argument);
EXPECT_EQ(result.tick, clock_result.tick - std::chrono::microseconds(100));

// The clock has been set back since the SystemTime was measured. The
// resulting steady time can't do better than whatever the clock returns
// (we wouldn't want to set the steady time point into the future).
clock_result = datadog::tracing::default_clock();
argument = clock_result.wall;
clock_result.wall -= std::chrono::milliseconds(100);
now = clock_result;
result = estimateTime(argument, clock);
EXPECT_EQ(result.wall, argument);
EXPECT_EQ(result.tick, clock_result.tick);
}

TEST(DatadogTracerTimeUtilTest, DefaultClock) {
// The one-parameter overload of `estimateTime` uses the system clock.
// We can at least check that the steady (monotonic, `.tick`) portion is
// approximately non-decreasing along "before," "during," and "after."
// Only "approximately," because the `datadog::tracing::default_clock` can't
// measure both clocks exactly simultaneously, so its correction to the
// steady time might actually set it back in time some tiny amount.
const datadog::tracing::Clock clock = datadog::tracing::default_clock;
const datadog::tracing::TimePoint before = clock();
const datadog::tracing::TimePoint estimated_before = estimateTime(before.wall);
const datadog::tracing::TimePoint after = clock();

const auto tolerance = std::chrono::microseconds(100);

EXPECT_LE(before.tick, after.tick);
EXPECT_LE(estimated_before.tick - before.tick, tolerance);
EXPECT_LE(after.tick - estimated_before.tick, tolerance);
}

} // namespace
} // namespace Datadog
} // namespace Tracers
} // namespace Extensions
} // namespace Envoy