Skip to content

Commit

Permalink
Merge pull request OneUptime#1625 from OneUptime/log-monitors
Browse files Browse the repository at this point in the history
Log monitors
  • Loading branch information
simlarsen authored Aug 5, 2024
2 parents a497099 + d0692eb commit 7b99467
Show file tree
Hide file tree
Showing 38 changed files with 688 additions and 335 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import MonitorType from "Common/Types/Monitor/MonitorType";
import { EVERY_MINUTE } from "Common/Utils/CronTime";
import MonitorService from "CommonServer/Services/MonitorService";
import logger from "CommonServer/Utils/Logger";
import ProbeMonitorResponseService from "CommonServer/Utils/Probe/ProbeMonitorResponse";
import MonitorResourceUtil from "CommonServer/Utils/Monitor/MonitorResource";
import Monitor from "Model/Models/Monitor";

RunCron(
Expand Down Expand Up @@ -67,7 +67,7 @@ RunCron(
onlyCheckForIncomingRequestReceivedAt: true,
};

await ProbeMonitorResponseService.processProbeResponse(incomingRequest);
await MonitorResourceUtil.monitorResource(incomingRequest);
} catch (error) {
logger.error(
`Error while processing incoming request monitor: ${monitor.id?.toString()}`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { EVERY_MINUTE } from "Common/Utils/CronTime";
import MonitorService from "CommonServer/Services/MonitorService";
import QueryHelper from "CommonServer/Types/Database/QueryHelper";
import logger from "CommonServer/Utils/Logger";
import ProbeMonitorResponseService from "CommonServer/Utils/Probe/ProbeMonitorResponse";
import MonitorResourceUtil from "CommonServer/Utils/Monitor/MonitorResource";
import Monitor from "Model/Models/Monitor";

RunCron(
Expand Down Expand Up @@ -56,9 +56,7 @@ RunCron(
hostname: "",
};

await ProbeMonitorResponseService.processProbeResponse(
serverMonitorResponse,
);
await MonitorResourceUtil.monitorResource(serverMonitorResponse);
} catch (error) {
logger.error(
`Error in ServerMonitor:CheckOnlineStatus for monitorId: ${monitor.id}`,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import OneUptimeDate from "Common/Types/Date";
import RunCron from "../../Utils/Cron";
import LIMIT_MAX from "Common/Types/Database/LimitMax";
import IncomingMonitorRequest from "Common/Types/Monitor/IncomingMonitor/IncomingMonitorRequest";
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/Database/QueryHelper";
import logger from "CommonServer/Utils/Logger";
import MonitorResourceUtil from "CommonServer/Utils/Monitor/MonitorResource";
import Monitor from "Model/Models/Monitor";
import CronTab from "CommonServer/Utils/CronTab";

RunCron(
"LogMonitor:MonitorLogMonitor",
{ schedule: EVERY_MINUTE, runOnStartup: false },
async () => {
logger.debug("Checking LogMonitor:MonitorLogMonitor");

const telemetryMonitors: Array<Monitor> = await MonitorService.findBy({
query: {
monitorType: QueryHelper.any([
MonitorType.Logs,
MonitorType.Traces,
MonitorType.Metrics,
]),
telemetryMonitorNextMonitorAt: QueryHelper.lessThanEqualToOrNull(
OneUptimeDate.getCurrentDate(),
),
},
props: {
isRoot: true,
},
select: {
_id: true,
monitorSteps: true,
createdAt: true,
monitoringInterval: true,
},
limit: LIMIT_MAX,
skip: 0,
});

const updatePromises: Array<Promise<void>> = [];

for (const telemetryMonitor of telemetryMonitors) {
if (!telemetryMonitor.monitoringInterval) {
continue;
}

let nextPing: Date = OneUptimeDate.addRemoveMinutes(
OneUptimeDate.getCurrentDate(),
1,
);

try {
nextPing = CronTab.getNextExecutionTime(
telemetryMonitor?.monitoringInterval as string,
);
} catch (err) {
logger.error(err);
}

updatePromises.push(
MonitorService.updateOneById({
id: telemetryMonitor.id!,
data: {
telemetryMonitorLastMonitorAt: OneUptimeDate.getCurrentDate(),
telemetryMonitorNextMonitorAt: nextPing,
},
props: {
isRoot: true,
},
}),
);
}

await Promise.all(updatePromises);

logger.debug(`Found ${telemetryMonitors.length} telemetry monitors`);

logger.debug(telemetryMonitors);

for (const monitor of telemetryMonitors) {
try {
if (!monitor.monitorSteps) {
logger.debug("Monitor has no steps. Skipping...");
continue;
}

const incomingRequest: IncomingMonitorRequest = {
monitorId: monitor.id!,
requestHeaders: undefined,
requestBody: undefined,
requestMethod: undefined,
incomingRequestReceivedAt:
monitor.incomingRequestReceivedAt || monitor.createdAt!,
onlyCheckForIncomingRequestReceivedAt: true,
};

await MonitorResourceUtil.monitorResource(incomingRequest);
} catch (error) {
logger.error(
`Error while processing incoming request monitor: ${monitor.id?.toString()}`,
);
logger.error(error);
}
}
},
);
11 changes: 11 additions & 0 deletions Common/Types/Log/LogSeverity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
enum LogSeverity {
Unspecified = "Unspecified",
Information = "Information",
Warning = "Warning",
Error = "Error",
Trace = "Trace",
Debug = "Debug",
Fatal = "Fatal",
}

export default LogSeverity;
3 changes: 3 additions & 0 deletions Common/Types/Monitor/CriteriaFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ export enum CheckOn {

ScreenSizeType = "Screen Size",
BrowserType = "Browser Type",

// Log monitors.
LogCount = "Log Count",
}

export interface ServerMonitorOptions {
Expand Down
54 changes: 54 additions & 0 deletions Common/Types/Monitor/MonitorCriteriaInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,31 @@ export default class MonitorCriteriaInstance extends DatabaseProperty {
return monitorCriteriaInstance;
}

if (arg.monitorType === MonitorType.Logs) {
const monitorCriteriaInstance: MonitorCriteriaInstance =
new MonitorCriteriaInstance();

monitorCriteriaInstance.data = {
id: ObjectID.generate().toString(),
monitorStatusId: arg.monitorStatusId,
filterCondition: FilterCondition.Any,
filters: [
{
checkOn: CheckOn.LogCount,
filterType: FilterType.GreaterThan,
value: 0, // if there are some logs then monitor is online.
},
],
incidents: [],
changeMonitorStatus: true,
createIncidents: false,
name: `Check if ${arg.monitorName} is online`,
description: `This criteria checks if the ${arg.monitorName} is online`,
};

return monitorCriteriaInstance;
}

if (arg.monitorType === MonitorType.SSLCertificate) {
const monitorCriteriaInstance: MonitorCriteriaInstance =
new MonitorCriteriaInstance();
Expand Down Expand Up @@ -283,6 +308,35 @@ export default class MonitorCriteriaInstance extends DatabaseProperty {
};
}

if (arg.monitorType === MonitorType.Logs) {
monitorCriteriaInstance.data = {
id: ObjectID.generate().toString(),
monitorStatusId: arg.monitorStatusId,
filterCondition: FilterCondition.Any,
filters: [
{
checkOn: CheckOn.LogCount,
filterType: FilterType.EqualTo,
value: 0, // if there are no logs then the monitor is offline
},
],
incidents: [
{
title: `${arg.monitorName} is offline`,
description: `${arg.monitorName} is currently offline.`,
incidentSeverityId: arg.incidentSeverityId,
autoResolveIncident: true,
id: ObjectID.generate().toString(),
onCallPolicyIds: [],
},
],
changeMonitorStatus: true,
createIncidents: true,
name: `Check if ${arg.monitorName} is offline`,
description: `This criteria checks if the ${arg.monitorName} is offline`,
};
}

if (arg.monitorType === MonitorType.IncomingRequest) {
monitorCriteriaInstance.data = {
id: ObjectID.generate().toString(),
Expand Down
30 changes: 30 additions & 0 deletions Common/Types/Monitor/MonitorStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import JSONFunctions from "../JSONFunctions";
import ObjectID from "../ObjectID";
import Port from "../Port";
import MonitorCriteria from "./MonitorCriteria";
import MonitorStepLogMonitor, {
MonitorStepLogMonitorUtil,
} from "./MonitorStepLogMonitor";
import MonitorType from "./MonitorType";
import BrowserType from "./SyntheticMonitors//BrowserType";
import ScreenSizeType from "./SyntheticMonitors/ScreenSizeType";
Expand All @@ -35,6 +38,9 @@ export interface MonitorStepType {
// this is for synthetic monitors.
screenSizeTypes?: Array<ScreenSizeType> | undefined;
browserTypes?: Array<BrowserType> | undefined;

// Log monitor type.
logMonitor?: MonitorStepLogMonitor | undefined;
}

export default class MonitorStep extends DatabaseProperty {
Expand All @@ -54,6 +60,7 @@ export default class MonitorStep extends DatabaseProperty {
customCode: undefined,
screenSizeTypes: undefined,
browserTypes: undefined,
logMonitor: undefined,
};
}

Expand All @@ -77,6 +84,7 @@ export default class MonitorStep extends DatabaseProperty {
customCode: undefined,
screenSizeTypes: undefined,
browserTypes: undefined,
logMonitor: undefined,
};

return monitorStep;
Expand Down Expand Up @@ -133,6 +141,11 @@ export default class MonitorStep extends DatabaseProperty {
return this;
}

public setLogMonitor(logMonitor: MonitorStepLogMonitor): MonitorStep {
this.data!.logMonitor = logMonitor;
return this;
}

public setCustomCode(customCode: string): MonitorStep {
this.data!.customCode = customCode;
return this;
Expand All @@ -157,6 +170,7 @@ export default class MonitorStep extends DatabaseProperty {
customCode: undefined,
screenSizeTypes: undefined,
browserTypes: undefined,
lgoMonitor: undefined,
},
};
}
Expand All @@ -182,6 +196,16 @@ export default class MonitorStep extends DatabaseProperty {
return "Monitor Destination is required.";
}

if (monitorType === MonitorType.Logs) {
if (!value.data.logMonitor) {
return "Log Monitor is required";
}

if (!value.data.logMonitor.lastXSecondsOfLogs) {
return "Monitor Last Minutes of Logs is required.";
}
}

if (
!value.data.customCode &&
(monitorType === MonitorType.CustomJavaScriptCode ||
Expand Down Expand Up @@ -240,6 +264,9 @@ export default class MonitorStep extends DatabaseProperty {
customCode: this.data.customCode || undefined,
screenSizeTypes: this.data.screenSizeTypes || undefined,
browserTypes: this.data.browserTypes || undefined,
logMonitor: this.data.logMonitor
? MonitorStepLogMonitorUtil.toJSON(this.data.logMonitor)
: undefined,
},
});
}
Expand Down Expand Up @@ -328,6 +355,9 @@ export default class MonitorStep extends DatabaseProperty {
screenSizeTypes:
(json["screenSizeTypes"] as Array<ScreenSizeType>) || undefined,
browserTypes: (json["browserTypes"] as Array<BrowserType>) || undefined,
logMonitor: json["logMonitor"]
? (json["logMonitor"] as JSONObject)
: undefined,
}) as any;

return monitorStep;
Expand Down
47 changes: 47 additions & 0 deletions Common/Types/Monitor/MonitorStepLogMonitor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import Dictionary from "../Dictionary";
import { JSONObject } from "../JSON";
import LogSeverity from "../Log/LogSeverity";
import ObjectID from "../ObjectID";

export default interface MonitorStepLogMonitor {
attributes: Dictionary<string | number | boolean>;
body: string;
severityText: Array<LogSeverity>;
telemetryServiceId: Array<ObjectID>;
lastXSecondsOfLogs: number;
}

export class MonitorStepLogMonitorUtil {
public static getDefault(): MonitorStepLogMonitor {
return {
attributes: {},
body: "",
severityText: [],
telemetryServiceId: [],
lastXSecondsOfLogs: 60,
};
}

public static fromJSON(json: JSONObject): MonitorStepLogMonitor {
return {
attributes:
(json["attributes"] as Dictionary<string | number | boolean>) || {},
body: json["body"] as string,
severityText: json["severityText"] as Array<LogSeverity>,
telemetryServiceId: ObjectID.fromJSONArray(
json["telemetryServiceId"] as Array<JSONObject>,
),
lastXSecondsOfLogs: json["lastXSecondsOfLogs"] as number,
};
}

public static toJSON(monitor: MonitorStepLogMonitor): JSONObject {
return {
attributes: monitor.attributes,
body: monitor.body,
severityText: monitor.severityText,
telemetryServiceId: ObjectID.toJSONArray(monitor.telemetryServiceId),
lastXSecondsOfLogs: monitor.lastXSecondsOfLogs,
};
}
}
Loading

0 comments on commit 7b99467

Please sign in to comment.