log4js.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. /**
  2. * @fileoverview log4js is a library to log in JavaScript in similar manner
  3. * than in log4j for Java (but not really).
  4. *
  5. * <h3>Example:</h3>
  6. * <pre>
  7. * const logging = require('log4js');
  8. * const log = logging.getLogger('some-category');
  9. *
  10. * //call the log
  11. * log.trace('trace me' );
  12. * </pre>
  13. *
  14. * NOTE: the authors below are the original browser-based log4js authors
  15. * don't try to contact them about bugs in this version :)
  16. * @author Stephan Strittmatter - http://jroller.com/page/stritti
  17. * @author Seth Chisamore - http://www.chisamore.com
  18. * @since 2005-05-20
  19. * Website: http://log4js.berlios.de
  20. */
  21. const debug = require('debug')('log4js:main');
  22. const fs = require('fs');
  23. const deepClone = require('rfdc')({ proto: true });
  24. const configuration = require('./configuration');
  25. const layouts = require('./layouts');
  26. const levels = require('./levels');
  27. const appenders = require('./appenders');
  28. const categories = require('./categories');
  29. const Logger = require('./logger');
  30. const clustering = require('./clustering');
  31. const connectLogger = require('./connect-logger');
  32. const recordingModule = require('./appenders/recording');
  33. let enabled = false;
  34. function sendLogEventToAppender(logEvent) {
  35. if (!enabled) return;
  36. debug('Received log event ', logEvent);
  37. const categoryAppenders = categories.appendersForCategory(
  38. logEvent.categoryName
  39. );
  40. categoryAppenders.forEach((appender) => {
  41. appender(logEvent);
  42. });
  43. }
  44. function loadConfigurationFile(filename) {
  45. debug(`Loading configuration from ${filename}`);
  46. try {
  47. return JSON.parse(fs.readFileSync(filename, 'utf8'));
  48. } catch (e) {
  49. throw new Error(
  50. `Problem reading config from file "${filename}". Error was ${e.message}`,
  51. e
  52. );
  53. }
  54. }
  55. function configure(configurationFileOrObject) {
  56. if (enabled) {
  57. // eslint-disable-next-line no-use-before-define
  58. shutdown();
  59. }
  60. let configObject = configurationFileOrObject;
  61. if (typeof configObject === 'string') {
  62. configObject = loadConfigurationFile(configurationFileOrObject);
  63. }
  64. debug(`Configuration is ${configObject}`);
  65. configuration.configure(deepClone(configObject));
  66. clustering.onMessage(sendLogEventToAppender);
  67. enabled = true;
  68. // eslint-disable-next-line no-use-before-define
  69. return log4js;
  70. }
  71. function isConfigured() {
  72. return enabled;
  73. }
  74. function recording() {
  75. return recordingModule;
  76. }
  77. /**
  78. * This callback type is called `shutdownCallback` and is displayed as a global symbol.
  79. *
  80. * @callback shutdownCallback
  81. * @param {Error} [error]
  82. */
  83. /**
  84. * Shutdown all log appenders. This will first disable all writing to appenders
  85. * and then call the shutdown function each appender.
  86. *
  87. * @param {shutdownCallback} [callback] - The callback to be invoked once all appenders have
  88. * shutdown. If an error occurs, the callback will be given the error object
  89. * as the first argument.
  90. */
  91. function shutdown(callback = () => {}) {
  92. if (typeof callback !== 'function') {
  93. throw new TypeError('Invalid callback passed to shutdown');
  94. }
  95. debug('Shutdown called. Disabling all log writing.');
  96. // First, disable all writing to appenders. This prevents appenders from
  97. // not being able to be drained because of run-away log writes.
  98. enabled = false;
  99. // Clone out to maintain a reference
  100. const appendersToCheck = Array.from(appenders.values());
  101. // Reset immediately to prevent leaks
  102. appenders.init();
  103. categories.init();
  104. // Count the number of shutdown functions
  105. const shutdownFunctions = appendersToCheck.reduce(
  106. (accum, next) => (next.shutdown ? accum + 1 : accum),
  107. 0
  108. );
  109. if (shutdownFunctions === 0) {
  110. debug('No appenders with shutdown functions found.');
  111. callback();
  112. }
  113. let completed = 0;
  114. let error;
  115. debug(`Found ${shutdownFunctions} appenders with shutdown functions.`);
  116. function complete(err) {
  117. error = error || err;
  118. completed += 1;
  119. debug(`Appender shutdowns complete: ${completed} / ${shutdownFunctions}`);
  120. if (completed >= shutdownFunctions) {
  121. debug('All shutdown functions completed.');
  122. callback(error);
  123. }
  124. }
  125. // Call each of the shutdown functions
  126. appendersToCheck
  127. .filter((a) => a.shutdown)
  128. .forEach((a) => a.shutdown(complete));
  129. }
  130. /**
  131. * Get a logger instance.
  132. * @static
  133. * @param {string} [category=default]
  134. * @return {Logger} instance of logger for the category
  135. */
  136. function getLogger(category) {
  137. if (!enabled) {
  138. configure(
  139. process.env.LOG4JS_CONFIG || {
  140. appenders: { out: { type: 'stdout' } },
  141. categories: { default: { appenders: ['out'], level: 'OFF' } },
  142. }
  143. );
  144. }
  145. return new Logger(category || 'default');
  146. }
  147. /**
  148. * @name log4js
  149. * @namespace Log4js
  150. * @property getLogger
  151. * @property configure
  152. * @property shutdown
  153. */
  154. const log4js = {
  155. getLogger,
  156. configure,
  157. isConfigured,
  158. shutdown,
  159. connectLogger,
  160. levels,
  161. addLayout: layouts.addLayout,
  162. recording,
  163. };
  164. module.exports = log4js;