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

Add Synchronous Metric Instruments SDK #179

Merged
merged 16 commits into from
Aug 3, 2020
Merged
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
21 changes: 21 additions & 0 deletions api/include/opentelemetry/common/attribute_value.h
Original file line number Diff line number Diff line change
@@ -24,5 +24,26 @@ using AttributeValue = nostd::variant<bool,
nostd::span<const uint64_t>,
nostd::span<const double>,
nostd::span<const nostd::string_view>>;

enum AttributeType
{
TYPE_BOOL,
TYPE_INT,
TYPE_INT64,
TYPE_UINT,
TYPE_UINT64,
TYPE_DOUBLE,
TYPE_STRING,
TYPE_CSTRING,
// TYPE_SPAN_BYTE, // TODO: not part of OT spec yet
TYPE_SPAN_BOOL,
TYPE_SPAN_INT,
TYPE_SPAN_INT64,
TYPE_SPAN_UINT,
TYPE_SPAN_UINT64,
TYPE_SPAN_DOUBLE,
TYPE_SPAN_STRING
};

} // namespace common
OPENTELEMETRY_END_NAMESPACE
242 changes: 242 additions & 0 deletions sdk/include/opentelemetry/sdk/metrics/instrument.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
#pragma once

#include "opentelemetry/metrics/instrument.h"
#include "opentelemetry/sdk/metrics/aggregator/aggregator.h"
#include "opentelemetry/sdk/metrics/record.h"
#include "opentelemetry/version.h"

#include <iostream>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>

namespace metrics_api = opentelemetry::metrics;
namespace trace_api = opentelemetry::trace;

OPENTELEMETRY_BEGIN_NAMESPACE
namespace sdk
{
namespace metrics
{

class Instrument : virtual public metrics_api::Instrument
{

public:
Instrument() = default;

Instrument(nostd::string_view name,
nostd::string_view description,
nostd::string_view unit,
bool enabled,
metrics_api::InstrumentKind kind)
: name_(name), description_(description), unit_(unit), enabled_(enabled), kind_(kind)
{}

// Returns true if the instrument is enabled and collecting data
virtual bool IsEnabled() override { return enabled_; }

// Return the instrument name
virtual nostd::string_view GetName() override { return name_; }

// Return the instrument description
virtual nostd::string_view GetDescription() override { return description_; }

// Return the insrument's units of measurement
virtual nostd::string_view GetUnits() override { return unit_; }

virtual metrics_api::InstrumentKind GetKind() override { return this->kind_; }

protected:
std::string name_;
std::string description_;
std::string unit_;
bool enabled_;
std::mutex mu_;
metrics_api::InstrumentKind kind_;
};

template <class T>
class BoundSynchronousInstrument : public Instrument,
virtual public metrics_api::BoundSynchronousInstrument<T>
{

public:
BoundSynchronousInstrument() = default;

BoundSynchronousInstrument(nostd::string_view name,
nostd::string_view description,
nostd::string_view unit,
bool enabled,
metrics_api::InstrumentKind kind,
std::shared_ptr<Aggregator<T>> agg)
: Instrument(name, description, unit, enabled, kind), agg_(agg)
{
this->inc_ref(); // increase reference count when instantiated
}

/**
* Frees the resources associated with this Bound Instrument.
* The Metric from which this instrument was created is not impacted.
*
* @param none
* @return void
*/
virtual void unbind() override { ref_ -= 1; }

/**
* Increments the reference count. This function is used when binding or instantiating.
*
* @param none
* @return void
*/
virtual void inc_ref() override { ref_ += 1; }

/**
* Returns the current reference count of the instrument. This value is used to
* later in the pipeline remove stale instruments.
*
* @param none
* @return current ref count of the instrument
*/
virtual int get_ref() override { return ref_; }

/**
* Records a single synchronous metric event via a call to the aggregator.
* Since this is a bound synchronous instrument, labels are not required in
* metric capture calls.
*
* @param value is the numerical representation of the metric being captured
* @return void
*/
virtual void update(T value) override
{
this->mu_.lock();
agg_->update(value);
this->mu_.unlock();
}

/**
* Returns the aggregator responsible for meaningfully combining update values.
*
* @param none
* @return the aggregator assigned to this instrument
*/
virtual std::shared_ptr<Aggregator<T>> GetAggregator() final { return agg_; }

private:
std::shared_ptr<Aggregator<T>> agg_;
int ref_ = 0;
};

template <class T>
class SynchronousInstrument : public Instrument,
virtual public metrics_api::SynchronousInstrument<T>
{

public:
SynchronousInstrument() = default;

SynchronousInstrument(nostd::string_view name,
nostd::string_view description,
nostd::string_view unit,
bool enabled,
metrics_api::InstrumentKind kind)
: Instrument(name, description, unit, enabled, kind)
{}

/**
* Returns a Bound Instrument associated with the specified labels. Multiples requests
* with the same set of labels may return the same Bound Instrument instance.
*
* It is recommended that callers keep a reference to the Bound Instrument
* instead of repeatedly calling this operation.
*
* @param labels the set of labels, as key-value pairs
* @return a Bound Instrument
*/
virtual nostd::shared_ptr<metrics_api::BoundSynchronousInstrument<T>> bind(
const trace::KeyValueIterable &labels) override
{
return nostd::shared_ptr<BoundSynchronousInstrument<T>>();
}

virtual void update(T value, const trace::KeyValueIterable &labels) override = 0;

/**
* Checkpoints instruments and returns a set of records which are ready for processing.
* This method should ONLY be called by the Meter Class as part of the export pipeline
* as it also prunes bound instruments with no active references.
*
* @param none
* @return vector of Records which hold the data attached to this synchronous instrument
*/
virtual std::vector<Record> GetRecords() = 0;
};

// Helper functions for turning a trace::KeyValueIterable into a string
inline void print_value(std::stringstream &ss,
common::AttributeValue &value,
bool jsonTypes = false)
{
switch (value.index())
{
case common::AttributeType::TYPE_STRING:
if (jsonTypes)
ss << '"';
ss << nostd::get<nostd::string_view>(value);
if (jsonTypes)
ss << '"';
break;
default:
#if __EXCEPTIONS
throw std::invalid_argument("Labels must be strings");
#else
std::terminate();
#endif
break;
}
};

// Utility function which converts maps to strings for better performance
inline std::string mapToString(const std::map<std::string, std::string> &conv)
{
std::stringstream ss;
ss << "{";
for (auto i : conv)
{
ss << i.first << ':' << i.second << ',';
}
ss << "}";
return ss.str();
}

inline std::string KvToString(const trace::KeyValueIterable &kv) noexcept
{
std::stringstream ss;
ss << "{";
size_t size = kv.size();
if (size)
{
size_t i = 1;
kv.ForEachKeyValue([&](nostd::string_view key, common::AttributeValue value) noexcept {
ss << "\"" << key << "\":";
print_value(ss, value, true);
if (size != i)
{
ss << ",";
}
i++;
return true;
});
};
ss << "}";
return ss.str();
}

} // namespace metrics
} // namespace sdk
OPENTELEMETRY_END_NAMESPACE
47 changes: 47 additions & 0 deletions sdk/include/opentelemetry/sdk/metrics/record.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#pragma once

#include <memory>
#include "opentelemetry/metrics/instrument.h"
#include "opentelemetry/nostd/variant.h"
#include "opentelemetry/sdk/metrics/aggregator/aggregator.h"

OPENTELEMETRY_BEGIN_NAMESPACE

namespace metrics_api = opentelemetry::metrics;

namespace sdk
{
namespace metrics
{
using AggregatorVariant = nostd::variant<std::shared_ptr<Aggregator<short>>,
std::shared_ptr<Aggregator<int>>,
std::shared_ptr<Aggregator<float>>,
std::shared_ptr<Aggregator<double>>>;
class Record
{
public:
explicit Record(nostd::string_view name,
nostd::string_view description,
std::string labels,
AggregatorVariant aggregator)
{
name_ = std::string(name);
description_ = std::string(description);
labels_ = labels;
aggregator_ = aggregator;
}

std::string GetName() { return name_; }
std::string GetDescription() { return description_; }
std::string GetLabels() { return labels_; }
AggregatorVariant GetAggregator() { return aggregator_; }

private:
std::string name_;
std::string description_;
std::string labels_;
AggregatorVariant aggregator_;
};
} // namespace metrics
} // namespace sdk
OPENTELEMETRY_END_NAMESPACE
Loading