Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/slaveofcode/express-tracify
Middleware & utility to support tracing with jaeger & opentracing on ExpressJs
https://github.com/slaveofcode/express-tracify
Last synced: about 8 hours ago
JSON representation
Middleware & utility to support tracing with jaeger & opentracing on ExpressJs
- Host: GitHub
- URL: https://github.com/slaveofcode/express-tracify
- Owner: slaveofcode
- License: mit
- Created: 2020-10-28T05:35:18.000Z (about 4 years ago)
- Default Branch: main
- Last Pushed: 2020-12-11T10:17:54.000Z (almost 4 years ago)
- Last Synced: 2024-11-06T18:08:41.045Z (13 days ago)
- Language: TypeScript
- Size: 568 KB
- Stars: 0
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Express Tracify
Library to support tracing with jaeger & opentracing on NodeJs with ExpressJs. This library is divided into 3 parts:1. Initialization
2. Middlewares (start & finish span, error capture)
3. 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](https://github.com/jaegertracing/jaeger-client-node) and [opentracing](https://github.com/opentracing/opentracing-javascript) on the implementation, so the parameter for *config* or *options* is also the same.
```js
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````js
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
```js
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.
```js
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`.
```js
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 pass
- `context` **Object** the custom context to pass on `function.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 span
- `options`: **Object** options to be assigned on `tracer.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.Tags
- `val`: **Any** value of the key tag#### .log(keyValuePairs, timestamp?: number)
Set logging value
- `keyValuePairs`: **Object{key: string, val: Any}** Key-Value pair of logging event
- `timestamp`: **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** means 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 item
- `value`: **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](https://github.com/slaveofcode/express-tracify/tree/main/example) folder to see full implementations