JavaScript Events
Service instances are able to emit regular JavaScript events. This provides a way to observe the service instance, and to decouple things.
As an example: You like to try out Prometheus to track certain data. One way of doing it, would be to directly integrate the Prometheus client into your commands and subscriptions.
But, by doing this, it would add a dependency, directly to your business code, with all the down sides. A better way is, to emit the data from your command or subscription as regular JavaScript events. Some function "outside" your service can listen to these events, and process the data.
PRO
- keeps the business code isolated
- easier to test and to handle errors
- the dependency will be in one place - easier to maintain, fewer duplicate code
- the dependency will become optional - e.g. can simpler replaced by other solutions
::: caution CONTRA
- a bit more overhead at the beginning :::
Event types
There are three types:
- technical events relating to the service itself are prefixed with
service-
(see ServiceEventsNames) - response messages, which have an event name assigned, are prefixed with
custom-
- additional events can be introduced by developers, and they must be prefixed with
misc-
Example
As an example, we will simply count the unhandled errors with the Prometheus client.
// observer.ts
import client from 'prom-client'
export const collectData = (serviceInstance: Service) => {
// Create a Registry which registers the metrics
const register = new client.Registry()
// Enable the collection of default metrics
client.collectDefaultMetrics({ register })
// Add a default label which is added to all metrics
register.setDefaultLabels({
serviceName: theService.info.serviceName,
serviceVersion: theService.info.serviceVersion,
})
// create a counter
const counter = new client.Counter({
name: 'unhandled_error_count',
help: 'metric_help',
});
serviceInstace.on(ServiceEventsNames.CommandUnhandledError, ()=> {
counter.inc()
})
serviceInstace.on(ServiceEventsNames.SubscriptionUnhandledError, ()=> {
counter.inc()
})
return register
}
// main or index file
// ...
import { collectData } from './observer'
// ...
// your regular instance creation an existing code
// ...
const register = collectData(serviceInstance)
// if you use the @purista/k8s-sdk
// expose the metrics like this
server.router.add('GET', '/metrics', async (_request, response) => {
response.setHeader('content-type', register.contentType)
response.end(await register.metrics())
})
As you can see, things are nicely separated and structured. You can re-use the collectData
for multiple services, if you want.
Custom events
You can emit custom JavaScript events in command and subscription functions. They must be prefixed with misc-
. They can emit any
Example of emitting custom events:
.setCommandFunction(async function (context, payload, parameter) {
const customValue = {
value: 'something'
}
this.emit('misc-object', customValue)
const customValue = {
value: 'something'
}
this.emit('misc-string', 'string_value')
this.emit('misc-number', 1)
this.emit('misc-boolean', 1)
})
Example of listening for custom events:
serviceInstace.on('misc-string', (eventPayload)=> {
console.log(eventPayload) // outputs: string_value
})
Use enum for custom events
It is recommended, to use your own enum for emitting and listener registration of events. Your code will become more maintable.