MethodFactory.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. 'use strict';
  2. const noop = () => {};
  3. const levels = Symbol('valid log levels');
  4. const instance = Symbol('a log instance');
  5. module.exports = class MethodFactory {
  6. constructor(logger) {
  7. this[instance] = logger;
  8. this[levels] = {
  9. TRACE: 0,
  10. DEBUG: 1,
  11. INFO: 2,
  12. WARN: 3,
  13. ERROR: 4,
  14. SILENT: 5
  15. };
  16. }
  17. get levels() {
  18. return this[levels];
  19. }
  20. get logger() {
  21. return this[instance];
  22. }
  23. set logger(logger) {
  24. this[instance] = logger;
  25. }
  26. get methods() {
  27. return Object.keys(this.levels)
  28. .map(key => key.toLowerCase())
  29. .filter(key => key !== 'silent');
  30. }
  31. // eslint-disable-next-line class-methods-use-this
  32. bindMethod(obj, methodName) {
  33. const method = obj[methodName];
  34. if (typeof method.bind === 'function') {
  35. return method.bind(obj);
  36. }
  37. try {
  38. return Function.prototype.bind.call(method, obj);
  39. } catch (e) {
  40. // Missing bind shim or IE8 + Modernizr, fallback to wrapping
  41. return function result() {
  42. // eslint-disable-next-line prefer-rest-params
  43. return Function.prototype.apply.apply(method, [obj, arguments]);
  44. };
  45. }
  46. }
  47. distillLevel(level) {
  48. let result = level;
  49. if (typeof result === 'string' && typeof this.levels[result.toUpperCase()] !== 'undefined') {
  50. result = this.levels[result.toUpperCase()];
  51. }
  52. if (this.levelValid(result)) {
  53. return result;
  54. }
  55. }
  56. levelValid(level) {
  57. if (typeof level === 'number' && level >= 0 && level <= this.levels.SILENT) {
  58. return true;
  59. }
  60. return false;
  61. }
  62. /**
  63. * Build the best logging method possible for this env
  64. * Wherever possible we want to bind, not wrap, to preserve stack traces.
  65. * Since we're targeting modern browsers, there's no need to wait for the
  66. * console to become available.
  67. */
  68. // eslint-disable-next-line class-methods-use-this
  69. make(methodName) {
  70. if (methodName === 'debug') {
  71. methodName = 'log';
  72. }
  73. /* eslint-disable no-console */
  74. if (typeof console[methodName] !== 'undefined') {
  75. return this.bindMethod(console, methodName);
  76. } else if (typeof console.log !== 'undefined') {
  77. return this.bindMethod(console, 'log');
  78. }
  79. /* eslint-enable no-console */
  80. return noop;
  81. }
  82. replaceMethods(logLevel) {
  83. const level = this.distillLevel(logLevel);
  84. if (level == null) {
  85. throw new Error(`loglevelnext: replaceMethods() called with invalid level: ${logLevel}`);
  86. }
  87. if (!this.logger || this.logger.type !== 'LogLevel') {
  88. throw new TypeError('loglevelnext: Logger is undefined or invalid. Please specify a valid Logger instance.');
  89. }
  90. this.methods.forEach((methodName) => {
  91. const { [methodName.toUpperCase()]: methodLevel } = this.levels;
  92. this.logger[methodName] = (methodLevel < level) ? noop : this.make(methodName);
  93. });
  94. // Define log.log as an alias for log.debug
  95. this.logger.log = this.logger.debug;
  96. }
  97. };