blob: 18293e8a4421bca88a1c86112641e0d4533656dc [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "logger/logger.h"
#include <memory>
#include <string>
#include "./event.pb.h"
#include "./logging.h"
#include "./observation2.pb.h"
#include "algorithms/rappor/rappor_config_helper.h"
#include "config/encodings.pb.h"
#include "config/id.h"
#include "config/metric_definition.pb.h"
#include "config/report_definition.pb.h"
#include "logger/event_record.h"
#include "util/datetime_util.h"
namespace cobalt {
namespace logger {
using ::cobalt::rappor::RapporConfigHelper;
using ::cobalt::util::ClockInterface;
using ::cobalt::util::SystemClock;
using ::cobalt::util::TimeToDayIndex;
using ::google::protobuf::RepeatedPtrField;
constexpr char TRACE_PREFIX[] = "[COBALT_EVENT_TRACE] ";
// EventLogger is an abstract interface used internally in logger.cc to
// dispatch logging logic based on Metric type. Below we create subclasses
// of EventLogger for each of several Metric types.
class EventLogger {
public:
explicit EventLogger(Logger* logger) : logger_(logger) {}
virtual ~EventLogger() = default;
// Finds the Metric with the given ID. Expects that this has type
// |expected_metric_type|. If not logs an error and returns.
// If so then Logs the Event specified by |event_record| to Cobalt.
Status Log(uint32_t metric_id,
MetricDefinition::MetricType expected_metric_type,
EventRecord* event_record);
protected:
const Encoder* encoder() { return logger_->encoder_; }
EventAggregator* event_aggregator() { return logger_->event_aggregator_; }
const ProjectContext* project_context() { return logger_->project_context_; }
Encoder::Result BadReportType(const MetricDefinition& metric,
const ReportDefinition& report);
private:
// Finishes setting up and then validates |event_record|.
Status FinalizeEvent(uint32_t metric_id,
MetricDefinition::MetricType expected_type,
EventRecord* event_record);
virtual Status ValidateEvent(const EventRecord& event_record);
// Given an EventRecord and a ReportDefinition, determines whether or not
// the Event should be used to update a local aggregation and if so passes
// the Event to the Local Aggregator.
virtual Status MaybeUpdateLocalAggregation(const ReportDefinition& report,
EventRecord* event_record);
// Given an EventRecord and a ReportDefinition, determines whether or not
// the Event should be used to generate an immediate Observation and if so
// does generate one and writes it to the Observation Store.
//
// |may_invalidate| indicates that the implementation is allowed to invalidate
// |event_record|. This should be set true only when it is known that
// |event_record| is no longer needed. Setting this true allows the data in
// |event_record| to be moved rather than copied.
Status MaybeGenerateImmediateObservation(const ReportDefinition& report,
bool may_invalidate,
EventRecord* event_record);
// Given an EventRecord and a ReportDefinition, determines whether or not
// the Event should be used to generate an immediate Observation and if so
// does generate one. This method is invoked by
// MaybeGenerateImmediateObservation().
//
// |may_invalidate| indicates that the implementation is allowed to invalidate
// |event_record|. This should be set true only when it is known that
// |event_record| is no longer needed. Setting this true allows the data in
// |event_record| to be moved rather than copied.
virtual Encoder::Result MaybeEncodeImmediateObservation(
const ReportDefinition& report, bool may_invalidate,
EventRecord* event_record);
std::string TraceEvent(Event* event);
void TraceLogFailure(const Status& status, EventRecord* event_record,
const ReportDefinition& report);
void TraceLogSuccess(EventRecord* event_record);
Logger* logger_;
};
// Implementation of EventLogger for metrics of type EVENT_OCCURRED.
class OccurrenceEventLogger : public EventLogger {
public:
explicit OccurrenceEventLogger(Logger* logger) : EventLogger(logger) {}
virtual ~OccurrenceEventLogger() = default;
private:
Status ValidateEvent(const EventRecord& event_record) override;
Encoder::Result MaybeEncodeImmediateObservation(
const ReportDefinition& report, bool may_invalidate,
EventRecord* event_record) override;
Status MaybeUpdateLocalAggregation(const ReportDefinition& report,
EventRecord* event_record) override;
};
// Implementation of EventLogger for metrics of type EVENT_COUNT.
class CountEventLogger : public EventLogger {
public:
explicit CountEventLogger(Logger* logger) : EventLogger(logger) {}
virtual ~CountEventLogger() = default;
private:
Encoder::Result MaybeEncodeImmediateObservation(
const ReportDefinition& report, bool may_invalidate,
EventRecord* event_record) override;
};
// Implementation of EventLogger for all of the numerical performance metric
// types. This is an abstract class. There are subclasses below for each
// metric type.
class IntegerPerformanceEventLogger : public EventLogger {
protected:
explicit IntegerPerformanceEventLogger(Logger* logger)
: EventLogger(logger) {}
virtual ~IntegerPerformanceEventLogger() = default;
private:
Encoder::Result MaybeEncodeImmediateObservation(
const ReportDefinition& report, bool may_invalidate,
EventRecord* event_record) override;
virtual uint32_t EventCode(const Event& event) = 0;
virtual std::string Component(const Event& event) = 0;
virtual int64_t IntValue(const Event& event) = 0;
};
// Implementation of EventLogger for metrics of type ELAPSED_TIME.
class ElapsedTimeEventLogger : public IntegerPerformanceEventLogger {
public:
explicit ElapsedTimeEventLogger(Logger* logger)
: IntegerPerformanceEventLogger(logger) {}
virtual ~ElapsedTimeEventLogger() = default;
private:
uint32_t EventCode(const Event& event) override;
std::string Component(const Event& event) override;
int64_t IntValue(const Event& event) override;
};
// Implementation of EventLogger for metrics of type FRAME_RATE.
class FrameRateEventLogger : public IntegerPerformanceEventLogger {
public:
explicit FrameRateEventLogger(Logger* logger)
: IntegerPerformanceEventLogger(logger) {}
virtual ~FrameRateEventLogger() = default;
private:
uint32_t EventCode(const Event& event) override;
std::string Component(const Event& event) override;
int64_t IntValue(const Event& event) override;
};
// Implementation of EventLogger for metrics of type MEMORY_USAGE.
class MemoryUsageEventLogger : public IntegerPerformanceEventLogger {
public:
explicit MemoryUsageEventLogger(Logger* logger)
: IntegerPerformanceEventLogger(logger) {}
virtual ~MemoryUsageEventLogger() = default;
private:
uint32_t EventCode(const Event& event) override;
std::string Component(const Event& event) override;
int64_t IntValue(const Event& event) override;
};
// Implementation of EventLogger for metrics of type INT_HISTOGRAM.
class IntHistogramEventLogger : public EventLogger {
public:
explicit IntHistogramEventLogger(Logger* logger) : EventLogger(logger) {}
virtual ~IntHistogramEventLogger() = default;
private:
Status ValidateEvent(const EventRecord& event_record) override;
Encoder::Result MaybeEncodeImmediateObservation(
const ReportDefinition& report, bool may_invalidate,
EventRecord* event_record) override;
};
// Implementation of EventLogger for metrics of type STRING_USED.
class StringUsedEventLogger : public EventLogger {
public:
explicit StringUsedEventLogger(Logger* logger) : EventLogger(logger) {}
virtual ~StringUsedEventLogger() = default;
private:
Encoder::Result MaybeEncodeImmediateObservation(
const ReportDefinition& report, bool may_invalidate,
EventRecord* event_record) override;
};
// Implementation of EventLogger for metrics of type CUSTOM.
class CustomEventLogger : public EventLogger {
public:
explicit CustomEventLogger(Logger* logger) : EventLogger(logger) {}
virtual ~CustomEventLogger() = default;
private:
Status ValidateEvent(const EventRecord& event_record) override;
Encoder::Result MaybeEncodeImmediateObservation(
const ReportDefinition& report, bool may_invalidate,
EventRecord* event_record) override;
};
//////////////////// Logger method implementations ////////////////////////
Logger::Logger(const Encoder* encoder, EventAggregator* event_aggregator,
ObservationWriter* observation_writer,
const ProjectContext* project, LoggerInterface* internal_logger)
: encoder_(encoder),
event_aggregator_(event_aggregator),
observation_writer_(observation_writer),
project_context_(project),
clock_(new SystemClock()) {
CHECK(project);
if (internal_logger) {
internal_metrics_.reset(new InternalMetricsImpl(internal_logger));
} else {
// We were not provided with a metrics logger. We must create one.
internal_metrics_.reset(new NoOpInternalMetrics());
}
if (event_aggregator_->UpdateAggregationConfigs(*project_context_) != kOK) {
LOG(ERROR) << "Failed to provide aggregation configurations to the "
"EventAggregator.";
}
}
Logger::Logger(const Encoder* encoder, ObservationWriter* observation_writer,
const ProjectContext* project, LoggerInterface* internal_logger)
: encoder_(encoder),
event_aggregator_(nullptr),
observation_writer_(observation_writer),
project_context_(project),
clock_(new SystemClock()) {
CHECK(project);
if (internal_logger) {
internal_metrics_.reset(new InternalMetricsImpl(internal_logger));
} else {
// We were not provided with a metrics logger. We must create one.
internal_metrics_.reset(new NoOpInternalMetrics());
}
}
Status Logger::LogEvent(uint32_t metric_id, uint32_t event_code) {
VLOG(4) << "Logger::LogEvent(" << metric_id << ", " << event_code
<< ") project=" << project_context_->FullyQualifiedName();
EventRecord event_record;
internal_metrics_->LoggerCalled(LoggerCallsMadeEventCode::LogEvent);
auto* occurrence_event = event_record.event->mutable_occurrence_event();
occurrence_event->set_event_code(event_code);
auto event_logger = std::make_unique<OccurrenceEventLogger>(this);
return event_logger->Log(metric_id, MetricDefinition::EVENT_OCCURRED,
&event_record);
}
Status Logger::LogEventCount(uint32_t metric_id, uint32_t event_code,
const std::string& component,
int64_t period_duration_micros, uint32_t count) {
internal_metrics_->LoggerCalled(LoggerCallsMadeEventCode::LogEventCount);
EventRecord event_record;
auto* count_event = event_record.event->mutable_count_event();
count_event->add_event_code(event_code);
count_event->set_component(component);
count_event->set_period_duration_micros(period_duration_micros);
count_event->set_count(count);
auto event_logger = std::make_unique<CountEventLogger>(this);
return event_logger->Log(metric_id, MetricDefinition::EVENT_COUNT,
&event_record);
}
Status Logger::LogElapsedTime(uint32_t metric_id, uint32_t event_code,
const std::string& component,
int64_t elapsed_micros) {
internal_metrics_->LoggerCalled(LoggerCallsMadeEventCode::LogElapsedTime);
EventRecord event_record;
auto* elapsed_time_event = event_record.event->mutable_elapsed_time_event();
elapsed_time_event->add_event_code(event_code);
elapsed_time_event->set_component(component);
elapsed_time_event->set_elapsed_micros(elapsed_micros);
auto event_logger = std::make_unique<ElapsedTimeEventLogger>(this);
return event_logger->Log(metric_id, MetricDefinition::ELAPSED_TIME,
&event_record);
}
Status Logger::LogFrameRate(uint32_t metric_id, uint32_t event_code,
const std::string& component, float fps) {
internal_metrics_->LoggerCalled(LoggerCallsMadeEventCode::LogFrameRate);
EventRecord event_record;
auto* frame_rate_event = event_record.event->mutable_frame_rate_event();
frame_rate_event->add_event_code(event_code);
frame_rate_event->set_component(component);
frame_rate_event->set_frames_per_1000_seconds(std::round(fps * 1000.0));
auto event_logger = std::make_unique<FrameRateEventLogger>(this);
return event_logger->Log(metric_id, MetricDefinition::FRAME_RATE,
&event_record);
}
Status Logger::LogMemoryUsage(uint32_t metric_id, uint32_t event_code,
const std::string& component, int64_t bytes) {
internal_metrics_->LoggerCalled(LoggerCallsMadeEventCode::LogMemoryUsage);
EventRecord event_record;
auto* memory_usage_event = event_record.event->mutable_memory_usage_event();
memory_usage_event->add_event_code(event_code);
memory_usage_event->set_component(component);
memory_usage_event->set_bytes(bytes);
auto event_logger = std::make_unique<MemoryUsageEventLogger>(this);
return event_logger->Log(metric_id, MetricDefinition::MEMORY_USAGE,
&event_record);
}
Status Logger::LogIntHistogram(uint32_t metric_id, uint32_t event_code,
const std::string& component,
HistogramPtr histogram) {
internal_metrics_->LoggerCalled(LoggerCallsMadeEventCode::LogIntHistogram);
EventRecord event_record;
auto* int_histogram_event = event_record.event->mutable_int_histogram_event();
int_histogram_event->add_event_code(event_code);
int_histogram_event->set_component(component);
int_histogram_event->mutable_buckets()->Swap(histogram.get());
auto event_logger = std::make_unique<IntHistogramEventLogger>(this);
return event_logger->Log(metric_id, MetricDefinition::INT_HISTOGRAM,
&event_record);
}
Status Logger::LogString(uint32_t metric_id, const std::string& str) {
internal_metrics_->LoggerCalled(LoggerCallsMadeEventCode::LogString);
EventRecord event_record;
auto* string_used_event = event_record.event->mutable_string_used_event();
string_used_event->set_str(str);
auto event_logger = std::make_unique<StringUsedEventLogger>(this);
return event_logger->Log(metric_id, MetricDefinition::STRING_USED,
&event_record);
}
Status Logger::LogCustomEvent(uint32_t metric_id, EventValuesPtr event_values) {
internal_metrics_->LoggerCalled(LoggerCallsMadeEventCode::LogCustomEvent);
EventRecord event_record;
auto* custom_event = event_record.event->mutable_custom_event();
custom_event->mutable_values()->swap(*event_values);
auto event_logger = std::make_unique<CustomEventLogger>(this);
return event_logger->Log(metric_id, MetricDefinition::CUSTOM, &event_record);
}
//////////////////// EventLogger method implementations ////////////////////////
std::string EventLogger::TraceEvent(Event* event) {
std::stringstream ss;
ss << "Day index: " << event->day_index() << std::endl;
if (event->has_occurrence_event()) {
auto e = event->occurrence_event();
ss << "OccurrenceEvent: " << e.event_code() << std::endl;
} else if (event->has_count_event()) {
auto e = event->count_event();
ss << "CountEvent:" << std::endl;
ss << "EventCodes:";
for (const auto& code : e.event_code()) {
ss << " " << code;
}
ss << ", Component: " << e.component()
<< ", PeriodDurationMicros: " << e.period_duration_micros()
<< ", Count: " << e.count() << std::endl;
} else if (event->has_elapsed_time_event()) {
auto e = event->elapsed_time_event();
ss << "ElapsedTimeEvent:" << std::endl;
ss << "EventCodes:";
for (const auto& code : e.event_code()) {
ss << " " << code;
}
ss << ", Component: " << e.component()
<< ", ElapsedMicros: " << e.elapsed_micros() << std::endl;
} else if (event->has_frame_rate_event()) {
auto e = event->frame_rate_event();
ss << "FrameRateEvent:" << std::endl;
ss << "EventCodes:";
for (const auto& code : e.event_code()) {
ss << " " << code;
}
ss << ", Component: " << e.component()
<< ", FramesPer1000Seconds: " << e.frames_per_1000_seconds()
<< std::endl;
} else if (event->has_memory_usage_event()) {
auto e = event->memory_usage_event();
ss << "MemoryUsageEvent:" << std::endl;
ss << "EventCodes:";
for (const auto& code : e.event_code()) {
ss << " " << code;
}
ss << ", Component: " << e.component() << ", Bytes: " << e.bytes()
<< std::endl;
} else if (event->has_int_histogram_event()) {
auto e = event->int_histogram_event();
ss << "IntHistogramEvent:" << std::endl;
ss << "EventCodes:";
for (const auto& code : e.event_code()) {
ss << " " << code;
}
ss << ", Component: " << e.component() << std::endl;
for (const auto& bucket : e.buckets()) {
ss << "| " << bucket.index() << " = " << bucket.count() << std::endl;
}
} else if (event->has_string_used_event()) {
auto e = event->string_used_event();
ss << "StringUsedEvent: " << e.str() << std::endl;
} else if (event->has_custom_event()) {
auto e = event->custom_event();
ss << "CustomEvent:";
if (e.values().empty()) {
ss << " (Empty)";
}
ss << std::endl;
for (const auto& entry : e.values()) {
switch (entry.second.data_case()) {
ss << "| " << entry.first << " = ";
case CustomDimensionValue::kStringValue:
ss << entry.second.string_value();
break;
case CustomDimensionValue::kIntValue:
ss << entry.second.int_value();
break;
case CustomDimensionValue::kBlobValue:
ss << "Blob";
break;
case CustomDimensionValue::kIndexValue:
ss << entry.second.index_value();
break;
case CustomDimensionValue::kDoubleValue:
ss << entry.second.double_value();
break;
default:
ss << "No dimension value";
break;
}
ss << std::endl;
}
}
return ss.str();
}
void EventLogger::TraceLogFailure(const Status& status,
EventRecord* event_record,
const ReportDefinition& report) {
if (!event_record->metric->meta_data().also_log_locally()) {
return;
}
LOG(INFO)
<< TRACE_PREFIX << "("
<< project_context()->RefMetric(event_record->metric).FullyQualifiedName()
<< "): Error (" << status << ")" << std::endl
<< TraceEvent(event_record->event.get())
<< "While trying to send report: " << report.report_name() << std::endl;
}
void EventLogger::TraceLogSuccess(EventRecord* event_record) {
if (!event_record->metric->meta_data().also_log_locally()) {
return;
}
LOG(INFO)
<< TRACE_PREFIX << "("
<< project_context()->RefMetric(event_record->metric).FullyQualifiedName()
<< "):" << std::endl
<< TraceEvent(event_record->event.get());
}
Status EventLogger::Log(uint32_t metric_id,
MetricDefinition::MetricType expected_metric_type,
EventRecord* event_record) {
// TODO(rudominer) Check the ReleaseStage of the Metric against the
// ReleaseStage of the project.
auto status = FinalizeEvent(metric_id, expected_metric_type, event_record);
if (status != kOK) {
return status;
}
int num_reports = event_record->metric->reports_size();
int report_index = 0;
if (event_record->metric->reports_size() == 0) {
VLOG(1) << "Warning: An event was logged for a metric with no reports "
"defined. Metric ["
<< MetricDebugString(*event_record->metric) << "] in project "
<< project_context()->DebugString() << ".";
}
for (const auto& report : event_record->metric->reports()) {
status = MaybeUpdateLocalAggregation(report, event_record);
if (status != kOK) {
TraceLogFailure(status, event_record, report);
return status;
}
// If we are processing the final report, then we set may_invalidate
// to true in order to allow data to be moved out of |event_record|
// instead of being copied. One example where this is useful is when
// creating an immediate Observation of type Histogram. In that case
// we can move the histogram from the Event to the Observation and
// avoid copying. Since the |event_record| is invalidated, any other
// operation on the |event_record| must be performed before this for
// loop.
bool may_invalidate = ++report_index == num_reports;
status =
MaybeGenerateImmediateObservation(report, may_invalidate, event_record);
if (status != kOK) {
TraceLogFailure(status, event_record, report);
return status;
}
}
TraceLogSuccess(event_record);
return kOK;
}
Status EventLogger::FinalizeEvent(uint32_t metric_id,
MetricDefinition::MetricType expected_type,
EventRecord* event_record) {
event_record->metric = project_context()->GetMetric(metric_id);
if (event_record->metric == nullptr) {
LOG(ERROR) << "There is no metric with ID '" << metric_id << "' registered "
<< "in project '" << project_context()->DebugString() << "'.";
return kInvalidArguments;
}
if (event_record->metric->metric_type() != expected_type) {
LOG(ERROR) << "Metric '" << MetricDebugString(*event_record->metric)
<< "' in project '" << project_context()->DebugString()
<< "' is not of type " << expected_type << ".";
return kInvalidArguments;
}
// Compute the day_index.
event_record->event->set_day_index(TimeToDayIndex(
std::chrono::system_clock::to_time_t(logger_->clock_->now()),
event_record->metric->time_zone_policy()));
return ValidateEvent(*event_record);
}
Status EventLogger::ValidateEvent(const EventRecord& event_record) {
return kOK;
}
// The default implementation of MaybeUpdateLocalAggregation does nothing
// and returns OK.
Status EventLogger::MaybeUpdateLocalAggregation(const ReportDefinition& report,
EventRecord* event_record) {
return kOK;
}
Status EventLogger::MaybeGenerateImmediateObservation(
const ReportDefinition& report, bool may_invalidate,
EventRecord* event_record) {
auto encoder_result =
MaybeEncodeImmediateObservation(report, may_invalidate, event_record);
if (encoder_result.status != kOK) {
return encoder_result.status;
}
if (encoder_result.observation == nullptr) {
return kOK;
}
return logger_->observation_writer_->WriteObservation(
*encoder_result.observation, std::move(encoder_result.metadata));
}
// The default implementation of MaybeEncodeImmediateObservation does
// nothing and returns OK.
Encoder::Result EventLogger::MaybeEncodeImmediateObservation(
const ReportDefinition& report, bool may_invalidate,
EventRecord* event_record) {
Encoder::Result result;
result.status = kOK;
result.observation = nullptr;
return result;
}
Encoder::Result EventLogger::BadReportType(const MetricDefinition& metric,
const ReportDefinition& report) {
LOG(ERROR) << "Invalid Cobalt config: Report " << report.report_name()
<< " for metric " << MetricDebugString(metric) << " in project "
<< project_context()->DebugString()
<< " is not of an appropriate type for the metric type.";
Encoder::Result encoder_result;
encoder_result.status = kInvalidConfig;
return encoder_result;
}
/////////////// OccurrenceEventLogger method implementations ///////////////////
// TODO(zmbush): Validate dimensions (for all subclasses of EventLogger).
Status OccurrenceEventLogger::ValidateEvent(const EventRecord& event_record) {
CHECK(event_record.event->has_occurrence_event());
const auto& occurrence_event = event_record.event->occurrence_event();
if (occurrence_event.event_code() > event_record.metric->max_event_code()) {
LOG(ERROR) << "The event_code " << occurrence_event.event_code()
<< " exceeds " << event_record.metric->max_event_code()
<< ", the max_event_code for Metric "
<< MetricDebugString(*event_record.metric) << " in project "
<< project_context()->DebugString() << ".";
return kInvalidArguments;
}
return kOK;
}
Encoder::Result OccurrenceEventLogger::MaybeEncodeImmediateObservation(
const ReportDefinition& report, bool may_invalidate,
EventRecord* event_record) {
const MetricDefinition& metric = *(event_record->metric);
const Event& event = *(event_record->event);
CHECK(event.has_occurrence_event());
const auto& occurrence_event = event.occurrence_event();
switch (report.report_type()) {
// Each report type has its own logic for generating immediate
// observations.
case ReportDefinition::SIMPLE_OCCURRENCE_COUNT: {
return encoder()->EncodeBasicRapporObservation(
project_context()->RefMetric(&metric), &report, event.day_index(),
occurrence_event.event_code(),
RapporConfigHelper::BasicRapporNumCategories(metric));
}
// Report type UNIQUE_N_DAY_ACTIVES is valid but should not result in
// generation of an immediate observation.
case ReportDefinition::UNIQUE_N_DAY_ACTIVES: {
Encoder::Result result;
result.status = kOK;
result.observation = nullptr;
result.metadata = nullptr;
return result;
}
default:
return BadReportType(metric, report);
}
}
Status OccurrenceEventLogger::MaybeUpdateLocalAggregation(
const ReportDefinition& report, EventRecord* event_record) {
// If the Logger was constructed without an EventAggregator, do nothing and
// return kOK.
// TODO(pesk): remove this clause when the deprecated Logger constructor is
// removed.
if (event_aggregator() == nullptr) {
return kOK;
}
switch (report.report_type()) {
case ReportDefinition::UNIQUE_N_DAY_ACTIVES: {
return event_aggregator()->LogUniqueActivesEvent(report.id(),
event_record);
}
default:
return kOK;
}
}
///////////// CountEventLogger method implementations //////////////////////////
Encoder::Result CountEventLogger::MaybeEncodeImmediateObservation(
const ReportDefinition& report, bool may_invalidate,
EventRecord* event_record) {
const MetricDefinition& metric = *(event_record->metric);
const Event& event = *(event_record->event);
CHECK(event.has_count_event());
const auto& count_event = event.count_event();
switch (report.report_type()) {
// Each report type has its own logic for generating immediate
// observations.
case ReportDefinition::EVENT_COMPONENT_OCCURRENCE_COUNT: {
return encoder()->EncodeIntegerEventObservation(
project_context()->RefMetric(&metric), &report, event.day_index(),
// TODO(zmbush): Pass all event_codes, not just the first.
count_event.event_code(0), count_event.component(),
count_event.count());
}
default:
return BadReportType(metric, report);
}
}
/////////////// IntegerPerformanceEventLogger method implementations ///////////
Encoder::Result IntegerPerformanceEventLogger::MaybeEncodeImmediateObservation(
const ReportDefinition& report, bool may_invalidate,
EventRecord* event_record) {
const MetricDefinition& metric = *(event_record->metric);
const Event& event = *(event_record->event);
switch (report.report_type()) {
// Each report type has its own logic for generating immediate
// observations.
case ReportDefinition::NUMERIC_AGGREGATION:
case ReportDefinition::NUMERIC_PERF_RAW_DUMP:
case ReportDefinition::INT_RANGE_HISTOGRAM: {
return encoder()->EncodeIntegerEventObservation(
project_context()->RefMetric(&metric), &report, event.day_index(),
EventCode(event), Component(event), IntValue(event));
break;
}
default:
return BadReportType(metric, report);
}
}
////////////// ElapsedTimeEventLogger method implementations ///////////////////
uint32_t ElapsedTimeEventLogger::EventCode(const Event& event) {
CHECK(event.has_elapsed_time_event());
// TODO(zmbush): Pass all event_codes, not just the first.
return event.elapsed_time_event().event_code(0);
}
std::string ElapsedTimeEventLogger::Component(const Event& event) {
CHECK(event.has_elapsed_time_event());
return event.elapsed_time_event().component();
}
int64_t ElapsedTimeEventLogger::IntValue(const Event& event) {
CHECK(event.has_elapsed_time_event());
return event.elapsed_time_event().elapsed_micros();
}
////////////// FrameRateEventLogger method implementations /////////////////
uint32_t FrameRateEventLogger::EventCode(const Event& event) {
CHECK(event.has_frame_rate_event());
// TODO(zmbush): Pass all event_codes, not just the first.
return event.frame_rate_event().event_code(0);
}
std::string FrameRateEventLogger::Component(const Event& event) {
CHECK(event.has_frame_rate_event());
return event.frame_rate_event().component();
}
int64_t FrameRateEventLogger::IntValue(const Event& event) {
CHECK(event.has_frame_rate_event());
return event.frame_rate_event().frames_per_1000_seconds();
}
////////////// MemoryUsageEventLogger method implementations ///////////////////
uint32_t MemoryUsageEventLogger::EventCode(const Event& event) {
CHECK(event.has_memory_usage_event());
// TODO(zmbush): Pass all event_codes, not just the first.
return event.memory_usage_event().event_code(0);
}
std::string MemoryUsageEventLogger::Component(const Event& event) {
CHECK(event.has_memory_usage_event());
return event.memory_usage_event().component();
}
int64_t MemoryUsageEventLogger::IntValue(const Event& event) {
CHECK(event.has_memory_usage_event());
return event.memory_usage_event().bytes();
}
/////////////// IntHistogramEventLogger method implementations /////////////////
Status IntHistogramEventLogger::ValidateEvent(const EventRecord& event_record) {
CHECK(event_record.event->has_int_histogram_event());
const auto& int_histogram_event = event_record.event->int_histogram_event();
const auto& metric = *(event_record.metric);
if (!metric.has_int_buckets()) {
LOG(ERROR) << "Invalid Cobalt config: Metric " << MetricDebugString(metric)
<< " in project " << project_context()->DebugString()
<< " does not have an |int_buckets| field set.";
return kInvalidConfig;
}
const auto& int_buckets = metric.int_buckets();
uint32_t num_valid_buckets;
switch (int_buckets.buckets_case()) {
case IntegerBuckets::kExponential:
num_valid_buckets = int_buckets.exponential().num_buckets();
break;
case IntegerBuckets::kLinear:
num_valid_buckets = int_buckets.linear().num_buckets();
break;
case IntegerBuckets::BUCKETS_NOT_SET:
LOG(ERROR) << "Invalid Cobalt config: Metric "
<< MetricDebugString(metric) << " in project "
<< project_context()->DebugString()
<< " has an invalid |int_buckets| field. Either exponential "
"or linear buckets must be specified.";
return kInvalidConfig;
}
// In addition to the specified num_buckets, there are the underflow and
// overflow buckets.
num_valid_buckets += 2;
size_t num_provided_buckets = int_histogram_event.buckets_size();
for (auto i = 0u; i < num_provided_buckets; i++) {
if (int_histogram_event.buckets(i).index() >= num_valid_buckets) {
LOG(ERROR) << "The provided histogram is invalid. The index value of "
<< int_histogram_event.buckets(i).index() << " in position "
<< i << " is out of bounds for Metric "
<< MetricDebugString(*event_record.metric) << " in project "
<< project_context()->DebugString() << ".";
return kInvalidArguments;
}
}
return kOK;
}
Encoder::Result IntHistogramEventLogger::MaybeEncodeImmediateObservation(
const ReportDefinition& report, bool may_invalidate,
EventRecord* event_record) {
const MetricDefinition& metric = *(event_record->metric);
const Event& event = *(event_record->event);
CHECK(event.has_int_histogram_event());
auto* int_histogram_event =
event_record->event->mutable_int_histogram_event();
switch (report.report_type()) {
// Each report type has its own logic for generating immediate
// observations.
case ReportDefinition::INT_RANGE_HISTOGRAM: {
HistogramPtr histogram =
std::make_unique<RepeatedPtrField<HistogramBucket>>();
if (may_invalidate) {
// We move the buckets out of |int_histogram_event| thereby
// invalidating that variable.
histogram->Swap(int_histogram_event->mutable_buckets());
} else {
histogram->CopyFrom(int_histogram_event->buckets());
}
return encoder()->EncodeHistogramObservation(
project_context()->RefMetric(&metric), &report, event.day_index(),
// TODO(zmbush): Pass all event_codes, not just the first.
int_histogram_event->event_code(0), int_histogram_event->component(),
std::move(histogram));
}
default:
return BadReportType(metric, report);
}
}
/////////////// StringUsedEventLogger method implementations ///////////////////
Encoder::Result StringUsedEventLogger::MaybeEncodeImmediateObservation(
const ReportDefinition& report, bool may_invalidate,
EventRecord* event_record) {
const MetricDefinition& metric = *(event_record->metric);
const Event& event = *(event_record->event);
CHECK(event.has_string_used_event());
const auto& string_used_event = event_record->event->string_used_event();
switch (report.report_type()) {
// Each report type has its own logic for generating immediate
// observations.
case ReportDefinition::HIGH_FREQUENCY_STRING_COUNTS: {
return encoder()->EncodeRapporObservation(
project_context()->RefMetric(&metric), &report, event.day_index(),
string_used_event.str());
}
case ReportDefinition::STRING_COUNTS_WITH_THRESHOLD: {
return encoder()->EncodeForculusObservation(
project_context()->RefMetric(&metric), &report, event.day_index(),
string_used_event.str());
}
default:
return BadReportType(metric, report);
}
}
/////////////// CustomEventLogger method implementations ///////////////////////
Status CustomEventLogger::ValidateEvent(const EventRecord& event_record) {
// TODO(ninai) Add proto validation.
return kOK;
}
Encoder::Result CustomEventLogger::MaybeEncodeImmediateObservation(
const ReportDefinition& report, bool may_invalidate,
EventRecord* event_record) {
const MetricDefinition& metric = *(event_record->metric);
const Event& event = *(event_record->event);
CHECK(event.has_custom_event());
auto* custom_event = event_record->event->mutable_custom_event();
switch (report.report_type()) {
// Each report type has its own logic for generating immediate
// observations.
case ReportDefinition::CUSTOM_RAW_DUMP: {
EventValuesPtr event_values = std::make_unique<
google::protobuf::Map<std::string, CustomDimensionValue>>();
if (may_invalidate) {
// We move the contents out of |custom_event| thereby invalidating
// that variable.
event_values->swap(*(custom_event->mutable_values()));
} else {
event_values = std::make_unique<
google::protobuf::Map<std::string, CustomDimensionValue>>(
custom_event->values());
}
return encoder()->EncodeCustomObservation(
project_context()->RefMetric(&metric), &report, event.day_index(),
std::move(event_values));
}
default:
return BadReportType(metric, report);
}
}
} // namespace logger
} // namespace cobalt