// 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. library fuchsia.cobalt; using fuchsia.mem; // Cobalt is the Fuchsia service used to log, collect and analyze metrics. // The two main pillars of Cobalt are protecting user privacy and providing // high-quality, aggregate metrics to serve system and component software // developers' needs. // // This file contains interfaces that allow clients to log Events to // Cobalt. // // To use Cobalt, you must have a Project and one or more Metrics registered // with the Cobalt registration system. You must also register one or more // Reports in order to see the results of your logging aggregated over // all Fuchsia devices. Registration of Projects, Metrics and Reports consists // of entries in the YAML files in this repo: // https://cobalt-analytics.googlesource.com/config/. // In a Garnet checkout that is mapped to //third_party/cobalt_config. // Each registered object has an integer ID and those IDs are used as // parameters in the methods in this file. // // While Cobalt's code and registration files are open source, the running // system being managed by the Cobalt team is currently intended to be used by // software engineers at Google in order to collect metrics in a way that // preserves our users' privacy. If you are a Google software engineer // please see our internal [user guide](go/fuchsia-cobalt-userguide) or // ask for assistance from the Cobalt [team](go/fuchsia-cobalt#comms). // // Usage: First use LoggerFactory to get a Logger for your // project. Then you log Events using the Log*() methods. // Events are accumulated by the cobalt FIDL service and periodically // Observations, derived from the logged Events, are sent to the Cobalt server, // where they are used to generate Reports. // The maximum size of a single Event is 100 KB. const int64 MAX_BYTES_PER_EVENT = 102400; // Response codes for Logger operations. enum Status : int32 { OK = 0; // For example the supplied metric id is invalid. INVALID_ARGUMENTS = 1; // An attempt was made to log an Event whose seralized size exceeds // MAX_BYTES_PER_EVENT. EVENT_TOO_BIG = 2; // Cobalt's local buffer is temporarily full and cannot handle any more // Events at this time. Try again later. This condition should be rare BUFFER_FULL = 3; // Catch-all for unexpected errors. INTERNAL_ERROR = -1; }; // The release stage of a client's project is used to decide which // metrics are allowed to be collected. enum ReleaseStage : int32 { // Only metrics targetting GA releases will be sent to the server. GA = 0; // Metrics targetting DOGFOOD and GA will be sent to the server. DOGFOOD = 10; // Metrics targetting FISHFOOD, DOGFOOD and GA will be sent to the server. FISHFOOD = 20; // All metrics will be sent to the server. DEBUG = 99; }; // A ProjectProfile is used to describe the client's Cobalt project struct ProjectProfile { // The |config| Buffer contains the bytes of a serialized Cobalt config // proto message. This specifies registered Metric and Report definitions // for a single project. fuchsia.mem.Buffer config; // The release stage of the client's project. ReleaseStage release_stage; }; [Discoverable, Layout = "Simple"] // LoggerFactory creates Loggers. interface LoggerFactory { // Creates a Logger for the given ProjectProfile. // // |profile| A ProjectProfile describing the Cobalt project that the // returned Logger is for. // // |status| Returns OK on success or INVALID_ARGUMENTS if the project // profile does not contain a valid Cobalt config with only a single // project_id. 1: CreateLogger(ProjectProfile profile, request logger) -> (Status status); // Creates a LoggerSimple for the given ProjectProfile. // // |profile| A ProjectProfile describing the Cobalt project that the // returned Logger is for. // // |status| Returns OK on success or INVALID_ARGUMENTS if the project // profile does not contain a valid Cobalt config with only a single // project_id. 2: CreateLoggerSimple(ProjectProfile profile, request logger) -> (Status status); }; ///////////////////////////////////////////////////////////////////// // LoggerBase Interface ///////////////////////////////////////////////////////////////////// // LoggerBase and its extensions are used to log Events to the Cobalt system. // The Cobalt FIDL service stores the Events locally for some period of time, // processes the Events to form Observations, and periodically uploads batches // of Observations to the Cobalt server. The Cobalt server processes the // Observations and generates Reports. See [TODO(rudominer)] for more // description of the Cobalt server and Reports. // // LoggerBase or one of its extensions is associated with a single Cobalt // project. // // This interface conforms to the Simple layout so that Simple bindings // may be generated for it. For the full interfaces, see Logger and LoggerSimple // below. [Layout = "Simple"] interface LoggerBase { // Logs the fact that an event has occurred. // // |metric_id| ID of the metric to use. It must be one of the Metrics // from the ProjectProfile used to obtain this Logger, and it must be of // type EVENT_OCCURRED. // // |event_type_index| The index of the event type that occurred. The indexed // set of all event types is specified in the metric definition. 1: LogEvent(uint32 metric_id, uint32 event_type_index) -> (Status status); // Logs that an event has occurred a given number of times. // // |metric_id| ID of the metric to use. It must be one of the Metrics // from the ProjectProfile used to obtain this Logger, and it must be of // type EVENT_COUNT. // // |event_type_index| The index of the event type that occurred. The indexed // set of all event types is specified in the metric definition. // // |component| Optionally, a component associated with the event may // also be logged. Any notion of component that makes sense may be // used or use the empty string if there is no natural notion of // component. // // |period_duration_micros| Optionally, the period of time over which // the |count| events occurred may be logged. If this is not // relevant the value may be set to 0. Otherwise specify the period // duration as a number of microseconds. // // |count| The number of times the event occurred. One may choose to // always set this value to 1 and always set // |period_duration_micros| to 0 in order to achieve a semantics // similar to the LogEventOccurred() method, but with a |component|. 2: LogEventCount(uint32 metric_id, uint32 event_type_index, string:64 component, int64 period_duration_micros, int64 count) -> (Status status); // Logs that an event lasted a given amount of time. // // |metric_id| ID of the metric to use. It must be one of the Metrics // from the ProjectProfile used to obtain this Logger, and it must be of // type ELAPSED_TIME. // // |event_type_index| The index of the event type that occurred. The indexed // set of all event types is specified in the metric definition. // // |component| Optionally, a component associated with the event may // also be logged. Any notion of component that makes sense may be // used or use the empty string if there is no natural notion of // component. // // |elapsed_micros| The elapsed time of the event, specified as a number // of microseconds. 3: LogElapsedTime(uint32 metric_id, uint32 event_type_index, string:64 component, int64 elapsed_micros) -> (Status status); // Logs a measured average frame rate. // // |metric_id| ID of the metric to use. It must be one of the Metrics // from the ProjectProfile used to obtain this Logger, and it must be of // type FRAME_RATE. // // |event_type_index| The index of the event type that associated with the // frame-rate measurement. The indexed set of all event types is // specified in the metric definition. // // |component| Optionally, a component associated with the frame-rate // measurement may also be logged. Any notion of component that makes // sense may be used or use the empty string if there is no natural // notion of component. // // |fps| The average-frame rate in frames-per-second. 4: LogFrameRate(uint32 metric_id, uint32 event_type_index, string:64 component, float32 fps) -> (Status status); // Logs a measured memory usage. // // |metric_id| ID of the metric to use. It must be one of the Metrics // from the ProjectProfile used to obtain this Logger, and it must be of // type MEMORY_USAGE. // // |event_type_index| The index of the event type associated with the memory // usage. The indexed set of all event types is specified in the metric // definition. // // |component| Optionally, a component associated with the memory usage // may also be logged. Any notion of component that makes sense may be // used or use the empty string if there is no natural notion of // component. // // |bytes| The memory used, in bytes. 5: LogMemoryUsage(uint32 metric_id, uint32 event_type_index, string:64 component, int64 bytes) -> (Status status); // Logs the fact that a given string was used, in a specific context. // The semantics of the context and the string is specified in the // Metric definition. // // This method is intended to be used in the following situation: // * The string s being logged does not contain PII or passwords. // * The set S of all possible strings that may be logged is large. // If the set S is small consider using LogEvent() instead. // * The ultimate data of interest is the statistical distribution of the // most commonly used strings from S over the population of all Fuchsia // devices. // // |metric_id| ID of the metric to use. It must be one of the Metrics // from the ProjectProfile used to obtain this Logger, and it must be of // type STRING_USED. // // |s| The string to log. This should be a human-readable string of // size no more than 256 bytes. 6: LogString(uint32 metric_id, string:256 s) -> (Status status); // This method is part of Cobalt's helper service for measuring the time // delta between two events that occur in different processes. This starts // the timer. A corresponding invocation of EndTimer() with the same // |timer_id| ends the timer. After both StartTimer() and EnvdTimer() have // been invoked, LogElapsedTime() will be invoked with the difference // between the end timestamp and the start timestamp as the value of // |duration_microseconds|. It is OK if Cobalt receives the EndTimer() // call before the StartTimer() call. // // |metric_id| ID of the metric to use. It must be one of the Metrics // from the ProjectProfile used to obtain this Logger, and it must be of // type ELAPSED_TIME. // // |event_type_index| The index of the event type to associate with the // elapsed time. This is passed to LogElapsedTime() // // |component| Optionally, a component associated with the event may // also be logged. See the description at LogElapsedTime(). // // |timer_id| The ID of the timer being started. This is an arbitrary // non-empty string provided by the caller and it is the caller's // responsibility to ensure that Cobalt receives a pair of // StartTimer(), EndTimer() calls with this id before the timeout // and without any intervening additional calls to StartTimer() // or EndTimer() using the same id. Once such a pair is received // Cobalt will delete the timer with this ID and after that the // ID may be re-used. // // |timestamp| The timestamp to set as the start of the timer. The units // must be microseconds. The absolute value does not matter, only the // difference between the end and start timestamps will be used. // // |timeout_s| The number of seconds Cobalt should wait to receive the // corresponding EndTimer() call with the same |timer_id|. If // Cobalt has already received the corresponding EndTimer() call // before receiving this StartTimer() call then this value is // ignored as the timeout has already been set by the EndTimer() // call. If Cobalt does not receive the corresponding EndTimer() // call before the timeout then the timer will be deleted and // this invocation of StartTimer() will be forgotten. Must be a // positive value less than 300. // // |status| Returns OK on success. There are two success cases: // (i) Cobalt does not currently have any timers with the given // timer_id. In that case this call creates a new timer with // the given ID and start timestamp. // (ii) Cobalt currently has a timer with the given timer_id for // which it has received exactly one EndTimer() call and no // StartTimer() calls. In this case Cobalt will delete the // timer and invoke LogElapsedTime() using the difference // between the end timestamp and the start timestamp as the // value of |duration_micors|. It is ok if this value is // negative. // Returns INVALID_ARGUMENTS if |timer_id| is empty, the timeout // is not positive and less than 5 minutes or Cobalt currently // has a timer with the given timer_ID and it already has a start // timestamp. In the last case Cobalt will delete the timer with // the given |timer_id| and this invocation of StartTimer() // will be forgotten. // Any error returned by LogElapsedTime() may also be returned by this // method. 7: StartTimer(uint32 metric_id, uint32 event_type_index, string:64 component, string:64 timer_id, uint64 timestamp, uint32 timeout_s) -> (Status status); // This method is part of Cobalt's helper service for measuring the time // delta between two events that occur in different processes. This ends // the timer. A corresponding invocation of StartTimer() with the same // |timer_id| starts the timer. After both StartTimer() and EnvdTimer() have // been invoked, LogElapsedTime() will be invoked with the difference // between the end timestamp and the start timestamp as the value of // |duration_microseconds|. It is OK if Cobalt receives the EndTimer() // call before the StartTimer() call. // // |timer_id| The ID of the timer being ended. This is an arbitrary // non-empty string provided by the caller and it is the caller's // responsibility to ensure that Cobalt receives a pair of // StartTimer(), EndTimer() calls with this id before the timeout // and without any intervening additional calls to StartTimer() // or EndTimer() using the same id. Once such a pair is received // Cobalt will delete the timer with this ID and after that the // ID may be re-used. // // |timestamp| The timestamp to set as the end of the timer. The units // must be microseconds. The absolute value does not matter, only the // difference between the end and start timestamps will be used. // // |timeout_s| The number of seconds Cobalt should wait to receive the // corresponding EndTimer() call with the same |timer_id|. If // Cobalt has already received the corresponding EndTimer() call // before receiving this StartTimer() call then this value is // ignored as the timeout has already been set by the EndTimer() // call. If Cobalt does not receive the corresponding EndTimer() // call before the timeout then the timer will be deleted and // this invocation of StartTimer() will be forgotten. Must be a // positive value less than 300. // // |status| Returns OK on success. There are two success cases: // (i) Cobalt does not currently have any timers with the given // timer_id. In that case this call creates a new timer with // the given ID and end timestamp. // (ii) Cobalt currently has a timer with the given timer_id for // which it has received exactly one StartTimer() call and no // EndTimer() calls. In this case Cobalt will delete the // timer and invoke LogElapsedTime() using the difference // between the end timestamp and the start timestamp as the // value of |duration_micors|. It is ok if this value is // negative. // Returns INVALID_ARGUMENTS if |timer_id| is empty, the timeout // is not positive and less than 5 minutes or Cobalt currently // has a timer with the given timer_ID and it already has an end // timestamp. In the last case Cobalt will delete the timer with // the given |timer_id| and this invocation of EndTimer() // will be forgotten. // Any error returned by LogElapsedTime() may also be returned by this // method. 8: EndTimer(string:64 timer_id, uint64 timestamp, uint32 timeout_s) -> (Status status); // Method ordinals >= 100 are reserved for sub-interfaces. }; ///////////////////////////////////////////////////////////////////// // Logger Interface ///////////////////////////////////////////////////////////////////// // A value for a custom Event. This is used by the method LogCustomEvent(). struct CustomEventValue { // The name of the Metric dimension this value is for. string dimension_name; // The value for that dimension. Value value; }; // A value that may be a string, int, double, or index. union Value { string string_value; int64 int_value; float64 double_value; uint32 index_value; }; // One bucket of histogram. This is used by the method LogIntHistogram(). struct HistogramBucket { // The index of the bucket. The meaning of the bucket is specified in the // Metric definition. uint32 index; // The number of values in that bucket. uint64 count; }; // Logger is an extension of the LoggerBase interface that adds some additional // methods that do not naturally conform to the Simple layout. We opt for // a natural easy-to-understand interface at the cost of not being "Simple". // See the interface LoggerSimple below for versions of some of these methods // that do conform to the Simple layout. interface Logger : LoggerBase { // Logs a histogram over a set of integer buckets. The meaning of the // Metric and the buckets is specified in the Metric definition. // // This method is intended to be used in situations where the client // wishes to aggregate a large number of integer-valued measurements // *in-process*, prior to submitting the data to Cobalt. // One reason a client may wish to do this is that the measurements occur // with very high frequency and it is not practical to make a FIDL call // for each individual measurement. // // |metric_id| ID of the metric to use. It must be one of the Metrics // from the ProjectProfile used to obtain this Logger, and it must be of // type INT_HISTOGRAM. // // |event_type_index| The index of the event type associated with the // integer-valued measurement. The indexed set of all event types is // specified in the metric definition. // // |component| Optionally, a component associated with integer-valued // measurements may also be logged. Any notion of component that makes // sense may be used or use the empty string if there is no natural // notion of component. // // |histogram| The histogram to log. Each HistogramBucket gives the count // for one bucket of the histogram. The definitions of the buckets is // given in the Metric definition. 100: LogIntHistogram(uint32 metric_id, uint32 event_type_index, string:64 component, vector histogram) -> (Status status); // Logs a custom Event. The semantics of the Metric are specified in the // Metric defintion. // // |metric_id| ID of the metric to use. It must be one of the Metrics // from the ProjectProfile used to obtain this Logger, and it must be of // type CUSTOM. // // |event_values| The values for the custom Event. There is one value for // each dimension of the Metric. The number and types of the values must // be consistent with the dimensions declared in the Metric definition. 101: LogCustomEvent(uint32 metric_id, vector event_values) -> (Status status); }; ///////////////////////////////////////////////////////////////////// // LoggerSimple Interface ///////////////////////////////////////////////////////////////////// // LoggerSimple is an extension of the LoggerBase interface that adds some // additional methods intended to be used by lower-levels of the Fuchsia system. // // This interface conforms to the Simple layout so that Simple bindings // may be generated for it. [Layout = "Simple"] interface LoggerSimple : LoggerBase { // Logs a histogram over a set of integer buckets. The meaning of the // Metric and the buckets is specified in the Metric definition. // // See the method LogIntHistogram() in the Logger interface for more // information. This method is similar except that it adheres to the // requirements of Simple layout. Instead of a vector of HistogramBucekts // this version takes two parallel vectors of bucket indices and the // corresponding bucket counts. 100: LogIntHistogram(uint32 metric_id, uint32 event_type_index, string:64 component, vector:100 bucket_indices, vector:100 bucket_counts) -> (Status status); }; ///////////////////////////////////////////////////////////////////// // SystemProfileUpdater Interface ///////////////////////////////////////////////////////////////////// // The state of a single experiment on a device or binary. struct Experiment { // The id of the experiment as defined by the A/B Experiment framework. uint64 experiment_id; // The id of the experiment arm as definedby the A/B Experiment framework. uint32 arm_id; }; [Discoverable] // The SystemDataUpdater interface allows callers to update the state of // the System Data in Cobalt. This includes the SystemProfile and experiment // state. The changes are global and affect all loggers running on the device. interface SystemDataUpdater { // Resets Cobalt's view of the system-wide experiment state and replaces it // with the given values. // // |experiments| All experiments the device has a notion of and the // arms the device belongs to for each of them. These are the only // experiments the device can collect data for. 1: SetExperimentState(vector experiments) -> (Status status); };