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

level-stats

v1.2.1

Published

A stats datastore built on top of Level.

Downloads

3

Readme

Level Stats

A stats datastore built on top of LevelUp. Contains (so far):

  • gauges
  • counters

Synopsis

Creating a gauge or counter is pretty simple. You just need an open Level instance, plus the string you want to name it.

const level = require('level')
const { Gauge, Counter } = require('level-stats')

// open DB
const db = level(path.join(__dirname, ".data", "test"))

Then, to create a Gauge which measures the temperature in your kitchen:

const kitchenTemp = Gauge(db, 'home.kitchen')

// set the current temperature whenever you read it
kitchenTemp.set(19.7)

// at some stage later
kitchenTemp.set(21.3)

// and later still
kitchenTemp.set(20.9)

Or to create a Counter which counts the number of cars going past your house:

const cars = Counter(db, 'chilts.tea')

// add one car
cars.add(1)

// you may send a larger number
cars.add(3)

Types

Gauges

Gauges are numbers that can (somtimes randomly) go up or down, such as temperature, amount of bandwidth used per hour, or your current speed.

Synopsis

Here's a small program to get you started:

// create a gauge - pass db and name (used for the sublevel prefix)
const kitchen = Gauge(db, 'home.kitchen')

// set a new value
kitchen.set(19.6)
kitchen.set(21.4)
// gives the aggregate { count: 2, min: 19.6, max: 21.4, val: 20.5 }

// aggregate hourly and daily
kitchen.aggregate(ms('1 hour'))
kitchen.aggregate(ms('1 day'))

// stream all the hourly aggregates
kitchen.createAggregateStream(ms('1 hour'))
  .on('data', console.log)
  .on('err', console.warn)

Gauge Constructor

Just pass the sublevel DB where to store the gauge measurements and a name (which is also used as the new sublevel prefix):

// these are functionally equivalent
const kitchenTemp = Gauge(db, 'kitchen')
const kitchenTemp = new Gauge(db, 'kitchen')

gauge.set(val, callback)

Adds a new measurement to this gauge. val must be a number. The callback just tells you when it's been added to LevelDB (or gives you an error).

gauge.val(callback)

Gets you the latest value of this gauge. It doesn't do any manipulation or aggregation, it just returns the last value it saw.

gauge.aggregate(periodLengthMs, callback)

Will aggregate one time period from the set of measurements/values and stores them in the DB for later retrieval. This function will:

  • do nothing if no values (measurements) are available
  • aggregates the first time period if no other aggregates have been performed
  • aggregates the next complete time period if at least one other aggregate has been performed
  • do nothing if the next period is also the current (incomplete) period

You are allowed to aggregate over multiple periodLengthMs as you see fit, such that you may want to aggregate hourly as well as daily. Or every 5 mins, 10 mins, and every 15 mins.

The periodLengthMs is the amount of time to aggregate over, in milliseconds. E.g. using ms or doing it yourself:

  • ms('5 mins') or 5 * 60 * 1000
  • ms('1 hour') or 60 * 60 * 1000
  • ms('6 hours') or 6 * 60 * 60 * 1000
  • ms('1 day') or 24 * 60 * 60 * 1000
  • Note: there is nothing stopping you aggregating over 314159ms, but that's your choice!

You should call this function regularly, and we'd suggest every 1/2 or 1/3 of the periodLengthMs, so that you keep on top of it. We let you do this when you want, rather than us, so you can call it whenever is convenient or makes more sense for your use-case.

e.g.

const periodMs = ms('1 hour')
setInterval(() => {
  kitchenTemp.aggregate(periodMs, err => {
    // check err!
    console.log('Done')
  })
}, periodMs/2)

gauge.createAggregateStream(periodLengthMs, opts)

Returns a LevelDB stream (an EventEmitter) but we'll add extra fields onto the data event if requested in opts. Emits all items in the aggregate defined by periodLengthMs.

If you ask for a periodLengthMs that hasn't been aggregated (using .aggregate(), you won't get any data since none is available yet.

Just like the LevelDB stream, you can pass these opts:

  • lt, lte, gt, gte
  • reverse
  • limit

The returned items look like:

item: {
  key: '1587441600000',
  value: {
    count: 6,
    min: 12.36,
    max: 21.33,
    avg: 17.131666666666668
  }
}

You can also pass these boolean options which will simplify or embellish the returned item:

  • keys (default: true)
  • date (default: false)
  • epoch (default: false)
  • iso (default: false)

Passing values: false (as is allowed in LevelDB) doesn't make sense here, so we delete it and therefore ignore it.

This embellishes each item with the corresponding field, such as:

item: {
  key: '1587441600000',
  value: {
    count: 6,
    min: 12.36,
    max: 21.33,
    avg: 17.131666666666668
  },
  date: 2020-04-21T04:00:00.000Z,
  epoch: 1587441600000,
  iso: '2020-04-21T04:00:00.000Z'
}

These options just mean you don't need to do any grunt work to turn them into dates, epochs, or ISO dates.

Counters

// create a counter - pass db and name (used for the sublevel prefix)
const cars = Counter(db, 'cars')

// add some onto the count
cars.add(1)
cars.add(3)
// gives the aggregate { count: 2, total: 4}

Counters are almost the same a gauges except counters:

  • don't aggregate min/max/avg
  • only provide:
    • count - the number of measurements added
    • total - the sum of all measurements

Also, counters use .add() instead of .set().

Other things such as .aggregate() and .createAggregateStream() are the same.

Future

These types will be added in the future.

  • timers (?)
  • sets (?)

Also, sets can be used as small sets (exact) or large sets (approximate). Large sets are implemented using HyperLogLogs

ChangeLog

  • v1.2.1
    • (FIX) Minor fix to ReadMe formatting
  • v1.2.0
    • (NEW) Counter
  • v1.1.0
    • Some refactoring
  • v1.0.0
    • (NEW) Gauge

Inspiration

Author

$ npx chilts

   ╒════════════════════════════════════════════════════╕
   │                                                    │
   │   Andrew Chilton (Personal)                        │
   │   -------------------------                        │
   │                                                    │
   │          Email : [email protected]             │
   │            Web : https://chilts.org                │
   │        Twitter : https://twitter.com/andychilton   │
   │         GitHub : https://github.com/chilts         │
   │         GitLab : https://gitlab.org/chilts         │
   │                                                    │
   │   Apps Attic Ltd (My Company)                      │
   │   ---------------------------                      │
   │                                                    │
   │          Email : [email protected]              │
   │            Web : https://appsattic.com             │
   │        Twitter : https://twitter.com/AppsAttic     │
   │         GitLab : https://gitlab.com/appsattic      │
   │                                                    │
   │   Node.js / npm                                    │
   │   -------------                                    │
   │                                                    │
   │        Profile : https://www.npmjs.com/~chilts     │
   │           Card : $ npx chilts                      │
   │                                                    │
   ╘════════════════════════════════════════════════════╛

License

MIT.

(Ends)