const serializeError = require( "serialize-error" ); const isFunction = require( "lodash.isfunction" ); const isString = require( "lodash.isstring" ); const stringify = require( "json-stringify-pretty-compact" ); const toXML = require( "to-xml" ).toXML; const promisify = require( "util" ).promisify; const error = require( "./errors" ); const idFnNotAFunction = error.idFnNotAFunction; const pathNotAString = error.pathNotAString; const DEFAULT_OUTPUT_FOLDER = "."; /** * @public * @author Pedro Miguel P. S. Martins * @version 1.0.2 * @module fs-error-logger * * @desc Writes errors into files in both JSON and XML. */ /** * @typedef options * @type {Object} * @property {string} [outputFolder="."] The default output folder. * @property {function} [idFn=Date.now] The default id generator * function. * * @desc Options object determining output folder and id generator function. */ /** * @alias module:fs-error-logger.logger * @param {Object} deps * @param {Object} deps.fs File system object that allows the logger to * write the files. Must have an async * <code>writeFile</code> function and an async * <code>mkdir</code> function. * @param {module:fs-error-logger~options} [opts] Options object determining output folder and * id generator function. * @returns {Object} * * @desc Returns a logger object, with the API that allows you to write errors to files. */ const logger = ( { fs }, { outputFolder = DEFAULT_OUTPUT_FOLDER, idFn = Date.now } ) => { if ( !isString( outputFolder ) ) throw pathNotAString( outputFolder ); if ( !isFunction( idFn ) ) throw idFnNotAFunction( idFn ); if ( outputFolder[ outputFolder.length - 1 ] === "/" ) outputFolder = outputFolder.substring( 0, outputFolder.length - 1 ); const writeFile = promisify( fs.writeFile ); const mkdir = promisify( fs.mkdir ); /** * @public * @function logJSON * @param {Error} error The error object we want to write. * @returns {Promise} * * @desc Resolves if the error was successfully written to a JSON file or rejects otherwise. The format of the file will be: <code>${error.name}_${idFn()}.json</code>. */ const logJSON = error => write( `${outputFolder}/${error.name}_${idFn()}.json`, stringify( serializeError( error ), { indent: 4 } ) ); /** * @public * @function logXML * @param {Error} error The error object we want to write. * @returns {Promise} * * @desc Resolves if the error was successfully written to a XML file or rejects otherwise. The format of the file will be: <code>${error.name}_${idFn()}.xml</code>. */ const logXML = error => write( `${outputFolder}/${error.name}_${idFn()}.xml`, toXML( { error: serializeError( error ) } ) ); /** * @private * @function write * @param {string} fileName The name of the file to be created. * @param {string} fileContent The content of the file. * @returns {Promise} * * @desc If successful, resolves after creates a file with given name * and content.Otherwise rejects with error. */ const write = ( fileName, fileContent ) => { if ( !fs.existsSync( outputFolder ) ) return mkdir( outputFolder ) .then( () => writeFile( fileName, fileContent ) ); return writeFile( fileName, fileContent ); }; /** * @public * @function setOutputFolder * @param {string} newFolder The path of the output folder. * @throws {PathNotAString} If <code>newFolder</code> is not a string. * * @desc Sets the output folder path to the one passed. */ const setOutputFolder = newFolder => { if ( !isString( newFolder ) ) throw pathNotAString( newFolder ); if ( newFolder === "" ) { outputFolder = DEFAULT_OUTPUT_FOLDER; } else { outputFolder = newFolder; } }; /** * @public * @function getOutputFolder * @returns {string} * * @desc Returns the current output folder path. */ const getOutputFolder = () => outputFolder; /** * @public * @function setIdFn * @param {function} newFn A new Id generator function. * @throws {IdFnNotAFunction} If <code>newFn</code> is not a function. * * @desc Sets the current Id generator function to the one given. */ const setIdFn = newFn => { if ( !isFunction( newFn ) ) throw idFnNotAFunction( newFn ); idFn = newFn; }; /** * @public * @function getIdFn * @returns {function} * * @desc Returns the current id generator function. */ const getIdFn = () => idFn; return { logJSON, logXML, setOutputFolder, getOutputFolder, setIdFn, getIdFn }; }; module.exports.logger = logger; const fs = require( "fs" ); /** * @private * @param {module:fs-error-logger~options} [opts] Options object determining output folder and * id generator function. * @returns {Object} * * @desc Returns a logger object with all the dependencies pre-injected and with the given options, ready to use. * * @example * const loggerFactory = require("fs-error-logger"); * const logger = loggerFactory({ outputFolder: ".", idFn: Date.now }); */ module.exports = opts => { if ( opts === undefined || opts === null ) return logger( { fs }, {} ); return logger( { fs }, opts ); };