Skip to main content
Version: 6.0.0

N|Solid JS API

The N|Solid Agent also exposes a JavaScript API for programmatic use or extension of its behavior.

These extensions allow the Observability features of the N|Solid Runtime's Agent to be triggered manually or enhanced with additional information. None of this is required for the default operation of N|Solid, which does not require any changes to your JS applications to start using.

The nsolid agent is the same singleton reference to the N|Solid agent when used anywhere. Simply use const nsolid = require('nsolid') to retrieve the nsolid agent.


nsolid.start()

Manually start and configure the N|Solid Agent. For example, if you have a configuration management platform or database that knows the hostname and port to your Console, you may obtain it and then invoke nsolid.start() to connect and begin reporting metrics. The agent can be started at any time. The keys are the same as the package.json configuration keys with the following difference:

  • version is specified as appVersion

Example:

nsolid.start({
command: 'nsolid-command-host.local:9001',
tags: ['experiment-4a', 'Auth service'],
app: 'nsolid-awesome',
appVersion: '1.0.0'
})

nsolid.appName / nsolid.app

Get the N|Solid app name. These are aliases.

nsolid.appName
'nsolid-awesome'
nsolid.app
'nsolid-asesome'

nsolid.appVersion

Get the application version string if defined in the package.json file.

nsolid.appVersion
'1.0.0'

nsolid.config

Get the N|Solid application config as loaded by the agent. Note that this can be changed at runtime via restarting the agent with new configuration or with a configuration change sent from a connected N|Solid Console Server.

nsolid.config
{
app: 'docs',
appVersion: '1.0.0',
blockedLoopThreshold: 200,
disablePackageScan: false,
env: 'prod',
hostname: 'anoxia',
iisNode: false,
interval: 3000,
pauseMetrics: false,
promiseTracking: false,
pubkey: ' ... ',
statsdBucket: 'nsolid.${env}.${app}.${hostname}.${shortId}',
tags: [ 'beta-service', 'experiment-4a' ],
tracingEnabled: false,
tracingModulesBlacklist: 0,
trackGlobalPackages: false
}

nsolid.getThreadName()

Get an N|Solid worker thread name. This allows labelling of worker threads inside of the N|Solid Pro UI for easier identification. All metrics payloads will include this name if set.

const { Worker, isMainThread } = require('worker_threads');
const { getThreadName, setThreadName } = require('nsolid')

if (!isMainThread) {
console.log(getThreadName()) // ''
setThreadName('worker-parser')
console.log(getThreadName()) // worker-parser
return setTimeout(() => {}, 1000)
}
const worker = new Worker(__filename)

nsolid.heapProfile(duration, trackAllocations)

Begins the collection of a Heap Profile. Intended to be run in the thread you want to profile. Accepts duration in milliseconds and a boolean flag trackAllocations to enable allocation tracking. Upon completion, it will be sent to the N|Solid Console Server. Read more here.

nsolid.heapProfile(120, true)

nsolid.heapProfileEnd(callback)

Allows the premature halt of an ongoing Heap Profile in this thread. Callback will return an error if there was no active Heap Profile to be stopped. This is the same as shortening the duration and the resulting Heap Profile will still be sent to the N|Solid Console Server.

nsolid.heapProfileEnd((err) => {
if (err) {
console.error('no Heap Profile to stop')
}
})

nsolid.heapProfileStream(threadId, duration, trackAllocations)

Allows the collection of a Heap Profile from any thread into your local application. This can be used to save Heap Profiles locally without sending them to the N|Solid Console Server. Accepts threadId which is the integer thread id of the worker thread to Profile. The default main thread is thread 0 and must be specified in order to Profile even if there are no other worker threads. Also accepts duration in milliseconds and a trackAllocations boolean. See more about Heap Profiling here

let profile = ''
const profileStream = nsolid.heapProfileStream(0, 20000, true)
profileStream.on('data', (data) => {
profile += data
})
profileStream.on('end', () => {
// handle the JSON contents now in ${profile}
})

nsolid.heapSampling(duration, settings)

Starts the collection of a Sampling Heap Profile on the current thread and sends it to the N|Solid Console Server. This a lighter-weight Heap Profile that provides higher-level statistics without as much impact on the application itself. It accepts a duration in milliseconds (default of 600000 or 10 minutes) and an options object to define how to perform the Sampling.

options structure:

  • sampleInterval (default 512 * 1024): The Heap is sampled every sampleInterval bytes. Use a smaller number for higher resolution but more overhead.
  • stackDepth (default 16): The maximum stap depth to capture when tracking allocations.
  • flags (default 0): The V8 flags that specify additional collection options. These Flags define the following options that are not exclusive of each other:
    • Force Garbage Collection
    • Include Objects collected during a Major GC event
    • Include Objects collected during a Minor GC event

These flags are bitflags, here is one way to use them:

const SamplingFlags = {
noFlags: 0,
forceGC: 1 << 0,
includeObjectsCollectedByMajorGC: 1 << 1,
includeObjectsCollectedByMinorGC: 1 << 2,
}

let myflag = SamplingFlags.forceGC | SamplingFlags.includeObjectsCollectedByMajorGC

nsolid.heapSampling(20000, {flags: myFlag})


nsolid.heapSamplingEnd(cb)

Allows the premature halt of an ongoing Heap Sampling in this thread. Callback will return an error if there was no active Heap Sampling to be stopped. This is the same as shortening the duration and the resulting Heap Sampling will still be sent to the N|Solid Console Server.

nsolid.heapSamplingEnd((err) => {
if (err) {
console.error('no Heap Sample to stop')
}
})

nsolid.heapSamplingStream(threadId, duration, settings)

Allows the collection of a Heap Sampling Profile from any thread into your local application. This can be used to save Heap Samples locally without sending them to the N|Solid Console Server. Accepts threadId which is the integer thread id of the worker thread to Profile. The default main thread is thread 0 and must be specified in order to Sample even if there are no other worker threads. Also accepts duration in milliseconds and a trackAllocations boolean. See more about Heap Sampling here

let profile = ''
const profileStream = nsolid.heapSamplingStream(0, 20000, {})
profileStream.on('data', (data) => {
profile += data
})
profileStream.on('end', () => {
// handle the JSON contents now in ${profile}
})

nsolid.id

Get the N|Solid agent id. This is treated as a UUID for this process inside of N|Solid systems.

nsolid.id
'9964df10bd45350092a8064727ea4c009bee1d91'

nsolid.info()

Returns identifying and static characteristic information about the process, host machine, and platform.

nsolid.info()
{
app: 'docs',
appVersion: '1.0.0',
arch: 'x64',
cpuCores: 12,
cpuModel: 'AMD Ryzen 5 2600 Six-Core Processor',
execPath: '/usr/bin/nsolid',
hostname: 'anoxia',
id: '797d1fa9fafd62002af42f919ab79900a455150d',
main: '',
nodeEnv: 'prod',
pid: 422446,
platform: 'linux',
processStart: 1716511183845,
tags: [ 'beta-service', 'experiment-4a' ],
totalMem: 8295538688,
versions: {
acorn: '8.11.3',
ada: '2.7.8',
ares: '1.28.1',
base64: '0.5.2',
brotli: '1.1.0',
cjs_module_lexer: '1.2.2',
cldr: '45.0',
icu: '75.1',
llhttp: '8.1.2',
modules: '115',
napi: '9',
nghttp2: '1.61.0',
nghttp3: '0.7.0',
ngtcp2: '1.1.0',
node: '20.13.1',
nsolid: '5.2.1',
openssl: '3.0.13+quic',
simdutf: '5.2.4',
tz: '2024a',
undici: '6.13.0',
unicode: '15.1',
uv: '1.46.0',
uvwasi: '0.0.20',
v8: '11.3.244.8-node.20',
zlib: '1.3.0.1-motley-7d77fb7'
}
}

If necessary, nsolid.info() can be called asynchronously.

nsolid.info((err, info) => {
// ...
})

nsolid.metrics()

Returns the latest recorded metrics values. Note that this format is specific to a single thread and is not the full set of metrics sent to connected endpoints.

nsolid.metrics()
{
threadName: '',
threadId: 0,
timestamp: 1716511535286,
activeHandles: 1,
activeRequests: 0,
heapTotal: 6000640,
totalHeapSizeExecutable: 524288,
totalPhysicalSize: 5976064,
totalAvailableSize: 2193381512,
heapUsed: 5351112,
heapSizeLimit: 2197815296,
mallocedMemory: 147552,
externalMem: 2256629,
peakMallocedMemory: 521312,
numberOfNativeContexts: 2,
numberOfDetachedContexts: 0,
gcCount: 7,
gcForcedCount: 0,
gcFullCount: 0,
gcMajorCount: 4,
dnsCount: 0,
httpClientAbortCount: 0,
httpClientCount: 0,
httpServerAbortCount: 0,
httpServerCount: 0,
loopIdleTime: 351289699466,
loopIterations: 222,
loopIterWithEvents: 160,
eventsProcessed: 215,
eventsWaiting: 160,
providerDelay: 64084249,
processingDelay: 0,
loopTotalCount: 222,
pipeServerCreatedCount: 0,
pipeServerDestroyedCount: 0,
pipeSocketCreatedCount: 0,
pipeSocketDestroyedCount: 0,
tcpServerCreatedCount: 0,
tcpServerDestroyedCount: 0,
tcpSocketCreatedCount: 0,
tcpSocketDestroyedCount: 0,
udpSocketCreatedCount: 0,
udpSocketDestroyedCount: 0,
promiseCreatedCount: 0,
promiseResolvedCount: 0,
fsHandlesOpenedCount: 3,
fsHandlesClosedCount: 2,
gcDurUs99Ptile: 0,
gcDurUsMedian: 0,
dns99Ptile: 0,
dnsMedian: 0,
httpClient99Ptile: 0,
httpClientMedian: 0,
httpServer99Ptile: 0,
httpServerMedian: 0,
loopUtilization: 0.000395,
res5s: 0.001525,
res1m: 0.000173,
res5m: 0.000096,
res15m: 0.000053,
loopAvgTasks: 0,
loopEstimatedLag: 0.000056,
loopIdlePercent: 99.96048,
title: 'nsolid',
user: 'bryce',
uptime: 351,
systemUptime: 65463,
freeMem: 4299878400,
blockInputOpCount: 0,
blockOutputOpCount: 32,
ctxSwitchInvoluntaryCount: 0,
ctxSwitchVoluntaryCount: 4024,
ipcReceivedCount: 0,
ipcSentCount: 0,
pageFaultHardCount: 0,
pageFaultSoftCount: 4337,
signalCount: 0,
swapCount: 0,
rss: 56938496,
load1m: 0,
load5m: 0.03,
load15m: 0.06,
cpuUserPercent: 0.066668,
cpuSystemPercent: 0.01117,
cpuPercent: 0.077838,
cpu: 0.077838
}

If callback is passed metrics are returned asynchronously.

nsolid.metrics((err, metrics) => {
// ...
})
tip

See Metrics in detail for descriptions of each metric.


nsolid.metricsPaused

Whether the agent is currently retrieving metrics or not. See nsolid.pauseMetrics() and nsolid.resumeMetrics() for changing this value.

> nsolid.metricsPaused
false
> nsolid.pauseMetrics()
undefined
> nsolid.metricsPaused
true
> nsolid.resumeMetrics()
undefined
> nsolid.metricsPaused
false

nsolid.on(customCommand, handler)

Registers a custom command listener that will be called if that custom command is sent over the NSOLID_COMMAND channel.

Requires a customCommand name and a handler. When called it will contain a request object with the optional value sent in the command. And two continuation functions called return and throw.

request object:

FieldDescription
valueThe value sent as the "data" portion of the custom command.
returnContinuation function to complete the N
throwError state continuation function to complete the N

Your handler must call one of request.return or request.throw.

danger

Custom commands can execute any JS in the handler. Use caution.

nsolid.on('my_command', (request) => {
console.log(`Value: ${request.value}`)
request.return({ok: true})
})

nsolid.otel

Returns an object holding the register and registerInstrumentations methods for use with the JavaScript OpenTelemetry API @opentelemetry/api library.

The register() function allows the use of OpenTelemetry TraceAPI. See a very basic example in the following code. Notice that for the traces to be generated the enviroment variable NSOLID_TRACING_ENABLED should be set.

note

Your application must have tracing enabled for this code example to function. Either start your application with NSOLID_TRACING_ENABLED=1 or by enabling Tracing inside of the N|Solid Pro UI.

const nsolid = require('nsolid');
const api = require('@opentelemetry/api');

if (!nsolid.otel.register(api)) {
throw new Error('Error registering api');
}

const tracer = api.trace.getTracer('Test tracer');
const span = tracer.startSpan('initial', { attributes: { a: 1, b: 2 }});
span.updateName('my name');
span.setAttributes({c: 3, d: 4 });
span.setAttribute('e', 5);
span.addEvent('my_event 1', Date.now());
span.addEvent('my_event 2', { attr1: 'val1', attr2: 'val2'}, Date.now());
span.end();

The registerInstrumentations method allows you to register instrumentation modules that use the OpenTelemetry TraceAPI from the OpenTelemetry echosystem. We suggest using the @opentelemetry/auto-instrumentation-node package to automatically instrument what is available to be instrumented.

const nsolid = require('nsolid')
const api = require('@opentelemetry/api')
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node')
nsolid.otel.registerInstrumentations(getNodeAutoInstrumentations())

nsolid.packages()

It retrieves the list of packages listed in the package.json file and its dependencies, it returns a list of all packages that can be required by walking through all directories that could contain a module.

nsolid.packages()
[
{
{
path: '/home/bryce/tmp/node_modules/infinite-monkey-db',
name: 'infinite-monkey-db',
version: '1.0.2',
main: 'index.js',
dependencies: [ '../readable-stream', '../stream-meter', '../timegap' ],
required: false
},
{
path: '/home/bryce/tmp/node_modules/stats-lite',
name: 'stats-lite',
version: '2.2.0',
main: 'stats.js',
dependencies: [ '../isnumber' ],
required: false
},
...
]

nsolid.packages() can also be called asynchronously:

nsolid.packages((err, packages) => {
// ...
})

nsolid.pauseMetrics()

Pauses the process metrics collection if unpaused, otherwise does nothing. Note: this method must be called on the main thread.

nsolid.pauseMetrics()

nsolid.processStart

Returns the start date of the process in milliseconds.

nsolid.processStart
1716575443913

nsolid.profile([duration])

This is the way to trigger a CPU profile programmatically from within your application and have the resulting profile saved on your N|Solid console. The default profile duration is 600000 milliseconds (10 minutes). The profiler can be stopped before the specified end time with the nsolid.profileEnd command.

// Example: Start a 1 minute CPU profile from inside of the application
nsolid.profile(60000 /* milliseconds */, err => {
if (err) {
// The profile could not be started!
}
// if no error, the profiler has started running
})

The profiler can also be started synchronously:

try {
nsolid.profile(600000 /* durationMilliseconds */)
} catch (err) {
// The profile could not be started!
}

nsolid.profileEnd()

Stops the profiler if running, allowing stopping before the duration expires.

nsolid.profileEnd(err => {
if (err) {
// Error stopping profiler
}
// profiler is now stopped
})

nsolid.resumeMetrics()

If metrics were paused, resume collection. Otherwise does nothing. Note: this method must be called on the main thread.

nsolid.resumeMetrics()

nsolid.saveFatalError(error)

When your N|Solid process terminates with an error, it will attempt to send that error message to the N|Solid Console prior to exit. If you must exit asynchronously from an uncaughtException and intercept this process, you may still report the error to the N|Solid Console via nsolid.saveFatalError() prior to your own manual shutdown.

process.on('uncaughtException', err => {
nsolid.saveFatalError(err)
someImportantCleanup()
proces.exit(1)
})

nsolid.setThreadName(name)

For easier management of worker threads inside the N|Solid Console, you can set text labels for them.

const { Worker, isMainThread } = require('worker_threads');
const { getThreadName, setThreadName } = require('nsolid')

if (!isMainThread) {
console.log(getThreadName()) // ''
setThreadName('worker-renamed')
console.log(getThreadName()) // worker-renamed
return setTimeout(() => {}, 1000)
} else {
const worker = new Worker(__filename)
}

The main thread can also be named:

const { isMainThread } = require('worker_threads');
const { setThreadName } = require('nsolid')

if (isMainThread) setThreadName('main-thread')

The value passed to nsolid.setThreadName must be a string, otherwise an error will be thrown.

const { Worker, isMainThread } = require('worker_threads');
const { getThreadName, setThreadName } = require('nsolid')

if (!isMainThread) {
try {
setThreadName(null)
} catch (err) { /** TypeError [ERR_INVALID_ARG_TYPE]: The "name" argument must be of type string. Received null */ }
} else {
const worker = new Worker(__filename)
}

nsolid.snapshot()

Trigger a Heap Snapshot from within your application and have the resulting snapshot(s) saved on your N|Solid console.

nsolid.snapshot(err => {
if (err) {
// The snapshot could not be created!
}
})

This can also be called asynchronously:

try {
nsolid.snapshot()
} catch (err) {
// The snapshot could not be created!
}

nsolid.startupTimes()

Retrieves the N|Solid tracked "startup times" of the process in a high resolution array of [seconds, nanoseconds]. The default phases are:

Startup Phase NameDescription
timeOriginThe starting reference point. Defines the relationship between timeOriginTimestamp and the rest of the values.
timeOriginTimestampThe timestamp of timeOrigin relative to UTC.
initialized_nodeThe amount of time it took to initialize node and start the core process.
initialized_v8To initialize the V8 engine.
loaded_environmentTo complete loading the environment and execute the initial top-level JavaScript code in core and your own main file.
bootstrap_completeTime at which bootstrap was complete and the application is ready to begin asynchronous operations.
loop_startWhen the first event loop iteration began relative to timeOrigin.
nsolid.startupTimes()
{
bootstrap_complete: [ 0, 16597032 ],
initialized_node: [ 0, 390247 ],
initialized_v8: [ 0, 2975541 ],
loaded_environment: [ 0, 8607176 ],
loop_start: [ 0, 35011317 ],
timeOrigin: [ 0, 0 ],
timeOriginTimestamp: [ 1635041, 700204934 ]
}

nsolid.startupTimes() can also be called asynchronously:

nsolid.startupTimes((err, startupTimes)) => {
// ...
}
tip

You can add your own startup phases to the startupTimes object with process.recordStartupTime(string).

// after creating a database client
process.recordStartupTime('db_initialized')

nsolid.startupTimes()
{
bootstrap_complete: [ 0, 15298719 ],
db_initialized: [ 6, 144053957 ], // DB initalization in context with the rest of the application bootstrap
initialized_node: [ 0, 297988 ],
initialized_v8: [ 0, 2603603 ],
loaded_environment: [ 0, 7437782 ],
loop_start: [ 0, 34829260 ],
timeOrigin: [ 0, 0 ],
timeOriginTimestamp: [ 1633924, 707504898 ]
}

nsolid.statsd

Accessor for the nsolid.statsd object for working with a StatsD endpoint:

PropertyDescription
counter:Send a "counter" type value to statsd. Will use NSOLID_STATSD_BUCKET and NSOLID_STATSD_TAGS if configured.
format:The statsd format object.
gauge:Send a "gauge" type value to statsd. Will use NSOLID_STATSD_BUCKET and NSOLID_STATSD_TAGS if configured.
sendRaw:Send a raw string to statsd. Caller is required to comply to statsd format (terminating newline not required). An array of strings is also permissible, they will be sent newline separated. If connected via UDP, data sent via sendRaw() will be split up into 1400Kb batches to fit within a standard MTU window, this applies to newline separated strings or an array of strings passed to this interface.
set:Send a "set" type value to statsd. Will use NSOLID_STATSD_BUCKET and NSOLID_STATSD_TAGS if configured.
status:Function that retrieves the statsd agent status.
tcpIp:If configured, returns the ip address tcp statsd data will be sent to.
timing:Send a "timing" type value to statsd. Will use NSOLID_STATSD_BUCKET and NSOLID_STATSD_TAGS if configured.
udpIp:If configured, returns the ip address udp statsd data will be sent to.

By default there is no configuration and the statsd object will have unconfigured status. The statsd statuses are:

  • unconfigured
  • initializing
  • connecting
  • ready

Example:

nsolid.statsd.status()
'unconfigured'

The nsolid.statsd.format object:

PropertyDescription
bucket:Returns the "bucket" string prepended to all auto-generated statsd metric strings.
counter:Format a "counter" string for name and value suitable for statsd.
gauge:Format a "gauge" string for name and value suitable for statsd.
set:Format a "set" string for name and value suitable for statsd.
timing:Format a "timing" string for name and value suitable for statsd.

Usage example:

> nsolid.statsd.format.bucket()
'nsolid.prod.webserver.anoxia.ced675f'

nsolid.tags


Get the N|Solid app tags (taken from the package.json file or NSOLID_TAGS environment variable).

$ NSOLID_STATSD=localhost:8125 NSOLID_STATSD_TAGS='testmode' nsolid
Welcome to N|Solid v20.13.1+ns5.2.1.
Type ".help" for more information.
> nsolid.statsd.format.tags()
'|#testmode'

nsolid.traceStats

Returns the latest nsolid.traceStats object getters. Note that this returns the getter functions which need to be called individually.

nsolid.traceStats
{
httpClientCount: [Getter],
httpServerCount: [Getter],
dnsCount: [Getter],
httpClientAbortCount: [Getter],
httpServerAbortCount: [Getter]
}
nsolid.traceStats.httpClientCount
1
PropertyDescription
dnsCountThe total number of DNS lookups performed during the life of the process.
httpClientCount... of outgoing HTTP(S) client requests performed.
httpClientAbortCount... of aborted or timed-out HTTP(S) client requests.
httpServerCount... of incoming HTTP(s) requests served.
httpServerAbortCount... of incoming HTTP(S) requests canceled.

nsolid.zmq

The nsolid.zmq object holds the status getter for the ZMQ communcation for the N|Solid Agent.

PropertyDescription
status:Function that retrieves the zmq agent status.

The zmq agent statuses are:

  • buffering
  • connecting
  • initializing
  • ready
  • unconfigured

Usage example:

nsolid.zmq.status()
'ready'