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

ut-port

v6.45.2

Published

UT port base

Downloads

62

Readme

UT Port

Base class to be usually extended when creating new ports.

Scope

  • Define base API for all ports.
  • Define facade API for managing multiple ports.
  • Initialize logging
  • Initialize metrics

API

This exports 2 APIs:

{
    Port,
    ports
}

Port public API

Port({bus, logFactory, config}) - create and return a port with the API below

  • bus - bus to be used by the port, saved in this.bus
  • logFactory - factory for creating loggers
  • config - port configuration to be merged with the port default in this.config

Port.prototype.init() - initializes internal variables of the port

Port.prototype.start() - starts the port, so that it can process messages. Usually here ports start to listen or initiate network / filesystem I/O

  • result - return promise so that start method of the next ports can be called after the returned promise resolves

Port.prototype.ready() - this is called after all ports are started

  • result - return promise so that ready method of the next ports can be called after the returned promise resolves. Usually if promise is returned, it will be resolved when the I/O operation initiated by the start method has finished.

Port.prototype.stop() - stops further processing of messages, closes connections / stops listening

Port.prototype.disconnect(reason) - throws disconnect error

  • reason - the reason for the disconnect error. This reason is logged in the error log and set as cause in the thrown error.

Port.prototype.pull(what, context) - creates pull streams for the port

  • what - can be one of the following:

    • falsy - the method will return an object with method push that can be used to add messages received from outside
    • net stream - use the stream to send and receive messages from outside
    • promise returning method - will execute the method. The method can communicate with external systems or just execute locally
  • context - context with which the pull streams are associated

Ports public API

ports({bus, logFactory}) - create and return a ports object.

Returns the API below, which is accessible through the bus in the ports namespace

  • bus - bus to use when creating ports
  • logFactory - logger factory to use when creating ports

ports.get({port}) - get port by id

  • port - id of the port

ports.fetch() - returns array with all the created ports

ports.create(portsConfig, envConfig) - create a new port

  • portsConfig - port configuration coming from the implementation. Can be array or single value of the following:

    • function - will call the function with envConfig as parameter, the function should return object to be used as configuration
    • object - will be used as configuration

    When portConfig is array, multiple ports are created, with configurations taken from the array.

  • envConfig - port configuration coming from the environment.

    If envConfig contains a property named port, then the value of this property will be merged to the port configuration.

    If envConfig contains a property matching the port id, then the value of this property will also be merged to the port configuration but with a higher priority than the port property.

ports.start() - start all ports

ports.stop({port}) - stop port

  • port - id of the port to stop

ports.move({port, x, y}) - set UI coordinates and returns the port

  • port - id of the port to set coordinates

  • x, y - coordinates to set

Port telemetry

Port telemetry includes two sets of data, which ports collect. These are generally split in non numeric and numeric data, also know as logs and metrics. When reporting these, the port uses a standard list of tags / fields to annotate the date. Their names are:

  • impl - name of the implementation or application
  • location - name of datacenter, cluster, etc. where the microservice runs
  • hostname - name of host where the microservice runs
  • pid - process id of the microservice
  • env - name of environment, usually dev, test, uat, prod, etc.
  • service - name of microservice
  • context - type of port, usually sql port, http port, httpserver port, etc.
  • name - id of port

Port logging

Each port initializes its own logger instance, so that logging level can be set per each port. By default, all ports will use info level for logging. The default can be changed using the configuration key utPort.logLevel:

{
  "utPort": {
    "logLevel": "debug"
  }
}

In addition to the standard tags, logging defines the following additional ones:

  • msg - includes information in text form, which can be used for indexing and searching
  • level - describes the logging level and can be one of:
    • 60 - fatal - application has reached an unrecoverable condition
    • 50 - error - expected error, the application will continue to process further requests
    • 40 - warn - application is processing unexpected data and will process it in an inefficient way
    • 30 - info - standard level of logging, that is of daily use of an application operator
    • 20 - debug - more verbose level of logging, that includes additional details, useful for application developers, may include data on API calls, parameters and results
    • 10 - trace - most detailed level, which may include low level details like network traces
  • @meta - API metadata, which includes the fields:
    • @meta.method - method name
    • @meta.opcode - optional operation code that defines the method variant to execute
    • @meta.mtid - describes the type of message and can be one of:
      • event - the application is processing an internal event
      • request - the application is processing an API request call
      • error - the application is processing an API response of type error
      • notification - the application is processing an API notification call
      • convert - the application is converting data from one format to another
    • @meta.trace
    • @meta.conId
  • error - when logging errors, this contains additional information for the error, represented in the following fields: error.type - string, representing the type of error error.method - the name of the method, which resulted with error error.stack - the call stack at the time of the error error.cause - any previous errors, that caused this error

Port metrics

Each port configures a default set of metrics, named as follows:

  • time_#, time_#_min, time_#_max - average, minimum and maximum stage execution time in milliseconds. Depending on execution stage, # can be one of:
    • q - Time spent in the incoming queue
    • r - Time spent in the receive hook
    • e - Time spent in the encode hook
    • x - Time spent in the execute hook
    • d - Time spent in the decode hook
    • s - Time spent in the send hook
    • w - Time spent in dispatch stage
  • time_rate - count of completed executions per second
  • time_count - count of completed executions
  • count_a#, count_a#_min, count_a#_max - current, minimum and maximum concurrent executions per stage. # can be one of:
    • r - Count of concurrently executing receive hooks
    • e - Count of concurrently executing encode hooks
    • x - Count of concurrently executing execute hooks
    • d - Count of concurrently executing decode hooks
    • s - Count of concurrently executing send hooks
    • w - Count of concurrently executing dispatches
  • ms - Count of sent messages per second
  • mr - Count of received messages per second
  • bs - Count of sent bytes per second
  • br - Count of received bytes per second

In addition to the standard tags, metrics define the following additional ones:

  • m - Method name. A special method name * is used to aggregate metrics for all methods.

Telemetry tools

Various open source tools are available to store, index and visualize telemetry data.

Elasticsearch

To index logs in an optimal way, an index can be created in elasticsearch using the following request elastic-index-ut-http

Grafana

To visualize metrics, the file grafana-ut-metrics.json can be imported in Grafana as a dashboard. It requires that metrics are stored in Prometheus and a datasource pointed to it exists in Grafana.

Automatic method import bindings

When defining modules, one can define a list of all bus methods that are going to be used withing the code as part of the factory function parameters definition.

Example:

// module wrapper
module.exports = () => function utModule() {
    return {
        orchestrator: () => [
            require('./api/script')
        ]
    }
};

// script
module.exports = function script({
    import: {
        userIdentityLookup
        db$userIdentityLookup
    }
}) {
    return {
        test() {
            await userIdentityLookup(params);
            // is identical to this.bus.importMethod('user.identity.lookup')(params)
            await db$userIdentityLookup(params);
            // is identical to this.bus.importMethod('db/user.identity.lookup')(params)
        }
    };
};

// additionally, the import options can be defined
// per method in the js/json configuration as follows
{
  utModule: {
      orchestrator: true,
      script: {
          import: {
              userIdentityLookup: {
                  cache: {
                      ttl: 6 * 60 * 60 * 1000
                  }
              }
          }
      }
  }
}

Also annotation-like syntax can be used to define multiple configurations for one and the same method

Example:

// module wrapper
module.exports = () => function utModule() {
    return {
        orchestrator: () => [
            require('./api/script')
        ]
    }
};

// script
module.exports = function script({
    import: {
        '@shortCache namespace.entity.action': alias
    }
}) {
    return {
        test() {
            // alias(params) is identical
            // to this.bus.importMethod('namespace.entity.action')(params)
        }
    };
};

// in the js/json configuration multiple options sets
// can be defined for the method above
// and they will be merged recursively
// in the specified order
// in this case: shortCache <- namespace.entity.action
{
  utModule: {
      orchestrator: true,
      script: {
          import: {
              shortCache: {
                  cache: {
                      ttl: 60 * 1000
                  }
              },
              'namespace.entity.action': {
                  cache: {
                      before: 'get',
                      after: 'set',
                      key: ({key}) => ({
                          id: key,
                          segment: 'namespace.entity.action'
                      })
                  }
              }
          }
      }
  }
}