|
@@ -1,9 +1,9 @@
|
|
|
// Third party
|
|
// Third party
|
|
|
-import { Component, ViewChild } from '@angular/core';
|
|
|
|
|
|
|
+import { Component } from '@angular/core';
|
|
|
import { ActivatedRoute } from '@angular/router';
|
|
import { ActivatedRoute } from '@angular/router';
|
|
|
|
|
|
|
|
// Common
|
|
// Common
|
|
|
-import { ServiceCheckData } from '../../../../../common/lib/http-check-data.module';
|
|
|
|
|
|
|
+import { HttpCheckStatus, ServiceCheckData } from '../../../../../common/lib/http-check-data.module';
|
|
|
|
|
|
|
|
// App
|
|
// App
|
|
|
import { convertToStatusTimelineData } from 'src/app/lib/conversions.lib';
|
|
import { convertToStatusTimelineData } from 'src/app/lib/conversions.lib';
|
|
@@ -53,7 +53,7 @@ export class ServiceCheckDetailPageComponent {
|
|
|
|
|
|
|
|
const pageEnd = end;
|
|
const pageEnd = end;
|
|
|
const pageStart = new Date(end.getTime() - 1000 * 60 * 60 * 4);
|
|
const pageStart = new Date(end.getTime() - 1000 * 60 * 60 * 4);
|
|
|
- this.log.entries = await this.serviceApi.queryServiceLog(serverID, serviceID, pageStart, pageEnd);
|
|
|
|
|
|
|
+ this.log.entries = await this.queryAndFillServiceLog(serverID, serviceID, pageStart, pageEnd, this.serviceCheck.interval);
|
|
|
|
|
|
|
|
this.log.end = end;
|
|
this.log.end = end;
|
|
|
this.log.start = start;
|
|
this.log.start = start;
|
|
@@ -83,21 +83,16 @@ export class ServiceCheckDetailPageComponent {
|
|
|
|
|
|
|
|
private async reloadOnScroll() {
|
|
private async reloadOnScroll() {
|
|
|
if (this.log.loading) return;
|
|
if (this.log.loading) return;
|
|
|
- if (!this.log.start || !this.log.end || !this.params || !this.log.visible) return;
|
|
|
|
|
|
|
+ if (!this.log.start || !this.log.end || !this.params || !this.serviceCheck || !this.log.visible) return;
|
|
|
try {
|
|
try {
|
|
|
this.log.loading = true;
|
|
this.log.loading = true;
|
|
|
|
|
|
|
|
const end = new Date(this.log.visible.first.getTime() - 1000);
|
|
const end = new Date(this.log.visible.first.getTime() - 1000);
|
|
|
let start = new Date(end.getTime() - 1000 * 60 * 60 * 2);
|
|
let start = new Date(end.getTime() - 1000 * 60 * 60 * 2);
|
|
|
-
|
|
|
|
|
if (start.getTime() < this.log.start.getTime()) start = this.log.start;
|
|
if (start.getTime() < this.log.start.getTime()) start = this.log.start;
|
|
|
|
|
|
|
|
- const entries = await this.serviceApi.queryServiceLog(this.params.serverID, this.params.serviceID, start, end);
|
|
|
|
|
-
|
|
|
|
|
- // TODO: Problem: This runs into infinite reloading if entries.length is 0 here (because nothing got logged in the last hour)
|
|
|
|
|
-
|
|
|
|
|
|
|
+ const entries = await this.queryAndFillServiceLog(this.params.serverID, this.params.serviceID, start, end, this.serviceCheck.interval);
|
|
|
this.log.entries = [...entries, ...(this.log.entries ?? [])];
|
|
this.log.entries = [...entries, ...(this.log.entries ?? [])];
|
|
|
-
|
|
|
|
|
this.log.loading = false;
|
|
this.log.loading = false;
|
|
|
|
|
|
|
|
setTimeout(this.logScroll.bind(this), 1000);
|
|
setTimeout(this.logScroll.bind(this), 1000);
|
|
@@ -121,4 +116,73 @@ export class ServiceCheckDetailPageComponent {
|
|
|
|
|
|
|
|
return { first: new Date(first), last: new Date(last) };
|
|
return { first: new Date(first), last: new Date(last) };
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ private async queryAndFillServiceLog(serverID: number, serviceID: number, start: Date, end: Date, interval: number) {
|
|
|
|
|
+ const expectedNum = Math.floor((end.getTime() - start.getTime()) / (interval * 1000));
|
|
|
|
|
+ if (expectedNum <= 0) return [];
|
|
|
|
|
+
|
|
|
|
|
+ const toleranceMs = 2500;
|
|
|
|
|
+ const entries = await this.serviceApi.queryServiceLog(serverID, serviceID, start, end);
|
|
|
|
|
+ if (entries.length < expectedNum) {
|
|
|
|
|
+ if (entries.length) {
|
|
|
|
|
+ // Insert dummy log entries before first existing entry?
|
|
|
|
|
+ if (entries[0].time.getTime() - start.getTime() > interval * 1000 + toleranceMs) {
|
|
|
|
|
+ const desiredNum = Math.floor((entries[0].time.getTime() - start.getTime()) / (interval * 1000));
|
|
|
|
|
+ entries.unshift(
|
|
|
|
|
+ ...Array(desiredNum)
|
|
|
|
|
+ .fill(0)
|
|
|
|
|
+ .map((v, i) => new Date(entries[0].time.getTime() - i * interval * 1000))
|
|
|
|
|
+ .reverse()
|
|
|
|
|
+ .map(this.dummyEntry)
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Insert dummy log entries after last existing entry?
|
|
|
|
|
+ if (end.getTime() - entries[entries.length - 1].time.getTime() > interval * 1000 + toleranceMs) {
|
|
|
|
|
+ const desiredNum = Math.floor((end.getTime() - entries[entries.length - 1].time.getTime()) / (interval * 1000));
|
|
|
|
|
+ entries.push(
|
|
|
|
|
+ ...Array(desiredNum)
|
|
|
|
|
+ .fill(0)
|
|
|
|
|
+ .map((v, i) => new Date(entries[entries.length - 1].time.getTime() + i * interval * 1000))
|
|
|
|
|
+ .map(this.dummyEntry)
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Insert dummy log entries in between existing entries?
|
|
|
|
|
+ entries
|
|
|
|
|
+ .slice()
|
|
|
|
|
+ .reverse()
|
|
|
|
|
+ .forEach((entry, invIdx, arr) => {
|
|
|
|
|
+ if (invIdx === 0) return; // skip last/newest entry
|
|
|
|
|
+
|
|
|
|
|
+ const newerEntry = arr[invIdx - 1];
|
|
|
|
|
+ const i = arr.length - 1 - invIdx;
|
|
|
|
|
+ if (newerEntry.time.getTime() - entry.time.getTime() > interval * 1000 + toleranceMs) {
|
|
|
|
|
+ const desiredNum = Math.floor((newerEntry.time.getTime() - entry.time.getTime()) / (interval * 1000));
|
|
|
|
|
+ entries.splice(
|
|
|
|
|
+ i,
|
|
|
|
|
+ 0,
|
|
|
|
|
+ ...Array(desiredNum)
|
|
|
|
|
+ .fill(0)
|
|
|
|
|
+ .map((v, idx) => new Date(entry.time.getTime() + idx * interval * 1000))
|
|
|
|
|
+ .map(this.dummyEntry)
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // Response all empty -> fill with #expectedNum dummy entries
|
|
|
|
|
+ entries.push(
|
|
|
|
|
+ ...Array(expectedNum)
|
|
|
|
|
+ .fill(0)
|
|
|
|
|
+ .map((v, i) => new Date(start.getTime() + i * interval * 1000))
|
|
|
|
|
+ .map(this.dummyEntry)
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return entries;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private dummyEntry(time: Date) {
|
|
|
|
|
+ return { time, data: [{ status: HttpCheckStatus.Invalid, message: 'missing log entry' }] };
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|