# Events

Events allow recording of e.g. individual occurrences of user actions,
say every time a view was open and from where.

Each event contains the following data:

- A timestamp, in milliseconds. The first event in any ping always has a value of `0`, and subsequent event timestamps are relative to it.
- The name of the event.
- A set of key-value pairs, where the keys are predefined in the `extra_keys` metric parameter, and the values are strings.

{{#include ../../../shared/blockquote-warning.html}}

##### Important

> Events are the most expensive metric type to record, transmit, store and analyze,
> so they should be used sparingly, and only when none of the other metric types are sufficient for answering your question.

## Recording API

### `record(object)`

_Added in: [v38.0.0](../../appendix/changelog/sdk.md#v3800-2021-05-12)_

Record a new event, with optional typed extra values.
This requires `type` annotations in the definition.
See [Extra metrics parameters](#extra-metric-parameters).

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

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

Note that an `enum` has been generated for handling the `extra_keys`: it has the same name as the event metric, with `Extra` added.

```Kotlin
import org.mozilla.yourApplication.GleanMetrics.Views

Views.loginOpened.record(Views.loginOpenedExtra(sourceOfLogin = "toolbar"))
```

</div>

<div data-lang="Java" class="tab"></div>

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

Note that an `enum` has been generated for handling the `extra_keys`: it has the same name as the event metric, with `Extra` added.

```Swift
Views.loginOpened.record(LoginOpenedExtra(sourceOfLogin: "toolbar"))
```

</div>

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

Note that an `enum` has been generated for handling the `extra_keys`: it has the same name as the event metric, with `_extra` added.

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

metrics.views.login_opened.record(LoginOpenedExtra(sourceOfLogin="toolbar"))
```

</div>

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

Note that an `enum` has been generated for handling the `extra_keys`: it has the same name as the event metric, with `Keys` added.

```Rust
use metrics::views::{self, LoginOpenedExtra};

let extra = LoginOpenedExtra { source_of_login: Some("toolbar".to_string()) };
views::login_opened.record(extra);
```

</div>

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

```js
import * as views from "./path/to/generated/files/views.js";

views.loginOpened.record({ sourceOfLogin: "toolbar" });
```

</div>

<div data-lang="Firefox Desktop" class="tab">

**C++**

```c++
#include "mozilla/glean/GleanMetrics.h"

using mozilla::glean::views::LoginOpenedExtra;
LoginOpenedExtra extra = { .source_of_login = Some("value"_ns) };
mozilla::glean::views::login_opened.Record(std::move(extra))
```

**JavaScript**

```js
const extra = { sourceOfLogin: "toolbar" };
Glean.views.loginOpened.record(extra);
```

</div>

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

### `record(map)` (_deprecated_)

_Deprecated in: [v38.0.0](../../appendix/changelog/sdk.md#v3800-2021-05-12)_
<!-- FIXME: Update anchor when v38.0.0 is actually released -->

Record a new event, with optional extra values.

{{#include ../../../shared/blockquote-info.html}}

##### Deprecation notice

> This API is used in v38.0.0 if an event has no `type` annotations in the definition.
> See [Extra metrics parameters](#extra-metric-parameters).
>
> In future versions extra values will default to a `string` type and this API will be removed.  
> In Rust and Firefox Desktop this API is not supported.

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

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

Note that an `enum` has been generated for handling the `extra_keys`: it has the same name as the event metric, with `Keys` added.

```Kotlin
import org.mozilla.yourApplication.GleanMetrics.Views

Views.loginOpened.record(mapOf(Views.loginOpenedKeys.sourceOfLogin to "toolbar"))
```

</div>

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

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

Views.INSTANCE.loginOpened.record();
```

</div>

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

Note that an `enum` has been generated for handling the `extra_keys`: it has the same name as the event metric, with `Keys` added.

```Swift
Views.loginOpened.record(extra: [.sourceOfLogin: "toolbar"])
```

</div>

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

Note that an `enum` has been generated for handling the `extra_keys`: it has the same name as the event metric, with `_keys` added.

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

metrics.views.login_opened.record(
    {
        metrics.views.login_opened_keys.SOURCE_OF_LOGIN: "toolbar"
    }
)
```

</div>

<div data-lang="Rust" class="tab"></div>

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

```js
import * as views from "./path/to/generated/files/views.js";

views.loginOpened.record({ sourceOfLogin: "toolbar" });
```

</div>

<div data-lang="Firefox Desktop" class="tab"></div>

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

#### Recorded errors

* [`invalid_overflow`](../../user/metrics/error-reporting.md): if any of the values in the `extras` object are greater than 50 bytes in length.  (Prior to Glean 31.5.0, this recorded an `invalid_value`).

## Testing API

### `testGetValue`

Get the list of recorded events.

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

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

```Kotlin
import org.mozilla.yourApplication.GleanMetrics.Views

val snapshot = Views.loginOpened.testGetValue()
assertEquals(2, snapshot.size)
val first = snapshot.single()
assertEquals("login_opened", first.name)
```

</div>

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

```Java
import org.mozilla.yourApplication.GleanMetrics.Views

assertEquals(Views.INSTANCE.loginOpened.testGetValue().size)
```

</div>

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

```swift
@testable import Glean

val snapshot = try! Views.loginOpened.testGetValue()
XCTAssertEqual(2, snapshot.size)
val first = snapshot[0]
XCTAssertEqual("login_opened", first.name)
```

</div>

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

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

snapshot = metrics.views.login_opened.test_get_value()
assert 2 == len(snapshot)
first = snapshot[0]
assert "login_opened" == first.name
```

</div>

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

```Rust
use metrics::views;

var snapshot = views::login_opened.test_get_value(None).unwrap();
assert_eq!(2, snapshot.len());
let first = &snapshot[0];
assert_eq!("login_opened", first.name);
```

</div>

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

```js
import * as views from "./path/to/generated/files/views.js";

const snapshot = await views.loginOpened.testGetValue();
assert.strictEqual(2, snapshot.length);
const first = snapshot[0]
assert.strictEqual("login_opened", first.name)
```

</div>

<div data-lang="Firefox Desktop" class="tab">

**C++**

```c++
#include "mozilla/glean/GleanMetrics.h"

auto optEvents = mozilla::glean::views::login_opened.TestGetValue();
auto events = optEvents.extract();
ASSERT_EQ(2UL, events.Length());
ASSERT_STREQ("login_opened", events[0].mName.get());
```

**JavaScript**

```js
var events = Glean.views.loginOpened.testGetValue();
Assert.equal(2, events.length);
Assert.equal("login_opened", events[0].name);
```

</div>

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

### `testHasValue`

Whether or not any event was recorded for a given event metric.

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

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

```Kotlin
import org.mozilla.yourApplication.GleanMetrics.Views

assertTrue(Views.loginOpened.testHasValue())
```

</div>

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

```Java
import org.mozilla.yourApplication.GleanMetrics.Views

assertTrue(Views.INSTANCE.loginOpened.testHasValue())
```

</div>

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

```swift
@testable import Glean

XCTAssert(Views.loginOpened.testHasValue())
```

</div>

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

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

assert metrics.views.login_opened.test_has_value()
```

</div>

<div data-lang="Rust" class="tab"></div>

<div data-lang="JavaScript" class="tab"></div>

<div data-lang="Firefox Desktop" class="tab"></div>

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

### `testGetNumRecordedErrors`

Get the number of errors recorded for a given event metric.

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

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

```Kotlin
import mozilla.telemetry.glean.testing.ErrorType
import org.mozilla.yourApplication.GleanMetrics.Views

assertEquals(0, Views.loginOpened.testGetNumRecordedErrors(ErrorType.InvalidOverflow))
```

</div>

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

```Kotlin
import mozilla.telemetry.glean.testing.ErrorType
import org.mozilla.yourApplication.GleanMetrics.Views

assertEquals(0, Views.INSTANCE.loginOpened.testGetNumRecordedErrors(ErrorType.InvalidOverflow))
```

</div>

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

```swift
@testable import Glean

XCTAssertEqual(0, Views.loginOpened.testGetNumRecordedErrors(.invalidOverflow))
```
</div>

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

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

assert 0 == metrics.views.login_opened.test_get_num_recorded_errors(
    ErrorType.INVALID_OVERFLOW
)
```

</div>

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

```Rust
use glean::ErrorType;
use metrics::views;

assert_eq!(
    0,
    views::login_opened.test_get_num_recorded_errors(ErrorType::InvalidOverflow, None)
);
```

</div>

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

```js
import * as views from "./path/to/generated/files/views.js";

assert.strictEqual(1, await views.loginOpened.testGetNumRecordedErrors("invalid_value"));
```
</div>

<div data-lang="Firefox Desktop" class="tab"></div>

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

## Metric parameters

Example event metric definition:

```yaml
views:
  login_opened:
    type: event
    description: |
      Recorded when the login view is opened.
    bugs:
      - https://bugzilla.mozilla.org/000000
    data_reviews:
      - https://bugzilla.mozilla.org/show_bug.cgi?id=000000#c3
    notification_emails:
      - me@mozilla.com
    expires: 2020-10-01
    extra_keys:
      source_of_login:
        description: The source from which the login view was opened, e.g. "toolbar".
        type: string
```

For a full reference on metrics parameters common to all metric types,
refer to the metrics [YAML format](../yaml/index.md) reference page.

### Extra metric parameters

#### `extra_keys`

The acceptable keys on the "extra" object sent with events.
A maximum of 10 extra keys is allowed.

Each extra key contains additional metadata:

- `description`: **Required.** A description of the key.
* `type`: The type of value this extra key can hold. One of `string`, `boolean`, `quantity`. Defaults to `string`.
  **Note**: If not specified only the legacy API on `record` is available.

## Data questions

* When and from where was the login view opened?
 
## Limits

* When 500 events are queued on the client an events ping is immediately sent.
* The `extra_keys` allows for a maximum of 10 keys.
* The keys in the `extra_keys` list must be in dotted snake case, with a maximum length of 40 bytes, when encoded as UTF-8.
* The values in the `extras` object have a maximum of 50 bytes, when encoded as UTF-8.
  
## Reference

* [Kotlin API docs](../../../javadoc/glean/mozilla.telemetry.glean.private/-event-metric-type/index.html)
* [Swift API docs](../../../swift/Classes/EventMetricType.html)
* [Python API docs](../../../python/glean/metrics/event.html)
* [Rust API docs](../../../docs/glean/private/event/struct.EventMetric.html)
* [Javascript API docs](https://mozilla.github.io/glean.js/classes/core_metrics_types_event.default.html)
