Skip to content

Commit

Permalink
Add Synchronous Metric Instruments SDK (#179)
Browse files Browse the repository at this point in the history
  • Loading branch information
ankit-bhargava authored Aug 3, 2020
1 parent 5298132 commit 3ac89eb
Show file tree
Hide file tree
Showing 6 changed files with 1,029 additions and 0 deletions.
21 changes: 21 additions & 0 deletions api/include/opentelemetry/common/attribute_value.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 3ac89eb

Please sign in to comment.