/* * This is a TypeScript port of the original Java version, which was written by * Gil Tene as described in * https://github.com/HdrHistogram/HdrHistogram * and released to the public domain, as explained at * http://creativecommons.org/publicdomain/zero/1.0/ */ import { NO_TAG } from "./Histogram"; import { decodeFromCompressedBase64 } from "./encoding"; import Histogram, { BitBucketSize } from "./Histogram"; const TAG_PREFIX = "Tag="; const TAG_PREFIX_LENGTH = "Tag=".length; /** * A histogram log reader. *
* Histogram logs are used to capture full fidelity, per-time-interval * histograms of a recorded value. *
* For example, a histogram log can be used to capture high fidelity * reaction-time logs for some measured system or subsystem component. * Such a log would capture a full reaction time histogram for each * logged interval, and could be used to later reconstruct a full * HdrHistogram of the measured reaction time behavior for any arbitrary * time range within the log, by adding [only] the relevant interval * histograms. *
* A valid interval description line contains an optional Tag=tagString * text field, followed by an interval description. *
* A valid interval description must contain exactly four text fields: *
Upon encountering any unexpected format errors in reading the next interval * from the file, this method will return a null. * @return a DecodedInterval, or a null if no appropriate interval found */ public nextIntervalHistogram( rangeStartTimeSec = 0, rangeEndTimeSec = Number.MAX_VALUE ): Histogram | null { while (this.currentLineIndex < this.lines.length) { const currentLine = this.lines[this.currentLineIndex]; this.currentLineIndex++; if (currentLine.startsWith("#[StartTime:")) { this.parseStartTimeFromLine(currentLine); } else if (currentLine.startsWith("#[BaseTime:")) { this.parseBaseTimeFromLine(currentLine); } else if ( currentLine.startsWith("#") || currentLine.startsWith('"StartTimestamp"') ) { // skip legend & meta data for now } else if (currentLine.includes(",")) { const tokens = currentLine.split(","); const [firstToken] = tokens; let tag: string; if (firstToken.startsWith(TAG_PREFIX)) { tag = firstToken.substring(TAG_PREFIX_LENGTH); tokens.shift(); } else { tag = NO_TAG; } const [ rawLogTimeStampInSec, rawIntervalLengthSec, , base64Histogram, ] = tokens; const logTimeStampInSec = Number.parseFloat(rawLogTimeStampInSec); if (!this.baseTimeSec) { // No explicit base time noted. Deduce from 1st observed time (compared to start time): if (logTimeStampInSec < this.startTimeSec - 365 * 24 * 3600.0) { // Criteria Note: if log timestamp is more than a year in the past (compared to // StartTime), we assume that timestamps in the log are not absolute this.baseTimeSec = this.startTimeSec; } else { // Timestamps are absolute this.baseTimeSec = 0.0; } } if (rangeEndTimeSec < logTimeStampInSec) { return null; } if (logTimeStampInSec < rangeStartTimeSec) { continue; } const histogram = decodeFromCompressedBase64( base64Histogram, this.bitBucketSize, this.useWebAssembly ); histogram.startTimeStampMsec = (this.baseTimeSec + logTimeStampInSec) * 1000; const intervalLengthSec = Number.parseFloat(rawIntervalLengthSec); histogram.endTimeStampMsec = (this.baseTimeSec + logTimeStampInSec + intervalLengthSec) * 1000; histogram.tag = tag; return histogram; } } return null; } private parseStartTimeFromLine(line: string) { this.startTimeSec = Number.parseFloat(line.split(" ")[1]); } private parseBaseTimeFromLine(line: string) { this.baseTimeSec = Number.parseFloat(line.split(" ")[1]); } } const splitLines = (logContent: string) => logContent.split(/\r\n|\r|\n/g); const shouldIncludeNoTag = (lines: string[]) => lines.find( (line) => !line.startsWith("#") && !line.startsWith('"') && !line.startsWith(TAG_PREFIX) && line.includes(",") ); export const listTags = (content: string) => { const lines = splitLines(content); const tags = lines .filter((line) => line.includes(",") && line.startsWith(TAG_PREFIX)) .map((line) => line.substring(TAG_PREFIX_LENGTH, line.indexOf(","))); const tagsWithoutDuplicates = new Set(tags); const result = Array.from(tagsWithoutDuplicates); if (shouldIncludeNoTag(lines)) { result.unshift("NO TAG"); } return result; }; export default HistogramLogReader;