blob: 4dfda4ce30f6d87a669f5b1222e2e41d2c3ccc00 [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/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/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) and
// (cobalt_internal::metrics::per_project_logger_calls_made) are logged for
// every call to Logger along with which method was called and the project
// that called it.
virtual void LoggerCalled(PerProjectLoggerCallsMadeMetricDimensionLoggerMethod method,
const Project& project) = 0;
// cobalt_internal::metrics::per_device_bytes_uploaded is logged when the
// Clearcut Uploader attempts or succeeds to upload observations from the
// device.
virtual void BytesUploaded(PerDeviceBytesUploadedMetricDimensionStatus upload_status,
size_t byte_count) = 0;
// cobalt_internal::metrics::per_project_bytes_uploaded is logged when the
// Shipping Manager attempts or succeeds to store observations on the device.
virtual void BytesUploaded(PerProjectBytesUploadedMetricDimensionStatus upload_status,
size_t byte_count, uint32_t customer_id, uint32_t project_id) = 0;
// cobalt_internal::metrics::per_project_bytes_stored is logged when the
// Observation Store attempts or succeeds to store observations on the device.
virtual void BytesStored(PerProjectBytesStoredMetricDimensionStatus upload_status,
size_t byte_count, uint32_t customer_id, uint32_t project_id) = 0;
// cobalt_internal::metrics::idle_observation_upload is logged when the
// Shipping Manager encrypts and uploads observations.
virtual void IdleObservationUpload(IdleObservationUploadMetricDimensionDeviceState state) = 0;
// cobalt_internal::metrics::inaccurate_clock_events_cached is logged when the
// UndatedEventManager saves an event while the clock is inaccurate.
virtual void InaccurateClockEventsCached(int64_t event_count, uint32_t customer_id,
uint32_t project_id) = 0;
// cobalt_internal::metrics::inaccurate_clock_events_dropped 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, uint32_t customer_id,
uint32_t project_id) = 0;
// cobalt_internal::metrics::set_software_distribution_info_called is logged
// when the SystemData receives SoftwareDistributionInfo.
virtual void SetSoftwareDistributionInfoCalled(
SetSoftwareDistributionInfoCalledEventCodes 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::storage_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);
}
// 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(PerProjectLoggerCallsMadeMetricDimensionLoggerMethod method,
const Project& project) override {}
void BytesUploaded(PerDeviceBytesUploadedMetricDimensionStatus upload_status,
size_t byte_count) override {}
// TODO(fxbug.dev/85571): NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
void BytesUploaded(PerProjectBytesUploadedMetricDimensionStatus upload_status, size_t byte_count,
uint32_t customer_id, uint32_t project_id) override {}
// TODO(fxbug.dev/85571): NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
void BytesStored(PerProjectBytesStoredMetricDimensionStatus upload_status, size_t byte_count,
uint32_t customer_id, uint32_t project_id) override {}
void IdleObservationUpload(IdleObservationUploadMetricDimensionDeviceState state) override {}
// TODO(fxbug.dev/85571): NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
void InaccurateClockEventsCached(int64_t event_count, uint32_t customer_id,
uint32_t project_id) override {}
// TODO(fxbug.dev/85571): NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
void InaccurateClockEventsDropped(int64_t event_count, uint32_t customer_id,
uint32_t project_id) override {}
void SetSoftwareDistributionInfoCalled(
SetSoftwareDistributionInfoCalledEventCodes 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 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 (non nullptr) LoggerInterface* that was provided to the
// Logger constructor.
class InternalMetricsImpl : public InternalMetrics {
public:
InternalMetricsImpl(LoggerInterface* logger, DiagnosticsInterface* diagnostics);
void LoggerCalled(PerProjectLoggerCallsMadeMetricDimensionLoggerMethod method,
const Project& project) override;
void BytesUploaded(PerDeviceBytesUploadedMetricDimensionStatus upload_status,
size_t byte_count) override;
void BytesUploaded(PerProjectBytesUploadedMetricDimensionStatus upload_status, size_t byte_count,
uint32_t customer_id, uint32_t project_id) override;
void BytesStored(PerProjectBytesStoredMetricDimensionStatus upload_status, size_t byte_count,
uint32_t customer_id, uint32_t project_id) override;
void IdleObservationUpload(IdleObservationUploadMetricDimensionDeviceState state) override;
void InaccurateClockEventsCached(int64_t event_count, uint32_t customer_id,
uint32_t project_id) override;
void InaccurateClockEventsDropped(int64_t event_count, uint32_t customer_id,
uint32_t project_id) override;
void SetSoftwareDistributionInfoCalled(
SetSoftwareDistributionInfoCalledEventCodes event_codes) override;
void TrackDiskUsage(StorageClass storage_class, size_t bytes, int64_t max_bytes) 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(PerProjectLoggerCallsMadeMetricDimensionLoggerMethod method,
const Project& project) override {
if (!paused_) {
wrapped_internal_metrics_->LoggerCalled(method, project);
}
}
void BytesUploaded(PerDeviceBytesUploadedMetricDimensionStatus upload_status,
size_t byte_count) override {
if (!paused_) {
wrapped_internal_metrics_->BytesUploaded(upload_status, byte_count);
}
}
void BytesUploaded(PerProjectBytesUploadedMetricDimensionStatus upload_status, size_t byte_count,
uint32_t customer_id, uint32_t project_id) override {
if (!paused_) {
wrapped_internal_metrics_->BytesUploaded(upload_status, byte_count, customer_id, project_id);
}
}
void BytesStored(PerProjectBytesStoredMetricDimensionStatus upload_status, size_t byte_count,
uint32_t customer_id, uint32_t project_id) override {
if (!paused_) {
wrapped_internal_metrics_->BytesStored(upload_status, byte_count, customer_id, project_id);
}
}
void IdleObservationUpload(IdleObservationUploadMetricDimensionDeviceState state) override {
if (!paused_) {
wrapped_internal_metrics_->IdleObservationUpload(state);
}
}
void InaccurateClockEventsCached(int64_t event_count, uint32_t customer_id,
uint32_t project_id) override {
if (!paused_) {
wrapped_internal_metrics_->InaccurateClockEventsCached(event_count, customer_id, project_id);
}
}
void InaccurateClockEventsDropped(int64_t event_count, uint32_t customer_id,
uint32_t project_id) override {
if (!paused_) {
wrapped_internal_metrics_->InaccurateClockEventsDropped(event_count, customer_id, project_id);
}
}
void SetSoftwareDistributionInfoCalled(
SetSoftwareDistributionInfoCalledEventCodes 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);
}
}
// 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_