Skip to content

Commit b2d3cb1

Browse files
committed
Add main code
1 parent 0595f35 commit b2d3cb1

File tree

8 files changed

+2058
-0
lines changed

8 files changed

+2058
-0
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#pragma once
5+
6+
#include "opentelemetry/exporters/statsd/metrics/macros.h"
7+
#include "opentelemetry/exporters/statsd/metrics/socket_tools.h"
8+
#include "opentelemetry/version.h"
9+
#include <memory>
10+
#include <string>
11+
12+
OPENTELEMETRY_BEGIN_NAMESPACE
13+
namespace exporter {
14+
namespace statsd {
15+
namespace metrics {
16+
constexpr char kSemicolon = ';';
17+
constexpr char kEqual = '=';
18+
constexpr char kEndpoint[] = "Endpoint";
19+
constexpr char kAccount[] = "Account";
20+
constexpr char kNamespace[] = "Namespace";
21+
22+
enum class TransportProtocol { kETW, kTCP, kUDP, kUNIX, kUnknown };
23+
24+
class ConnectionStringParser {
25+
26+
public:
27+
ConnectionStringParser(const std::string &connection_string)
28+
: account_(""), namespace_(""), transport_protocol_{TransportProtocol::kUnknown} {
29+
std::string::size_type key_pos = 0;
30+
std::string::size_type key_end;
31+
std::string::size_type val_pos;
32+
std::string::size_type val_end;
33+
bool is_endpoint_found = false;
34+
while ((key_end = connection_string.find(kEqual, key_pos)) !=
35+
std::string::npos) {
36+
if ((val_pos = connection_string.find_first_not_of(kEqual, key_end)) ==
37+
std::string::npos)
38+
{
39+
break;
40+
}
41+
val_end = connection_string.find(kSemicolon, val_pos);
42+
auto key = connection_string.substr(key_pos, key_end - key_pos);
43+
auto value = connection_string.substr(val_pos, val_end - val_pos);
44+
key_pos = val_end;
45+
if (key_pos != std::string::npos)
46+
{
47+
++key_pos;
48+
}
49+
if (key == kNamespace) {
50+
namespace_ = value;
51+
} else if (key == kAccount) {
52+
account_ = value;
53+
} else if (key == kEndpoint) {
54+
is_endpoint_found = true;
55+
size_t pos = value.find("://", 0);
56+
if (pos != std::string::npos)
57+
{
58+
auto scheme = std::string(value.begin(), value.begin() + pos);
59+
connection_string_ = value.substr(pos + strlen("://"));
60+
#ifdef HAVE_UNIX_DOMAIN
61+
if (scheme == "unix") {
62+
transport_protocol_ = TransportProtocol::kUNIX;
63+
}
64+
#else
65+
if (scheme == "unix") {
66+
LOG_ERROR("Unix domain socket not supported on this platform")
67+
}
68+
#endif
69+
if (scheme == "tcp") {
70+
transport_protocol_ = TransportProtocol::kTCP;
71+
}
72+
if (scheme == "udp") {
73+
transport_protocol_ = TransportProtocol::kUDP;
74+
}
75+
}
76+
}
77+
}
78+
#ifdef _WIN32
79+
if (account_.size() && namespace_.size() && !is_endpoint_found) {
80+
transport_protocol_ = TransportProtocol::kETW;
81+
}
82+
#endif
83+
}
84+
85+
bool IsValid() { return transport_protocol_ != TransportProtocol::kUnknown; }
86+
87+
std::string account_;
88+
std::string namespace_;
89+
TransportProtocol transport_protocol_;
90+
std::string connection_string_;
91+
};
92+
} // namespace metrics
93+
} // namespace statsd
94+
} // namespace exporter
95+
OPENTELEMETRY_END_NAMESPACE
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#pragma once
5+
6+
#include "opentelemetry/version.h"
7+
8+
#include <vector>
9+
10+
OPENTELEMETRY_BEGIN_NAMESPACE
11+
namespace exporter {
12+
namespace statsd {
13+
namespace metrics {
14+
15+
// These enums are defined in
16+
// file: test/decoder/ifx_metrics_bin.ksy (enum metric_event_type)
17+
enum class MetricsEventType : uint16_t {
18+
Uint64Metric = 50,
19+
DoubleScaledToLongMetric = 51,
20+
BatchMetric = 52,
21+
ExternallyAggregatedUlongMetric = 53,
22+
ExternallyAggregatedDoubleMetric = 54,
23+
DoubleMetric = 55,
24+
ExternallyAggregatedUlongDistributionMetric = 56,
25+
ExternallyAggregatedDoubleDistributionMetric = 57,
26+
ExternallyAggregatedDoubleScaledToLongDistributionMetric = 58,
27+
Undefined = 100
28+
};
29+
30+
class DataTransport {
31+
public:
32+
virtual bool Connect() noexcept = 0;
33+
virtual bool Send(MetricsEventType event_type, const char *data,
34+
uint16_t length) noexcept = 0;
35+
virtual bool Disconnect() noexcept = 0;
36+
virtual ~DataTransport() = default;
37+
};
38+
} // namespace metrics
39+
} // namespace statsd
40+
} // namespace exporter
41+
OPENTELEMETRY_END_NAMESPACE
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#pragma once
5+
6+
#include "opentelemetry/common/spin_lock_mutex.h"
7+
#include "opentelemetry/common/timestamp.h"
8+
#include "opentelemetry/exporters/geneva/metrics/connection_string_parser.h"
9+
#include "opentelemetry/exporters/geneva/metrics/data_transport.h"
10+
#include "opentelemetry/exporters/geneva/metrics/exporter_options.h"
11+
#include "opentelemetry/sdk/metrics/push_metric_exporter.h"
12+
#include "opentelemetry/sdk/metrics/data/metric_data.h"
13+
14+
15+
OPENTELEMETRY_BEGIN_NAMESPACE
16+
namespace exporter {
17+
namespace geneva {
18+
namespace metrics {
19+
20+
constexpr size_t kBufferSize = 65360; // the maximum ETW payload (inclusive)
21+
constexpr size_t kMaxDimensionNameSize = 256;
22+
constexpr size_t kMaxDimensionValueSize = 1024;
23+
constexpr size_t kBinaryHeaderSize = 4; // event_id (2) + body_length (2)
24+
constexpr size_t kMetricPayloadSize =
25+
24; // count_dimension (2) + reserverd_word (2) + reserverd_dword(4) +
26+
// timestamp_utc (8) + metric_data (8)
27+
constexpr size_t kExternalPayloadSize =
28+
40; // count_dimension (2) + reserverd_word (2) + count (4) + timestamp_utc
29+
// (8) + metric_data_sum (8) + metric_data_min(8) + metric_data_max(8)
30+
31+
// time conversion constants
32+
constexpr uint32_t kWindowsTicksPerSecond =
33+
10000000; // windows ticks are in 100 ns
34+
constexpr uint64_t kSecondsToUnixTime =
35+
11644473600L; // number of seconds between windows epoch start
36+
// 1601-01-01T00:00:00Z and UNIX/Linux epoch
37+
// (1970-01-01T00:00:00Z)
38+
39+
const std::string kAttributeNamespaceKey = "_microsoft_metrics_namespace";
40+
const std::string kAttributeAccountKey = "_microsoft_metrics_account";
41+
42+
using ValueType = nostd::variant<int64_t, double>;
43+
44+
/**
45+
* The Geneva metrics exporter exports metrics data to Geneva
46+
*/
47+
class Exporter final : public opentelemetry::sdk::metrics::PushMetricExporter {
48+
public:
49+
Exporter(const ExporterOptions &options);
50+
51+
opentelemetry::sdk::common::ExportResult
52+
Export(const opentelemetry::sdk::metrics::ResourceMetrics &data) noexcept
53+
override;
54+
55+
sdk::metrics::AggregationTemporality GetAggregationTemporality(
56+
sdk::metrics::InstrumentType instrument_type) const noexcept override;
57+
58+
bool ForceFlush(std::chrono::microseconds timeout =
59+
(std::chrono::microseconds::max)()) noexcept override;
60+
61+
bool Shutdown(std::chrono::microseconds timeout =
62+
(std::chrono::microseconds::max)()) noexcept override;
63+
64+
private:
65+
const ExporterOptions options_;
66+
ConnectionStringParser connection_string_parser_;
67+
const sdk::metrics::AggregationTemporalitySelector
68+
aggregation_temporality_selector_;
69+
bool is_shutdown_ = false;
70+
mutable opentelemetry::common::SpinLockMutex lock_;
71+
std::unique_ptr<DataTransport> data_transport_;
72+
73+
// metrics storage
74+
char buffer_[kBufferSize];
75+
76+
size_t SerializeNonHistogramMetrics(sdk::metrics::AggregationType,
77+
MetricsEventType,
78+
const sdk::metrics::ValueType &,
79+
common::SystemTimestamp,
80+
const std::string &,
81+
const sdk::metrics::PointAttributes &);
82+
size_t SerializeHistogramMetrics(
83+
sdk::metrics::AggregationType, MetricsEventType, uint64_t,
84+
const sdk::metrics::ValueType &, const sdk::metrics::ValueType &,
85+
const sdk::metrics::ValueType &, const std::vector<double> &boundaries,
86+
const std::vector<uint64_t> &counts, common::SystemTimestamp,
87+
const std::string &, const sdk::metrics::PointAttributes &);
88+
};
89+
90+
template <class T>
91+
static void SerializeInt(char *buffer, size_t &index, T value) {
92+
*(reinterpret_cast<T *>(buffer + index)) = value;
93+
index += sizeof(T);
94+
}
95+
96+
static void SerializeString(char *buffer, size_t &index,
97+
const std::string &str) {
98+
auto size = str.size();
99+
SerializeInt<uint16_t>(buffer, index, static_cast<uint16_t>(size));
100+
if (size > 0) {
101+
memcpy(buffer + index, str.c_str(), size);
102+
}
103+
index += size;
104+
}
105+
106+
static std::string AttributeValueToString(
107+
const opentelemetry::sdk::common::OwnedAttributeValue &value) {
108+
std::string result;
109+
if (nostd::holds_alternative<bool>(value)) {
110+
result = nostd::get<bool>(value) ? "true" : "false";
111+
} else if (nostd::holds_alternative<int>(value)) {
112+
result = std::to_string(nostd::get<int>(value));
113+
} else if (nostd::holds_alternative<int64_t>(value)) {
114+
result = std::to_string(nostd::get<int64_t>(value));
115+
} else if (nostd::holds_alternative<unsigned int>(value)) {
116+
result = std::to_string(nostd::get<unsigned int>(value));
117+
} else if (nostd::holds_alternative<uint64_t>(value)) {
118+
result = std::to_string(nostd::get<uint64_t>(value));
119+
} else if (nostd::holds_alternative<double>(value)) {
120+
result = std::to_string(nostd::get<double>(value));
121+
} else if (nostd::holds_alternative<std::string>(value)) {
122+
result = nostd::get<std::string>(value);
123+
} else {
124+
LOG_WARN("[Geneva Metrics Exporter] AttributeValueToString - "
125+
" Nested attributes not supported - ignored");
126+
}
127+
return result;
128+
}
129+
130+
static uint64_t UnixTimeToWindowsTicks(uint64_t unix_epoch_secs) {
131+
uint64_t secs_since_windows_epoch = unix_epoch_secs + kSecondsToUnixTime;
132+
return (secs_since_windows_epoch * (uint64_t)kWindowsTicksPerSecond);
133+
}
134+
135+
} // namespace metrics
136+
} // namespace geneva
137+
} // namespace exporter
138+
OPENTELEMETRY_END_NAMESPACE
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#pragma once
5+
6+
#include <string>
7+
8+
#include "opentelemetry/version.h"
9+
10+
OPENTELEMETRY_BEGIN_NAMESPACE
11+
namespace exporter {
12+
namespace statsd {
13+
namespace metrics {
14+
15+
struct ExporterOptions {
16+
// clang-format off
17+
/*
18+
Format -
19+
Windows:
20+
Account={MetricAccount};NameSpace={MetricNamespace}
21+
Linux:
22+
Endpoint=unix://{UDS Path};Account={MetricAccount};Namespace={MetricNamespace}
23+
*/
24+
// clang-format off
25+
std::string connection_string;
26+
const std::map<std::string, std::string> prepopulated_dimensions;
27+
};
28+
} // namespace metrics
29+
} // namespace statsd
30+
} // namespace exporter
31+
OPENTELEMETRY_END_NAMESPACE
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#pragma once
5+
6+
#include "opentelemetry/exporters/geneva/metrics/connection_string_parser.h"
7+
#include "opentelemetry/exporters/geneva/metrics/data_transport.h"
8+
#include "opentelemetry/exporters/geneva/metrics/socket_tools.h"
9+
#include "opentelemetry/version.h"
10+
11+
#include <memory>
12+
13+
OPENTELEMETRY_BEGIN_NAMESPACE
14+
namespace exporter {
15+
namespace geneva {
16+
namespace metrics {
17+
class SocketDataTransport : public DataTransport {
18+
public:
19+
SocketDataTransport(const ConnectionStringParser &parser);
20+
bool Connect() noexcept override;
21+
bool Send(MetricsEventType event_type, const char *data,
22+
uint16_t length) noexcept override;
23+
bool Disconnect() noexcept override;
24+
~SocketDataTransport() = default;
25+
26+
private:
27+
// Socket connection is re-established for every batch of events
28+
SocketTools::SocketParams socketparams_{AF_UNIX, SOCK_STREAM, 0};
29+
SocketTools::Socket socket_;
30+
std::unique_ptr<SocketTools::SocketAddr> addr_;
31+
bool connected_{false};
32+
};
33+
} // namespace metrics
34+
} // namespace geneva
35+
} // namespace exporter
36+
OPENTELEMETRY_END_NAMESPACE

0 commit comments

Comments
 (0)