0.0.4 • Published 1 month ago

playwright-prometheus-remote-write-reporter v0.0.4

Weekly downloads
-
License
MIT
Repository
-
Last release
1 month ago

playwright-prometheus-remote-write-reporter

Get Started

NOTE: We need to configure prometheus as a precondition.

  1. Enable feature flag - remote write receiver
  2. in prometheus.yml file add next configuration:
...
remote_write:
    # local?
  - url: http://localhost:9090/api/v1/write
...

You may found complete example in example folder.

Installation

npm i playwright-prometheus-remote-write-reporter # npm
yarn add playwright-prometheus-remote-write-reporter # yarn
pnpm add playwright-prometheus-remote-write-reporter # pnpm
bun a playwright-prometheus-remote-write-reporter # bun

Configure reporter

In your playwright.config.ts add next lines:

export default defineConfig({
// ...
  reporter: [
    ['playwright-prometheus-remote-write-reporter', {
      // options object
    }]
  ],
// ...
})

Reporter Options

OptionDescriptionDefault
serverUrlRemote writer server URLhttp://localhost:9090/api/v1/write
headersCustom headers for prometheus. E.g. {header1: 'value1'}undefined
prefixCustom metric prefix namepw_
auth.usernameBasic auth. usernameundefined
auth.passwordBasic auth. passwordundefined

Collected metrics

Each metric name starts from prefix. By default it's pw_. So every metric name described without prefix.

Test(s)

this metrics below sends periodically and you may found when they sends

NameDescriptionWhen Sends (hook name)
configplaywright configuration objectonExit
projectplaywright project object. E.g. chromium, firefoxonExit
testtest objectonTestEnd
test_attachmenttest attachment informationonTestEnd
test_attachment_sizeattachment size in bytesonTestEnd
test_annnotationannotations for 1 testonTestEnd
test_errortest errors informationonTestEnd
test_durationtest duration in millisecondsonTestEnd
test_retry_countcount of retries for 1 testonTestEnd
tests_attachment_total_sizetotal attachment size in bytes for all testsonExit
tests_total_durationtime for all testsonExit
tests_total_counttotal count of all testsonExit
tests_skip_countcount of all skipped testsonExit
tests_pass_countcount of all passed testsonExit
tests_fail_countcount of all failed testsonExit
tests_attachment_total_countcount of attachments across all testsonExit
error_countcount of errorsonError
stdoutstdout for test. Reporter logs have label: internal="true"onStdOut
stderrstdout for test. Reporter logs have label: internal="true"onStdErr

Node.js internals

This metrics collects every reporter lifecycle.

Playwright Reporter API hooks

NameDescriptionValue
node_envenvironment variables 1.process.env
node_argvcommand-line arguments passed when the Node.js process was launched (playwright) 3process.argv
node_versionsversion strings of Node.js and its dependencies 4process.versions
node_osinformation about current operation system 5os
node_cpu_systemcpu system utilization 6process.cpuUsage
node_cpu_usercpu user utilization 6process.cpuUsage
node_memory_externalmemory usage of the Node.js process measured in bytes 7process.memotyUsage
node_memory_array_buffersmemory usage of the Node.js process measured in bytes 7process.memotyUsage
node_memory_heap_usedmemory usage of the Node.js process measured in bytes 7process.memotyUsage
node_memory_rssmemory usage of the Node.js process measured in bytes 7process.memotyUsage
node_memory_heap_totalmemory usage of the Node.js process measured in bytes 7process.memotyUsage

1: Do not use "process.env.name" variable since it can overwrite your "node_env" metric.

2: docs: https://nodejs.org/docs/latest/api/process.html#processenv

3: docs: https://nodejs.org/docs/latest/api/process.html#processargv

4: docs: https://nodejs.org/docs/latest/api/process.html#processversions

5: docs: https://nodejs.org/docs/latest/api/os.html#osarch

6: docs: https://nodejs.org/docs/latest/api/process.html#processcpuusagepreviousvalue

7: docs: https://nodejs.org/docs/latest/api/process.html#processmemoryusage

Using custom metrics

You can define own metrics following next code:

import { test } from '@playwright/test'
import { Counter, Gauge } from 'playwright-prometheus-remote-write-reporter'

const countOfUrlCalls = new Counter({
  // only name is requrired
  name: 'url_open' // will automatically appends prefix
}, 0) // starts from 0

test('simple counter test', async ({ page }) => {
  await page.goto('https://example.com')
  countOfUrlCalls.inc()

  // ... rest test
})

test.afterAll(() => {
  countOfUrlCalls.collect() // sends metrics to prometheus
})

Counter

Counters go up, and reset when the process restarts.

Counter API

  • constructor(labels, initialValue)
    • labels: Record<string, string> - collected metrics. only name field is required.
    • initialValue: number - default is 0. If metric is constant we recommend to set to 1
  • inc([value]) increments your counter.
    • value: number | undefined - count of increasing
  • collect() - Send metrics to prometheus
  • labels(label) - append extra labels
    • label: Record<string, string>. Do not overwrite name property.

Gauge

Gauges are similar to Counters but a Gauge's value can be decreased.

Gauge API

Same for Counter

  • set(value) - set gauge value
    • value: number
  • zero() - same as set(0)
  • dec([value]) - decrement gauge value
    • value: number | undefined

Best practice

We Highly recommend use one of 2 practiceies for send metrics to prometheus via collect method:

  • on afterEach/afterAll hook
  • on base hook

example:

import { test as base } from '@playwright/test'
import { Counter, Gauge } from 'playwright-prometheus-remote-write-reporter'

type Context = {
  urlCalls: Counter
}

const test = base.extend<Context>({
  urlCalls: async ({}, use) => {
    const counter = new Counter({name: 'url_calls'})
    await use(counter)
    // automatically sends metrics
    counter.collect()
  }
})

test('extended test', ({urlCalls}) => {
  // ...
  urlCalls.inc()
})

// or
const anotherCounter = new Counter({name: 'custom_counter'})
base('some test', () => {
  anotherCounter.inc()
})
base.afterAll(() => {
  anotherCounter.colect()
})