blob: 5c99b836bc50f03284d0afa1d8a9b876efb5c852 [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.
#ifndef COBALT_SRC_LOGGER_INTERNAL_METRICS_H_
#define COBALT_SRC_LOGGER_INTERNAL_METRICS_H_
#include <memory>
#include <queue>
#include <string>
#include "src/lib/util/not_null.h"
#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:
// InternalMetricsFlusher calls Flush on the contained InternalMetrics whenever it goes out of
// scope.
class InternalMetricsFlusher {
public:
~InternalMetricsFlusher() { metrics_->Flush(); }
private:
friend class InternalMetrics;
// We only want InternalMetrics to be able to construct an InternalMetricsFlusher.
explicit InternalMetricsFlusher(InternalMetrics* metrics) : metrics_(metrics) {
metrics_->IncrementFlushers();
}
InternalMetrics* metrics_;
};
// 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 util::NotNullUniquePtr<InternalMetrics> NewWithLogger(LoggerInterface* logger,
DiagnosticsInterface* diagnostics);
// LoggerCalled (cobalt_internal::metrics::logger_calls_made_migrated) is
// logged for every call to Logger along with which method was called.
virtual void LoggerCalled(LoggerCallsMadeMigratedMetricDimensionLoggerMethod 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::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);
}
// Used to track what the quota state of each project on the system is.
//
// |event_type|: tracks what the state is (1: Below quota, 2: Above quota, 3: Above quota and log
// rejected).
virtual void LocalAggregationQuotaEvent(const lib::ProjectIdentifier& project_identifier,
int event_type) = 0;
// Used to ensure that internal metrics data is flushed before a scope is exited.
//
// All calls to InternalMetrics methods should only be made after a Flusher has been created.
InternalMetricsFlusher Flusher() { return InternalMetricsFlusher(this); }
protected:
friend class InternalMetricsPauseWrapper;
// Flushes queued internal metrics.
//
// This is necessary because we collect some metrics in places where a lock is
// held (for example inside of LocalAggregation). If we immediately logged
// again, this would cause a deadlock as we are still holding the lock, but
// the logger call requires taking it again. With the queue-and-flush model,
// we make sure that all of the internal metrics logging is done once at the
// end when no more locks are held.
virtual void Flush() = 0;
// Notifies the internal metrics implementation that a flusher has been created.
virtual void IncrementFlushers() = 0;
public:
// 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(LoggerCallsMadeMigratedMetricDimensionLoggerMethod 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 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,
int event_type) override {}
protected:
void Flush() override {}
void IncrementFlushers() override {}
public:
[[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(LoggerCallsMadeMigratedMetricDimensionLoggerMethod 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 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,
int event_type) override;
protected:
void Flush() override;
void IncrementFlushers() override;
public:
[[nodiscard]] bool IsRealImpl() const override { return true; }
~InternalMetricsImpl() override = default;
private:
void Queue(std::function<void()>&& lambda);
struct Protected {
int32_t flusher_count = 0;
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(LoggerCallsMadeMigratedMetricDimensionLoggerMethod 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 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,
int 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(); }
void IncrementFlushers() override { wrapped_internal_metrics_->IncrementFlushers(); }
[[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_