text: var util = require('util');
  var _ = require('lodash');
  
  var loggerInstance;
  
  var defaultProvider = {
      log   : console.log,
      debug : console.log,    // use .log(); since console does not have .debug()
      info  : console.info,
      warn  : console.warn,
      error : console.error
  }
  
  // log level 'weight'
  var LEVELS = {
      debug  : 10,
      info   : 20,
      warn   : 30,
      error  : 50,
      silent : 80
  }
  
  module.exports = {
      // singleton
      getInstance : function () {
          if (!loggerInstance) {
              loggerInstance = new Logger();
          }
  
          return loggerInstance;
      }
  };
  
  function Logger () {
      var logLevel;
      var provider;
  
      var api = {
          log : log,
          debug : debug,
          info : info,
          warn : warn,
          error : error,
          setLevel : function (v) {
              if (isValidLevel(v)) {
                  logLevel = v;
              }
          },
          setProvider : function (fn) {
              if (fn && isValidProvider(fn)) {
                  provider = fn(defaultProvider);
              }
          }
      };
  
  
      init();
  
      return api;
  
      function init () {
          api.setLevel('info');
          api.setProvider(function () {
              return defaultProvider;
          });
      }
  
      // log will log messages, regardless of logLevels
      function log () {
          provider.log(_interpolate.apply(null, arguments));
      }
  
      function debug () {
          if (_showLevel('debug')) {
              provider.debug(_interpolate.apply(null, arguments));
          }
      }
  
      function info () {
          if (_showLevel('info')) {
              provider.info(_interpolate.apply(null, arguments));
          }
      }
  
      function warn () {
          if (_showLevel('warn')) {
              provider.warn(_interpolate.apply(null, arguments));
          }
      }
  
      function error () {
          if (_showLevel('error')) {
              provider.error(_interpolate.apply(null, arguments));
          }
      }
  
      /**
       * Decide to log or not to log, based on the log levels 'weight'
       * @param  {String}  showLevel [debug, info, warn, error, silent]
       * @return {Boolean}
       */
      function _showLevel (showLevel) {
          var result = false;
          var currentLogLevel = LEVELS[logLevel];
  
          if (currentLogLevel && (currentLogLevel <= LEVELS[showLevel])) {
              result = true;
          }
  
          return result;
      }
  
      // make sure logged messages and its data are return interpolated
      // make it possible for additional log data, such date/time or custom prefix.
      function _interpolate () {
          var fn = _.spread(util.format);
          var result = fn(_.slice(arguments));
  
          return result;
      }
  
      function isValidProvider (fnProvider) {
          var result = true;
  
          if (fnProvider && !_.isFunction(fnProvider)) {
              throw new Error('[HPM] Log provider config error. Expecting a function.');
          }
  
          return result;
      }
  
      function isValidLevel (levelName) {
          var validLevels = _.keys(LEVELS);
          var isValid = _.includes(validLevels, levelName);
  
          if (!isValid) {
              throw new Error('[HPM] Log level error. Invalid logLevel.');
          }
  
          return isValid;
      }
  }
  
  