Currency Service
You are viewing the English version of this page because it has not yet been fully translated. Interested in helping out? See Contributing.
This service provides functionality to convert amounts between different currencies.
Traces
Initializing Tracing
The OpenTelemetry SDK is initialized from main
using the initTracer
function
defined in tracer_common.h
void initTracer()
{
auto exporter = opentelemetry::exporter::otlp::OtlpGrpcExporterFactory::Create();
auto processor =
opentelemetry::sdk::trace::SimpleSpanProcessorFactory::Create(std::move(exporter));
std::vector<std::unique_ptr<opentelemetry::sdk::trace::SpanProcessor>> processors;
processors.push_back(std::move(processor));
std::shared_ptr<opentelemetry::sdk::trace::TracerContext> context =
opentelemetry::sdk::trace::TracerContextFactory::Create(std::move(processors));
std::shared_ptr<opentelemetry::trace::TracerProvider> provider =
opentelemetry::sdk::trace::TracerProviderFactory::Create(context);
// Set the global trace provider
opentelemetry::trace::Provider::SetTracerProvider(provider);
// set global propagator
opentelemetry::context::propagation::GlobalTextMapPropagator::SetGlobalPropagator(
opentelemetry::nostd::shared_ptr<opentelemetry::context::propagation::TextMapPropagator>(
new opentelemetry::trace::propagation::HttpTraceContext()));
}
Create new spans
New spans can be created and started using
Tracer->StartSpan("spanName", attributes, options)
. After a span is created
you need to start and put it into active context using
Tracer->WithActiveSpan(span)
. You can find an example of this in the Convert
function.
std::string span_name = "CurrencyService/Convert";
auto span =
get_tracer("currencyservice")->StartSpan(span_name,
{{SemanticConventions::kRpcSystem, "grpc"},
{SemanticConventions::kRpcService, "oteldemo.CurrencyService"},
{SemanticConventions::kRpcMethod, "Convert"},
{SemanticConventions::kRpcGrpcStatusCode, 0}},
options);
auto scope = get_tracer("currencyservice")->WithActiveSpan(span);
Adding attributes to spans
You can add an attribute to a span using Span->SetAttribute(key, value)
.
span->SetAttribute("app.currency.conversion.from", from_code);
span->SetAttribute("app.currency.conversion.to", to_code);
Add span events
Adding span events is accomplished using Span->AddEvent(name)
.
span->AddEvent("Conversion successful, response sent back");
Set span status
Make sure to set your span status to Ok
, or Error
accordingly. You can do
this using Span->SetStatus(status)
span->SetStatus(StatusCode::kOk);
Tracing context propagation
In C++ propagation is not automatically handled. You need to extract it from the
caller and inject the propagation context into subsequent spans. The
GrpcServerCarrier
class defines a method to extract context from inbound gRPC
requests which is leveraged in the service call implementations.
The GrpcServerCarrier
class is defined in tracer_common.h
as follows:
class GrpcServerCarrier : public opentelemetry::context::propagation::TextMapCarrier
{
public:
GrpcServerCarrier(ServerContext *context) : context_(context) {}
GrpcServerCarrier() = default;
virtual opentelemetry::nostd::string_view Get(
opentelemetry::nostd::string_view key) const noexcept override
{
auto it = context_->client_metadata().find(key.data());
if (it != context_->client_metadata().end())
{
return it->second.data();
}
return "";
}
virtual void Set(opentelemetry::nostd::string_view key,
opentelemetry::nostd::string_view value) noexcept override
{
// Not required for server
}
ServerContext *context_;
};
This class is leveraged in the Convert
method to extract context and create a
StartSpanOptions
object to contain the right context which is used when
creating new spans.
StartSpanOptions options;
options.kind = SpanKind::kServer;
GrpcServerCarrier carrier(context);
auto prop = context::propagation::GlobalTextMapPropagator::GetGlobalPropagator();
auto current_ctx = context::RuntimeContext::GetCurrent();
auto new_context = prop->Extract(carrier, current_ctx);
options.parent = GetSpan(new_context)->GetContext();
Metrics
Initializing Metrics
The OpenTelemetry MeterProvider
is initialized from main()
using the
initMeter()
function defined in meter_common.h
.
void initMeter()
{
// Build MetricExporter
otlp_exporter::OtlpGrpcMetricExporterOptions otlpOptions;
auto exporter = otlp_exporter::OtlpGrpcMetricExporterFactory::Create(otlpOptions);
// Build MeterProvider and Reader
metric_sdk::PeriodicExportingMetricReaderOptions options;
std::unique_ptr<metric_sdk::MetricReader> reader{
new metric_sdk::PeriodicExportingMetricReader(std::move(exporter), options) };
auto provider = std::shared_ptr<metrics_api::MeterProvider>(new metric_sdk::MeterProvider());
auto p = std::static_pointer_cast<metric_sdk::MeterProvider>(provider);
p->AddMetricReader(std::move(reader));
metrics_api::Provider::SetMeterProvider(provider);
}
Starting IntCounter
A global currency_counter
variable is created at main()
calling the function
initIntCounter()
defined in meter_common.h
.
nostd::unique_ptr<metrics_api::Counter<uint64_t>> initIntCounter()
{
std::string counter_name = name + "_counter";
auto provider = metrics_api::Provider::GetMeterProvider();
nostd::shared_ptr<metrics_api::Meter> meter = provider->GetMeter(name, version);
auto int_counter = meter->CreateUInt64Counter(counter_name);
return int_counter;
}
Counting currency conversion requests
The method CurrencyCounter()
is implemented as follows:
void CurrencyCounter(const std::string& currency_code)
{
std::map<std::string, std::string> labels = { {"currency_code", currency_code} };
auto labelkv = common::KeyValueIterableView<decltype(labels)>{ labels };
currency_counter->Add(1, labelkv);
}
Every time the function Convert()
is called, the currency code received as
to_code
is used to count the conversions.
CurrencyCounter(to_code);
Logs
The OpenTelemetry LoggerProvider
is initialized from main()
using the
initLogger()
function defined in logger_common.h
.
void initLogger() {
otlp::OtlpGrpcLogRecordExporterOptions loggerOptions;
auto exporter = otlp::OtlpGrpcLogRecordExporterFactory::Create(loggerOptions);
auto processor = logs_sdk::SimpleLogRecordProcessorFactory::Create(std::move(exporter));
std::vector<std::unique_ptr<logs_sdk::LogRecordProcessor>> processors;
processors.push_back(std::move(processor));
auto context = logs_sdk::LoggerContextFactory::Create(std::move(processors));
std::shared_ptr<logs::LoggerProvider> provider = logs_sdk::LoggerProviderFactory::Create(std::move(context));
opentelemetry::logs::Provider::SetLoggerProvider(provider);
}
Using the LoggerProvider
The initialized Logger Provider is called from main
in server.cpp
:
logger = getLogger(name);
It assigns the logger to a local variable called logger
:
nostd::shared_ptr<opentelemetry::logs::Logger> logger;
Which is then used throughout the code whenever we need to log a line:
logger->Info(std::string(__func__) + " conversion successful");
Comentarios
¿Fue útil esta página?
Thank you. Your feedback is appreciated!
Please let us know how we can improve this page. Your feedback is appreciated!