| // 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 |