Skip to content
Open
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ Main (unreleased)

- Add `truncate` stage for `loki.process` to truncate log entries, label values, and structured_metadata values. (@dehaansa)

- `faro.receiver` can now fetch sourcemaps from remote locations. When multiple locations are configured, local on-disk paths will be checked before remote paths. (@Oxel40)

### Enhancements

- Add support of `tls` in components `loki.source.(awsfirehose|gcplog|heroku|api)` and `prometheus.receive_http` and `pyroscope.receive_http`. (@fgouteroux)
Expand Down
53 changes: 41 additions & 12 deletions docs/sources/reference/components/faro/faro.receiver.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,13 @@ The following strings are valid log line formats:

You can use the following blocks with `faro.receiver`:

| Block | Description | Required |
|----------------------------------------------|------------------------------------------------------|----------|
| [`output`][output] | Configures where to send collected telemetry data. | yes |
| [`server`][server] | Configures the HTTP server. | no |
| `server` > [`rate_limiting`][rate_limiting] | Configures rate limiting for the HTTP server. | no |
| [`sourcemaps`][sourcemaps] | Configures sourcemap retrieval. | no |
| `sourcemaps` > [`location`][location] | Configures on-disk location for sourcemap retrieval. | no |
| Block | Description | Required |
|----------------------------------------------|----------------------------------------------------|----------|
| [`output`][output] | Configures where to send collected telemetry data. | yes |
| [`server`][server] | Configures the HTTP server. | no |
| `server` > [`rate_limiting`][rate_limiting] | Configures rate limiting for the HTTP server. | no |
| [`sourcemaps`][sourcemaps] | Configures sourcemap retrieval. | no |
| `sourcemaps` > [`location`][location] | Configures the location for sourcemap retrieval. | no |

The > symbol indicates deeper levels of nesting.
For example, `sourcemaps` > `location` refers to a `location` block defined inside a `sourcemaps` block.
Expand Down Expand Up @@ -151,18 +151,18 @@ The `*` character indicates a wildcard.
By default, sourcemap downloads are subject to a timeout of `"1s"`, specified by the `download_timeout` argument.
Setting `download_timeout` to `"0s"` disables timeouts.

To retrieve sourcemaps from disk instead of the network, specify one or more [`location` blocks][location].
To retrieve sourcemaps from disk or another network location, specify one or more [`location` blocks][location].
When `location` blocks are provided, they're checked first for sourcemaps before falling back to downloading.

#### `location`

The `location` block declares a location where sourcemaps are stored on the filesystem.
You can specify the `location` block multiple times to declare multiple locations where sourcemaps are stored.

| Name | Type | Description | Default | Required |
|------------------------|----------|-----------------------------------------------------|---------|----------|
| `minified_path_prefix` | `string` | The prefix of the minified path sent from browsers. | | yes |
| `path` | `string` | The path on disk where sourcemaps are stored. | | yes |
| Name | Type | Description | Default | Required |
|------------------------|----------|-----------------------------------------------------------|---------|----------|
| `minified_path_prefix` | `string` | The prefix of the minified path sent from browsers. | | yes |
| `path` | `string` | The path on disk or base URL where sourcemaps are stored. | | yes |

The `minified_path_prefix` argument determines the prefix of paths to JavaScript files, such as `http://example.com/`.
The `path` argument then determines where to find the sourcemap for the file.
Expand All @@ -184,6 +184,35 @@ To look up the sourcemaps for a file hosted at `http://example.com/example.js`,
Optionally, the value for the `path` argument may contain `{{ .Release }}` as a template value, such as `/var/my-app/{{ .Release }}/build`.
The template value is replaced with the release value provided by the [Faro Web App SDK][faro-sdk].

When you specify a remote location, the procedure for retrieving the sourcemaps is the same as for a location block with a local path, except that the component retrieves the sourcemap from a remote HTTP server.

In the following example, the `faro.receiver` sends a GET request to `http://foo.com/blob/sourcemaps/example.js.map` and retrieves the sourcemap for a file hosted at
`http://example.com/example.js`.

You can specify multiple location blocks. For example:

```alloy
location {
path = "http://foo.com/blob/sourcemaps/"
minified_path_prefix = "http://example.com/"
}

```alloy
location {
path = "/var/my-app/build"
minified_path_prefix = "http://example.com/"
}
location {
path = "http://foo.com/blob/sourcemaps/"
minified_path_prefix = "http://example.com/"
}
```

The `faro.receiver` component searches through all locations for the sourcemap files.
Local on-disk paths take precedence over remote paths.
For a file hosted at `http://example.com/example.js`, the `faro.receiver` first checks
the path `/var/my-app/build/example.js.map`, and then tries to retrieve `http://foo.com/blob/sourcemaps/example.js.map`.

## Exported fields

`faro.receiver` doesn't export any fields.
Expand Down
50 changes: 49 additions & 1 deletion internal/component/faro/receiver/sourcemaps.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,14 +185,30 @@ func (store *sourceMapsStoreImpl) GetSourceMap(sourceURL string, release string)
func (store *sourceMapsStoreImpl) getSourceMapContent(sourceURL string, release string) (content []byte, sourceMapURL string, err error) {
// Attempt to find the source map in the filesystem first.
for _, loc := range store.locs {
if hasHttpPrefix(loc.Path) {
continue
}

content, sourceMapURL, err = store.getSourceMapFromFileSystem(sourceURL, release, loc)
if content != nil || err != nil {
return content, sourceMapURL, err
}
}

// Attempt to find the source map in the remote locations.
for _, loc := range store.locs {
if !(hasHttpPrefix(loc.Path)) {
continue
}

content, sourceMapURL, err = store.getSourceMapFromRemote(sourceURL, release, loc)
if content != nil || err != nil {
return content, sourceMapURL, err
}
}

// Attempt to download the sourcemap if enabled.
if strings.HasPrefix(sourceURL, "http") && urlMatchesOrigins(sourceURL, store.args.DownloadFromOrigins) && store.args.Download {
if store.args.Download && hasHttpPrefix(sourceURL) && urlMatchesOrigins(sourceURL, store.args.DownloadFromOrigins) {
return store.downloadSourceMapContent(sourceURL)
}
return nil, "", nil
Expand Down Expand Up @@ -242,6 +258,34 @@ func (store *sourceMapsStoreImpl) getSourceMapFromFileSystem(sourceURL string, r
return content, sourceURL, err
}

func (store *sourceMapsStoreImpl) getSourceMapFromRemote(sourceURL string, release string, loc *sourcemapFileLocation) (content []byte, sourceMapURL string, err error) {
if len(sourceURL) == 0 || !strings.HasPrefix(sourceURL, loc.MinifiedPathPrefix) || strings.HasSuffix(sourceURL, "/") {
return nil, "", nil
}

var rootPath bytes.Buffer

err = loc.pathTemplate.Execute(&rootPath, struct{ Release string }{Release: cleanFilePathPart(release)})
if err != nil {
return nil, "", err
}

subPath := strings.TrimPrefix(strings.Split(sourceURL, "?")[0], loc.MinifiedPathPrefix) + ".map"
mapURL, err := url.JoinPath(rootPath.String(), subPath)
if err != nil {
level.Debug(store.log).Log("msg", "failed to construct sourcemap url for remote location", "base_path", rootPath, "sub_path", subPath, "err", err)
return nil, "", err
}

content, err = store.downloadFileContents(mapURL)
if err != nil {
level.Debug(store.log).Log("msg", "failed to download sourcemap file from remote location", "url", mapURL, "err", err)
return nil, "", err
}

return content, sourceURL, err
}

func (store *sourceMapsStoreImpl) downloadSourceMapContent(sourceURL string) (content []byte, resolvedSourceMapURL string, err error) {
level.Debug(store.log).Log("msg", "attempting to download source file", "url", sourceURL)

Expand Down Expand Up @@ -341,6 +385,10 @@ func urlMatchesOrigins(URL string, origins []string) bool {
return false
}

func hasHttpPrefix(URL string) bool {
return strings.HasPrefix(URL, "http://") || strings.HasPrefix(URL, "https://")
}

func cleanFilePathPart(x string) string {
return strings.TrimLeft(strings.ReplaceAll(strings.ReplaceAll(x, "\\", ""), "/", ""), ".")
}
Expand Down
Loading