trace-event.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. "use strict";
  2. /**
  3. * trace-event - A library to create a trace of your node app per
  4. * Google's Trace Event format:
  5. * // JSSTYLED
  6. * https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU
  7. */
  8. Object.defineProperty(exports, "__esModule", { value: true });
  9. exports.Tracer = void 0;
  10. const stream_1 = require("stream");
  11. function evCommon() {
  12. var hrtime = process.hrtime(); // [seconds, nanoseconds]
  13. var ts = hrtime[0] * 1000000 + Math.round(hrtime[1] / 1000); // microseconds
  14. return {
  15. ts,
  16. pid: process.pid,
  17. tid: process.pid // no meaningful tid for node.js
  18. };
  19. }
  20. class Tracer extends stream_1.Readable {
  21. constructor(opts = {}) {
  22. super();
  23. this.noStream = false;
  24. this.events = [];
  25. if (typeof opts !== "object") {
  26. throw new Error("Invalid options passed (must be an object)");
  27. }
  28. if (opts.parent != null && typeof opts.parent !== "object") {
  29. throw new Error("Invalid option (parent) passed (must be an object)");
  30. }
  31. if (opts.fields != null && typeof opts.fields !== "object") {
  32. throw new Error("Invalid option (fields) passed (must be an object)");
  33. }
  34. if (opts.objectMode != null &&
  35. (opts.objectMode !== true && opts.objectMode !== false)) {
  36. throw new Error("Invalid option (objectsMode) passed (must be a boolean)");
  37. }
  38. this.noStream = opts.noStream || false;
  39. this.parent = opts.parent;
  40. if (this.parent) {
  41. this.fields = Object.assign({}, opts.parent && opts.parent.fields);
  42. }
  43. else {
  44. this.fields = {};
  45. }
  46. if (opts.fields) {
  47. Object.assign(this.fields, opts.fields);
  48. }
  49. if (!this.fields.cat) {
  50. // trace-viewer *requires* `cat`, so let's have a fallback.
  51. this.fields.cat = "default";
  52. }
  53. else if (Array.isArray(this.fields.cat)) {
  54. this.fields.cat = this.fields.cat.join(",");
  55. }
  56. if (!this.fields.args) {
  57. // trace-viewer *requires* `args`, so let's have a fallback.
  58. this.fields.args = {};
  59. }
  60. if (this.parent) {
  61. // TODO: Not calling Readable ctor here. Does that cause probs?
  62. // Probably if trying to pipe from the child.
  63. // Might want a serpate TracerChild class for these guys.
  64. this._push = this.parent._push.bind(this.parent);
  65. }
  66. else {
  67. this._objectMode = Boolean(opts.objectMode);
  68. var streamOpts = { objectMode: this._objectMode };
  69. if (this._objectMode) {
  70. this._push = this.push;
  71. }
  72. else {
  73. this._push = this._pushString;
  74. streamOpts.encoding = "utf8";
  75. }
  76. stream_1.Readable.call(this, streamOpts);
  77. }
  78. }
  79. /**
  80. * If in no streamMode in order to flush out the trace
  81. * you need to call flush.
  82. */
  83. flush() {
  84. if (this.noStream === true) {
  85. for (const evt of this.events) {
  86. this._push(evt);
  87. }
  88. this._flush();
  89. }
  90. }
  91. _read(_) { }
  92. _pushString(ev) {
  93. var separator = "";
  94. if (!this.firstPush) {
  95. this.push("[");
  96. this.firstPush = true;
  97. }
  98. else {
  99. separator = ",\n";
  100. }
  101. this.push(separator + JSON.stringify(ev), "utf8");
  102. }
  103. _flush() {
  104. if (!this._objectMode) {
  105. this.push("]");
  106. }
  107. }
  108. child(fields) {
  109. return new Tracer({
  110. parent: this,
  111. fields: fields
  112. });
  113. }
  114. begin(fields) {
  115. return this.mkEventFunc("B")(fields);
  116. }
  117. end(fields) {
  118. return this.mkEventFunc("E")(fields);
  119. }
  120. completeEvent(fields) {
  121. return this.mkEventFunc("X")(fields);
  122. }
  123. instantEvent(fields) {
  124. return this.mkEventFunc("I")(fields);
  125. }
  126. mkEventFunc(ph) {
  127. return (fields) => {
  128. var ev = evCommon();
  129. // Assign the event phase.
  130. ev.ph = ph;
  131. if (fields) {
  132. if (typeof fields === "string") {
  133. ev.name = fields;
  134. }
  135. else {
  136. for (const k of Object.keys(fields)) {
  137. if (k === "cat") {
  138. ev.cat = fields.cat.join(",");
  139. }
  140. else {
  141. ev[k] = fields[k];
  142. }
  143. }
  144. }
  145. }
  146. if (!this.noStream) {
  147. this._push(ev);
  148. }
  149. else {
  150. this.events.push(ev);
  151. }
  152. };
  153. }
  154. }
  155. exports.Tracer = Tracer;
  156. /*
  157. * These correspond to the "Async events" in the Trace Events doc.
  158. *
  159. * Required fields:
  160. * - name
  161. * - id
  162. *
  163. * Optional fields:
  164. * - cat (array)
  165. * - args (object)
  166. * - TODO: stack fields, other optional fields?
  167. *
  168. * Dev Note: We don't explicitly assert that correct fields are
  169. * used for speed (premature optimization alert!).
  170. */