Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug 1658349 - Allow using the quantity metric type outside of Gecko #1198

Merged
merged 9 commits into from
Sep 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

[Full changelog](https://github.com/mozilla/glean/compare/v32.3.2...main)

* General
* Allow using quantity metric type outside of Gecko ([#1198](https://github.com/mozilla/glean/pull/1198))

# v32.3.2 (2020-09-11)

[Full changelog](https://github.com/mozilla/glean/compare/v32.3.1...v32.3.2)
Expand Down
2 changes: 1 addition & 1 deletion docs/user/metrics/counter.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ using static Mozilla.YourApplication.GleanMetrics.Controls;
Assert.True(Controls.refreshPressed.TestHasValue());
// Does the counter have the expected value?
Assert.Equal(6, Controls.refreshPressed.TestGetValue());
// Did the counter record an negative value?
// Did the counter record a negative value?
Assert.Equal(
1, Controls.refreshPressed.TestGetNumRecordedErrors(ErrorType.InvalidValue)
);
Expand Down
127 changes: 113 additions & 14 deletions docs/user/metrics/quantity.md
Original file line number Diff line number Diff line change
@@ -1,53 +1,152 @@
# Quantity

Used to record a single non-negative integer value.
Used to record a single non-negative integer value or 0.
brizental marked this conversation as resolved.
Show resolved Hide resolved
For example, the width of the display in pixels.

> **Note**: Quantities are currently only allowed for GeckoView metrics (the `gecko_datapoint` parameter is present) and thus have only a Kotlin API.
> **IMPORTANT** If you need to _count_ something (e.g. number of tabs open or number of times a button is pressed) prefer using the [Counter](./counter.md) metric type, which has a specific API for counting things and also takes care of resetting the count at the correct time.
brizental marked this conversation as resolved.
Show resolved Hide resolved

## Configuration

Say you're adding a new quantity for the width of the display in pixels. First you need to add an entry for the quantity to the `metrics.yaml` file:

```YAML
gfx:
display_width:
display:
width:
type: quantity
description: >
The width of the display, in pixels.
unit: pixels
gecko_datapoint: DISPLAY_W_PIXELS
...
```

Note that quantities have a required `unit` parameter, which is a free-form string for documentation purposes.

## API

{{#include ../../tab_header.md}}

<div data-lang="Kotlin" class="tab">

```Kotlin
import org.mozilla.yourApplication.GleanMetrics.Gfx
import org.mozilla.yourApplication.GleanMetrics.Display

Gfx.displayWidth.set(width)
Display.width.set(width)
```

There are test APIs available too:

```Kotlin
import org.mozilla.yourApplication.GleanMetrics.Gfx
import org.mozilla.yourApplication.GleanMetrics.Display

// Was anything recorded?
assertTrue(Gfx.displayWidth.testHasValue())
assertTrue(Display.width.testHasValue())
// Does the quantity have the expected value?
assertEquals(6, Gfx.displayWidth.testGetValue())
assertEquals(6, Display.width.testGetValue())
// Did it record an error due to a negative value?
assertEquals(1, Gfx.displayWidth.testGetNumRecordedErrors(ErrorType.InvalidValue))
assertEquals(1, Display.width.testGetNumRecordedErrors(ErrorType.InvalidValue))
```

## Limits
</div>

```Java
import org.mozilla.yourApplication.GleanMetrics.Display;

Display.INSTANCE.width.set(width);
```

There are test APIs available too:

```Java
import org.mozilla.yourApplication.GleanMetrics.Display;

// Was anything recorded?
assertTrue(Display.INSTANCE.width.testHasValue());
// Does the quantity have the expected value?
assertEquals(6, Display.INSTANCE.width.testGetValue());
// Did the quantity record a negative value?
assertEquals(
1, Display.INSTANCE.width.testGetNumRecordedErrors(ErrorType.InvalidValue)
);
```

</div>

<div data-lang="Swift" class="tab">

```Swift
Display.width.set(width)
```

There are test APIs available too:

```Swift
@testable import Glean

// Was anything recorded?
XCTAssert(Display.width.testHasValue())
// Does the quantity have the expected value?
XCTAssertEqual(6, try Display.width.testGetValue())
// Did the quantity record a negative value?
XCTAssertEqual(1, Display.width.testGetNumRecordedErrors(.invalidValue))
```

* Quantities must be non-negative integers.
</div>

<div data-lang="Python" class="tab">

```Python
from glean import load_metrics
metrics = load_metrics("metrics.yaml")

metrics.display.width.set(width)
```

There are test APIs available too:

```Python
# Was anything recorded?
assert metrics.display.width.test_has_value()
# Does the quantity have the expected value?
assert 6 == metrics.display.width.test_get_value()
# Did the quantity record an negative value?
from glean.testing import ErrorType
assert 1 == metrics.display.width.test_get_num_recorded_errors(
ErrorType.INVALID_VALUE
)
```

</div>

<div data-lang="C#" class="tab">

```C#
using static Mozilla.YourApplication.GleanMetrics.Display;

Display.width.Set(width);
```

There are test APIs available too:

```C#
using static Mozilla.YourApplication.GleanMetrics.Display;

// Was anything recorded?
Assert.True(Display.width.TestHasValue());
// Does the counter have the expected value?
Assert.Equal(6, Display.width.TestGetValue());
// Did the counter record an negative value?
Assert.Equal(
1, Display.width.TestGetNumRecordedErrors(ErrorType.InvalidValue)
);
```

</div>

{{#include ../../tab_footer.md}}

## Limits

* Quantities are only available for metrics that come from Gecko.
* Quantities must be non-negative integers or 0.
brizental marked this conversation as resolved.
Show resolved Hide resolved

## Examples

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ class QuantityMetricTypeTest {
}

@Test
fun `negative values are not counted`() {
fun `negative values are not recorded`() {
// Define a 'quantityMetric' quantity metric, which will be stored in "store1"
val quantityMetric = QuantityMetricType(
disabled = false,
Expand All @@ -139,7 +139,7 @@ class QuantityMetricTypeTest {
)

quantityMetric.set(-10L)
// Check that count was NOT incremented.
// Check that quantity was NOT recorded
assertFalse(quantityMetric.testHasValue("store1"))

// Make sure that the errors have been recorded
Expand Down
31 changes: 31 additions & 0 deletions glean-core/csharp/Glean/LibGleanFFI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,37 @@ internal static extern Int32 glean_string_list_test_get_num_recorded_errors(
string storage_name
);

// Quantity

[DllImport(SharedGleanLibrary, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
internal static extern UInt64 glean_new_quantity_metric(
string category,
string name,
string[] send_in_pings,
Int32 send_in_pings_len,
Int32 lifetime,
bool disabled
);

[DllImport(SharedGleanLibrary, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
internal static extern void glean_destroy_quantity_metric(IntPtr handle);

[DllImport(SharedGleanLibrary, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
internal static extern void glean_quantity_set(UInt64 metric_id, Int32 value);

[DllImport(SharedGleanLibrary, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
internal static extern Int32 glean_quantity_test_get_value(UInt64 metric_id, string storage_name);

[DllImport(SharedGleanLibrary, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
internal static extern bool glean_quantity_test_has_value(UInt64 metric_id, string storage_name);

[DllImport(SharedGleanLibrary, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
internal static extern Int32 glean_quantity_test_get_num_recorded_errors(
UInt64 metric_id,
Int32 error_type,
String storage_name
);

// Custom pings

[DllImport(SharedGleanLibrary, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
Expand Down
128 changes: 128 additions & 0 deletions glean-core/csharp/Glean/Metrics/QuantityMetricType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

using Mozilla.Glean.FFI;
using System;

namespace Mozilla.Glean.Private
{
/// <summary>
/// This implements the developer facing API for recording quantity metrics.
///
/// Instances of this class type are automatically generated by the parsers at build time,
/// allowing developers to record values that were previously registered in the metrics.yaml file.
///
/// The quantity API only exposes the [set] method.
//
/// </summary>
public sealed class QuantityMetricType {
private bool disabled;
private string[] sendInPings;
private UInt64 handle;

/// <summary>
/// The public constructor used by automatically generated metrics.
/// </summary>
public QuantityMetricType(
bool disabled,
string category,
Lifetime lifetime,
string name,
string[] sendInPings
) : this(0, disabled, sendInPings)
{
handle = LibGleanFFI.glean_new_quantity_metric(
category: category,
name: name,
send_in_pings: sendInPings,
send_in_pings_len: sendInPings.Length,
lifetime: (int)lifetime,
disabled: disabled);
}

internal QuantityMetricType(
UInt64 handle,
bool disabled,
string[] sendInPings
)
{
this.disabled = disabled;
this.sendInPings = sendInPings;
this.handle = handle;
}


/// <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>
/// 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_quantity_test_has_value(this.handle, ping);
}

/// <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 Int32 TestGetValue(string pingName = null)
{
Dispatchers.AssertInTestingMode();

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

string ping = pingName ?? sendInPings[0];
return LibGleanFFI.glean_quantity_test_get_value(this.handle, ping);
}

/// <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];
return LibGleanFFI.glean_quantity_test_get_num_recorded_errors(
handle, (int)errorType, ping
);
}
}
}
Loading