# ⛯ Grafana Faro + qryn


# Faro

**Grafana** just released [Faro](https://grafana.com/blog/2022/11/02/introducing-grafana-faro-oss-application-observability/?mdm=social), an open source project for application frontend observability that enables an easy way to track the health of your web applications.

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1667417885504/obfzRpFSR.png align="left")

**Faro** provides a **JavaScript library** you can add to your frontend application to collect **logs, errors, and performance metrics**, add metadata and forward it to the Grafana Agent which writes data to **Prometheus, Loki, or Tempo**.

 _This sounds very exciting!_  

Will Faro work with **qryn** out of the box? Let's look at the app diagram:

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1667417702333/G6LQsV12X.png align="left")

**Loki, Prometheus and Tempo** - _this sounds like a perfect polyglot match!_

<img src="https://user-images.githubusercontent.com/1423657/191464140-48851b2c-ada0-4cfb-b27d-315edc0ba890.gif">

Let's go instrument webapps using [Faro](https://github.com/grafana/faro-web-sdk/blob/main/docs/sources/tutorials/quick-start-browser.md) and our **ClickHouse** powered stack!


### Grafana Agent
The recipe uses a **Grafana Agent** powered public collector endpoint to _receive and forward_ webapplication telemetry to _Loki, Tempo, and Prometheus_... or just **qryn**. Since we are _drop-in compatible_, all we have to change are the API URL endpoints.

```
metrics:
  wal_directory: /tmp/wal
  global: {}
  configs:
    - name: default
      remote_write:
        - url: https://qryn:3100/api/prom/push
          basic_auth:
            username: xxx
            password: xxx
logs:
  positions_directory: /tmp/loki-pos
  configs:
    - name: default
      scrape_configs: []
      clients:
        - url: https://xxx:xxx@qryn:3100/loki/api/v1/push
traces:
  configs:
    - name: default
      remote_write:
        - endpoint: qryn:3100
          basic_auth:
            username: xxx
            password: xxx
      receivers:
        otlp:
          protocols:
            grpc:
            http:
              cors:
                allowed_origins:
                  - http://localhost:1234
                max_age: 7200
integrations:
  app_agent_receiver_configs:
    - autoscrape:
        enable: true
        metrics_instance: 'default'
      api_key: 'secret' # optional, if set, client will be required to provide it via x-api-key header
      instance: 'frontend'
      logs_instance: 'default'
      traces_instance: 'default'
      server:
        host: 0.0.0.0
        port: 12345
        cors_allowed_origins:
          - 'https://my-app.example.com'
      logs_labels: # labels to add to loki log record
        app: frontend # static value
        kind: # value will be taken from log items. exception, log, measurement, etc
      logs_send_timeout: 5000
      sourcemaps:
        download: true # will download source file, extract source map location,
        # download source map and use it to transform stack trace locations
```

### Faro SDK
The Grafana **Faro Web SDK** comes baked with a lot of useful features:

- Set up in seconds with just two lines of code
- Lightweight
- Automatic instrumentation that captures errors, logs, and performance metrics
- Pre-configured tracing system based on OpenTelemetry with automatic instrumentations
- Easy-to-use API for manual instrumentation
- Fully customizable and extensible

Follow the [integration document](https://github.com/grafana/faro-web-sdk/blob/main/docs/sources/tutorials/quick-start-browser.md#install-grafana-faro-web-sdk) to instrument your webapplication.

> Take your sweet time to follow the [tutorial](https://github.com/grafana/faro-web-sdk/blob/main/docs/sources/tutorials/quick-start-browser.md#install-grafana-faro-web-sdk) 

#### SDK Example
We can refer to the Grafana example for manually sending some test data.

```
import { LogLevel } from '@grafana/faro-web-sdk';

// there's a global property
const faro = window.faro;

// send a log message
// by default info, warn and error levels are captured.
// trace, debug and log are not
console.info('Hello world', 123);
// or
faro.api.pushLog(['Hello world', 123], { level: LogLevel.Debug });

// log with context
faro.api.pushLog(['Sending update'], {
  context: {
    payload: thePayload,
  },
  level: LogLevel.TRACE,
});

// set user metadata, to be included with every event. All properties optional
faro.api.setUser({
  email: 'bob@example.com',
  id: '123abc',
  username: 'bob',
  attributes: {
    role: 'manager',
  },
});

// unset user
faro.api.resetUser();
// set session metadata, to be included with every event
faro.api.setSession(createSession({ plan: 'paid' }));
// unset session
faro.api.resetSession();
// push measurement
faro.api.pushMeasurement({
  type: 'cart-transaction',
  values: {
    delay: 122,
    duration: 4000,
  },
});

// push an error
faro.api.pushError(new Error('everything went horribly wrong'));
// push an event
faro.api.pushEvent('navigation', { url: window.location.href });
// pause faro, preventing events from being sent
faro.pause();
// resume sending events
faro.unpause();
```


### Sample Dashboards
Last but not least, include the sample dashboards to complete your setup.

- [Web Application Dashboard](https://github.com/grafana/faro-web-sdk/blob/main/dashboards/frontend-application.json)

- [Grafana Agent app agent receiver dashboard](https://github.com/grafana/faro-web-sdk/blob/main/dashboards/app-agent-receiver.json)

<br>

##### Update
Jachen tried using Faro and came up with his tiny alternative: [boscaiolog](https://blog.qryn.dev/client-logs-from-anywhere)


## Conclusion
**Faro** is compatible with **qryn** out of the box. And **qryn** is amazing. 💛

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1667417655111/J0E6IixDe.png align="left")
