source: trip-planner-front/node_modules/hdr-histogram-js/src/HistogramLogReader.ts@ 188ee53

Last change on this file since 188ee53 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 6.8 KB
Line 
1/*
2 * This is a TypeScript port of the original Java version, which was written by
3 * Gil Tene as described in
4 * https://github.com/HdrHistogram/HdrHistogram
5 * and released to the public domain, as explained at
6 * http://creativecommons.org/publicdomain/zero/1.0/
7 */
8import { NO_TAG } from "./Histogram";
9import { decodeFromCompressedBase64 } from "./encoding";
10import Histogram, { BitBucketSize } from "./Histogram";
11
12const TAG_PREFIX = "Tag=";
13const TAG_PREFIX_LENGTH = "Tag=".length;
14
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 */
56class HistogramLogReader {
57 startTimeSec: number;
58 baseTimeSec: number;
59
60 lines: string[];
61 currentLineIndex: number;
62 bitBucketSize: BitBucketSize;
63 useWebAssembly: boolean;
64
65 constructor(
66 logContent: string,
67 bitBucketSize: BitBucketSize = 32,
68 useWebAssembly: boolean = false
69 ) {
70 this.lines = splitLines(logContent);
71 this.currentLineIndex = 0;
72 this.bitBucketSize = bitBucketSize;
73 this.useWebAssembly = useWebAssembly;
74 }
75
76 /**
77 * Read the next interval histogram from the log. Returns a Histogram object if
78 * an interval line was found, or null if not.
79 * <p>Upon encountering any unexpected format errors in reading the next interval
80 * from the file, this method will return a null.
81 * @return a DecodedInterval, or a null if no appropriate interval found
82 */
83 public nextIntervalHistogram(
84 rangeStartTimeSec = 0,
85 rangeEndTimeSec = Number.MAX_VALUE
86 ): Histogram | null {
87 while (this.currentLineIndex < this.lines.length) {
88 const currentLine = this.lines[this.currentLineIndex];
89 this.currentLineIndex++;
90 if (currentLine.startsWith("#[StartTime:")) {
91 this.parseStartTimeFromLine(currentLine);
92 } else if (currentLine.startsWith("#[BaseTime:")) {
93 this.parseBaseTimeFromLine(currentLine);
94 } else if (
95 currentLine.startsWith("#") ||
96 currentLine.startsWith('"StartTimestamp"')
97 ) {
98 // skip legend & meta data for now
99 } else if (currentLine.includes(",")) {
100 const tokens = currentLine.split(",");
101 const [firstToken] = tokens;
102 let tag: string;
103 if (firstToken.startsWith(TAG_PREFIX)) {
104 tag = firstToken.substring(TAG_PREFIX_LENGTH);
105 tokens.shift();
106 } else {
107 tag = NO_TAG;
108 }
109
110 const [
111 rawLogTimeStampInSec,
112 rawIntervalLengthSec,
113 ,
114 base64Histogram,
115 ] = tokens;
116 const logTimeStampInSec = Number.parseFloat(rawLogTimeStampInSec);
117
118 if (!this.baseTimeSec) {
119 // No explicit base time noted. Deduce from 1st observed time (compared to start time):
120 if (logTimeStampInSec < this.startTimeSec - 365 * 24 * 3600.0) {
121 // Criteria Note: if log timestamp is more than a year in the past (compared to
122 // StartTime), we assume that timestamps in the log are not absolute
123 this.baseTimeSec = this.startTimeSec;
124 } else {
125 // Timestamps are absolute
126 this.baseTimeSec = 0.0;
127 }
128 }
129
130 if (rangeEndTimeSec < logTimeStampInSec) {
131 return null;
132 }
133 if (logTimeStampInSec < rangeStartTimeSec) {
134 continue;
135 }
136 const histogram = decodeFromCompressedBase64(
137 base64Histogram,
138 this.bitBucketSize,
139 this.useWebAssembly
140 );
141 histogram.startTimeStampMsec =
142 (this.baseTimeSec + logTimeStampInSec) * 1000;
143 const intervalLengthSec = Number.parseFloat(rawIntervalLengthSec);
144 histogram.endTimeStampMsec =
145 (this.baseTimeSec + logTimeStampInSec + intervalLengthSec) * 1000;
146
147 histogram.tag = tag;
148
149 return histogram;
150 }
151 }
152 return null;
153 }
154
155 private parseStartTimeFromLine(line: string) {
156 this.startTimeSec = Number.parseFloat(line.split(" ")[1]);
157 }
158
159 private parseBaseTimeFromLine(line: string) {
160 this.baseTimeSec = Number.parseFloat(line.split(" ")[1]);
161 }
162}
163
164const splitLines = (logContent: string) => logContent.split(/\r\n|\r|\n/g);
165
166const shouldIncludeNoTag = (lines: string[]) =>
167 lines.find(
168 (line) =>
169 !line.startsWith("#") &&
170 !line.startsWith('"') &&
171 !line.startsWith(TAG_PREFIX) &&
172 line.includes(",")
173 );
174
175export const listTags = (content: string) => {
176 const lines = splitLines(content);
177 const tags = lines
178 .filter((line) => line.includes(",") && line.startsWith(TAG_PREFIX))
179 .map((line) => line.substring(TAG_PREFIX_LENGTH, line.indexOf(",")));
180 const tagsWithoutDuplicates = new Set(tags);
181 const result = Array.from(tagsWithoutDuplicates);
182 if (shouldIncludeNoTag(lines)) {
183 result.unshift("NO TAG");
184 }
185 return result;
186};
187
188export default HistogramLogReader;
Note: See TracBrowser for help on using the repository browser.