2
\$\begingroup\$

I provide a class to use but for convenience I want the class to implement some instance methods as static methods. This was fast and concise but it feels smelly, probably because I'm modifying the class after I create an instance of it. Is this ok?

  const logLevel    = !args['v'] || args['v'] === true ? 0 : parseInt(args['v']);
  const chalk       = new Chalk.constructor({enabled: useColor});
  const nameLength  = args['format-name-length'] || 25;

  class Logger{
    constructor(name='', _stdout=stdout, _stderr=stderr){
      this._name = this._getPaddedName(name);
      this._stdout = _stdout;
      this._stderr = _stderr;
      this.error.bind(this);
      this.info.bind(this);
      this.warn.bind(this);
    }
    error(level, ...args){
      if(level <= logLevel){
        this._print(this._stderr, args, 'red', level);
      }
    }
    info(level, ...args){
      if(level <= logLevel){
        this._print(this._stdout, args, 'blue', level);
      }
    }
    warn(level, ...args){
      if(level <= logLevel){
        this._print(this._stderr, args, 'yellow', level);
      }
    }
    _getLogId(color, level){
      return chalk[color](
        moment().format("YYYY:MM:DD h:mm:ss")
        + ` ${this._name}`
        + ` [${level}]: `
      );
    }
    _getPaddedName(name){
      let nameArray = name.split('')
        .slice(0, nameLength);
      if(nameLength > name.length){
        nameArray = nameArray
          .concat(new Array(nameLength - name.length).fill(null));
      }
      let ellipsis = nameArray.slice(nameLength - 3)
        .reduce((prev, next)=>!!(prev || next), false);
      return nameArray
        .slice(0, nameLength - 3)
        .map(char=>char || '-')
        .concat(ellipsis ? ['.', '.', '.'] : ['-', '-', '-'])
        .join('');
    }
    _format(arg, color, level){
      let logId = this._getLogId(color, level);
      return util.format(arg)
        .replace(/^/, logId)
        .replace(/(\r?\n)/g, `\n${logId}`)
        .replace(/((?:\r?\n)?$)/, '\n');
    }
    _print(stream, args, color, level){
      args.forEach(arg=>{
        if(arg !== ''){
          stream.write(this._format(arg, color, level));
        }
      });
    }
  }

  let defaultLogger = new Logger('logger');

  Logger.error = defaultLogger.error.bind(defaultLogger);
  Logger.info = defaultLogger.info.bind(defaultLogger);
  Logger.warn = defaultLogger.warn.bind(defaultLogger);

  return Logger;
\$\endgroup\$
3
  • 1
    \$\begingroup\$ I agree this isn't a clean approach. To give a good answer, though, can you show some examples of client code. That is, it's not entirely clear how you intend to use this logger and what it's responsibilities should be. Also, what features, eg, was console.log missing that inspired you to write this? \$\endgroup\$ Commented Jan 21, 2017 at 18:46
  • \$\begingroup\$ I need to filter the log output by level. Also, I need to prefix every line of multi line strings with an identifier which console doesn't do. \$\endgroup\$ Commented Jan 21, 2017 at 19:56
  • \$\begingroup\$ Our site standard, as stated in the How to Ask guidelines, is to state the purpose of the code in the title, rather than your concern about the code. I've rolled back the title you set in Rev 3. \$\endgroup\$ Commented Jan 22, 2017 at 6:58

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.