npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@shopify/koa-performance

v3.7.0

Published

Creating middleware that sends performance-related data through StatsD

Downloads

3,533

Readme

@shopify/koa-performance

Build Status Build Status License: MIT npm version npm bundle size (minified + gzip)

Middleware which makes it easy to send metrics from your front-end application and forward them to a StatsD server of your choice.

Best used with @shopify/performance and/or @shopify/react-performance.

Table of Contents

Quick-start

Install the package

First we will need to install the npm package.

yarn add @shopify/koa-performance

Add the middleware

Next we import the clientPerformanceMetrics factory into our server, use it to create a middleware to collect performance data, and mount it. We use something koa-mount to restrict it to a specific endpoint. If your application uses koa-router, you can use that instead.

// server.ts
import Koa from 'koa';
import mount from 'koa-mount';
import {StatsDClient} from '@shopify/statsd';
import {clientPerformanceMetrics} from '@shopify/koa-performance';

// create our Koa instance for the server
const app = new Koa();

const statsd = new StatsDClient({
  prefix: 'ExampleCode.',
  host: 'YOUR STATSD HOST HERE',
  port: 3000,
});

app.use(mount('/client-metrics', clientPerformanceMetrics({statsd})));

// other middleware for your app
// ...

app.listen(3000, () => {
  console.log('listening on port 3000');
});

Now the app will respond to requests to /client-metrics. The middleware returned from clientPerformanceMetrics expects to receive JSON POST requests meeting the following interface:

interface Metrics {
  // the path the app was responding to when metrics were collected
  pathname: string;
  // data from `navigator.connection`
  connection: Partial<BrowserConnection>;
  // @shopify/performance lifecycle events
  events: LifecycleEvent[];
  // the user's locale (e.g., `en-CA`)
  locale?: string;
  // @shopify/performance navigation data
  navigations: {
    details: NavigationDefinition;
    metadata: NavigationMetadata;
  }[];
}

You can also create the StatsDClient directly in the middleware but the preferred solution is to pass an instance of your StatsDClient so you can reuse the same instance across your application.

app.use(
  mount(
    '/client-metrics',
    clientPerformanceMetrics({
      // the prefix for metrics sent to StatsD
      prefix: 'ExampleCode.',
      // the host of the statsd server you want to send metrics to
      statsdHost: 'YOUR STATSD HOST HERE'
      // the port of the statsd server you want to send metrics to
      statsdPort: 3000,
    })
  )
);

Verify with CURL

To confirm the endpoint is working we can make a CURL request. Run your server and paste this in your terminal.

curl 'http://localhost:3000/client-metrics' -H 'Content-Type: application/json' --data-binary '{"connection":{"onchange":null,"effectiveType":"4g","rtt":100,"downlink":1.75,"saveData":false},"events":[{"type":"ttfb","start":5631.300000008196,"duration":0},{"type":"ttfp","start":5895.370000012917,"duration":0},{"type":"ttfcp","start":5895.370000012917,"duration":0},{"type":"dcl","start":9874.819999997271,"duration":0},{"type":"load","start":10426.089999993565,"duration":0}],"navigations":[],"pathname":"/some-path"}' --compressed

You should get a 200 response back, and see console logs about metrics being skipped (since we are in development).

Send data from the frontend

We have verified that our middleware is setup correctly and ready to recieve reports. However, it is only useful if we send it real data from a our frontend code.

React applications can use components from @shopify/react-performance to collect and send metrics to the server in the right format. Check out @shopify/react-performance's README for details.

Non-React applications must use @shopify/performance directly and setup their own performance reports with it's API.

API

clientPerformanceMetrics

A middleware factory which returns a Koa middleware for parsing and sanitizing performance reports sent as JSON, and sending them to a StatsD server.

It takes options conforming to the following interface:

interface Options {
  // the StatsD Client instance
  statsd?: StatsDClient;
  // the prefix for metrics sent to StatsD
  prefix?: string;
  // whether the app is being run in development mode.
  development?: boolean;
  // the host of the statsd server you want to send metrics to
  statsdHost?: string;
  // the port of the statsd server you want to send metrics to
  statsdPort?: number;
  // threshold in milliseconds to skip metrics
  anomalousNavigationDurationThreshold?: number;
  // instance to use to log metrics
  logger?: Logger;
  // a function to use to customize the tags to be sent with all metrics
  additionalTags?(
    metricsBody: Metrics,
    userAgent: string,
  ): {[key: string]: string | number | boolean};
  // a function to use to customize the tags to be sent with navigation metrics
  additionalNavigationTags?(navigation: Navigation): {
    [key: string]: string | number | boolean;
  };
  // a function to use to send extra metrics for each navigation
  additionalNavigationMetrics?(
    navigation: Navigation,
  ): {name: string; value: any}[];
}

Examples

Basic

The simplest use of the middleware factory passes only the connection information for an application's StatsD server, and the prefix.

const statsd = new StatsDClient({
  prefix: 'ExampleCode.',
  host: process.env.STATSD_HOST,
  port: process.env.STATSD_PORT,
});
const middleware = clientPerformanceMetrics({statsd});

Extra tags

Often, applications will want to categorize distribution data using custom tags. The additionalTags and additionalNavigationTags allow custom tags to be derived from the data sent to the middleware. The tags will then be attached to outgoing StatsD distribution calls.

const statsd = new StatsDClient({
  prefix: 'ExampleCode.',
  host: process.env.STATSD_HOST,
  port: process.env.STATSD_PORT,
});
const middleware = clientPerformanceMetrics({
  statsd,
  additionalNavigationTags: (navigation) => ({
    navigationTarget: navigation.target,
  }),
  additionalTags: (metricsBody) => ({rtt: metricsBody.connection.rtt}),
});

Extra metrics

Applications also commonly need to send custom distribution data. The additionalNavigationMetrics option allow custom metrics to be derived from the data sent to the middleware. These will then be sent using StatsD's distribution method.

const statsd = new StatsDClient({
  prefix: 'ExampleCode.',
  host: process.env.STATSD_HOST,
  port: process.env.STATSD_PORT,
});
const middleware = clientPerformanceMetrics({
  statsd,
  additionalNavigationMetrics: ({events}) => {
    const weight = navigation.events
      .filter((event) => event.size != null)
      .reduce((total, event) => {
        total + event.size;
      }, 0);

    return [
      {
        name: 'navigationTotalResourceWeight',
        value: weight,
      },
    ];
  },
});