chore(log): remove build step

It also helps with compatibility with native ESM for default exports.
This commit is contained in:
Julien Fontanet 2021-05-18 21:23:19 +02:00
parent a126b5b61b
commit 0c6d920682
19 changed files with 331 additions and 333 deletions

View File

@ -1 +0,0 @@
module.exports = require('../../@xen-orchestra/babel-config')(require('./package.json'))

View File

@ -1 +0,0 @@
../../scripts/babel-eslintrc.js

View File

@ -1 +1,107 @@
module.exports = require('./dist/configure')
const createConsoleTransport = require('./transports/console')
const { LEVELS, resolve } = require('./levels')
const { compileGlobPattern } = require('./utils')
// ===================================================================
const createTransport = config => {
if (typeof config === 'function') {
return config
}
if (Array.isArray(config)) {
const transports = config.map(createTransport)
const { length } = transports
return function () {
for (let i = 0; i < length; ++i) {
transports[i].apply(this, arguments)
}
}
}
let { filter } = config
let transport = createTransport(config.transport)
const level = resolve(config.level)
if (filter !== undefined) {
if (typeof filter === 'string') {
const re = compileGlobPattern(filter)
filter = log => re.test(log.namespace)
}
const orig = transport
transport = function (log) {
if ((level !== undefined && log.level >= level) || filter(log)) {
return orig.apply(this, arguments)
}
}
} else if (level !== undefined) {
const orig = transport
transport = function (log) {
if (log.level >= level) {
return orig.apply(this, arguments)
}
}
}
return transport
}
const symbol = typeof Symbol !== 'undefined' ? Symbol.for('@xen-orchestra/log') : '@@@xen-orchestra/log'
const { env } = process
global[symbol] = createTransport({
// display warnings or above, and all that are enabled via DEBUG or
// NODE_DEBUG env
filter: [env.DEBUG, env.NODE_DEBUG].filter(Boolean).join(','),
level: resolve(env.LOG_LEVEL, LEVELS.INFO),
transport: createConsoleTransport(),
})
const configure = config => {
global[symbol] = createTransport(config)
}
exports.configure = configure
// -------------------------------------------------------------------
const catchGlobalErrors = logger => {
// patch process
const onUncaughtException = error => {
logger.error('uncaught exception', { error })
}
const onUnhandledRejection = error => {
logger.warn('possibly unhandled rejection', { error })
}
const onWarning = error => {
logger.warn('Node warning', { error })
}
process.on('uncaughtException', onUncaughtException)
process.on('unhandledRejection', onUnhandledRejection)
process.on('warning', onWarning)
// patch EventEmitter
const EventEmitter = require('events')
const { prototype } = EventEmitter
const { emit } = prototype
function patchedEmit(event, error) {
if (event === 'error' && this.listenerCount(event) === 0) {
logger.error('unhandled error event', { error })
return false
}
return emit.apply(this, arguments)
}
prototype.emit = patchedEmit
return () => {
process.removeListener('uncaughtException', onUncaughtException)
process.removeListener('unhandledRejection', onUnhandledRejection)
process.removeListener('warning', onWarning)
if (prototype.emit === patchedEmit) {
prototype.emit = emit
}
}
}
exports.catchGlobalErrors = catchGlobalErrors

View File

@ -1,5 +1,5 @@
import createTransport from './transports/console'
import LEVELS, { resolve } from './levels'
const createTransport = require('./transports/console')
const { LEVELS, resolve } = require('./levels')
const symbol = typeof Symbol !== 'undefined' ? Symbol.for('@xen-orchestra/log') : '@@@xen-orchestra/log'
if (!(symbol in global)) {
@ -68,5 +68,7 @@ prototype.wrap = function (message, fn) {
}
}
export const createLogger = namespace => new Logger(namespace)
export { createLogger as default }
const createLogger = namespace => new Logger(namespace)
module.exports = exports = createLogger
exports.createLogger = createLogger

View File

@ -1,5 +1,5 @@
const LEVELS = Object.create(null)
export { LEVELS as default }
exports.LEVELS = LEVELS
// https://github.com/trentm/node-bunyan#levels
LEVELS.FATAL = 60 // service/app is going down
@ -8,7 +8,8 @@ LEVELS.WARN = 40 // something went wrong but it's not fatal
LEVELS.INFO = 30 // detail on unusual but normal operation
LEVELS.DEBUG = 20
export const NAMES = Object.create(null)
const NAMES = Object.create(null)
exports.NAMES = NAMES
for (const name in LEVELS) {
NAMES[LEVELS[name]] = name
}
@ -16,7 +17,7 @@ for (const name in LEVELS) {
// resolves to the number representation of a level
//
// returns `defaultLevel` if invalid
export const resolve = (level, defaultLevel) => {
const resolve = (level, defaultLevel) => {
const type = typeof level
if (type === 'number') {
if (level in NAMES) {
@ -30,6 +31,7 @@ export const resolve = (level, defaultLevel) => {
}
return defaultLevel
}
exports.resolve = resolve
Object.freeze(LEVELS)
Object.freeze(NAMES)

View File

@ -1,8 +1,8 @@
/* eslint-env jest */
import { forEach, isInteger } from 'lodash'
const { forEach, isInteger } = require('lodash')
import LEVELS, { NAMES, resolve } from './levels'
const { LEVELS, NAMES, resolve } = require('./levels')
describe('LEVELS', () => {
it('maps level names to their integer values', () => {

View File

@ -16,7 +16,6 @@
"url": "https://vates.fr"
},
"preferGlobal": false,
"main": "dist/",
"browserslist": [
">2%"
],
@ -27,21 +26,7 @@
"lodash": "^4.17.4",
"promise-toolbox": "^0.19.2"
},
"devDependencies": {
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"babel-plugin-lodash": "^3.3.2",
"cross-env": "^7.0.2",
"rimraf": "^3.0.0"
},
"scripts": {
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
"clean": "rimraf dist/",
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
"prebuild": "yarn run clean",
"predev": "yarn run prebuild",
"prepublishOnly": "yarn run build",
"postversion": "npm publish"
}
}

View File

@ -1,105 +0,0 @@
import createConsoleTransport from './transports/console'
import LEVELS, { resolve } from './levels'
import { compileGlobPattern } from './utils'
// ===================================================================
const createTransport = config => {
if (typeof config === 'function') {
return config
}
if (Array.isArray(config)) {
const transports = config.map(createTransport)
const { length } = transports
return function () {
for (let i = 0; i < length; ++i) {
transports[i].apply(this, arguments)
}
}
}
let { filter } = config
let transport = createTransport(config.transport)
const level = resolve(config.level)
if (filter !== undefined) {
if (typeof filter === 'string') {
const re = compileGlobPattern(filter)
filter = log => re.test(log.namespace)
}
const orig = transport
transport = function (log) {
if ((level !== undefined && log.level >= level) || filter(log)) {
return orig.apply(this, arguments)
}
}
} else if (level !== undefined) {
const orig = transport
transport = function (log) {
if (log.level >= level) {
return orig.apply(this, arguments)
}
}
}
return transport
}
const symbol = typeof Symbol !== 'undefined' ? Symbol.for('@xen-orchestra/log') : '@@@xen-orchestra/log'
const { env } = process
global[symbol] = createTransport({
// display warnings or above, and all that are enabled via DEBUG or
// NODE_DEBUG env
filter: [env.DEBUG, env.NODE_DEBUG].filter(Boolean).join(','),
level: resolve(env.LOG_LEVEL, LEVELS.INFO),
transport: createConsoleTransport(),
})
export const configure = config => {
global[symbol] = createTransport(config)
}
// -------------------------------------------------------------------
export const catchGlobalErrors = logger => {
// patch process
const onUncaughtException = error => {
logger.error('uncaught exception', { error })
}
const onUnhandledRejection = error => {
logger.warn('possibly unhandled rejection', { error })
}
const onWarning = error => {
logger.warn('Node warning', { error })
}
process.on('uncaughtException', onUncaughtException)
process.on('unhandledRejection', onUnhandledRejection)
process.on('warning', onWarning)
// patch EventEmitter
const EventEmitter = require('events')
const { prototype } = EventEmitter
const { emit } = prototype
function patchedEmit(event, error) {
if (event === 'error' && this.listenerCount(event) === 0) {
logger.error('unhandled error event', { error })
return false
}
return emit.apply(this, arguments)
}
prototype.emit = patchedEmit
return () => {
process.removeListener('uncaughtException', onUncaughtException)
process.removeListener('unhandledRejection', onUnhandledRejection)
process.removeListener('warning', onWarning)
if (prototype.emit === patchedEmit) {
prototype.emit = emit
}
}
}

View File

@ -1,79 +0,0 @@
import LEVELS, { NAMES } from '../levels'
const { DEBUG, ERROR, FATAL, INFO, WARN } = LEVELS
let formatLevel, formatNamespace
if (process.stdout !== undefined && process.stdout.isTTY && process.stderr !== undefined && process.stderr.isTTY) {
const ansi = (style, str) => `\x1b[${style}m${str}\x1b[0m`
const LEVEL_STYLES = {
[DEBUG]: '2',
[ERROR]: '1;31',
[FATAL]: '1;31',
[INFO]: '1',
[WARN]: '1;33',
}
formatLevel = level => {
const style = LEVEL_STYLES[level]
const name = NAMES[level]
return style === undefined ? name : ansi(style, name)
}
const NAMESPACE_COLORS = [
196,
202,
208,
214,
220,
226,
190,
154,
118,
82,
46,
47,
48,
49,
50,
51,
45,
39,
33,
27,
21,
57,
93,
129,
165,
201,
200,
199,
198,
197,
]
formatNamespace = namespace => {
// https://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
let hash = 0
for (let i = 0, n = namespace.length; i < n; ++i) {
hash = ((hash << 5) - hash + namespace.charCodeAt(i)) | 0
}
return ansi(`1;38;5;${NAMESPACE_COLORS[Math.abs(hash) % NAMESPACE_COLORS.length]}`, namespace)
}
} else {
formatLevel = str => NAMES[str]
formatNamespace = str => str
}
const consoleTransport = ({ data, level, namespace, message, time }) => {
const fn =
/* eslint-disable no-console */
level < INFO ? console.log : level < WARN ? console.info : level < ERROR ? console.warn : console.error
/* eslint-enable no-console */
const args = [time.toISOString(), formatNamespace(namespace), formatLevel(level), message]
if (data != null) {
args.push(data)
}
fn.apply(console, args)
}
export default () => consoleTransport

View File

@ -1,64 +0,0 @@
import fromCallback from 'promise-toolbox/fromCallback'
import prettyFormat from 'pretty-format' // eslint-disable-line node/no-extraneous-import
import { createTransport } from 'nodemailer' // eslint-disable-line node/no-extraneous-import
import { evalTemplate, required } from '../utils'
import { NAMES } from '../levels'
export default ({
// transport options (https://nodemailer.com/smtp/)
auth,
authMethod,
host,
ignoreTLS,
port,
proxy,
requireTLS,
secure,
service,
tls,
// message options (https://nodemailer.com/message/)
bcc,
cc,
from = required('from'),
to = required('to'),
subject = '[{{level}} - {{namespace}}] {{time}} {{message}}',
}) => {
const transporter = createTransport(
{
auth,
authMethod,
host,
ignoreTLS,
port,
proxy,
requireTLS,
secure,
service,
tls,
disableFileAccess: true,
disableUrlAccess: true,
},
{
bcc,
cc,
from,
to,
}
)
return log =>
fromCallback(cb =>
transporter.sendMail(
{
subject: evalTemplate(subject, key =>
key === 'level' ? NAMES[log.level] : key === 'time' ? log.time.toISOString() : log[key]
),
text: prettyFormat(log.data),
},
cb
)
)
}

View File

@ -1,7 +0,0 @@
export default () => {
const memoryLogger = log => {
logs.push(log)
}
const logs = (memoryLogger.logs = [])
return memoryLogger
}

View File

@ -1,41 +0,0 @@
import fromCallback from 'promise-toolbox/fromCallback'
import splitHost from 'split-host'
import { createClient, Facility, Severity, Transport } from 'syslog-client'
import LEVELS from '../levels'
// https://github.com/paulgrove/node-syslog-client#syslogseverity
const LEVEL_TO_SEVERITY = {
[LEVELS.FATAL]: Severity.Critical,
[LEVELS.ERROR]: Severity.Error,
[LEVELS.WARN]: Severity.Warning,
[LEVELS.INFO]: Severity.Informational,
[LEVELS.DEBUG]: Severity.Debug,
}
const facility = Facility.User
export default target => {
const opts = {}
if (target !== undefined) {
if (target.startsWith('tcp://')) {
target = target.slice(6)
opts.transport = Transport.Tcp
} else if (target.startsWith('udp://')) {
target = target.slice(6)
opts.transport = Transport.Udp
}
;({ host: target, port: opts.port } = splitHost(target))
}
const client = createClient(target, opts)
return log =>
fromCallback(cb =>
client.log(log.message, {
facility,
severity: LEVEL_TO_SEVERITY[log.level],
})
)
}

View File

@ -1 +1,82 @@
module.exports = require('../dist/transports/console.js')
const { LEVELS, NAMES } = require('../levels')
const { DEBUG, ERROR, FATAL, INFO, WARN } = LEVELS
let formatLevel, formatNamespace
if (process.stdout !== undefined && process.stdout.isTTY && process.stderr !== undefined && process.stderr.isTTY) {
const ansi = (style, str) => `\x1b[${style}m${str}\x1b[0m`
const LEVEL_STYLES = {
[DEBUG]: '2',
[ERROR]: '1;31',
[FATAL]: '1;31',
[INFO]: '1',
[WARN]: '1;33',
}
formatLevel = level => {
const style = LEVEL_STYLES[level]
const name = NAMES[level]
return style === undefined ? name : ansi(style, name)
}
const NAMESPACE_COLORS = [
196,
202,
208,
214,
220,
226,
190,
154,
118,
82,
46,
47,
48,
49,
50,
51,
45,
39,
33,
27,
21,
57,
93,
129,
165,
201,
200,
199,
198,
197,
]
formatNamespace = namespace => {
// https://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
let hash = 0
for (let i = 0, n = namespace.length; i < n; ++i) {
hash = ((hash << 5) - hash + namespace.charCodeAt(i)) | 0
}
return ansi(`1;38;5;${NAMESPACE_COLORS[Math.abs(hash) % NAMESPACE_COLORS.length]}`, namespace)
}
} else {
formatLevel = str => NAMES[str]
formatNamespace = str => str
}
const consoleTransport = ({ data, level, namespace, message, time }) => {
const fn =
/* eslint-disable no-console */
level < INFO ? console.log : level < WARN ? console.info : level < ERROR ? console.warn : console.error
/* eslint-enable no-console */
const args = [time.toISOString(), formatNamespace(namespace), formatLevel(level), message]
if (data != null) {
args.push(data)
}
fn.apply(console, args)
}
const createTransport = () => consoleTransport
module.exports = exports = createTransport

View File

@ -1 +1,66 @@
module.exports = require('../dist/transports/email.js')
const fromCallback = require('promise-toolbox/fromCallback')
const nodemailer = require('nodemailer') // eslint-disable-line node/no-extraneous-import
const prettyFormat = require('pretty-format') // eslint-disable-line node/no-extraneous-import
const { evalTemplate, required } = require('../utils')
const { NAMES } = require('../levels')
function createTransport({
// transport options (https://nodemailer.com/smtp/)
auth,
authMethod,
host,
ignoreTLS,
port,
proxy,
requireTLS,
secure,
service,
tls,
// message options (https://nodemailer.com/message/)
bcc,
cc,
from = required('from'),
to = required('to'),
subject = '[{{level}} - {{namespace}}] {{time}} {{message}}',
}) {
const transporter = nodemailer.createTransport(
{
auth,
authMethod,
host,
ignoreTLS,
port,
proxy,
requireTLS,
secure,
service,
tls,
disableFileAccess: true,
disableUrlAccess: true,
},
{
bcc,
cc,
from,
to,
}
)
return log =>
fromCallback(cb =>
transporter.sendMail(
{
subject: evalTemplate(subject, key =>
key === 'level' ? NAMES[log.level] : key === 'time' ? log.time.toISOString() : log[key]
),
text: prettyFormat(log.data),
},
cb
)
)
}
module.exports = exports = createTransport

View File

@ -1 +1,9 @@
module.exports = require('../dist/transports/memory.js')
function createTransport() {
const memoryLogger = log => {
logs.push(log)
}
const logs = (memoryLogger.logs = [])
return memoryLogger
}
module.exports = exports = createTransport

View File

@ -1 +1,43 @@
module.exports = require('../dist/transports/syslog.js')
const fromCallback = require('promise-toolbox/fromCallback')
const splitHost = require('split-host')
const { createClient, Facility, Severity, Transport } = require('syslog-client')
const LEVELS = require('../levels')
// https://github.com/paulgrove/node-syslog-client#syslogseverity
const LEVEL_TO_SEVERITY = {
[LEVELS.FATAL]: Severity.Critical,
[LEVELS.ERROR]: Severity.Error,
[LEVELS.WARN]: Severity.Warning,
[LEVELS.INFO]: Severity.Informational,
[LEVELS.DEBUG]: Severity.Debug,
}
const facility = Facility.User
function createTransport(target) {
const opts = {}
if (target !== undefined) {
if (target.startsWith('tcp://')) {
target = target.slice(6)
opts.transport = Transport.Tcp
} else if (target.startsWith('udp://')) {
target = target.slice(6)
opts.transport = Transport.Udp
}
;({ host: target, port: opts.port } = splitHost(target))
}
const client = createClient(target, opts)
return log =>
fromCallback(cb =>
client.log(log.message, {
facility,
severity: LEVEL_TO_SEVERITY[log.level],
})
)
}
module.exports = exports = createTransport

View File

@ -1,19 +1,20 @@
import escapeRegExp from 'lodash/escapeRegExp'
const escapeRegExp = require('lodash/escapeRegExp')
// ===================================================================
const TPL_RE = /\{\{(.+?)\}\}/g
export const evalTemplate = (tpl, data) => {
const evalTemplate = (tpl, data) => {
const getData = typeof data === 'function' ? (_, key) => data(key) : (_, key) => data[key]
return tpl.replace(TPL_RE, getData)
}
exports.evalTemplate = evalTemplate
// -------------------------------------------------------------------
const compileGlobPatternFragment = pattern => pattern.split('*').map(escapeRegExp).join('.*')
export const compileGlobPattern = pattern => {
const compileGlobPattern = pattern => {
const no = []
const yes = []
pattern.split(/[\s,]+/).forEach(pattern => {
@ -40,19 +41,22 @@ export const compileGlobPattern = pattern => {
return new RegExp(raw.join(''))
}
exports.compileGlobPattern = compileGlobPattern
// -------------------------------------------------------------------
export const required = name => {
const required = name => {
throw new Error(`missing required arg ${name}`)
}
exports.required = required
// -------------------------------------------------------------------
export const serializeError = error => ({
const serializeError = error => ({
...error, // Copy enumerable properties.
code: error.code,
message: error.message,
name: error.name,
stack: error.stack,
})
exports.serializeError = serializeError

View File

@ -1,6 +1,6 @@
/* eslint-env jest */
import { compileGlobPattern } from './utils'
const { compileGlobPattern } = require('./utils')
describe('compileGlobPattern()', () => {
it('works', () => {

View File

@ -33,6 +33,7 @@
>
> In case of conflict, the highest (lowest in previous list) `$version` wins.
- @xen-orchestra/log patch
- xen-api minor
- xo-server-auth-saml minor
- xo-server-backup-reports patch