193 lines
6.9 KiB
JavaScript
193 lines
6.9 KiB
JavaScript
import pino from 'pino'
|
|
import envPaths from 'env-paths'
|
|
import { sync as mkdir } from 'make-dir'
|
|
import { dirname, basename } from 'path'
|
|
import pinoPretty from 'pino-pretty'
|
|
import uniqid from 'uniqid'
|
|
|
|
// TODO add custom levels
|
|
// is pro log to file working???
|
|
|
|
function child (bindings = {}, options = {}) {
|
|
const pinoOpts = Object.assign({}, options.pino)
|
|
const opts = Object.assign({}, options)
|
|
delete opts.pino
|
|
|
|
const enabled = !!process.env.UCI_ENV
|
|
|
|
let pretty; let filter; let LOG_PATH; let DATE_TIME; let destination; let file
|
|
|
|
let hidden = Array.isArray(opts.hide) ? opts.hide : [opts.hide]
|
|
|
|
const PRETTY_DEFAULTS = { translateTime: true, colorize: true, levelFirst: true }
|
|
const DEFAULT_FILTER_PROPS = ['level', 'time', 'pid', 'msg']
|
|
const WHERE_FILTER_PROPS = ['package', 'file', 'class', 'method', 'function', 'line']
|
|
|
|
if (enabled) {
|
|
if (opts.env) (opts.envForce) ? (process.env.UCI_ENV = opts.env) : (process.env.UCI_ENV = process.env.UCI_ENV || opts.env)
|
|
if (process.env.UCI_ENV === 'node') process.env.UCI_ENV = process.env.NODE_ENV
|
|
if ((process.env.UCI_ENV.indexOf('dev') > -1 || process.env.UCI_LOG_PRETTY) && process.env.UCI_LOG_JSON !== 'true') { // pretty is on
|
|
// process UCI_LOG_PRETTY
|
|
try {
|
|
pretty = JSON.parse(process.env.UCI_LOG_PRETTY)
|
|
} catch (error) {
|
|
// console.log('LOGGER: could not JSON parse',process.env.UCI_LOG_PRETTY )
|
|
}
|
|
if (process.env.UCI_LOG_PRETTY === 'verbose') pretty = { include: 'all' }
|
|
pretty = pretty || opts.pretty || {}
|
|
pretty = Object.assign({}, PRETTY_DEFAULTS, pretty)
|
|
pretty.search = process.env.UCI_LOG_SEARCH
|
|
pretty.include = pretty.include === 'all' ? 'all' : `level${pretty.include ? ',' + pretty.include : ''}`
|
|
pretty.ignore = pretty.include === 'all' ? null : Object.keys(opts).filter(key => pretty.include.indexOf(key) === -1).join()
|
|
if (process.env.UCI_LOG_PRETTY === 'terse' || process.env.UCI_LOG_PRETTY === 'where' || pretty.filter) {
|
|
filter = filterPretty // only call prefilter if some filter is supplied, otherwise
|
|
pretty.filter = [...DEFAULT_FILTER_PROPS, ...(process.env.UCI_LOG_PRETTY === 'where' ? WHERE_FILTER_PROPS : []), ...(pretty.filter || [])]
|
|
pretty.ignore = null
|
|
}
|
|
}
|
|
|
|
DATE_TIME = new Date().toString()
|
|
LOG_PATH = (process.env.UCI_ENV.indexOf('pro') > -1 || process.env.UCI_ENV === 'logfile')
|
|
? (process.env.UCI_LOG_PATH || `${envPaths(opts.appName || opts.name || 'default').log}/${opts.logFileName || DATE_TIME}.log`)
|
|
: undefined
|
|
if (LOG_PATH) {
|
|
mkdir(dirname(LOG_PATH)) // makes recursively for any missing parent directories
|
|
destination = LOG_PATH
|
|
}
|
|
} // end enabled
|
|
|
|
// default bindings
|
|
bindings.appName = process.env.UCI_LOG_APP || bindings.appName || bindings.name
|
|
bindings.library = bindings.library || typeof bindings.package === 'string' ? (bindings.package.includes('@') ? (bindings.package.replace(/[@]+/g, '')).split('/')[0] : undefined) : undefined
|
|
bindings.repo = bindings.repo || ((typeof bindings.package === 'string') ? bindings.package.replace(/[@]+/g, '').replace(/[/]+/g, '-') : undefined)
|
|
bindings.file = bindings.file || ((typeof bindings.package === 'string') ? `src/${basename(bindings.package)}.js` : undefined)
|
|
bindings.class = bindings.class || (bindings.class !== false && typeof bindings.package === 'string') ? capitalize(basename(bindings.package)) : undefined
|
|
bindings.instanceCreatedHR = DATE_TIME
|
|
bindings.instanceCreated = Date.now()
|
|
|
|
// console.dir(pretty)
|
|
// console.dir(logOpts)
|
|
|
|
if (pinoOpts.destination) {
|
|
destination = pinoOpts.destination
|
|
delete pinoOpts.destination
|
|
}
|
|
|
|
const defaultLevel = pinoOpts.level || opts.level || process.env.UCI_LOG_LEVEL || 'info'
|
|
|
|
const defaultOpts = {
|
|
enabled: enabled,
|
|
safe: true,
|
|
level: defaultLevel,
|
|
formatters: {
|
|
bindings (obj) {
|
|
return { machine: obj }
|
|
}
|
|
},
|
|
timestamp: pino.stdTimeFunctions.epochTime,
|
|
serializers: {
|
|
req: pino.stdSerializers.req,
|
|
res: pino.stdSerializers.res
|
|
},
|
|
prettyPrint: pretty,
|
|
prettifier: filter // only call prefilter if some filter is supplied, otherwise see line 53
|
|
}
|
|
|
|
const logger = pino(
|
|
Object.assign({}, defaultOpts, pinoOpts),
|
|
// if production not enabled then LOG_PATH is empty and logs go to stdout/stderr and can be piped from there
|
|
destination
|
|
)
|
|
|
|
const child = logger.child({ meta: bindings }, {
|
|
formatters: {
|
|
log (obj) {
|
|
// console.log('formatter', obj.level, child.level)
|
|
obj.dateTime = new Date().toString()
|
|
if (!pretty) {
|
|
obj.label = child.level
|
|
if (!opts.noID) obj[opts.keyID || '_id'] = uniqid()
|
|
} else {
|
|
obj.file = obj.file || file
|
|
hidden.forEach(prop => {
|
|
// console.log('hiding', prop, obj[prop])
|
|
obj[prop] = undefined
|
|
})
|
|
}
|
|
return obj
|
|
}
|
|
}
|
|
})
|
|
|
|
child.default = defaultLevel
|
|
child.clear = () => {
|
|
if (enabled) {
|
|
if (process.env.UCI_ENV.indexOf('pro') > -1) return null
|
|
} // enable feature here
|
|
}
|
|
child.div = value => {
|
|
if (enabled) {
|
|
if (process.env.UCI_ENV.indexOf('dev') > -1) console.log(`===== ${value} ========`)
|
|
}
|
|
}
|
|
child.lvlset = (level) => { if (enabled) child.level = level || child.default }
|
|
|
|
child.hide = (props) => {
|
|
hidden = arraysMerge(props, hidden)
|
|
}
|
|
|
|
child.unhide = (props) => {
|
|
hidden = arraysFilter(props, hidden)
|
|
}
|
|
|
|
child.locate = locate
|
|
|
|
return child
|
|
}
|
|
|
|
export default child
|
|
const levels = pino.levels
|
|
export { child as logger, levels, locate, arraysMerge }
|
|
|
|
function filterPretty (options) {
|
|
// console.log('filter options',options)
|
|
return function prettifier (inputData) {
|
|
// console.log('calling filter',inputData)
|
|
let log
|
|
// let parsedData
|
|
if (typeof inputData === 'string') {
|
|
try { log = JSON.Parse(inputData) } catch (err) { return inputData }
|
|
} else log = inputData
|
|
const filteredLog = {}
|
|
options.filter.forEach(prop => { filteredLog[prop] = log[prop] })
|
|
// console.log('filtered',options.filter, filteredLog)
|
|
return pinoPretty(options)(filteredLog)
|
|
}
|
|
}
|
|
|
|
// TODO move these to @uci-utils/helpers
|
|
|
|
function capitalize (s) { return s.charAt(0).toUpperCase() + s.slice(1) }
|
|
|
|
function locate (frame = 2, ret) {
|
|
if (typeof frame === 'string') { ret = frame; frame = 2 }
|
|
const e = new Error()
|
|
const stack = e.stack.toString().split(/\r\n|\n/)
|
|
const RE = /file:\/\/(.*):(\d+):(?:\d+)[^\d]*$/
|
|
const reg = RE.exec(stack[frame])
|
|
const str = ret ? (ret === 'n' ? reg[2] : reg[1]) : `${reg[2]}:${reg[1]}`
|
|
return str // return line#:path
|
|
}
|
|
|
|
function arraysMerge (els, arr) {
|
|
els = Array.isArray(els) ? els : [els]
|
|
return [...new Set([...arr, ...els])].filter(el => el != null)
|
|
}
|
|
|
|
function arraysFilter (els, arr) {
|
|
els = Array.isArray(els) ? els : [els]
|
|
return arr.filter(function (item) {
|
|
return !els.includes(item)
|
|
})
|
|
}
|