LoggingEvent.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. /* eslint max-classes-per-file: ["error", 2] */
  2. /* eslint no-underscore-dangle: ["error", { "allow": ["_getLocationKeys"] }] */
  3. const flatted = require('flatted');
  4. const levels = require('./levels');
  5. class SerDe {
  6. constructor() {
  7. const deserialise = {
  8. __LOG4JS_undefined__: undefined,
  9. __LOG4JS_NaN__: Number('abc'),
  10. __LOG4JS_Infinity__: 1 / 0,
  11. '__LOG4JS_-Infinity__': -1 / 0,
  12. };
  13. this.deMap = deserialise;
  14. this.serMap = {};
  15. Object.keys(this.deMap).forEach((key) => {
  16. const value = this.deMap[key];
  17. this.serMap[value] = key;
  18. });
  19. }
  20. canSerialise(key) {
  21. if (typeof key === 'string') return false;
  22. return key in this.serMap;
  23. }
  24. serialise(key) {
  25. if (this.canSerialise(key)) return this.serMap[key];
  26. return key;
  27. }
  28. canDeserialise(key) {
  29. return key in this.deMap;
  30. }
  31. deserialise(key) {
  32. if (this.canDeserialise(key)) return this.deMap[key];
  33. return key;
  34. }
  35. }
  36. const serde = new SerDe();
  37. /**
  38. * @name LoggingEvent
  39. * @namespace Log4js
  40. */
  41. class LoggingEvent {
  42. /**
  43. * Models a logging event.
  44. * @constructor
  45. * @param {string} categoryName name of category
  46. * @param {Log4js.Level} level level of message
  47. * @param {Array} data objects to log
  48. * @param {Error} [error]
  49. * @author Seth Chisamore
  50. */
  51. constructor(categoryName, level, data, context, location, error) {
  52. this.startTime = new Date();
  53. this.categoryName = categoryName;
  54. this.data = data;
  55. this.level = level;
  56. this.context = Object.assign({}, context); // eslint-disable-line prefer-object-spread
  57. this.pid = process.pid;
  58. this.error = error;
  59. if (typeof location !== 'undefined') {
  60. if (!location || typeof location !== 'object' || Array.isArray(location))
  61. throw new TypeError(
  62. 'Invalid location type passed to LoggingEvent constructor'
  63. );
  64. this.constructor._getLocationKeys().forEach((key) => {
  65. if (typeof location[key] !== 'undefined') this[key] = location[key];
  66. });
  67. }
  68. }
  69. /** @private */
  70. static _getLocationKeys() {
  71. return [
  72. 'fileName',
  73. 'lineNumber',
  74. 'columnNumber',
  75. 'callStack',
  76. 'className',
  77. 'functionName',
  78. 'functionAlias',
  79. 'callerName',
  80. ];
  81. }
  82. serialise() {
  83. return flatted.stringify(this, (key, value) => {
  84. // JSON.stringify(new Error('test')) returns {}, which is not really useful for us.
  85. // The following allows us to serialize errors (semi) correctly.
  86. if (value instanceof Error) {
  87. // eslint-disable-next-line prefer-object-spread
  88. value = Object.assign(
  89. { message: value.message, stack: value.stack },
  90. value
  91. );
  92. }
  93. // JSON.stringify({a: Number('abc'), b: 1/0, c: -1/0}) returns {a: null, b: null, c: null}.
  94. // The following allows us to serialize to NaN, Infinity and -Infinity correctly.
  95. // JSON.stringify([undefined]) returns [null].
  96. // The following allows us to serialize to undefined correctly.
  97. return serde.serialise(value);
  98. });
  99. }
  100. static deserialise(serialised) {
  101. let event;
  102. try {
  103. const rehydratedEvent = flatted.parse(serialised, (key, value) => {
  104. if (value && value.message && value.stack) {
  105. const fakeError = new Error(value);
  106. Object.keys(value).forEach((k) => {
  107. fakeError[k] = value[k];
  108. });
  109. value = fakeError;
  110. }
  111. return serde.deserialise(value);
  112. });
  113. this._getLocationKeys().forEach((key) => {
  114. if (typeof rehydratedEvent[key] !== 'undefined') {
  115. if (!rehydratedEvent.location) rehydratedEvent.location = {};
  116. rehydratedEvent.location[key] = rehydratedEvent[key];
  117. }
  118. });
  119. event = new LoggingEvent(
  120. rehydratedEvent.categoryName,
  121. levels.getLevel(rehydratedEvent.level.levelStr),
  122. rehydratedEvent.data,
  123. rehydratedEvent.context,
  124. rehydratedEvent.location,
  125. rehydratedEvent.error
  126. );
  127. event.startTime = new Date(rehydratedEvent.startTime);
  128. event.pid = rehydratedEvent.pid;
  129. if (rehydratedEvent.cluster) {
  130. event.cluster = rehydratedEvent.cluster;
  131. }
  132. } catch (e) {
  133. event = new LoggingEvent('log4js', levels.ERROR, [
  134. 'Unable to parse log:',
  135. serialised,
  136. 'because: ',
  137. e,
  138. ]);
  139. }
  140. return event;
  141. }
  142. }
  143. module.exports = LoggingEvent;