@soinlabs/hawk
Version:
Package to better manage errors, logs, and its notifications
192 lines (172 loc) • 5.68 kB
JavaScript
const cleanStack = require('clean-stack')
const os = require('os')
const STEP_INIT_INDEX = 7
const KEYWORDS_FOR_EXCLUDING_STEPS = ['ErrorBuilder', 'node_modules']
class StepGetter {
/**
* It build a new stack-trace with customized format from error stack trace
* @param {Error - StandardError - LoggedError} error
*/
constructor(error) {
this.error = error
this.customizedStack = []
this.stepTrace = []
this._buildCustomizeStack()
this._fillStepTrace()
}
/**
* It returns the first object from stepTrace (first excecution point)
* the object contains two properties: file path and excecuted method
* @return {JSON}
*/
getOriginStep() {
const stepTrace = this.getStepTrace()
let originStep = null
if (stepTrace[0]) {
originStep = stepTrace[0]
}
return originStep
}
/**
* It returns the last object from stepTrace (last excecution point)
* the object contains two properties: file path and excecuted method
* @return {JSON}
*/
getFinalStep() {
const stepTrace = this.getStepTrace()
const stepTraceLength = stepTrace.length
let finalStep = null
if (stepTraceLength > 0) {
finalStep = stepTrace[stepTraceLength - 1]
}
return finalStep
}
/**
* It returns the stack trace without internal modules, the stack is cleaned
* and formated into a list of objects {calledFuction:string, path:string}
* @return {[JSON]}
*/
getStepTrace() {
return this.stepTrace
}
/**
* It returns an list of all previous called functions
* that invocate an error creation
* @return {[String]}
*/
getTracedFuctions() {
const getCalledFunctions = this._getElementsFromStepTrace('calledFunction')
return getCalledFunctions()
}
/**
* It returns an list of all previous path-files
* that invocate an error creation
* @return {[String]}
*/
getTracedPaths() {
const getPaths = this._getElementsFromStepTrace('path')
return getPaths()
}
/**
* It returns a list of requested properties (calledFunction or path)
* from stepTrace
* @param {String} requestedPropertie
*/
_getElementsFromStepTrace(requestedPropertie) {
return () => {
let elementsFromStepTrace = []
for (const element of this.stepTrace) {
elementsFromStepTrace.push(element[requestedPropertie])
}
return elementsFromStepTrace
}
}
/**
* It fill a list using stack trace from error, the list
* contains each line (separated with \n) from error.stack
* remove home directory of each line
*/
_buildCustomizeStack() {
if (this.error && this.error.stack) {
let homeDir = typeof os.homedir === 'undefined' ? '' : os.homedir()
homeDir = homeDir.split('\\').join('/') // it is equivalent to homeDir.replaceAll('\\','/')... new homeDir as User/.../...
let stack = cleanStack(this.error.stack)
stack = stack.split(homeDir).join('~') // it is equivalent to stack.replace(/homeDir/g, '~')
this._fillCustomizeStack(stack)
}
}
/**
* It takes a error.stack and obtain only usefull steps
* calledFunction <space> path and put each line into a list
*/
_fillCustomizeStack(stack) {
let linesFromStack = []
linesFromStack = stack.split('\n')
for (const line of linesFromStack) {
this.customizedStack.push(line.substr(STEP_INIT_INDEX))
}
this.customizedStack.shift()
}
/**
* Using filled customizedStack, it fills a list
* with JSON {calledFunction, path}
*/
_fillStepTrace() {
for (const step of this.customizedStack) {
if (!this._excludeStep(step)) {
this.stepTrace.push({
calledFunction: this._getFunctionFromStep(step),
path: this._getPathFromStep(step),
})
}
}
}
/**
* Because each line (step) from customizedStack
* has the format calledFunction <space> path, then
* this method split the step and get only calledFunction
* if step doesn't have a path, then the function is the step
* @return {String}
* */
_getFunctionFromStep(step) {
let functionIntoStep = null
const initOfPath = step.indexOf('~') //path inits with ~
if (initOfPath > 1) {
functionIntoStep = step.substr(0, initOfPath - 2) //remove 'space' and '('
} else {
//if step doesn't have a path
functionIntoStep = step
}
return functionIntoStep
}
/**
* Because each line (step) from customizedStack
* has the format calledFunction <space> path, then
* this method split the step and get only path
* Note: the path-file could contain spaces, thus the path could be chunked
* in many parts... In this case, method join each chunck (without spaces) in only one part
* @return {String}
* */
_getPathFromStep(step) {
//it checks if step have a path (path inits with ~)
let pathIntoStep = null
if (step.indexOf('~') > 0) {
const stepParts = step.split(' ') // it is for divide called functions and paths (they are spletted by space)
stepParts.shift() // remove first, because at the first position is the fuction
//in case that the path was chunked in many parts, we join them again
for (let index = 0; index < stepParts.length - 1; ++index) {
stepParts[index + 1] = stepParts[index] + stepParts[index + 1]
stepParts.shift()
}
pathIntoStep = stepParts.shift() // get first one (and only one)
}
return pathIntoStep
}
_excludeStep(step) {
for (const keyWord of KEYWORDS_FOR_EXCLUDING_STEPS) {
if (step.includes(keyWord)) return true
}
return false
}
}
module.exports = StepGetter