| // 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. |
| |
| #ifndef COBALT_SRC_LOGGER_INTERNAL_METRICS_H_ |
| #define COBALT_SRC_LOGGER_INTERNAL_METRICS_H_ |
| |
| #include <memory> |
| #include <queue> |
| #include <string> |
| |
| #include "src/lib/util/protected_fields.h" |
| #include "src/logger/internal_metrics_config.cb.h" |
| #include "src/logger/logger_interface.h" |
| #include "src/public/diagnostics_interface.h" |
| #include "src/public/lib/registry_identifiers.h" |
| #include "src/registry/project.pb.h" |
| |
| namespace cobalt::logger { |
| |
| // InternalMetrics defines the methods used for collecting cobalt-internal |
| // metrics. |
| class InternalMetrics { |
| public: |
| // Returns a pointer to an InternalMetrics object which can be used for |
| // collecting cobalt-internal metrics. |
| // |
| // |logger| the logger used to log internal metrics. If the pointer is null, |
| // NoOpInternalMetrics will be used. |
| // |
| // |diagnostics|: The implementation to send diagnostic information about the |
| // functioning of the Cobalt Core library. If null, no diagnostic information |
| // will be sent. |
| static std::unique_ptr<InternalMetrics> NewWithLogger(LoggerInterface* logger, |
| DiagnosticsInterface* diagnostics); |
| |
| // LoggerCalled (cobalt_internal::metrics::logger_calls_made_migrated) and |
| // (cobalt_internal::metrics::per_project_logger_calls_made_migrated) are |
| // logged for every call to Logger along with which method was called and |
| // the project that called it. |
| virtual void LoggerCalled(PerProjectLoggerCallsMadeMigratedMetricDimensionLoggerMethod method, |
| const Project& project) = 0; |
| |
| // cobalt_internal::metrics::bytes_uploaded is logged when the Clearcut |
| // Uploader attempts or succeeds to upload observations from the device. |
| virtual void BytesUploaded(BytesUploadedMetricDimensionStatus upload_status, |
| size_t byte_count) = 0; |
| |
| // cobalt_internal::metrics::per_project_bytes_uploaded_migrated is logged when |
| // the Shipping Manager attempts or succeeds to store observations on the device. |
| virtual void BytesUploaded(PerProjectBytesUploadedMigratedMetricDimensionStatus upload_status, |
| size_t byte_count, |
| const lib::ProjectIdentifier& project_identifier) = 0; |
| |
| // cobalt_internal::metrics::per_project_bytes_stored_migrated is logged when the |
| // Observation Store attempts or succeeds to store observations on the device. |
| virtual void BytesStored(PerProjectBytesStoredMigratedMetricDimensionStatus upload_status, |
| size_t byte_count, const lib::ProjectIdentifier& project_identifier) = 0; |
| |
| // cobalt_internal::metrics::idle_observation_upload_migrated is logged when the |
| // Shipping Manager encrypts and uploads observations. |
| virtual void IdleObservationUpload( |
| IdleObservationUploadMigratedMetricDimensionDeviceState state) = 0; |
| |
| // cobalt_internal::metrics::inaccurate_clock_events_cached_migrated is logged when |
| // the UndatedEventManager saves an event while the clock is inaccurate. |
| virtual void InaccurateClockEventsCached(int64_t event_count) = 0; |
| |
| // cobalt_internal::metrics::inaccurate_clock_events_dropped_migrated is logged when |
| // the UndatedEventManager drops a saved event while the clock is inaccurate and it |
| // runs out of space. |
| virtual void InaccurateClockEventsDropped(int64_t event_count) = 0; |
| |
| // cobalt_internal::metrics::set_software_distribution_info_called_migrated is logged |
| // when the SystemData receives SoftwareDistributionInfo. |
| virtual void SetSoftwareDistributionInfoCalled( |
| SetSoftwareDistributionInfoCalledMigratedEventCodes event_codes) = 0; |
| |
| // Used to mark which class this disk usage is coming from. Can be: |
| // - ObservationStore |
| // - LocalAggregateStorage |
| // - AggregateStore |
| // - ObservationHistory |
| using StorageClass = MetricsMetricDimensionStorageClass; |
| |
| // cobalt_internal::metrics::store_usage is used to track how much data is stored per-class on |
| // disk. |
| virtual void TrackDiskUsage(StorageClass storage_class, size_t bytes, int64_t max_bytes) = 0; |
| void TrackDiskUsage(StorageClass storage_class, size_t bytes) { |
| TrackDiskUsage(storage_class, bytes, -1); |
| } |
| |
| virtual void LocalAggregationQuotaEvent( |
| const lib::ProjectIdentifier& project_identifier, |
| LocalAggregationQuotaMetricDimensionEventType event_type) = 0; |
| |
| // Flush queued internal metrics. |
| virtual void Flush() = 0; |
| |
| // Returns true if this is an instance of the real implementation of |
| // InternalMetrics that will really do internal logging and false if this |
| // is a fake or no-op implementation. |
| [[nodiscard]] virtual bool IsRealImpl() const = 0; |
| |
| virtual ~InternalMetrics() = default; |
| }; |
| |
| // NoOpInternalMetrics is to be used when the LoggerInterface* provided to the |
| // Logger constructor is nullptr. It stubs out all of the calls in the |
| // InternalMetrics interface, allowing code to safely make these calls even if |
| // no LoggerInterface* was provided. |
| class NoOpInternalMetrics : public InternalMetrics { |
| public: |
| void LoggerCalled(PerProjectLoggerCallsMadeMigratedMetricDimensionLoggerMethod method, |
| const Project& project) override {} |
| |
| void BytesUploaded(BytesUploadedMetricDimensionStatus upload_status, size_t byte_count) override { |
| } |
| |
| void BytesUploaded(PerProjectBytesUploadedMigratedMetricDimensionStatus upload_status, |
| size_t byte_count, const lib::ProjectIdentifier& project_identifier) override { |
| } |
| |
| void BytesStored(PerProjectBytesStoredMigratedMetricDimensionStatus upload_status, |
| size_t byte_count, const lib::ProjectIdentifier& project_identifier) override {} |
| |
| void IdleObservationUpload( |
| IdleObservationUploadMigratedMetricDimensionDeviceState state) override {} |
| |
| void InaccurateClockEventsCached(int64_t event_count) override {} |
| |
| void InaccurateClockEventsDropped(int64_t event_count) override {} |
| |
| void SetSoftwareDistributionInfoCalled( |
| SetSoftwareDistributionInfoCalledMigratedEventCodes event_codes) override {} |
| |
| // TODO(fxbug.dev/85571): NOLINTNEXTLINE(bugprone-easily-swappable-parameters) |
| void TrackDiskUsage(StorageClass storage_class, size_t bytes, int64_t max_bytes) override {} |
| |
| void LocalAggregationQuotaEvent( |
| const lib::ProjectIdentifier& project_identifier, |
| LocalAggregationQuotaMetricDimensionEventType event_type) override {} |
| |
| void Flush() override {} |
| |
| [[nodiscard]] bool IsRealImpl() const override { return false; } |
| |
| ~NoOpInternalMetrics() override = default; |
| }; |
| |
| // InternalMetricsImpl is the actual implementation of InternalMetrics. It is a wrapper around the |
| // LoggerInterface that was provided to the Logger constructor. |
| class InternalMetricsImpl : public InternalMetrics { |
| public: |
| InternalMetricsImpl(LoggerInterface& logger, DiagnosticsInterface* diagnostics); |
| |
| void LoggerCalled(PerProjectLoggerCallsMadeMigratedMetricDimensionLoggerMethod method, |
| const Project& project) override; |
| |
| void BytesUploaded(BytesUploadedMetricDimensionStatus upload_status, size_t byte_count) override; |
| |
| void BytesUploaded(PerProjectBytesUploadedMigratedMetricDimensionStatus upload_status, |
| size_t byte_count, const lib::ProjectIdentifier& project_identifier) override; |
| |
| void BytesStored(PerProjectBytesStoredMigratedMetricDimensionStatus upload_status, |
| size_t byte_count, const lib::ProjectIdentifier& project_identifier) override; |
| |
| void IdleObservationUpload( |
| IdleObservationUploadMigratedMetricDimensionDeviceState state) override; |
| |
| void InaccurateClockEventsCached(int64_t event_count) override; |
| |
| void InaccurateClockEventsDropped(int64_t event_count) override; |
| |
| void SetSoftwareDistributionInfoCalled( |
| SetSoftwareDistributionInfoCalledMigratedEventCodes event_codes) override; |
| |
| void TrackDiskUsage(StorageClass storage_class, size_t bytes, int64_t max_bytes) override; |
| |
| void LocalAggregationQuotaEvent( |
| const lib::ProjectIdentifier& project_identifier, |
| LocalAggregationQuotaMetricDimensionEventType event_type) override; |
| |
| void Flush() override; |
| |
| [[nodiscard]] bool IsRealImpl() const override { return true; } |
| |
| ~InternalMetricsImpl() override = default; |
| |
| private: |
| struct Protected { |
| std::vector<std::function<void()>> queued_lambdas; |
| }; |
| util::ProtectedFields<Protected> fields_; |
| LoggerInterface& logger_; // not owned |
| DiagnosticsInterface* diagnostics_; // not owned |
| }; |
| |
| // InternalMetricsPtr holds either a pointer to an InternalMetrics implementation, or a pointer to |
| // an instance of NoOpInternalMetrics. This class guarantees that the pointer is always valid. |
| class InternalMetricsPtr { |
| public: |
| InternalMetricsPtr() { reset(nullptr); } |
| explicit InternalMetricsPtr(InternalMetrics* internal_metrics) { reset(internal_metrics); } |
| |
| void reset(InternalMetrics* internal_metrics) { |
| if (internal_metrics) { |
| ptr_ = internal_metrics; |
| } else { |
| ptr_ = &noop_internal_metrics_; |
| } |
| } |
| |
| [[nodiscard]] InternalMetrics* get() const { return ptr_; } |
| InternalMetrics* operator->() const { return ptr_; } |
| InternalMetrics& operator*() const { return *ptr_; } |
| |
| private: |
| NoOpInternalMetrics noop_internal_metrics_; |
| InternalMetrics* ptr_; |
| }; |
| |
| // InternalMetricsPauseWrapper wraps an instance of InternalMetrics, and adds the ability to pause |
| // logging. This is important to support pausing logging during batch logging to cobalt. |
| class InternalMetricsPauseWrapper : public InternalMetrics { |
| public: |
| explicit InternalMetricsPauseWrapper(InternalMetrics* internal_metrics) |
| : wrapped_internal_metrics_(internal_metrics) {} |
| |
| void LoggerCalled(PerProjectLoggerCallsMadeMigratedMetricDimensionLoggerMethod method, |
| const Project& project) override { |
| if (!paused_) { |
| wrapped_internal_metrics_->LoggerCalled(method, project); |
| } |
| } |
| |
| void BytesUploaded(BytesUploadedMetricDimensionStatus upload_status, size_t byte_count) override { |
| if (!paused_) { |
| wrapped_internal_metrics_->BytesUploaded(upload_status, byte_count); |
| } |
| } |
| |
| void BytesUploaded(PerProjectBytesUploadedMigratedMetricDimensionStatus upload_status, |
| size_t byte_count, const lib::ProjectIdentifier& project_identifier) override { |
| if (!paused_) { |
| wrapped_internal_metrics_->BytesUploaded(upload_status, byte_count, project_identifier); |
| } |
| } |
| |
| void BytesStored(PerProjectBytesStoredMigratedMetricDimensionStatus upload_status, |
| size_t byte_count, const lib::ProjectIdentifier& project_identifier) override { |
| if (!paused_) { |
| wrapped_internal_metrics_->BytesStored(upload_status, byte_count, project_identifier); |
| } |
| } |
| |
| void IdleObservationUpload( |
| IdleObservationUploadMigratedMetricDimensionDeviceState state) override { |
| if (!paused_) { |
| wrapped_internal_metrics_->IdleObservationUpload(state); |
| } |
| } |
| |
| void InaccurateClockEventsCached(int64_t event_count) override { |
| if (!paused_) { |
| wrapped_internal_metrics_->InaccurateClockEventsCached(event_count); |
| } |
| } |
| |
| void InaccurateClockEventsDropped(int64_t event_count) override { |
| if (!paused_) { |
| wrapped_internal_metrics_->InaccurateClockEventsDropped(event_count); |
| } |
| } |
| |
| void SetSoftwareDistributionInfoCalled( |
| SetSoftwareDistributionInfoCalledMigratedEventCodes event_codes) override { |
| if (!paused_) { |
| wrapped_internal_metrics_->SetSoftwareDistributionInfoCalled(event_codes); |
| } |
| } |
| |
| void TrackDiskUsage(StorageClass storage_class, size_t bytes, int64_t max_bytes) override { |
| if (!paused_) { |
| wrapped_internal_metrics_->TrackDiskUsage(storage_class, bytes, max_bytes); |
| } |
| } |
| |
| void LocalAggregationQuotaEvent( |
| const lib::ProjectIdentifier& project_identifier, |
| LocalAggregationQuotaMetricDimensionEventType event_type) override { |
| if (!paused_) { |
| wrapped_internal_metrics_->LocalAggregationQuotaEvent(project_identifier, event_type); |
| } |
| } |
| |
| // After PauseLogging is called, all calls to log internal metrics will be |
| // ignored. |
| void PauseLogging() { paused_ = true; } |
| |
| // Once ResumeLogging is called, calls to log internal metrics are will no |
| // longer be ignored. |
| void ResumeLogging() { paused_ = false; } |
| |
| void Flush() override { wrapped_internal_metrics_->Flush(); } |
| |
| [[nodiscard]] bool IsRealImpl() const override { return wrapped_internal_metrics_->IsRealImpl(); } |
| |
| void reset(InternalMetrics* internal_metrics) { |
| wrapped_internal_metrics_.reset(internal_metrics); |
| } |
| |
| private: |
| InternalMetricsPtr wrapped_internal_metrics_; |
| bool paused_ = false; |
| }; |
| |
| } // namespace cobalt::logger |
| |
| #endif // COBALT_SRC_LOGGER_INTERNAL_METRICS_H_ |