Merge branch 'merge-dir'

This commit is contained in:
Simon Larsen
2024-08-07 10:43:17 -06:00
10 changed files with 106 additions and 48 deletions

View File

@@ -4,19 +4,19 @@ import LIMIT_MAX, { LIMIT_PER_PROJECT } from "Common/Types/Database/LimitMax";
import MonitorType from "Common/Types/Monitor/MonitorType";
import { EVERY_MINUTE } from "Common/Utils/CronTime";
import MonitorService from "CommonServer/Services/MonitorService";
import QueryHelper from "CommonServer/Types/AnalyticsDatabase/QueryHelper";
import logger from "CommonServer/Utils/Logger";
import MonitorResourceUtil from "CommonServer/Utils/Monitor/MonitorResource";
import Monitor from "Common/Models/DatabaseModels/Monitor";
import CronTab from "CommonServer/Utils/CronTab";
import MonitorStep from "Common/Types/Monitor/MonitorStep";
import LogMonitorResponse from "Common/Types/Monitor/LogMonitor/LogMonitorResponse";
import MonitorStepLogMonitor from "Common/Types/Monitor/MonitorStepLogMonitor";
import MonitorStepLogMonitor, {
MonitorStepLogMonitorUtil,
} from "Common/Types/Monitor/MonitorStepLogMonitor";
import BadDataException from "Common/Types/Exception/BadDataException";
import LogService from "CommonServer/Services/LogService";
import Query from "CommonServer/Types/AnalyticsDatabase/Query";
import Log from "Common/Models/AnalyticsModels/Log";
import Search from "Common/Types/BaseDatabase/Search";
import PositiveNumber from "Common/Types/PositiveNumber";
import JSONFunctions from "Common/Types/JSONFunctions";
import DatabaseQueryHelper from "CommonServer/Types/Database/QueryHelper";
@@ -176,39 +176,7 @@ const monitorLogs: MonitorLogsFunction = async (data: {
throw new BadDataException("Log query is missing");
}
const query: Query<Log> = {};
if (logQuery.attributes) {
query.attributes = logQuery.attributes;
}
if (logQuery.body) {
query.body = new Search(logQuery.body);
}
if (logQuery.severityTexts && logQuery.severityTexts.length > 0) {
query.severityText = QueryHelper.any(
logQuery.severityTexts as Array<string>,
);
}
if (logQuery.telemetryServiceIds && logQuery.telemetryServiceIds.length > 0) {
query.serviceId = QueryHelper.any(logQuery.telemetryServiceIds);
}
if (!logQuery.lastXSecondsOfLogs) {
throw new BadDataException("Last X seconds of logs is missing");
}
const lastXSecondsOfLogs: number = logQuery.lastXSecondsOfLogs;
const endDate: Date = OneUptimeDate.getCurrentDate();
const startDate: Date = OneUptimeDate.addRemoveSeconds(
endDate,
lastXSecondsOfLogs * -1,
);
query.time = QueryHelper.inBetween(startDate, endDate);
const query: Query<Log> = MonitorStepLogMonitorUtil.toQuery(logQuery);
const countLogs: PositiveNumber = await LogService.countBy({
query: query,

View File

@@ -37,10 +37,14 @@ import {
ManyToOne,
} from "typeorm";
import TelemetryType from "../../Types/Telemetry/TelemetryType";
import Query from "../../Types/BaseDatabase/Query";
import Log from "../AnalyticsModels/Log";
import Span from "../AnalyticsModels/Span";
import Metric from "../AnalyticsModels/Metric";
export interface TelemetryIncidentQuery {
telemetryType: TelemetryType;
telemetryQuery: JSONObject;
telemetryQuery: Query<Log> | Query<Span> | Query<Metric>;
}
@EnableDocumentation()

View File

@@ -25,8 +25,24 @@ export default class InBetween extends SerializableObject {
endValue: number | Date | string,
) {
super();
this.endValue = endValue;
this.startValue = startValue;
if (
typeof startValue === "string" &&
OneUptimeDate.isValidDateString(startValue)
) {
this.startValue = OneUptimeDate.fromString(startValue);
} else {
this.startValue = startValue;
}
if (
typeof endValue === "string" &&
OneUptimeDate.isValidDateString(endValue)
) {
this.endValue = OneUptimeDate.fromString(endValue);
} else {
this.endValue = endValue;
}
}
public override toJSON(): JSONObject {

View File

@@ -935,6 +935,10 @@ export default class OneUptimeDate {
return this.getGmtOffsetFriendlyString(offset) + " " + timezone;
}
public static isValidDateString(date: string): boolean {
return moment(date).isValid();
}
public static getGmtOffsetFriendlyString(offset: number): string {
const hours: number = Math.abs(offset) / 60;
const minutes: number = Math.abs(offset) % 60;

View File

@@ -261,6 +261,8 @@ export default class JSONFunctions {
return val;
} else if (val instanceof DatabaseProperty) {
return val;
} else if (val instanceof SerializableObject) {
return val;
} else if (
val &&
typeof val === Typeof.Object &&

View File

@@ -234,6 +234,7 @@ export default class BaseAnalyticsAPI<
let groupBy: GroupBy<AnalyticsDataModel> = {};
if (req.body) {
query = JSONFunctions.deserialize(
req.body["query"],
) as Query<AnalyticsDataModel>;

View File

@@ -41,7 +41,9 @@ import ProbeApiIngestResponse from "Common/Types/Probe/ProbeApiIngestResponse";
import ProbeMonitorResponse from "Common/Types/Probe/ProbeMonitorResponse";
import Typeof from "Common/Types/Typeof";
import MonitorMetricsByMinute from "Common/Models/AnalyticsModels/MonitorMetricsByMinute";
import Incident from "Common/Models/DatabaseModels/Incident";
import Incident, {
TelemetryIncidentQuery,
} from "Common/Models/DatabaseModels/Incident";
import IncidentSeverity from "Common/Models/DatabaseModels/IncidentSeverity";
import IncidentStateTimeline from "Common/Models/DatabaseModels/IncidentStateTimeline";
import Monitor from "Common/Models/DatabaseModels/Monitor";
@@ -50,6 +52,8 @@ import MonitorStatusTimeline from "Common/Models/DatabaseModels/MonitorStatusTim
import OnCallDutyPolicy from "Common/Models/DatabaseModels/OnCallDutyPolicy";
import OneUptimeDate from "Common/Types/Date";
import LogMonitorCriteria from "./Criteria/LogMonitorCriteria";
import LogMonitorResponse from "Common/Types/Monitor/LogMonitor/LogMonitorResponse";
import TelemetryType from "Common/Types/Telemetry/TelemetryType";
export default class MonitorResourceUtil {
public static async monitorResource(
@@ -333,12 +337,24 @@ export default class MonitorResourceUtil {
}`,
);
let telemetryQuery: TelemetryIncidentQuery | undefined = undefined;
if (dataToProcess && (dataToProcess as LogMonitorResponse).logQuery) {
telemetryQuery = {
telemetryQuery: (dataToProcess as LogMonitorResponse).logQuery,
telemetryType: TelemetryType.Log,
};
}
await this.criteriaMetCreateIncidentsAndUpdateMonitorStatus({
monitor: monitor,
rootCause: response.rootCause,
dataToProcess: dataToProcess,
autoResolveCriteriaInstanceIdIncidentIdsDictionary,
criteriaInstance: criteriaInstanceMap[response.criteriaMetId!]!,
props: {
telemetryQuery: telemetryQuery,
},
});
} else if (
!response.criteriaMetId &&
@@ -649,6 +665,9 @@ export default class MonitorResourceUtil {
autoResolveCriteriaInstanceIdIncidentIdsDictionary: Dictionary<
Array<string>
>;
props: {
telemetryQuery?: TelemetryIncidentQuery | undefined;
};
}): Promise<void> {
// criteria filters are met, now process the actions.
@@ -790,6 +809,10 @@ export default class MonitorResourceUtil {
incident.isCreatedAutomatically = true;
if (input.props.telemetryQuery) {
incident.telemetryQuery = input.props.telemetryQuery;
}
if (
input.dataToProcess &&
(input.dataToProcess as ProbeMonitorResponse).probeId

View File

@@ -386,10 +386,12 @@ export default class ModelAPI {
);
if (result.isSuccess()) {
return BaseModel.fromJSONObject(
const baseModel: TBaseModel = BaseModel.fromJSONObject(
result.data as JSONObject,
data.modelType,
);
return baseModel;
}
this.checkStatusCode(result);

View File

@@ -35,7 +35,7 @@ const DashboardLogsViewer: FunctionComponent<ComponentProps> = (
type RefreshQueryFunction = () => Query<Log>;
const refreshQuery: RefreshQueryFunction = (): Query<Log> => {
let query: Query<Log> = {};
const query: Query<Log> = {};
if (props.telemetryServiceIds && props.telemetryServiceIds.length > 0) {
query.serviceId = new Includes(props.telemetryServiceIds);
@@ -49,11 +49,10 @@ const DashboardLogsViewer: FunctionComponent<ComponentProps> = (
query.spanId = new Includes(props.spanIds);
}
if (props.logQuery) {
query = {
...query,
...props.logQuery,
};
if (props.logQuery && Object.keys(props.logQuery).length > 0) {
for (const key in props.logQuery) {
(query as any)[key] = (props.logQuery as any)[key] as any;
}
}
return query;

View File

@@ -28,7 +28,9 @@ import BaseAPI from "CommonUI/src/Utils/API/API";
import GlobalEvent from "CommonUI/src/Utils/GlobalEvents";
import ModelAPI, { ListResult } from "CommonUI/src/Utils/ModelAPI/ModelAPI";
import Navigation from "CommonUI/src/Utils/Navigation";
import Incident from "Common/Models/DatabaseModels/Incident";
import Incident, {
TelemetryIncidentQuery,
} from "Common/Models/DatabaseModels/Incident";
import IncidentSeverity from "Common/Models/DatabaseModels/IncidentSeverity";
import IncidentState from "Common/Models/DatabaseModels/IncidentState";
import IncidentStateTimeline from "Common/Models/DatabaseModels/IncidentStateTimeline";
@@ -41,6 +43,10 @@ import React, {
useState,
} from "react";
import UserElement from "../../../Components/User/User";
import Card from "CommonUI/src/Components/Card/Card";
import DashboardLogsViewer from "../../../Components/Logs/LogsViewer";
import TelemetryType from "Common/Types/Telemetry/TelemetryType";
import JSONFunctions from "Common/Types/JSONFunctions";
const IncidentView: FunctionComponent<
PageComponentProps
@@ -55,6 +61,9 @@ const IncidentView: FunctionComponent<
const [error, setError] = useState<string>("");
const [isLoading, setIsLoading] = useState<boolean>(false);
const [telemetryQuery, setTelemetryQuery] =
useState<TelemetryIncidentQuery | null>(null);
const fetchData: PromiseVoidFunction = async (): Promise<void> => {
try {
setIsLoading(true);
@@ -96,6 +105,23 @@ const IncidentView: FunctionComponent<
sort: {},
});
const incident: Incident | null = await ModelAPI.getItem({
id: modelId,
modelType: Incident,
select: {
telemetryQuery: true,
},
});
let telemetryQuery: TelemetryIncidentQuery | null = null;
if (incident?.telemetryQuery) {
telemetryQuery = JSONFunctions.deserialize(
incident?.telemetryQuery as any,
) as any;
}
setTelemetryQuery(telemetryQuery);
setIncidentStates(incidentStates.data as IncidentState[]);
setIncidentStateTimeline(
incidentTimelines.data as IncidentStateTimeline[],
@@ -660,6 +686,19 @@ const IncidentView: FunctionComponent<
modelId: modelId,
}}
/>
{telemetryQuery && telemetryQuery.telemetryType === TelemetryType.Log && (
<div>
<Card title={"Logs"} description={"Logs for this incident."}>
<DashboardLogsViewer
id="logs-preview"
logQuery={telemetryQuery.telemetryQuery}
limit={10}
noLogsMessage="No logs found"
/>
</Card>
</div>
)}
</Fragment>
);
};