blob: e383a19dd71aeb030f4c284b25e546dfb4e0dacc [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 "src/logger/logger.h"
#include <memory>
#include <string>
#include "google/protobuf/repeated_field.h"
#include "src/logger/event_loggers.h"
#include "src/logger/event_record.h"
#include "src/logger/status.h"
#include "src/logging.h"
#include "src/registry/metric_definition.pb.h"
#include "src/registry/report_definition.pb.h"
namespace cobalt::logger {
using local_aggregation::EventAggregator;
using LoggerMethod = PerProjectLoggerCallsMadeMetricDimensionLoggerMethod;
namespace {
template <class EventType>
void CopyEventCodesAndComponent(const std::vector<uint32_t>& event_codes,
const std::string& component, EventType* event) {
for (auto event_code : event_codes) {
event->add_event_code(event_code);
}
event->set_component(component);
}
template <class EventType>
void CopyEventCodes(const std::vector<uint32_t>& event_codes, EventType* event) {
for (auto event_code : event_codes) {
event->add_event_code(event_code);
}
}
} // namespace
Logger::Logger(std::unique_ptr<ProjectContext> project_context, const Encoder* encoder,
EventAggregator* event_aggregator,
local_aggregation::LocalAggregation* local_aggregation,
ObservationWriter* observation_writer, system_data::SystemDataInterface* system_data,
bool enable_replacement_metrics, InternalMetrics* internal_metrics)
: Logger(std::move(project_context), encoder, event_aggregator, local_aggregation,
observation_writer, system_data, nullptr, std::weak_ptr<UndatedEventManager>(),
enable_replacement_metrics, internal_metrics) {}
Logger::Logger(std::unique_ptr<ProjectContext> project_context, const Encoder* encoder,
EventAggregator* event_aggregator,
local_aggregation::LocalAggregation* local_aggregation,
ObservationWriter* observation_writer, system_data::SystemDataInterface* system_data,
util::ValidatedClockInterface* validated_clock,
std::weak_ptr<UndatedEventManager> undated_event_manager,
bool enable_replacement_metrics, InternalMetrics* internal_metrics)
: project_context_(std::move(project_context)),
encoder_(encoder),
event_aggregator_(event_aggregator),
local_aggregation_(local_aggregation),
observation_writer_(observation_writer),
system_data_(system_data),
validated_clock_(validated_clock),
undated_event_manager_(std::move(undated_event_manager)),
enable_replacement_metrics_(enable_replacement_metrics),
internal_metrics_(internal_metrics) {
CHECK(project_context_);
CHECK(encoder_);
CHECK(event_aggregator_);
CHECK(local_aggregation_);
CHECK(observation_writer_);
if (!validated_clock_) {
local_validated_clock_ =
std::make_unique<util::AlwaysAccurateClock>(std::make_unique<util::SystemClock>());
validated_clock_ = local_validated_clock_.get();
}
// If we were passed an internal Logger then we are not an internal Logger.
is_internal_logger_ = !internal_metrics;
if (event_aggregator_->UpdateAggregationConfigs(*project_context_) != kOK) {
LOG(ERROR) << "Failed to provide aggregation configurations to the "
"EventAggregator.";
}
}
Status Logger::LogEvent(uint32_t metric_id, uint32_t event_code) {
auto event_record = std::make_unique<EventRecord>(project_context_, metric_id);
Status replacement_status = kOK;
if (event_record->metric()->replacement_metric_id() != 0 && enable_replacement_metrics_) {
PauseInternalLogging();
auto replacement_event_record = std::make_unique<EventRecord>(
project_context_, event_record->metric()->replacement_metric_id());
switch (replacement_event_record->metric()->metric_type()) {
case MetricDefinition::OCCURRENCE: {
replacement_status =
LogOccurrence(replacement_event_record->metric()->id(), 1, {event_code});
if (replacement_status != kOK) {
LOG(WARNING) << "Failed to log replacement metric "
<< replacement_event_record->FullMetricName() << " for metric "
<< event_record->FullMetricName() << ": " << replacement_status;
}
} break;
default:
replacement_status = kInvalidConfig;
LOG(WARNING) << "Unable to log replacement metric "
<< replacement_event_record->FullMetricName() << " for metric "
<< event_record->FullMetricName() << ": " << replacement_status;
break;
}
ResumeInternalLogging();
}
internal_metrics_.LoggerCalled(LoggerMethod::LogEvent, project_context_->project());
auto* event_occurred_event = event_record->event()->mutable_event_occurred_event();
event_occurred_event->set_event_code(event_code);
Status status = Log(metric_id, MetricDefinition::EVENT_OCCURRED, std::move(event_record));
if (replacement_status != kOK) {
return replacement_status;
}
return status;
}
Status Logger::LogEventCount(uint32_t metric_id, const std::vector<uint32_t>& event_codes,
const std::string& component, int64_t period_duration_micros,
uint32_t count) {
internal_metrics_.LoggerCalled(LoggerMethod::LogEventCount, project_context_->project());
auto event_record = std::make_unique<EventRecord>(project_context_, metric_id);
Status replacement_status = kOK;
if (event_record->metric()->replacement_metric_id() != 0 && enable_replacement_metrics_) {
PauseInternalLogging();
auto replacement_event_record = std::make_unique<EventRecord>(
project_context_, event_record->metric()->replacement_metric_id());
switch (replacement_event_record->metric()->metric_type()) {
case MetricDefinition::OCCURRENCE: {
replacement_status =
LogOccurrence(replacement_event_record->metric()->id(), count, event_codes);
if (replacement_status != kOK) {
LOG(WARNING) << "Failed to log replacement metric "
<< replacement_event_record->FullMetricName() << " for metric "
<< event_record->FullMetricName() << ": " << replacement_status;
}
} break;
case MetricDefinition::STRING: {
for (int i = 0; i < count; i++) {
replacement_status =
LogString(replacement_event_record->metric()->id(), component, event_codes);
if (replacement_status != kOK) {
LOG(WARNING) << "Failed to log replacement metric "
<< replacement_event_record->FullMetricName() << " for metric "
<< event_record->FullMetricName() << ": " << replacement_status;
}
}
} break;
case MetricDefinition::INTEGER: {
replacement_status =
LogInteger(replacement_event_record->metric()->id(), count, event_codes);
if (replacement_status != kOK) {
LOG(WARNING) << "Failed to log replacement metric "
<< replacement_event_record->FullMetricName() << " for metric "
<< event_record->FullMetricName() << ": " << replacement_status;
}
} break;
default:
replacement_status = kInvalidConfig;
LOG(WARNING) << "Unable to log replacement metric "
<< replacement_event_record->FullMetricName() << " for metric "
<< event_record->FullMetricName() << ": " << replacement_status;
break;
}
ResumeInternalLogging();
}
auto* event_count_event = event_record->event()->mutable_event_count_event();
CopyEventCodesAndComponent(event_codes, component, event_count_event);
event_count_event->set_period_duration_micros(period_duration_micros);
event_count_event->set_count(count);
Status status = Log(metric_id, MetricDefinition::EVENT_COUNT, std::move(event_record));
if (replacement_status != kOK) {
return replacement_status;
}
return status;
}
const int64_t kNanosecondsPerMicrosecond = 1000;
const int64_t kMicrosecondsPerMillisecond = 1000;
const int64_t kMicrosecondsPerSecond = kMicrosecondsPerMillisecond * 1000;
const int64_t kMicrosecondsPerMinute = kMicrosecondsPerSecond * 60;
Status Logger::LogElapsedTime(uint32_t metric_id, const std::vector<uint32_t>& event_codes,
const std::string& component, int64_t elapsed_micros) {
internal_metrics_.LoggerCalled(LoggerMethod::LogElapsedTime, project_context_->project());
auto event_record = std::make_unique<EventRecord>(project_context_, metric_id);
Status replacement_status = kOK;
if (event_record->metric()->replacement_metric_id() != 0 && enable_replacement_metrics_) {
PauseInternalLogging();
auto replacement_event_record = std::make_unique<EventRecord>(
project_context_, event_record->metric()->replacement_metric_id());
switch (replacement_event_record->metric()->metric_type()) {
case MetricDefinition::INTEGER: {
int64_t converted_value = 0;
switch (replacement_event_record->metric()->metric_units()) {
case cobalt::MetricUnits::NANOSECONDS:
converted_value = elapsed_micros * kNanosecondsPerMicrosecond;
break;
case cobalt::MetricUnits::MICROSECONDS:
converted_value = elapsed_micros;
break;
case cobalt::MetricUnits::MILLISECONDS:
converted_value = elapsed_micros / kMicrosecondsPerMillisecond;
break;
case cobalt::MetricUnits::SECONDS:
converted_value = elapsed_micros / kMicrosecondsPerSecond;
break;
case cobalt::MetricUnits::MINUTES:
converted_value = elapsed_micros / kMicrosecondsPerMinute;
break;
default:
// We can't reasonably convert this value, so we use the original value.
converted_value = elapsed_micros;
break;
}
replacement_status =
LogInteger(replacement_event_record->metric()->id(), converted_value, event_codes);
if (replacement_status != kOK) {
LOG(WARNING) << "Failed to log replacement metric "
<< replacement_event_record->FullMetricName() << " for metric "
<< event_record->FullMetricName() << ": " << replacement_status;
}
} break;
default:
replacement_status = kInvalidConfig;
LOG(WARNING) << "Unable to log replacement metric "
<< replacement_event_record->FullMetricName() << " for metric "
<< event_record->FullMetricName() << ": " << replacement_status;
break;
}
ResumeInternalLogging();
}
auto* elapsed_time_event = event_record->event()->mutable_elapsed_time_event();
CopyEventCodesAndComponent(event_codes, component, elapsed_time_event);
elapsed_time_event->set_elapsed_micros(elapsed_micros);
Status status = Log(metric_id, MetricDefinition::ELAPSED_TIME, std::move(event_record));
if (replacement_status != kOK) {
return replacement_status;
}
return status;
}
Status Logger::LogFrameRate(uint32_t metric_id, const std::vector<uint32_t>& event_codes,
const std::string& component, float fps) {
internal_metrics_.LoggerCalled(LoggerMethod::LogFrameRate, project_context_->project());
auto event_record = std::make_unique<EventRecord>(project_context_, metric_id);
Status replacement_status = kOK;
if (event_record->metric()->replacement_metric_id() != 0 && enable_replacement_metrics_) {
PauseInternalLogging();
auto replacement_event_record = std::make_unique<EventRecord>(
project_context_, event_record->metric()->replacement_metric_id());
switch (replacement_event_record->metric()->metric_type()) {
case MetricDefinition::INTEGER: {
replacement_status =
// NOLINTNEXTLINE readability-magic-numbers
LogInteger(replacement_event_record->metric()->id(), std::round(fps * 1000.0),
event_codes);
if (replacement_status != kOK) {
LOG(WARNING) << "Failed to log replacement metric "
<< replacement_event_record->FullMetricName() << " for metric "
<< event_record->FullMetricName() << ": " << replacement_status;
}
break;
}
default:
replacement_status = kInvalidConfig;
LOG(WARNING) << "Unable to log replacement metric "
<< replacement_event_record->FullMetricName() << " for metric "
<< event_record->FullMetricName() << ": " << replacement_status;
break;
}
ResumeInternalLogging();
}
auto* frame_rate_event = event_record->event()->mutable_frame_rate_event();
CopyEventCodesAndComponent(event_codes, component, frame_rate_event);
// NOLINTNEXTLINE readability-magic-numbers
frame_rate_event->set_frames_per_1000_seconds(std::round(fps * 1000.0));
Status status = Log(metric_id, MetricDefinition::FRAME_RATE, std::move(event_record));
if (replacement_status != kOK) {
return replacement_status;
}
return status;
}
Status Logger::LogMemoryUsage(uint32_t metric_id, const std::vector<uint32_t>& event_codes,
const std::string& component, int64_t bytes) {
internal_metrics_.LoggerCalled(LoggerMethod::LogMemoryUsage, project_context_->project());
auto event_record = std::make_unique<EventRecord>(project_context_, metric_id);
Status replacement_status = kOK;
if (event_record->metric()->replacement_metric_id() != 0 && enable_replacement_metrics_) {
PauseInternalLogging();
auto replacement_event_record = std::make_unique<EventRecord>(
project_context_, event_record->metric()->replacement_metric_id());
switch (replacement_event_record->metric()->metric_type()) {
case MetricDefinition::INTEGER: {
replacement_status =
LogInteger(replacement_event_record->metric()->id(), bytes, event_codes);
if (replacement_status != kOK) {
LOG(WARNING) << "Failed to log replacement metric "
<< replacement_event_record->FullMetricName() << " for metric "
<< event_record->FullMetricName() << ": " << replacement_status;
}
break;
}
default:
replacement_status = kInvalidConfig;
LOG(WARNING) << "Unable to log replacement metric "
<< replacement_event_record->FullMetricName() << " for metric "
<< event_record->FullMetricName() << ": " << replacement_status;
break;
}
ResumeInternalLogging();
}
auto* memory_usage_event = event_record->event()->mutable_memory_usage_event();
CopyEventCodesAndComponent(event_codes, component, memory_usage_event);
memory_usage_event->set_bytes(bytes);
Status status = Log(metric_id, MetricDefinition::MEMORY_USAGE, std::move(event_record));
if (replacement_status != kOK) {
return replacement_status;
}
return status;
}
Status Logger::LogIntHistogram(uint32_t metric_id, const std::vector<uint32_t>& event_codes,
const std::string& component, HistogramPtr histogram) {
internal_metrics_.LoggerCalled(LoggerMethod::LogIntHistogram, project_context_->project());
auto event_record = std::make_unique<EventRecord>(project_context_, metric_id);
Status replacement_status = kOK;
if (event_record->metric()->replacement_metric_id() != 0 && enable_replacement_metrics_) {
PauseInternalLogging();
auto replacement_event_record = std::make_unique<EventRecord>(
project_context_, event_record->metric()->replacement_metric_id());
switch (replacement_event_record->metric()->metric_type()) {
case MetricDefinition::INTEGER_HISTOGRAM: {
auto histogram_copy =
std::make_unique<google::protobuf::RepeatedPtrField<cobalt::HistogramBucket>>();
histogram_copy->CopyFrom(*histogram.get());
replacement_status = LogIntegerHistogram(replacement_event_record->metric()->id(),
std::move(histogram_copy), event_codes);
if (replacement_status != kOK) {
LOG(WARNING) << "Failed to log replacement metric "
<< replacement_event_record->FullMetricName() << " for metric "
<< event_record->FullMetricName() << ": " << replacement_status;
}
break;
}
default:
replacement_status = kInvalidConfig;
LOG(WARNING) << "Unable to log replacement metric "
<< replacement_event_record->FullMetricName() << " for metric "
<< event_record->FullMetricName() << ": " << replacement_status;
break;
}
ResumeInternalLogging();
}
auto* int_histogram_event = event_record->event()->mutable_int_histogram_event();
CopyEventCodesAndComponent(event_codes, component, int_histogram_event);
int_histogram_event->mutable_buckets()->Swap(histogram.get());
Status status = Log(metric_id, MetricDefinition::INT_HISTOGRAM, std::move(event_record));
if (replacement_status != kOK) {
return replacement_status;
}
return status;
}
Status Logger::LogOccurrence(uint32_t metric_id, uint64_t count,
const std::vector<uint32_t>& event_codes) {
internal_metrics_.LoggerCalled(LoggerMethod::LogOccurrence, project_context_->project());
auto event_record = std::make_unique<EventRecord>(project_context_, metric_id);
OccurrenceEvent* occurrence_event = event_record->event()->mutable_occurrence_event();
CopyEventCodes(event_codes, occurrence_event);
occurrence_event->set_count(count);
return Log(metric_id, MetricDefinition::OCCURRENCE, std::move(event_record));
}
Status Logger::LogInteger(uint32_t metric_id, int64_t value,
const std::vector<uint32_t>& event_codes) {
internal_metrics_.LoggerCalled(LoggerMethod::LogInteger, project_context_->project());
auto event_record = std::make_unique<EventRecord>(project_context_, metric_id);
IntegerEvent* integer_event = event_record->event()->mutable_integer_event();
CopyEventCodes(event_codes, integer_event);
integer_event->set_value(value);
return Log(metric_id, MetricDefinition::INTEGER, std::move(event_record));
}
Status Logger::LogIntegerHistogram(uint32_t metric_id, HistogramPtr histogram,
const std::vector<uint32_t>& event_codes) {
internal_metrics_.LoggerCalled(LoggerMethod::LogIntegerHistogram, project_context_->project());
auto event_record = std::make_unique<EventRecord>(project_context_, metric_id);
IntegerHistogramEvent* integer_histogram_event =
event_record->event()->mutable_integer_histogram_event();
CopyEventCodes(event_codes, integer_histogram_event);
integer_histogram_event->mutable_buckets()->Swap(histogram.get());
return Log(metric_id, MetricDefinition::INTEGER_HISTOGRAM, std::move(event_record));
}
Status Logger::LogString(uint32_t metric_id, const std::string& string_value,
const std::vector<uint32_t>& event_codes) {
internal_metrics_.LoggerCalled(LoggerMethod::LogString, project_context_->project());
auto event_record = std::make_unique<EventRecord>(project_context_, metric_id);
StringEvent* string_event = event_record->event()->mutable_string_event();
CopyEventCodes(event_codes, string_event);
string_event->set_string_value(string_value);
return Log(metric_id, MetricDefinition::STRING, std::move(event_record));
}
Status Logger::LogCustomEvent(uint32_t metric_id, EventValuesPtr event_values) {
internal_metrics_.LoggerCalled(LoggerMethod::LogCustomEvent, project_context_->project());
auto event_record = std::make_unique<EventRecord>(project_context_, metric_id);
auto* custom_event = event_record->event()->mutable_custom_event();
custom_event->mutable_values()->swap(*event_values);
return Log(metric_id, MetricDefinition::CUSTOM, std::move(event_record));
}
Status Logger::LogSerializedCustomEvent(uint32_t metric_id,
std::unique_ptr<std::string> serialized_proto) {
internal_metrics_.LoggerCalled(LoggerMethod::LogSerializedCustomEvent,
project_context_->project());
auto event_record = std::make_unique<EventRecord>(project_context_, metric_id);
auto* custom_event = event_record->event()->mutable_custom_event();
custom_event->mutable_serialized_proto()->swap(*serialized_proto);
return Log(metric_id, MetricDefinition::CUSTOM, std::move(event_record));
}
Status Logger::Log(uint32_t metric_id, MetricDefinition::MetricType metric_type,
std::unique_ptr<EventRecord> event_record) {
const int kVerboseLoggingLevel = (is_internal_logger_ ? 9 : 7);
VLOG(kVerboseLoggingLevel) << "Received logged event for " << event_record->FullMetricName();
auto event_logger =
internal::EventLogger::Create(metric_type, encoder_, event_aggregator_, local_aggregation_,
observation_writer_, system_data_);
Status validation_result =
event_logger->PrepareAndValidateEvent(metric_id, metric_type, event_record.get());
if (validation_result != kOK) {
return validation_result;
}
auto now = validated_clock_->now();
if (!now) {
VLOG(kVerboseLoggingLevel)
<< "Buffering logged event because we don't yet have an accurate clock. ";
// Missing system time means that the clock is not valid, so save the event until it is.
auto undated_event_manager = undated_event_manager_.lock();
if (undated_event_manager) {
return undated_event_manager->Save(std::move(event_record));
}
// A missing UndatedEventManager is handled by retrying the clock, which should now be valid.
// If we fall through to here, then a race condition has occurred in which the clock that we
// thought was not validated, has become validated. Retrying the clock should succeed.
now = validated_clock_->now();
if (!now) {
// This should never happen, if it does then it's a bug.
LOG(ERROR) << "Clock is invalid but there is no UndatedEventManager that will save the "
"event, dropping event for metric: "
<< metric_id;
return Status::kOther;
}
}
Status result = event_logger->Log(std::move(event_record), *now);
// Flush queued internal metrics.
internal_metrics_.Flush();
return result;
}
void Logger::PauseInternalLogging() { internal_metrics_.PauseLogging(); }
void Logger::ResumeInternalLogging() { internal_metrics_.ResumeLogging(); }
} // namespace cobalt::logger