express-tracify
v0.1.3
Published
Middleware & utility to support tracing with jaeger & opentracing on ExpressJs
Downloads
5
Maintainers
Readme
Express Tracify
Library to support tracing with jaeger & opentracing on NodeJs with ExpressJs. This library is divided into 3 parts:
- Initialization
- Middlewares (start & finish span, error capture)
- Manual trace function
Installation
NPM
$ npm i express-tracify
Init & Middleware Configuration
The example code below is using configuration via a direct parameter on the Init
function, however, you can also use Environment Variables to set them.
This library using jaeger-client-node and opentracing on the implementation, so the parameter for config or options is also the same.
const Express = require('express')
const { Init, Middleware, ErrMiddlewareWrapper } = require('@usetada/express-tracify')
// part 1: initialization
Init({
tracer: {
config: {
serviceName: 'SVC 1',
sampler: {
type: 'const',
param: 1,
},
},
options: {}
}
})
const app = Express()
// part 2: middleware to create default request (auto start & finish span)
app.use(Middleware())
// You ExpressJs HTTP handlers
// part 2: middleware to wrap error handler
app.use(ErrMiddlewareWrapper((err, req, res, next) => {
return res.status(500).json({
error: true,
message: 'Something Wrong!',
})
}))
Trace Function
Function tracing is also possible by wrapping your function handler with WrapHandler
const { WrapHandler } = require('@usetada/express-tracify')
const Home = WrapHandler(function (req, res) {
res.json({
page: 'Home',
})
}, 'Home Handler')
app.get('/', Home)
This example will automatically add a new span with the name Home Handler.
Traceable Sub-function
The advantage of wrapping your function handler with WrapHandler
is you can also create another function tracing by using traceFn, but this function only available when your handler is not using fat-arrow style, because it will use the injected (this) context.
Following the previous example
const getFromDB = () => {
// some process to trace
}
// part 3: manual trace on function
const Home = WrapHandler(async function (req, res) {
const TgetFromDB = this.traceFn(getFromDB, 'getFromDB') // set span Name 'getFromDB'
const data = await TgetFromDB()
res.json({
page: 'Home',
data,
})
}, 'Home Handler')
You'll notice the getFromDB
function is using fat-arrow, it's impossible to call (this) in the context, by changing it to a non-fat-arrow function, you can call the traceFn
from inside the function.
function addLog() {
// some logging
}
function getFromDB {
// "this" is refers to TraceWrapper class
// so calling .traceFn again in here is automatically
// create new span, as a child of "getFromDB" span
const TaddLog = this.traceFn(addLog)
TaddLog('getting from DB')
// some process to trace
}
const Home = WrapHandler(async function (req, res) {
const TgetFromDB = this.traceFn(getFromDB) // span name 'getFromDB'
const data = await TgetFromDB()
res.json({
page: 'Home',
data,
})
}, 'Home Handler')
by using named function function getFromDB() you don't have to set a custom span name like before, the function getFromDB itself now has a name, gathering from the constructor, and traceFn
will use that name if the second parameter is not given.
If the function wrapped by traceFn
is calling "this" again inside the operation, it will refer to the TraceWrapper class, which has the traceFn
that can be used to do tracing deeply on more sub-functions.
Prevent "this" context override
As you can see in the previous example, this library overrides the context of the current "this" object. If you working with a Class object where the operation is happening on the method calls, that would be a problem.
To handle that, you can insert additional context on the third parameter, and then you can use the tracer injected on the class method where stored on $__tracer
.
class Person {
name;
constructor(name) {
this.name = name
}
sayName() {
// use .traceFn via this.$__tracer.traceFn here
console.log(`Hi ${this.name}`)
}
}
WrapHandler(handler() {
const p = new Person('aditya')
const sayPersonName = this.traceFn(p.sayName, 'Person: sayName', { context: p })
sayPersonName() // Hi aditya
})
Apis
TracerWrapper
Base class for tracing functions
.traceFn(fn, operationName, opts?: {context: Object })
Will return new traced function that ready to execute.
fn
: Function handler or function to trace (required)operationName
: String Custom operation name, if not supplied will use[TheFunction].name
value, or empty ("")opts
: Object Options to passcontext
Object the custom context to pass onfunction.apply
calls
.traceFnExec(fn, operationName, opts)
Same as .traceFn
but will immediatelly execute the given function on fn
param.
.traceFns(fns)
Will return multiple traced functions, the order is based on the given functions parameter
fns
: Array array of parameter of.traceFn
, the shorthand for creating multiple traced functions.
e.g.
const [tracedFn1, tracedFn2] = this.traceFns([
[() => {}, 'function 1'],
[() => {}, 'function 2', { context: this }]
])
.getSpan()
Will return the current span object in the current context
.createChildSpan(operationName, options)
Create new child span, based on the current span context
operationName
: String Operation name of new child spanoptions
: Object options to be assigned ontracer.startSpan
execution (optional)
.setOperationName(name)
Set custom operation name on the current span context
name
: String the name of operation
.setTag(key, val)
Set opentracing Tag
key
: String Key name, refers to Opentracing.Tagsval
: Any value of the key tag
.log(keyValuePairs, timestamp?: number)
Set logging value
keyValuePairs
: Object{key: string, val: Any} Key-Value pair of logging eventtimestamp
: Number The timestamp in milliseconds since the Unix epoch (optional)
.setTagPriority(priorityNumber = 1)
An alias for setting tag priority on Tags.SAMPLING_PRIORITY
, by default 1 (priority)
priorityNumber
: Number the value of sampling priority 1 menas always captured
.setTagError(errMsg)
Shorthand for easily create tag error and set sampling priority to 1
errMsg
: String | Error error message as a string or error object
.setBaggageItem(key, value)
Set baggage item on the current trace context
key
: String key or the name of baggage itemvalue
: String the value of baggage item
.getBaggageItem(key)
Get baggage item on the current trace context
key
: String key or the name of baggage item
Examples
You can take a look at example folder to see full implementations