k6 `v0.46` is here 🎉! This release includes:

- xk6-browser extension version bump to `v1.0.2`, which includes breaking changes.
- Send custom headers to Loki log output
- Cloud Traces, the new integration between k6 and [Tempo](https://github.com/grafana/tempo)
- Ability to configure TLS per gRPC connection
- Cloud output v2
- And many UX improvements, bug fixes, and internal improvements

## Breaking Changes

### Browser

In this release, the xk6-browser extension version is bumped up to `v1.0.2`, as it includes multiple breaking changes concerning options, browser lifecycle, and metrics. See the [migration guide](https://k6.io/docs/using-k6-browser/migrating-to-k6-v0-46/) for making your test scripts compatible with the new version.

**Options** `devtools`, `env` and `proxy` are deprecated ([browser#868](https://github.com/grafana/xk6-browser/pull/868), [browser#870](https://github.com/grafana/xk6-browser/pull/870), [browser#872](https://github.com/grafana/xk6-browser/pull/872)). Additionally, browser `launch`/`connect` options are no longer defined in the corresponding JS API methods, instead the test execution related options are now defined inside the browser scenario options (see [#3036](https://github.com/grafana/k6/pull/3036)), and the other more "environmental options", such as `headless`, `debug`, `executablePath`, are set as ENV vars. Also, the `slowMo` option is no longer supported, although it might be supported again in the future through a different API ([browser#876](https://github.com/grafana/xk6-browser/pull/876)).

**Metrics** also went through a few changes. The Web Vitals metrics are renamed to use the `browser_` prefix and short namings (e.g.: `webvital_first_input_delay` -> `browser_web_vital_fid`) ([browser#885](https://github.com/grafana/xk6-browser/pull/885), [browser#903](https://github.com/grafana/xk6-browser/pull/903)), and the rating metric is removed, as it is now set as a label in the corresponding Web Vitals metrics ([browser#915](https://github.com/grafana/xk6-browser/pull/915)).
The browser HTTP metrics have also been modified, as these are now split from the HTTP module ones, so there are new `browser_` prefixed HTTP metrics, specifically for request duration and failed requests ([browser#916](https://github.com/grafana/xk6-browser/pull/916)).

Another big change introduced in this version affects the way the **browser lifecycle** is handled. Users no longer have to explicitly initialize/close the browser instance through the JS API. Instead, a browser instance will be automatically initialized/closed at the beginning/end of each iteration if the browser type is set in scenario options (see [#3036](https://github.com/grafana/k6/pull/3036)). This also means that the `chromium` entity from `k6/experimental/browser` import path is no longer valid. Instead, the `browser` entity provides access to the browser methods such as `browser.newPage()` ([browser#910](https://github.com/grafana/xk6-browser/pull/910), [browser#944](https://github.com/grafana/xk6-browser/pull/944)). This change means that the `browser.on()` method is no longer applicable, and therefore it has been removed ([browser#919](https://github.com/grafana/xk6-browser/pull/919)).

Additionally, related to **import changes**, the browser module version is no longer an exported field ([browser#923](https://github.com/grafana/xk6-browser/pull/923)).

Last but not least, this release also includes constraints on **browser contexts** usage as now only one `browserContext` is allowed per iteration, which means that the user can create a new `browserContext`, close it, and then create it again; but can not have more than one "live" `browserContext`. Instead, [scenarios](https://k6.io/docs/using-k6/scenarios/) should be used to separate independent actions in a test ([browser#929](https://github.com/grafana/xk6-browser/pull/929), [browser#945](https://github.com/grafana/xk6-browser/pull/945)).

With all these changes, a simple browser test now looks like:
```js
import { browser } from 'k6/experimental/browser';

export const options = {
  scenarios: {
    ui: {
      executor: 'shared-iterations',
      options: {
        browser: {
          type: 'chromium', // chromium is the only supported browser type so as long as
                            // the option is set, Chromium/Google Chrome will be used
        },
      },
    },
  },
};

export default async function () {
  const page = browser.newPage();
  try {
    await page.goto('https://grafana.com')
  } finally {
    page.close();
  }
}
```

### Deprecations

- [#3121](https://github.com/grafana/k6/issues/3121) deprecates the `loadimpact/k6` docker image. We recommend switching to the `grafana/k6` image as soon as possible.


## New features

### Loki log output sending additional headers [#3227](https://github.com/grafana/k6/pull/3227)

k6 has been able to send logs to [Loki](https://github.com/grafana/loki) for nearly 3 years since [v0.28.0](https://github.com/grafana/k6/releases/tag/v0.28.0) but didn't support any way to authenticate.

Now, it can be configured to send additional headers on every request.

This can be done by using the new `header` config option, similar to `label`:

```
k6 --log-output=loki=http://example.org,header.X-My-Header=123,header.Authorization=mytoken ...
```

The example above will now send the header `X-My-Header` with the value `123` and the `Authorization` header with the value `mytoken`.

Thanks to @federicotdn for adding this feature.

### Cloud Traces [#3100](https://github.com/grafana/k6/pull/3100), [#3202](https://github.com/grafana/k6/pull/3202)

This release supports the new integration between k6 and Tempo in the cloud. [Grafana Cloud k6](https://grafana.com/products/cloud/k6) and Grafana Cloud Tempo customers will be able to start their traces in k6 using the existing `k6/experimental/tracing` module to enrich their test run analysis page with metrics and aggregations from tracing data.

The new Cloud traces will work "out of the box" for `k6 cloud` runs. In case of `k6 run` execution, the `K6_CLOUD_TRACES_ENABLED` environment variable has to be set to `true`.

### Ability to configure TLS per gRPC connection [#3159](https://github.com/grafana/k6/pull/3159), [xk6-grpc#25](https://github.com/grafana/xk6-grpc/pull/25)

The `k6/net/grpc` and `k6/experimental/grpc` modules now support configuring TLS per-connection via [ConnectParams](https://k6.io/docs/javascript-api/k6-net-grpc/client/client-connect/#connectparams). This is useful when connecting to a single gRPC server, using different TLS settings for each VUs, as well as when connecting to multiple gRPC servers from the same VU.

<details>
<summary> Expand to see an example of the new functionality.</summary>

```javascript
import grpc from "k6/experimental/grpc";
import { check } from "k6";
import { SharedArray } from "k6/data";
import exec from "k6/execution";

// note: the services in this example don't exist. If you would like
// to run this example, make sure to replace the URLs, and
// the cacerts, cert, key, and password variables.
const grpcArgs = new SharedArray("grpc", () => {
    // Using SharedArray here so that not every VU gets a copy of every certificate a key
    return [
        {
            host: "foo1.grpcbin.test.k6.io:9001",
            plaintext: false,
            params: {
                tls: {
                    cacerts: [open("cacerts0.pem")],
                    cert: open("cert0.pem"),
                    key: open("key0.pem"),
                },
            },
        },
        {
            host: "foo2.grpcbin.test.k6.io:9002",
            params: {
                plaintext: false,
                tls: {
                    cacerts: open("cacerts1.pem"),
                    cert: open("cert1.pem"),
                    key: open("key1.pem"),
                    password: "cert1-passphrase",
                },
            },
        },
    ];
});

const client = new grpc.Client();

export default () => {
    if (__ITER === 0) {
        // Take one config and use it for this one VU
        let grpcArg = grpcArgs[exec.vu.idInTest % grpcArgs.length];
        client.connect(grpcArg.host, grpcArg.params);
    }

    const response = client.invoke("hello.HelloService/SayHello", {
        greeting: "Bert",
    });

    check(response, {
        "status is OK": (r) => r && r.status === grpc.StatusOK,
    });

    console.log(JSON.stringify(response.message));
};
```

</details>

Thanks @chrismoran-mica for the contribution 🙇‍♂️.

### Cloud Output v2 [#3117](https://github.com/grafana/k6/issues/3117)

After years of great service, we decided to refresh the k6 Cloud output introducing a more efficient end-to-end solution for ingesting the generated tests' metrics. The main change regards the protocol used for flushing metrics that is now a binary-based payload over HTTP.

The new output reduces the resources a load generator uses for tests that produce many metrics. There is no significant difference in the user experience; it's expected to be the same.

The one thing worth highlighting is that the new output is strict about tags, and it'll drops tags if they are reserved. For example:

- A custom tag named `test_run_id`, since that is reserved for internal k6 Cloud operations
- Any tag with a key that starts with two underscores (`__`), that is marked by Prometheus convention as reserved

This is not yet the default Cloud output for the test runs executed from local machines (`k6 run -o cloud`), but it is expected to be transparently enabled in the upcoming weeks.

The full list of related PRs: [#3104](https://github.com/grafana/k6/pull/3104), [#3108](https://github.com/grafana/k6/pull/3108), [#3120](https://github.com/grafana/k6/pull/3120), [#3125](https://github.com/grafana/k6/pull/3125), [#3162](https://github.com/grafana/k6/pull/3162), [#3169](https://github.com/grafana/k6/pull/3169), [#3182](https://github.com/grafana/k6/pull/3182), [#3186](https://github.com/grafana/k6/pull/3186), [#3187](https://github.com/grafana/k6/pull/3187), [#3193](https://github.com/grafana/k6/pull/3193), [#3195](https://github.com/grafana/k6/pull/3195), [#3206](https://github.com/grafana/k6/pull/3206), [#3226](https://github.com/grafana/k6/pull/3226), [#3157](https://github.com/grafana/k6/pull/3157), [#3172](https://github.com/grafana/k6/pull/3172).


## UX improvements and enhancements

- [#3176](https://github.com/grafana/k6/pull/3176) Adds a `js/promises` package, which enables extension developers to easily create promises that will be dispatched to the eventloop using the `New` function.
- [#3181](https://github.com/grafana/k6/pull/3181) Adds a `RunOnEventLoop` method to the `modulestest.Runtime` type, which allows extensions developers to run code on the event loop from their tests.
- [xk6-grpc#19](https://github.com/grafana/xk6-grpc/pull/19) Fixes EOF (end of file) error that was produced when writing to a closed gRPC stream. Additionally, if no error handlers are set up, the error is logged to the console.
- [browser#881](https://github.com/grafana/xk6-browser/pull/881) Adds a new `browser_http_req_failed` metric.
- [browser#901](https://github.com/grafana/xk6-browser/pull/901) Adds support for shadow DOM piercing in `locator`. Thanks to @tmc for its implementation.
- [browser#953](https://github.com/grafana/xk6-browser/pull/953) Improves Web Vitals reporting for better accuracy and consistency.

## Bug fixes

- [#3178](https://github.com/grafana/k6/pull/3178), [xk6-grpc#23](https://github.com/grafana/xk6-grpc/pull/23) Fixes the registration of nested protobuf messages in the gRPC module. Thanks @thiagodpf for reporting and fixing the bug!
- [#3194](https://github.com/grafana/k6/pull/3194) Fixes loki log output exiting before push is finished.
- [#3231](https://github.com/grafana/k6/pull/3231) Fixes the tracing module sampling option to default to `1.0` when not set by the user.
- [browser#866](https://github.com/grafana/xk6-browser/pull/866) Disables the timeout on browser process execution.
- [browser#942](https://github.com/grafana/xk6-browser/pull/942) Fixes deadlock in `frameNavigated`.
- [browser#943](https://github.com/grafana/xk6-browser/pull/943) Fixes a race condition in navigation and lifecycle events.
- [browser#979](https://github.com/grafana/xk6-browser/pull/979) Fixes a deadlock on `pagehide` event evaluation when `page.close()` is called.
- [browser#980](https://github.com/grafana/xk6-browser/pull/980) Fixes Loki log output for console log serializer.
- [browser#991](https://github.com/grafana/xk6-browser/pull/991) Fixes data directory not being cleared during `browser.close()` panic.

## Maintenance and internal improvements

- [#3112](https://github.com/grafana/k6/pull/3112) Adds an event system for core JS modules.
- [#3170](https://github.com/grafana/k6/pull/3170) Upgraded the version of gRPC k6 demo service dependency to address a security vulnerability ([CVE-2023-32731]).
- [#3211](https://github.com/grafana/k6/pull/3211) Updates the experimental gRPC module version used by k6.
- [#3210](https://github.com/grafana/k6/pull/3210) Updates the xk6-output-prometheus-remote version used by k6.
- [#3136](https://github.com/grafana/k6/pull/3136) Updates k6 Go dependencies.
- [#3131](https://github.com/grafana/k6/pull/3131) Updates our Pull Request template to be more structured and include a checklist for contributors.
- [#3177](https://github.com/grafana/k6/pull/3177) Updates the version of golangci-lint we use to the latest version.
- [#3163](https://github.com/grafana/k6/pull/3163) Fixes our [gRPC example](https://github.com/grafana/k6/blob/8fa0e6b9a8b63f430df34047e4393b281ff9ee30/examples/grpc.js).
- [#3196](https://github.com/grafana/k6/pull/3196) Logs packages lint fixes, and test for loki flushing at end.
- [browser#849](https://github.com/grafana/xk6-browser/pull/849), [browser#972](https://github.com/grafana/xk6-browser/pull/972), [browser#977](https://github.com/grafana/xk6-browser/pull/977), [browser#978](https://github.com/grafana/xk6-browser/pull/978), [browser#983](https://github.com/grafana/xk6-browser/pull/983) Log fixes and improvements.
- [browser#889](https://github.com/grafana/xk6-browser/pull/889), [browser#889](https://github.com/grafana/xk6-browser/pull/899), [browser#902](https://github.com/grafana/xk6-browser/pull/902), [browser#935](https://github.com/grafana/xk6-browser/pull/935), [browser#936](https://github.com/grafana/xk6-browser/pull/936) Internal refactors.
- [browser#904](https://github.com/grafana/xk6-browser/pull/904), [browser#959](https://github.com/grafana/xk6-browser/pull/959) Test fixes.
 - [browser#906](https://github.com/grafana/xk6-browser/pull/906) Parses the scenarios JSON definition from the `K6_INSTANCE_SCENARIOS` environment variable.
 - [browser#918](https://github.com/grafana/xk6-browser/pull/918) Replaces the usage of `os.LookupEnv` for k6 `LookupEnv` and abstracts environment variables lookup.
 - [browser#984](https://github.com/grafana/xk6-browser/pull/984) Ensures CDP requests message ID are unique at the connection scope.

