Exemplo n.º 1
0
 private Glean()
 {
     // Private constructor to disallow instantiation since
     // this is meant to be a singleton. It only wires up the
     // glean-core logging on the Rust side.
     LibGleanFFI.glean_enable_logging();
 }
Exemplo n.º 2
0
        /// <summary>
        /// Get the specific metric for a given label.
        ///
        /// If a set of acceptable labels were specified in the metrics.yaml file,
        /// and the given label is not in the set, it will be recorded under the
        /// special `__other__`.
        ///
        /// If a set of acceptable labels was not specified in the metrics.yaml file,
        /// only the first 16 unique labels will be used. After that, any additional
        /// labels will be recorded under the special `__other__` label.
        ///
        /// Labels must be snake_case and less than 30 characters. If an invalid label
        /// is used, the metric will be recorded in the special `__other__` label.
        /// </summary>
        /// <param name="label">The label</param>
        /// <exception cref="InvalidOperationException">
        /// If the type of  `T`  is currently not supported.
        /// </exception>
        public T this[string label]
        {
            get {
                // Note the double `(T)(ILabeledSubmetricInterface)` cast before returning. This is
                // required in order to make the compiler not complain. Since the `where` clause for
                // this class cannot list more than one class, we need all our supported subtypes to
                // implement a common interface and use that interface as the T type constraint. This
                // allows us to then explicitly cast back to T, which is otherwise impossible.
                switch (submetric)
                {
                case BooleanMetricType _:
                {
                    UInt64 handle = LibGleanFFI.glean_labeled_boolean_metric_get(this.handle, label);
                    return((T)(ILabeledSubmetricInterface) new BooleanMetricType(handle, disabled, sendInPings));
                }

                case CounterMetricType _:
                {
                    UInt64 handle = LibGleanFFI.glean_labeled_counter_metric_get(this.handle, label);
                    return((T)(ILabeledSubmetricInterface) new CounterMetricType(handle, disabled, sendInPings));
                }

                case StringMetricType _:
                {
                    UInt64 handle = LibGleanFFI.glean_labeled_string_metric_get(this.handle, label);
                    return((T)(ILabeledSubmetricInterface) new StringMetricType(handle, disabled, sendInPings));
                }

                default:
                    throw new InvalidOperationException("Can not get a submetric of this metric type");
                }
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Indicate that an experiment is running. Glean will then add an
        /// experiment annotation to the environment which is sent with pings. This
        /// information is not persisted between runs.
        /// </summary>
        /// <param name="experimentId">The id of the active experiment (maximum 100 bytes)</param>
        /// <param name="branch">The experiment branch (maximum 100 bytes)</param>
        /// <param name="extra">Optional metadata to output with the ping</param>
        public void SetExperimentActive(string experimentId, string branch, Dictionary <string, string> extra = null)
        {
            // The Map is sent over FFI as a pair of arrays, one containing the
            // keys, and the other containing the values.
            string[] keys   = null;
            string[] values = null;

            Int32 numKeys = 0;

            if (extra != null)
            {
                // While the `ToArray` functions below could throw `ArgumentNullException`, this would
                // only happen if `extra` (and `extra.Keys|Values`) is null. Which is not the case, since
                // we're specifically checking this.
                // Note that the order of `extra.Keys` and `extra.Values` is unspecified, but guaranteed
                // to be the same. See
                // https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2.values?view=netstandard-2.0#remarks
                keys    = extra.Keys.ToArray();
                values  = extra.Values.ToArray();
                numKeys = extra.Count();
            }

            // We dispatch this asynchronously so that, if called before the Glean SDK is
            // initialized, it doesn't get ignored and will be replayed after init.
            Dispatchers.LaunchAPI(() => {
                LibGleanFFI.glean_set_experiment_active(
                    experimentId,
                    branch,
                    keys,
                    values,
                    numKeys
                    );
            });
        }
Exemplo n.º 4
0
        /// <summary>
        /// Set a datetime value, truncating it to the metric's resolution.
        /// </summary>
        /// <param name="value"> The [Date] value to set. If not provided, will record the current time.
        public void Set(DateTimeOffset value = new DateTimeOffset())
        {
            if (disabled)
            {
                return;
            }
            // The current time of datetime offset.
            var currentTime = value.DateTime;
            // InvariantCulture calendar still preserves timezones and locality information,
            // it just formats them in a way to ease persistence.
            var calendar = CultureInfo.InvariantCulture.Calendar;

            Dispatchers.LaunchAPI(() => {
                LibGleanFFI.glean_datetime_set(
                    handle,
                    year: calendar.GetYear(currentTime),
                    month: calendar.GetMonth(currentTime),
                    day: calendar.GetDayOfMonth(currentTime),
                    hour: calendar.GetHour(currentTime),
                    minute: calendar.GetMinute(currentTime),
                    second: calendar.GetSecond(currentTime),
                    nano: Convert.ToInt64(1000000 * calendar.GetMilliseconds(currentTime)),
                    offset_seconds: Convert.ToInt32((currentTime - value.UtcDateTime).TotalSeconds)
                    );;
            });
        }
Exemplo n.º 5
0
        /// <summary>
        /// Returns the number of errors recorded for the given metric.
        /// </summary>
        /// <param name="errorType">The type of the error recorded.</param>
        /// <param name="pingName">Represents the name of the ping to retrieve the metric for.
        /// Defaults to the first value in `sendInPings`.</param>
        /// <returns>the number of errors recorded for the metric.</returns>
        public Int32 TestGetNumRecordedErrors(Testing.ErrorType errorType, string pingName = null)
        {
            Dispatchers.AssertInTestingMode();

            string ping = pingName ?? sendInPings[0];


            switch (submetric)
            {
            case BooleanMetricType _:
            {
                return(LibGleanFFI.glean_labeled_boolean_test_get_num_recorded_errors(handle, (int)errorType, ping));
            }

            case CounterMetricType _:
            {
                return(LibGleanFFI.glean_labeled_counter_test_get_num_recorded_errors(handle, (int)errorType, ping));
            }

            case StringMetricType _:
            {
                return(LibGleanFFI.glean_labeled_string_test_get_num_recorded_errors(handle, (int)errorType, ping));
            }

            default:
                throw new InvalidOperationException("Can not return errors for this metric type");
            }
        }
Exemplo n.º 6
0
        /// <summary>
        /// Tests whether a value is stored for the metric for testing purposes only. This function will
        /// attempt to await the last task (if any) writing to the the metric's storage engine before
        /// returning a value.
        /// </summary>
        /// <param name="pingName">represents the name of the ping to retrieve the metric for Defaults
        /// to the first value in `sendInPings`</param>
        /// <returns>true if metric value exists, otherwise false</returns>
        public bool TestHasValue(string pingName = null)
        {
            Dispatchers.AssertInTestingMode();

            string ping = pingName ?? sendInPings[0];

            return(LibGleanFFI.glean_string_test_has_value(this.handle, ping) != 0);
        }
Exemplo n.º 7
0
        /// <summary>
        /// Returns the stored data for the requested active experiment, for testing purposes only.
        /// </summary>
        /// <param name="experimentId">The id of the experiment to look for.</param>
        /// <exception cref="System.NullReferenceException">Thrown when there is no data for the experiment.</exception>
        /// <returns>The `RecordedExperimentData` for the experiment</returns>
        public RecordedExperimentData TestGetExperimentData(string experimentId)
        {
            Dispatchers.AssertInTestingMode();

            string rawData = LibGleanFFI.glean_experiment_test_get_data(experimentId).AsString();

            return(RecordedExperimentData.FromJsonString(rawData));
        }
Exemplo n.º 8
0
 /// <summary>
 /// Indicate that an experiment is no longer running.
 /// </summary>
 /// <param name="experimentId">The id of the experiment to deactivate.</param>
 public void SetExperimentInactive(string experimentId)
 {
     // We dispatch this asynchronously so that, if called before the Glean SDK is
     // initialized, it doesn't get ignored and will be replayed after init.
     Dispatchers.LaunchAPI(() => {
         LibGleanFFI.glean_set_experiment_inactive(experimentId);
     });
 }
Exemplo n.º 9
0
        /// <summary>
        /// Internal only, synchronous API for setting a boolean value.
        /// </summary>
        /// <param name="value">This is a user defined boolean value.</param>
        internal void SetSync(bool value)
        {
            if (disabled)
            {
                return;
            }

            LibGleanFFI.glean_boolean_set(this.handle, Convert.ToByte(value));
        }
Exemplo n.º 10
0
        /// <summary>
        /// Internal only, synchronous API for setting a string value.
        /// </summary>
        /// <param name="value">This is a user defined string value. If the length of the string exceeds
        /// the maximum length, it will be truncated</param>
        internal void SetSync(string value)
        {
            if (disabled)
            {
                return;
            }

            LibGleanFFI.glean_string_set(this.handle, value);
        }
Exemplo n.º 11
0
        /// <summary>
        /// Internal only, synchronous API for setting a UUID value.
        /// </summary>
        /// <param name="value">This is a user defined boolean value.</param>
        internal void SetSync(Guid value)
        {
            if (disabled)
            {
                return;
            }

            LibGleanFFI.glean_uuid_set(this.handle, value.ToString());
        }
Exemplo n.º 12
0
        /// <summary>
        /// Add to counter value synchronously.
        ///
        /// This is only to be used within the Glean SDK.
        /// </summary>
        /// <param name="amount">this is the amount to increment the counter by, defaulting to 1
        /// if called without parameters.</param>
        internal void AddSync(Int32 amount)
        {
            if (disabled)
            {
                return;
            }

            LibGleanFFI.glean_counter_add(handle, amount);
        }
Exemplo n.º 13
0
        /**
         * Returns the number of errors recorded for the given metric.
         *
         * @param errorType The type of the error recorded.
         * @param pingName represents the name of the ping to retrieve the metric for.
         *                 Defaults to the first value in `sendInPings`.
         * @return the number of errors recorded for the metric.
         */
        public Int32 TestGetNumRecordedErrors(Testing.ErrorType errorType, string pingName = null)
        {
            Dispatchers.AssertInTestingMode();

            string ping = pingName ?? sendInPings[0];

            return(LibGleanFFI.glean_string_test_get_num_recorded_errors(
                       this.handle, (int)errorType, ping
                       ));
        }
Exemplo n.º 14
0
        /// <summary>
        /// Explicitly set the timespan value, in nanoseconds.
        ///
        /// This API should only be used if your library or application requires recording
        /// times in a way that can not make use of `Start`/`Stop`/`Cancel`.
        ///
        /// `SetRawNanos` does not overwrite a running timer or an already existing value.
        /// </summary>
        /// <param name="elapsedNanos">The elapsed time to record, in nanoseconds.</param>
        public void SetRawNanos(ulong elapsedNanos)
        {
            if (disabled)
            {
                return;
            }

            Dispatchers.LaunchAPI(() => {
                LibGleanFFI.glean_timespan_set_raw_nanos(handle, elapsedNanos);
            });
        }
Exemplo n.º 15
0
        /// <summary>
        /// Set a quantity value.
        /// </summary>
        /// <param name="value">The value to set. Must be non-negative.</param>
        public void Set(Int32 value)
        {
            if (disabled)
            {
                return;
            }

            Dispatchers.LaunchAPI(() => {
                LibGleanFFI.glean_quantity_set(this.handle, value);
            });
        }
        /// <summary>
        /// Record a single value, in the unit specified by `memoryUnit`, to the distribution.
        /// </summary>
        /// <param name="sample">the value</param>
        public void Accumulate(UInt64 sample)
        {
            if (disabled)
            {
                return;
            }

            Dispatchers.LaunchAPI(() => {
                LibGleanFFI.glean_memory_distribution_accumulate(handle, sample);
            });
        }
Exemplo n.º 17
0
        /// <summary>
        /// TEST ONLY FUNCTION.
        /// Destroy the owned glean-core handle.
        /// </summary>
        internal void TestDestroyGleanHandle()
        {
            if (!IsInitialized())
            {
                // We don't need to destroy Glean: it wasn't initialized.
                return;
            }

            LibGleanFFI.glean_destroy_glean();
            initialized = false;
        }
Exemplo n.º 18
0
        /// <summary>
        /// Set a JWE value.
        /// </summary>
        /// <param name="value"> The [`compact representation`](https://tools.ietf.org/html/rfc7516#appendix-A.2.7) of a JWE value.</param>
        public void setWithCompactRepresentation(string value)
        {
            if (disabled)
            {
                return;
            }

            Dispatchers.LaunchAPI(() => {
                LibGleanFFI.glean_jwe_set_with_compact_representation(this.handle, value);
            });
        }
Exemplo n.º 19
0
        /// <summary>
        /// Build a JWE value from it's elements and set to it.
        /// </summary>
        /// <param name="header">A variable-size JWE protected header.</param>
        /// <param name="key">A variable-size encrypted key.
        /// This can be an empty octet sequence.</param>
        /// <param name="initVector">A fixed-size, 96-bit, base64 encoded Jwe initialization vector.
        /// If not required by the encryption algorithm, can be an empty octet sequence.</param>
        /// <param name="cipherText">The variable-size base64 encoded cipher text.</param>
        /// <param name="authTag">A fixed-size, 132-bit, base64 encoded authentication tag.
        /// Can be an empty octet sequence.</param>
        public void Set(string header, string key, string initVector, string cipherText, string authTag)
        {
            if (disabled)
            {
                return;
            }

            Dispatchers.LaunchAPI(() => {
                LibGleanFFI.glean_jwe_set(this.handle, header, key, initVector, cipherText, authTag);
            });
        }
Exemplo n.º 20
0
        /// <summary>
        /// Abort a previous `Start` call. No error is recorded if no `Start` was called.
        /// </summary>
        public void Cancel()
        {
            if (disabled)
            {
                return;
            }

            Dispatchers.LaunchAPI(() => {
                LibGleanFFI.glean_timespan_cancel(handle);
            });
        }
Exemplo n.º 21
0
 /// <summary>
 /// Get whether or not Glean is allowed to record and upload data.
 ///
 /// Caution: the result is only correct if Glean is already initialized.
 /// </summary>
 /// <returns>`true` if Glean is allowed to record and upload data.</returns>
 bool GetUploadEnabled()
 {
     if (IsInitialized())
     {
         return(LibGleanFFI.glean_is_upload_enabled() != 0);
     }
     else
     {
         return(false);
     }
 }
Exemplo n.º 22
0
        /// <summary>
        /// Sets a string list to one or more metric stores in a synchronous way.
        /// This is only to be used for the glean-ac to glean-core data migration.
        /// </summary>
        /// <param name="value">This is a user defined string list.</param>
        internal void SetSync(string[] value)
        {
            if (disabled)
            {
                return;
            }

            LibGleanFFI.glean_string_list_set(
                this.handle,
                value,
                value.Length);
        }
        /// <summary>
        /// Abort a previous `Start` call. No error is recorded if no `Start` was called.
        /// </summary>
        /// <param name="timerId">
        /// The `GleanTimerId` associated with this timing. This allows for concurrent timing
        /// of events associated with different ids to the same timing distribution metric.
        /// </param>
        public void Cancel(GleanTimerId timerId)
        {
            if (disabled || timerId == null)
            {
                return;
            }

            Dispatchers.LaunchAPI(() =>
            {
                LibGleanFFI.glean_timing_distribution_cancel(handle, timerId);
            });
        }
Exemplo n.º 24
0
        /// <summary>
        /// Appends a string value to one or more string list metric stores.
        /// If the length of the string exceeds the maximum length, it will be truncated.
        /// </summary>
        /// <param name="value">This is a user defined string value.</param>
        public void Add(string value)
        {
            if (disabled)
            {
                return;
            }

            Dispatchers.LaunchAPI(() => {
                LibGleanFFI.glean_string_list_add(
                    this.handle, value);
            });
        }
Exemplo n.º 25
0
        /// <summary>
        /// Stop tracking time for the provided metric.
        /// Sets the metric to the elapsed time, but does not overwrite an already
        /// existing value.
        /// This will record an error if no `Start` was called or there is an already
        /// existing value.
        /// </summary>
        public void Stop()
        {
            if (disabled)
            {
                return;
            }

            ulong stopTime = HighPrecisionTimestamp.GetTimestamp(TimeUnit.Nanosecond);

            Dispatchers.LaunchAPI(() => {
                LibGleanFFI.glean_timespan_set_stop(handle, stopTime);
            });
        }
Exemplo n.º 26
0
        /// <summary>
        /// Returns the stored value for testing purposes only. This function will attempt to await the
        /// last task (if any) writing to the the metric's storage engine before returning a value.
        /// @throws [NullPointerException] if no value is stored
        /// </summary>
        /// <param name="pingName">represents the name of the ping to retrieve the metric for.
        /// Defaults to the first value in `sendInPings`</param>
        /// <returns>value of the stored metric</returns>
        /// <exception cref="System.NullReferenceException">Thrown when the metric contains no value</exception>
        public string TestGetValue(string pingName = null)
        {
            Dispatchers.AssertInTestingMode();

            if (!TestHasValue(pingName))
            {
                throw new NullReferenceException();
            }

            string ping = pingName ?? sendInPings[0];

            return(LibGleanFFI.glean_string_test_get_value(this.handle, ping).AsString());
        }
Exemplo n.º 27
0
        /// <summary>
        /// TEST ONLY FUNCTION.
        /// Destroy the owned glean-core handle.
        /// </summary>
        internal void TestDestroyGleanHandle()
        {
            if (!IsInitialized())
            {
                // We don't need to destroy Glean: it wasn't initialized.
                return;
            }

            LibGleanFFI.glean_destroy_glean();
            // Reset all state.
            Dispatchers.QueueInitialTasks = true;
            initFinished = false;
            initialized  = false;
        }
        /// <summary>
        /// Returns the stored value for testing purposes only. This function will attempt to await the
        /// last task (if any) writing to the the metric's storage engine before returning a value.
        /// </summary>
        /// <param name="pingName">represents the name of the ping to retrieve the metric for.
        /// Defaults to the first value in `sendInPings`</param>
        /// <returns>value of the stored metric</returns>
        /// <exception cref="System.NullReferenceException">Thrown when the metric contains no value</exception>
        public DistributionData TestGetValue(string pingName = null)
        {
            Dispatchers.AssertInTestingMode();

            if (!TestHasValue(pingName))
            {
                throw new NullReferenceException();
            }

            string ping = pingName ?? sendInPings[0];

            return(DistributionData.FromJsonString(
                       LibGleanFFI.glean_timing_distribution_test_get_value_as_json_string(handle, ping).AsString()));
        }
Exemplo n.º 29
0
 public StringMetricType(
     bool disabled,
     string category,
     Lifetime lifetime,
     string name,
     string[] sendInPings
     ) : this(0, disabled, sendInPings)
 {
     handle = LibGleanFFI.glean_new_string_metric(
         category: category,
         name: name,
         send_in_pings: sendInPings,
         send_in_pings_len: sendInPings.Length,
         lifetime: (int)lifetime,
         disabled: disabled);
 }
        /// <summary>
        /// Start tracking time for the provided metric. This records an error if
        /// it’s already tracking time (i.e. start was already called with no
        /// corresponding `StopAndAccumulate`): in that case the original start time will
        /// be preserved.
        /// </summary>
        /// <returns>
        /// The `GleanTimerId` object to associate with this timing or `null`
        /// if the collection was disabled.
        /// </returns>
        public GleanTimerId Start()
        {
            if (disabled)
            {
                return(null);
            }

            // Even though the Rust code for `Start` runs synchronously, the Rust
            // code for `StopAndAccumulate` runs asynchronously, and we need to use the same
            // clock for start and stop. Therefore we take the time on the C# side, both
            // here and in `StopAndAccumulate`.
            ulong startTime = HighPrecisionTimestamp.GetTimestamp(TimeUnit.Nanosecond);

            // No dispatcher, we need the return value
            return((GleanTimerId)LibGleanFFI.glean_timing_distribution_set_start(handle, startTime));
        }