HistogramLogReader.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.listTags = void 0;
  4. /*
  5. * This is a TypeScript port of the original Java version, which was written by
  6. * Gil Tene as described in
  7. * https://github.com/HdrHistogram/HdrHistogram
  8. * and released to the public domain, as explained at
  9. * http://creativecommons.org/publicdomain/zero/1.0/
  10. */
  11. const Histogram_1 = require("./Histogram");
  12. const encoding_1 = require("./encoding");
  13. const TAG_PREFIX = "Tag=";
  14. const TAG_PREFIX_LENGTH = "Tag=".length;
  15. /**
  16. * A histogram log reader.
  17. * <p>
  18. * Histogram logs are used to capture full fidelity, per-time-interval
  19. * histograms of a recorded value.
  20. * <p>
  21. * For example, a histogram log can be used to capture high fidelity
  22. * reaction-time logs for some measured system or subsystem component.
  23. * Such a log would capture a full reaction time histogram for each
  24. * logged interval, and could be used to later reconstruct a full
  25. * HdrHistogram of the measured reaction time behavior for any arbitrary
  26. * time range within the log, by adding [only] the relevant interval
  27. * histograms.
  28. * <h3>Histogram log format:</h3>
  29. * A histogram log file consists of text lines. Lines beginning with
  30. * the "#" character are optional and treated as comments. Lines
  31. * containing the legend (starting with "Timestamp") are also optional
  32. * and ignored in parsing the histogram log. All other lines must
  33. * be valid interval description lines. Text fields are delimited by
  34. * commas, spaces.
  35. * <p>
  36. * A valid interval description line contains an optional Tag=tagString
  37. * text field, followed by an interval description.
  38. * <p>
  39. * A valid interval description must contain exactly four text fields:
  40. * <ul>
  41. * <li>StartTimestamp: The first field must contain a number parse-able as a Double value,
  42. * representing the start timestamp of the interval in seconds.</li>
  43. * <li>intervalLength: The second field must contain a number parse-able as a Double value,
  44. * representing the length of the interval in seconds.</li>
  45. * <li>Interval_Max: The third field must contain a number parse-able as a Double value,
  46. * which generally represents the maximum value of the interval histogram.</li>
  47. * <li>Interval_Compressed_Histogram: The fourth field must contain a text field
  48. * parse-able as a Base64 text representation of a compressed HdrHistogram.</li>
  49. * </ul>
  50. * The log file may contain an optional indication of a starting time. Starting time
  51. * is indicated using a special comments starting with "#[StartTime: " and followed
  52. * by a number parse-able as a double, representing the start time (in seconds)
  53. * that may be added to timestamps in the file to determine an absolute
  54. * timestamp (e.g. since the epoch) for each interval.
  55. */
  56. class HistogramLogReader {
  57. constructor(logContent, bitBucketSize = 32, useWebAssembly = false) {
  58. this.lines = splitLines(logContent);
  59. this.currentLineIndex = 0;
  60. this.bitBucketSize = bitBucketSize;
  61. this.useWebAssembly = useWebAssembly;
  62. }
  63. /**
  64. * Read the next interval histogram from the log. Returns a Histogram object if
  65. * an interval line was found, or null if not.
  66. * <p>Upon encountering any unexpected format errors in reading the next interval
  67. * from the file, this method will return a null.
  68. * @return a DecodedInterval, or a null if no appropriate interval found
  69. */
  70. nextIntervalHistogram(rangeStartTimeSec = 0, rangeEndTimeSec = Number.MAX_VALUE) {
  71. while (this.currentLineIndex < this.lines.length) {
  72. const currentLine = this.lines[this.currentLineIndex];
  73. this.currentLineIndex++;
  74. if (currentLine.startsWith("#[StartTime:")) {
  75. this.parseStartTimeFromLine(currentLine);
  76. }
  77. else if (currentLine.startsWith("#[BaseTime:")) {
  78. this.parseBaseTimeFromLine(currentLine);
  79. }
  80. else if (currentLine.startsWith("#") ||
  81. currentLine.startsWith('"StartTimestamp"')) {
  82. // skip legend & meta data for now
  83. }
  84. else if (currentLine.includes(",")) {
  85. const tokens = currentLine.split(",");
  86. const [firstToken] = tokens;
  87. let tag;
  88. if (firstToken.startsWith(TAG_PREFIX)) {
  89. tag = firstToken.substring(TAG_PREFIX_LENGTH);
  90. tokens.shift();
  91. }
  92. else {
  93. tag = Histogram_1.NO_TAG;
  94. }
  95. const [rawLogTimeStampInSec, rawIntervalLengthSec, , base64Histogram,] = tokens;
  96. const logTimeStampInSec = Number.parseFloat(rawLogTimeStampInSec);
  97. if (!this.baseTimeSec) {
  98. // No explicit base time noted. Deduce from 1st observed time (compared to start time):
  99. if (logTimeStampInSec < this.startTimeSec - 365 * 24 * 3600.0) {
  100. // Criteria Note: if log timestamp is more than a year in the past (compared to
  101. // StartTime), we assume that timestamps in the log are not absolute
  102. this.baseTimeSec = this.startTimeSec;
  103. }
  104. else {
  105. // Timestamps are absolute
  106. this.baseTimeSec = 0.0;
  107. }
  108. }
  109. if (rangeEndTimeSec < logTimeStampInSec) {
  110. return null;
  111. }
  112. if (logTimeStampInSec < rangeStartTimeSec) {
  113. continue;
  114. }
  115. const histogram = encoding_1.decodeFromCompressedBase64(base64Histogram, this.bitBucketSize, this.useWebAssembly);
  116. histogram.startTimeStampMsec =
  117. (this.baseTimeSec + logTimeStampInSec) * 1000;
  118. const intervalLengthSec = Number.parseFloat(rawIntervalLengthSec);
  119. histogram.endTimeStampMsec =
  120. (this.baseTimeSec + logTimeStampInSec + intervalLengthSec) * 1000;
  121. histogram.tag = tag;
  122. return histogram;
  123. }
  124. }
  125. return null;
  126. }
  127. parseStartTimeFromLine(line) {
  128. this.startTimeSec = Number.parseFloat(line.split(" ")[1]);
  129. }
  130. parseBaseTimeFromLine(line) {
  131. this.baseTimeSec = Number.parseFloat(line.split(" ")[1]);
  132. }
  133. }
  134. const splitLines = (logContent) => logContent.split(/\r\n|\r|\n/g);
  135. const shouldIncludeNoTag = (lines) => lines.find((line) => !line.startsWith("#") &&
  136. !line.startsWith('"') &&
  137. !line.startsWith(TAG_PREFIX) &&
  138. line.includes(","));
  139. exports.listTags = (content) => {
  140. const lines = splitLines(content);
  141. const tags = lines
  142. .filter((line) => line.includes(",") && line.startsWith(TAG_PREFIX))
  143. .map((line) => line.substring(TAG_PREFIX_LENGTH, line.indexOf(",")));
  144. const tagsWithoutDuplicates = new Set(tags);
  145. const result = Array.from(tagsWithoutDuplicates);
  146. if (shouldIncludeNoTag(lines)) {
  147. result.unshift("NO TAG");
  148. }
  149. return result;
  150. };
  151. exports.default = HistogramLogReader;
  152. //# sourceMappingURL=HistogramLogReader.js.map