/**
* This file is part of Sesatheque.
* Copyright 2014-2015, Association Sésamath
*
* Sesatheque is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3
* as published by the Free Software Foundation.
*
* Sesatheque is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Sesatheque (LICENCE.txt).
* @see http://www.gnu.org/licenses/agpl.txt
*
*
* Ce fichier fait partie de l'application Sésathèque, créée par l'association Sésamath.
*
* Sésathèque est un logiciel libre ; vous pouvez le redistribuer ou le modifier suivant
* les termes de la GNU Affero General Public License version 3 telle que publiée par la
* Free Software Foundation.
* Sésathèque est distribué dans l'espoir qu'il sera utile, mais SANS AUCUNE GARANTIE
* sans même la garantie tacite de QUALITÉ MARCHANDE ou d'ADÉQUATION à UN BUT PARTICULIER.
* Consultez la GNU Affero General Public License pour plus de détails.
* Vous devez avoir reçu une copie de la GNU General Public License en même temps que Sésathèque
* (cf LICENCE.txt et http://vvlibri.org/fr/Analyse/gnu-affero-general-public-license-v3-analyse
* pour une explication en français)
*/
'use strict'
var dump = require('./dump')
var tools = require('../index')
var formatDate = tools.formatDate
var hasProp = tools.hasProp
// attention, on duplique plus bas pour l'export
var levels = {
debug: 0,
info: 1,
notice: 1,
warn: 2,
warning: 2,
error: 3,
alert: 4,
critical: 4
}
var prefixes = [
'[debug]',
'[info]',
'[warning]',
'[error]',
'[CRITICAL]'
]
var loggers = [
console.info || console.log, // 0 => debug
console.log, // 1 => info|notice
console.warn || console.error || console.log, // 2 => warning
console.error || console.log, // 3 => error
console.error || console.log // 4 => critical|alert
]
// valeurs par défaut
/**
* Niveau minimum requis pour affichage (un niveau 2 affiche warning, error et critical)
* @private
* @type {number}
*/
var logLevel = levels.info
// on peut tourner sous node comme dans un navigateur
var isNode = typeof process !== 'undefined' && process.env
var isCluster = isNode && hasProp(process.env, 'NODE_APP_INSTANCE')
/**
* Flag pour savoir s'il faut ajouter la date en préfixe (false par défaut dans un navigateur et true ailleurs)
* @private
* @type {boolean}
*/
var hasDatePrefix = isNode && !isCluster // en mode cluster c'est pm2 qui met le préfixe de date
var datePrefix = '[%O]'
// idem pour le process
var hasProcessPrefix = isCluster
// NODE_APP_INSTANCE est une string, donc '0' ne sera pas falsy
var processPrefix = (isCluster && process.env.NODE_APP_INSTANCE) || 'N/A'
/**
* Écrit en console en ajoutant les préfixes
* @param {Array} args arguments d'une autre fct (pseudo Array)
* @param {number} level
* @private
*/
function _log (args, level) {
try {
var logger = loggers[level] || console.log
var prefix = ''
// date en 1er
if (hasDatePrefix) prefix = formatDate(datePrefix)
// puis processId
if (hasProcessPrefix) {
if (prefix) prefix += ' '
prefix += '[' + processPrefix + ']'
}
// puis logLevel
if (prefix) prefix += ' '
prefix += prefixes[level]
// avec le 1er argument sur la même ligne, mais on râle si y’en a pas (pour avoir la trace et retrouver l'appelant)
if (args.length < 1) args = [Error('fonction de log appelée sans contenu')]
logger(prefix, args[0])
// puis les autres sans préfixe
if (args.length > 1) for (var i = 1; i < args.length; i++) logger(args[i])
} catch (e) {
// rien, fallait un environnement décent avec console...
}
}
/**
* Un console.log qui plante pas sur les anciens IE (ou d'autres navigateurs qui n'auraient pas de console.log)
* @param {...*} arguments Nombre variable d'arguments, chacun sera passé à console.log ou console.error si c'est une erreur
* @service sesajstools/utils/log
*/
function log () {
if (logLevel < levels.warning) {
_log(arguments, levels.info)
}
}
// les logger des ≠ niveaux
log.debug = function debug () {
if (logLevel < levels.info) {
_log(arguments, levels.debug)
}
}
log.info = function info () {
if (logLevel < levels.warning) {
_log(arguments, levels.info)
}
}
log.warn = function warn () {
if (logLevel < levels.error) {
_log(arguments, levels.warning)
}
}
log.ifError = function error () {
if (error && logLevel < levels.critical) {
_log(arguments, levels.error)
}
}
log.error = function error () {
if (logLevel < levels.critical) {
_log(arguments, levels.error)
}
}
log.critical = function critical () {
_log(arguments, levels.critical)
}
/**
* Passe logLevel au niveau error (désactive donc seulement les infos et warnings…)
* Pour comptatibilité ascendante, à remplacer par log.setLevel(log.levels.error)
* @deprecated
*/
log.disable = function disable () {
logLevel = levels.error
}
/**
* Supprime le préfixe de date
*/
log.disableDatePrefix = function disableDatePrefix () {
hasDatePrefix = false
}
/**
* Supprime le préfixe de process
*/
log.disableProcessPrefix = function disableProcessPrefix () {
hasProcessPrefix = false
}
/**
* Dump un objet en console si le niveau de log est <= à celui indiqué
* @param {string} message affiché avant le dump
* @param {*} objToDump peut être n'importe quoi
* @param {object} [options]
* @param {string|number} [options.max=1000] le nb max de caractères affichés du dump
* @param {string|number} [options.level=debug] le niveau d'affichage souhaité
*/
log.dump = function (message, objToDump, options) {
if (typeof options !== 'object') options = {}
var level = options.level || levels.debug
if (typeof level === 'string') level = levels[level] || levels.debug
var dumped = dump(objToDump)
var max = options.max || 1000
if (dumped.length > max) dumped = dumped.substr(0, max) + '…'
if (logLevel <= level) _log([message, dumped], level)
}
/**
* Passe logLevel au niveau info (qui le rend plus bavard)
* Pour comptatibilité ascendante, à remplacer par log.setLevel(log.levels.info)
* @deprecated
*/
log.enable = function enable () {
logLevel = levels.info
}
/**
* Active le préfixe de date
*/
log.enableDatePrefix = function enableDatePrefix () {
hasDatePrefix = true
}
/**
* Active le préfixe de process
*/
log.enableProcessPrefix = function enableProcessPrefix () {
if (typeof process === 'undefined') console.error('process not available')
else hasProcessPrefix = true
}
/**
* Modifie le préfixe de date (cf formats gérés par formatDate)
* @param prefix
*/
log.setDatePrefix = function setDatePrefix (prefix) {
if (typeof prefix === 'string') datePrefix = prefix
else log.error(new Error('Le préfixe de log doit être une string'))
}
/**
* Modifie le niveau de log
* @param {number|string} level
*/
log.setLogLevel = function setLogLevel (level) {
switch (level) {
case 0:
case '0':
case 'debug':
logLevel = levels.debug
break
case 1:
case '1':
case 'info':
case 'notice':
logLevel = levels.notice
break
case 2:
case '2':
case 'warn':
case 'warning':
logLevel = levels.warning
break
case 3:
case '3':
case 'err':
case 'error':
logLevel = levels.error
break
case 4:
case '4':
case 'alert':
case 'crit':
case 'critical':
logLevel = levels.critical
break
default:
log.error('niveau d’erreur ' + level)
}
}
// et on ajoute les levels, mais sans ref pour empêcher de modifier nos constantes locale de l'extérieur
log.levels = {
debug: 0,
info: 1,
notice: 1,
warn: 2,
warning: 2,
error: 3,
alert: 4,
critical: 4
}
// ce serait plus élégant de cloner (quoi que, en es5 ça reste moche) mais ça empêche l'autocompletion
module.exports = log