From 24a46e4e1323f6047a1164e520927c931e7ea437 Mon Sep 17 00:00:00 2001 From: Christian Simon Date: Wed, 29 May 2024 13:51:46 +0100 Subject: [PATCH] Use dynamic labels for nodejs examples and docs (#3309) * Use dynamic labels for nodejs This updates the nodejs examples and docs to align with the v0.3.11 release. It also adds nodejs to the updater script Co-authored-by: Kim Nylander <104772500+knylander-grafana@users.noreply.github.com> * Consistent spelling/casing of Wall/CPU --------- Co-authored-by: Kim Nylander <104772500+knylander-grafana@users.noreply.github.com> --- .../configure-client/language-sdks/nodejs.md | 47 ++++++++++++++++++- .../nodejs/express-pull/Dockerfile | 1 + .../nodejs/express-pull/index.js | 12 +++-- .../nodejs/express-pull/package.json | 2 +- .../nodejs/express-pull/yarn.lock | 8 ++-- .../nodejs/express-ts-inline/index.ts | 18 ++++--- .../nodejs/express-ts-inline/package.json | 2 +- .../nodejs/express-ts-inline/yarn.lock | 8 ++-- .../nodejs/express-ts/index.ts | 18 ++++--- .../nodejs/express-ts/package.json | 2 +- .../nodejs/express-ts/yarn.lock | 8 ++-- .../nodejs/express/Dockerfile | 1 + .../nodejs/express/index.js | 12 +++-- .../nodejs/express/package.json | 2 +- .../nodejs/express/yarn.lock | 8 ++-- tools/update_examples.go | 37 ++++++++++++++- 16 files changed, 144 insertions(+), 42 deletions(-) diff --git a/docs/sources/configure-client/language-sdks/nodejs.md b/docs/sources/configure-client/language-sdks/nodejs.md index 6d48970afc..2e59ccae6d 100644 --- a/docs/sources/configure-client/language-sdks/nodejs.md +++ b/docs/sources/configure-client/language-sdks/nodejs.md @@ -47,11 +47,44 @@ Pyroscope.init({ Pyroscope.start() ``` -Note: If you'd prefer to use Pull mode you can do so using the [Grafana Agent]({{< relref "../grafana-agent" >}}). +[comment]: <> (TODO This needs its own page like https://grafana.com/docs/pyroscope/latest/configure-client/grafana-agent/go_pull/) +{{< admonition type="note" >}} +If you'd prefer, you can use Pull mode using [Grafana Alloy](https://grafana.com/docs/alloy/latest/) (recommended) or [Grafana Agent]({{< relref "../grafana-agent" >}}) (legacy). +{{< /admonition >}} + + +### Configuration options + +| Init parameter | ENVIRONMENT VARIABLE | Type | DESCRIPTION | +|-------------------------------|-------------------------------------------|----------------|-----------------------------------------------------------------------------------| +| `appName: | `PYROSCOPE_APPLICATION_NAME` | String | Sets the `service_name` label | +| `serverAddress:` | `PYROSCOPE_SERVER_ADDRESS` | String | URL of the Pyroscope Server | +| `basicAuthUser:` | n/a | String | Username for basic auth / Grafana Cloud stack user ID (Default `""`) | +| `basicAuthPassword:` | n/a | String | Password for basic auth / Grafana Cloud API key (Default `""`) | +| `flushIntervalMs:` | `PYROSCOPE_FLUSH_INTERVAL_MS` | Number | Interval when profiles are sent to the server (Default `60000`) | +| `heapSamplingIntervalBytes` | `PYROSCOPE_HEAP_SAMPLING_INTERVAL_BYTES` | Number | Average number of bytes between samples. (Default `524288`) | +| `heapStackDepth:` | `PYROSCOPE_HEAP_STACK_DEPTH` | Number | Maximum stack depth for heap samples (Default `64`) | +| `wallSamplingDurationMs:` | `PYROSCOPE_WALL_SAMPLING_DURATION_MS` | Number | Duration of a single wall profile (Default `60000`) | +| `wallSamplingIntervalMicros:` | `PYROSCOPE_WALL_SAMPLING_INTERVAL_MICROS` | Number | Interval of how often wall samples are collected (Default `10000` | +| `wallCollectCpuTime:` | `PYROSCOPE_WALL_COLLECT_CPU_TIME` | Boolean | Enable CPU time collection for wall profiles (Default `false`) | +| `tags:` | n/a | [LabelSet] | Static labels applying to all profiles collected (Default `{}`) | +| `sourceMapper:` | n/a | [SourceMapper] | Provide source file mapping information (Default `undefined`) | + +[LabelSet]:https://github.com/DataDog/pprof-nodejs/blob/v5.3.0/ts/src/v8-types.ts#L59-L61 +[SourceMapper]:https://github.com/DataDog/pprof-nodejs/blob/v5.3.0/ts/src/sourcemapper/sourcemapper.ts#L152 + ### Add profiling labels to Node.js applications -It is possible to add tags (labels) to the profiling data. These tags can be used to filter the data in the UI. Dynamic tagging isn't supported yet. +#### Static labels + +You can add static labels to the profiling data. +These labels can be used to filter the data in the UI and apply for all profiles collected. +Common static labels include: + +* `hostname` +* `region` +* `team` ```javascript Pyroscope.init({ @@ -65,6 +98,16 @@ Pyroscope.init({ Pyroscope.start() ``` +#### Dynamic labels for Wall/CPU profiles + +In Wall/CPU profiles, labels can also be attached dynamically and help to separate different code paths: + +```javascript +Pyroscope.wrapWithLabels({ vehicle: 'bike' }, () => + slowCode() +); +``` + ## Send data to Pyroscope OSS or Grafana Cloud ```javascript diff --git a/examples/language-sdk-instrumentation/nodejs/express-pull/Dockerfile b/examples/language-sdk-instrumentation/nodejs/express-pull/Dockerfile index 939be653ce..708be4d4c9 100644 --- a/examples/language-sdk-instrumentation/nodejs/express-pull/Dockerfile +++ b/examples/language-sdk-instrumentation/nodejs/express-pull/Dockerfile @@ -7,4 +7,5 @@ RUN yarn install COPY index.js . ENV DEBUG=pyroscope +ENV PYROSCOPE_WALL_COLLECT_CPU_TIME=true CMD ["node", "index.js"] diff --git a/examples/language-sdk-instrumentation/nodejs/express-pull/index.js b/examples/language-sdk-instrumentation/nodejs/express-pull/index.js index dd266d6b58..a73afae86e 100644 --- a/examples/language-sdk-instrumentation/nodejs/express-pull/index.js +++ b/examples/language-sdk-instrumentation/nodejs/express-pull/index.js @@ -31,13 +31,19 @@ Pyroscope.init({ tags: { region } }); app.use(expressMiddleware()); app.get('/bike', function bikeSearchHandler(req, res) { - return genericSearchHandler(0.5)(req, res); + Pyroscope.wrapWithLabels({ vehicle: 'bike' }, () => + genericSearchHandler(0.5)(req, res) + ); }); app.get('/car', function carSearchHandler(req, res) { - return genericSearchHandler(1)(req, res); + Pyroscope.wrapWithLabels({ vehicle: 'car' }, () => + genericSearchHandler(1)(req, res) + ); }); app.get('/scooter', function scooterSearchHandler(req, res) { - return genericSearchHandler(0.25)(req, res); + Pyroscope.wrapWithLabels({ vehicle: 'scooter' }, () => + genericSearchHandler(0.25)(req, res) + ); }); app.listen(port, () => { diff --git a/examples/language-sdk-instrumentation/nodejs/express-pull/package.json b/examples/language-sdk-instrumentation/nodejs/express-pull/package.json index 05ef9204e5..1fad9d924f 100644 --- a/examples/language-sdk-instrumentation/nodejs/express-pull/package.json +++ b/examples/language-sdk-instrumentation/nodejs/express-pull/package.json @@ -10,7 +10,7 @@ "author": "", "license": "Apache-2.0", "dependencies": { - "@pyroscope/nodejs": "v0.3.9", + "@pyroscope/nodejs": "v0.3.11", "express": "^4.19.2", "morgan": "^1.10.0" }, diff --git a/examples/language-sdk-instrumentation/nodejs/express-pull/yarn.lock b/examples/language-sdk-instrumentation/nodejs/express-pull/yarn.lock index 4e6744f105..0ef5de8d9f 100644 --- a/examples/language-sdk-instrumentation/nodejs/express-pull/yarn.lock +++ b/examples/language-sdk-instrumentation/nodejs/express-pull/yarn.lock @@ -66,10 +66,10 @@ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== -"@pyroscope/nodejs@v0.3.9": - version "0.3.9" - resolved "https://registry.yarnpkg.com/@pyroscope/nodejs/-/nodejs-0.3.9.tgz#4ca4499a8715a22b5d8847239fc91d1df2a90e8a" - integrity sha512-To0b003umbwd4RM4MYXFe1HnEraC+Zz9VDlHT2olqPL+9XATjNOx9HYK7ErC92gvGXqPxwhZwWC3bDqNwkSVhw== +"@pyroscope/nodejs@v0.3.11": + version "0.3.11" + resolved "https://registry.yarnpkg.com/@pyroscope/nodejs/-/nodejs-0.3.11.tgz#d3ffed8423b628701d06cdc6ef97fd5943d91939" + integrity sha512-xqxUDrzgdfTic4QU3FyvPvO3iAF63zEEI+gXgBBNA6MrJVOJxaEDJkeOGnH0AT7yG/vLJVmSeo4+VyIKrCmztw== dependencies: "@datadog/pprof" "^5.3.0" axios "^0.28.0" diff --git a/examples/language-sdk-instrumentation/nodejs/express-ts-inline/index.ts b/examples/language-sdk-instrumentation/nodejs/express-ts-inline/index.ts index 2e2d44e698..d85af51fb2 100644 --- a/examples/language-sdk-instrumentation/nodejs/express-ts-inline/index.ts +++ b/examples/language-sdk-instrumentation/nodejs/express-ts-inline/index.ts @@ -26,16 +26,22 @@ const genericSearchHandler = (p: number) => (req: any, res: any) => { }; app.get('/bike', function bikeSearchHandler(req, res) { - return genericSearchHandler(0.2)(req, res); + Pyroscope.wrapWithLabels({ vehicle: 'bike' }, () => + genericSearchHandler(0.2)(req, res) + ); }); app.get('/car', function carSearchHandler(req, res) { - return genericSearchHandler(1)(req, res); + Pyroscope.wrapWithLabels({ vehicle: 'car' }, () => + genericSearchHandler(1)(req, res) + ); }); app.get('/scooter', function scooterSearchHandler(req, res) { - return genericSearchHandler(0.5)(req, res); + Pyroscope.wrapWithLabels({ vehicle: 'scooter' }, () => + genericSearchHandler(0.5)(req, res) + ); }); -SourceMapper.create(["."]) +SourceMapper.create(['.']) .then((sourceMapper) => { Pyroscope.init({ appName: 'nodejs', @@ -46,8 +52,8 @@ SourceMapper.create(["."]) Pyroscope.start(); }) .catch((e) => { - console.error(e) - }) + console.error(e); + }); app.listen(port, () => { console.log( diff --git a/examples/language-sdk-instrumentation/nodejs/express-ts-inline/package.json b/examples/language-sdk-instrumentation/nodejs/express-ts-inline/package.json index 30ca869b66..723bf1301c 100644 --- a/examples/language-sdk-instrumentation/nodejs/express-ts-inline/package.json +++ b/examples/language-sdk-instrumentation/nodejs/express-ts-inline/package.json @@ -10,7 +10,7 @@ "author": "", "license": "Apache-2.0", "dependencies": { - "@pyroscope/nodejs": "0.3.10", + "@pyroscope/nodejs": "v0.3.11", "axios": "^0.26.1", "express": "^4.17.3", "morgan": "^1.10.0", diff --git a/examples/language-sdk-instrumentation/nodejs/express-ts-inline/yarn.lock b/examples/language-sdk-instrumentation/nodejs/express-ts-inline/yarn.lock index a3fb1c1639..64ffec1846 100644 --- a/examples/language-sdk-instrumentation/nodejs/express-ts-inline/yarn.lock +++ b/examples/language-sdk-instrumentation/nodejs/express-ts-inline/yarn.lock @@ -66,10 +66,10 @@ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== -"@pyroscope/nodejs@0.3.10": - version "0.3.10" - resolved "https://registry.yarnpkg.com/@pyroscope/nodejs/-/nodejs-0.3.10.tgz#84bf5ad2258c614c307b46ed0dd98277e32bd582" - integrity sha512-/GHbVp7i48HW08tnDiqp54wQquGrDokiiYQnUtWMcOtiDaSnmwgHlhuZIOS1/P8MatHqIhIChBGn1Bpi53xGlw== +"@pyroscope/nodejs@v0.3.11": + version "0.3.11" + resolved "https://registry.yarnpkg.com/@pyroscope/nodejs/-/nodejs-0.3.11.tgz#d3ffed8423b628701d06cdc6ef97fd5943d91939" + integrity sha512-xqxUDrzgdfTic4QU3FyvPvO3iAF63zEEI+gXgBBNA6MrJVOJxaEDJkeOGnH0AT7yG/vLJVmSeo4+VyIKrCmztw== dependencies: "@datadog/pprof" "^5.3.0" axios "^0.28.0" diff --git a/examples/language-sdk-instrumentation/nodejs/express-ts/index.ts b/examples/language-sdk-instrumentation/nodejs/express-ts/index.ts index 2275174ed6..0ac251d9d6 100644 --- a/examples/language-sdk-instrumentation/nodejs/express-ts/index.ts +++ b/examples/language-sdk-instrumentation/nodejs/express-ts/index.ts @@ -26,16 +26,22 @@ const genericSearchHandler = (p: number) => (req: any, res: any) => { }; app.get('/bike', function bikeSearchHandler(req, res) { - return genericSearchHandler(0.2)(req, res); + Pyroscope.wrapWithLabels({ vehicle: 'bike' }, () => + genericSearchHandler(0.2)(req, res) + ); }); app.get('/car', function carSearchHandler(req, res) { - return genericSearchHandler(1)(req, res); + Pyroscope.wrapWithLabels({ vehicle: 'car' }, () => + genericSearchHandler(1)(req, res) + ); }); app.get('/scooter', function scooterSearchHandler(req, res) { - return genericSearchHandler(0.5)(req, res); + Pyroscope.wrapWithLabels({ vehicle: 'scooter' }, () => + genericSearchHandler(0.5)(req, res) + ); }); -SourceMapper.create(["."]) +SourceMapper.create(['.']) .then((sourceMapper) => { Pyroscope.init({ appName: 'nodejs', @@ -46,8 +52,8 @@ SourceMapper.create(["."]) Pyroscope.start(); }) .catch((e) => { - console.error(e) - }) + console.error(e); + }); app.listen(port, () => { console.log( diff --git a/examples/language-sdk-instrumentation/nodejs/express-ts/package.json b/examples/language-sdk-instrumentation/nodejs/express-ts/package.json index e228bba1b8..2c7a384eb3 100644 --- a/examples/language-sdk-instrumentation/nodejs/express-ts/package.json +++ b/examples/language-sdk-instrumentation/nodejs/express-ts/package.json @@ -10,7 +10,7 @@ "author": "", "license": "Apache-2.0", "dependencies": { - "@pyroscope/nodejs": "0.3.9", + "@pyroscope/nodejs": "v0.3.11", "axios": "^0.28.0", "express": "^4.19.2", "morgan": "^1.10.0", diff --git a/examples/language-sdk-instrumentation/nodejs/express-ts/yarn.lock b/examples/language-sdk-instrumentation/nodejs/express-ts/yarn.lock index 90883be0bc..69b464e665 100644 --- a/examples/language-sdk-instrumentation/nodejs/express-ts/yarn.lock +++ b/examples/language-sdk-instrumentation/nodejs/express-ts/yarn.lock @@ -66,10 +66,10 @@ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== -"@pyroscope/nodejs@0.3.9": - version "0.3.9" - resolved "https://registry.yarnpkg.com/@pyroscope/nodejs/-/nodejs-0.3.9.tgz#4ca4499a8715a22b5d8847239fc91d1df2a90e8a" - integrity sha512-To0b003umbwd4RM4MYXFe1HnEraC+Zz9VDlHT2olqPL+9XATjNOx9HYK7ErC92gvGXqPxwhZwWC3bDqNwkSVhw== +"@pyroscope/nodejs@v0.3.11": + version "0.3.11" + resolved "https://registry.yarnpkg.com/@pyroscope/nodejs/-/nodejs-0.3.11.tgz#d3ffed8423b628701d06cdc6ef97fd5943d91939" + integrity sha512-xqxUDrzgdfTic4QU3FyvPvO3iAF63zEEI+gXgBBNA6MrJVOJxaEDJkeOGnH0AT7yG/vLJVmSeo4+VyIKrCmztw== dependencies: "@datadog/pprof" "^5.3.0" axios "^0.28.0" diff --git a/examples/language-sdk-instrumentation/nodejs/express/Dockerfile b/examples/language-sdk-instrumentation/nodejs/express/Dockerfile index 939be653ce..708be4d4c9 100644 --- a/examples/language-sdk-instrumentation/nodejs/express/Dockerfile +++ b/examples/language-sdk-instrumentation/nodejs/express/Dockerfile @@ -7,4 +7,5 @@ RUN yarn install COPY index.js . ENV DEBUG=pyroscope +ENV PYROSCOPE_WALL_COLLECT_CPU_TIME=true CMD ["node", "index.js"] diff --git a/examples/language-sdk-instrumentation/nodejs/express/index.js b/examples/language-sdk-instrumentation/nodejs/express/index.js index 70b37e5c58..ac29ee3df3 100644 --- a/examples/language-sdk-instrumentation/nodejs/express/index.js +++ b/examples/language-sdk-instrumentation/nodejs/express/index.js @@ -32,13 +32,19 @@ Pyroscope.init({ Pyroscope.start(); app.get('/bike', function bikeSearchHandler(req, res) { - return genericSearchHandler(0.5)(req, res); + Pyroscope.wrapWithLabels({ vehicle: 'bike' }, () => + genericSearchHandler(0.5)(req, res) + ); }); app.get('/car', function carSearchHandler(req, res) { - return genericSearchHandler(1)(req, res); + Pyroscope.wrapWithLabels({ vehicle: 'car' }, () => + genericSearchHandler(1)(req, res) + ); }); app.get('/scooter', function scooterSearchHandler(req, res) { - return genericSearchHandler(0.25)(req, res); + Pyroscope.wrapWithLabels({ vehicle: 'scooter' }, () => + genericSearchHandler(0.25)(req, res) + ); }); app.listen(port, () => { diff --git a/examples/language-sdk-instrumentation/nodejs/express/package.json b/examples/language-sdk-instrumentation/nodejs/express/package.json index 05661da6e6..d97ad3668a 100644 --- a/examples/language-sdk-instrumentation/nodejs/express/package.json +++ b/examples/language-sdk-instrumentation/nodejs/express/package.json @@ -10,7 +10,7 @@ "author": "", "license": "Apache-2.0", "dependencies": { - "@pyroscope/nodejs": "0.3.9", + "@pyroscope/nodejs": "v0.3.11", "express": "^4.19.2", "morgan": "^1.10.0" }, diff --git a/examples/language-sdk-instrumentation/nodejs/express/yarn.lock b/examples/language-sdk-instrumentation/nodejs/express/yarn.lock index a79f085c5c..0ef5de8d9f 100644 --- a/examples/language-sdk-instrumentation/nodejs/express/yarn.lock +++ b/examples/language-sdk-instrumentation/nodejs/express/yarn.lock @@ -66,10 +66,10 @@ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== -"@pyroscope/nodejs@0.3.9": - version "0.3.9" - resolved "https://registry.yarnpkg.com/@pyroscope/nodejs/-/nodejs-0.3.9.tgz#4ca4499a8715a22b5d8847239fc91d1df2a90e8a" - integrity sha512-To0b003umbwd4RM4MYXFe1HnEraC+Zz9VDlHT2olqPL+9XATjNOx9HYK7ErC92gvGXqPxwhZwWC3bDqNwkSVhw== +"@pyroscope/nodejs@v0.3.11": + version "0.3.11" + resolved "https://registry.yarnpkg.com/@pyroscope/nodejs/-/nodejs-0.3.11.tgz#d3ffed8423b628701d06cdc6ef97fd5943d91939" + integrity sha512-xqxUDrzgdfTic4QU3FyvPvO3iAF63zEEI+gXgBBNA6MrJVOJxaEDJkeOGnH0AT7yG/vLJVmSeo4+VyIKrCmztw== dependencies: "@datadog/pprof" "^5.3.0" axios "^0.28.0" diff --git a/tools/update_examples.go b/tools/update_examples.go index e38074c17a..2e7eb3a916 100644 --- a/tools/update_examples.go +++ b/tools/update_examples.go @@ -8,6 +8,7 @@ import ( "net/http" "os" "os/exec" + "path/filepath" "slices" "strconv" "strings" @@ -28,14 +29,46 @@ func main() { updateJava() updateRuby() updatePython() - udpateDotnet() + updateDotnet() + updateNodeJS() } func getGHToken() { ghToken, _ = s.sh("gh auth token") } -func udpateDotnet() { +func extractNodeJSVersion(tag Tag) *version { + re := regexp.MustCompile("(v)(\\d+).(\\d+).(\\d+)") + match := re.FindStringSubmatch(tag.Name) + if match != nil { + if match[1] == "v" { + major, err := strconv.Atoi(match[2]) + requireNoError(err, "strconv") + minor, err := strconv.Atoi(match[3]) + requireNoError(err, "strconv") + patch, err := strconv.Atoi(match[4]) + requireNoError(err, "strconv") + return &version{major: major, minor: minor, patch: patch, tag: tag} + } + } + return nil +} + +func updateNodeJS() { + tags := getTagsV("grafana/pyroscope-nodejs", extractNodeJSVersion) + last := tags[len(tags)-1] + fmt.Println(last) + + replPackageJson := fmt.Sprintf(` "@pyroscope/nodejs": "v%s",`, last.version()) + rePackageJson := regexp.MustCompile(` "@pyroscope/nodejs": "[^"]+",`) + for _, x := range []string{"express", "express-pull", "express-ts", "express-ts-inline"} { + path := filepath.Join("examples/language-sdk-instrumentation/nodejs", x) + replaceInplace(rePackageJson, filepath.Join(path, "package.json"), replPackageJson) + s.sh(fmt.Sprintf(`cd "%s" && yarn`, path)) + } +} + +func updateDotnet() { tags := getTagsV("grafana/pyroscope-dotnet", extractDotnetVersion()) last := tags[len(tags)-1] fmt.Println(last)