add logs to monitor probe

This commit is contained in:
Simon Larsen
2023-06-25 10:23:42 +01:00
parent c3c871f9cc
commit d5e07fc788
10 changed files with 144 additions and 9 deletions

View File

@@ -117,7 +117,7 @@ export default class MonitorCriteriaInstance extends DatabaseProperty {
title: `${arg.monitorType} monitor is offline`,
description: `${arg.monitorType} monitor is currently offline.`,
incidentSeverityId: arg.incidentSeverityId,
autoResolveIncident: false
autoResolveIncident: true,
},
],
changeMonitorStatus: true,
@@ -152,7 +152,7 @@ export default class MonitorCriteriaInstance extends DatabaseProperty {
title: `${arg.monitorType} monitor is offline`,
description: `${arg.monitorType} monitor is currently offline.`,
incidentSeverityId: arg.incidentSeverityId,
autoResolveIncident: false
autoResolveIncident: true,
},
],
changeMonitorStatus: true,

View File

@@ -10,4 +10,5 @@ export default interface ProbeMonitorResponse {
responseBody?: string | JSONObject | undefined;
monitorStepId: ObjectID;
monitorId: ObjectID;
probeId: ObjectID;
}

View File

@@ -85,7 +85,8 @@ const MonitorCriteriaIncidentForm: FunctionComponent<ComponentProps> = (
},
title: 'Auto Resolve Incident',
stepId: 'incident-details',
description: 'Automatically resolve this incident when this criteria is no longer met.',
description:
'Automatically resolve this incident when this criteria is no longer met.',
fieldType: FormFieldSchemaType.Toggle,
required: false,
},

View File

@@ -79,7 +79,8 @@ const MonitorCriteriaIncidentForm: FunctionComponent<ComponentProps> = (
{
key: 'autoResolveIncident',
title: 'Auto Resolve Incident',
description: 'Automatically resolve this incident when this criteria is no longer met.',
description:
'Automatically resolve this incident when this criteria is no longer met.',
fieldType: FieldType.Boolean,
placeholder: 'No',
},

View File

@@ -32,12 +32,15 @@ import { LIMIT_PER_PROJECT } from 'Common/Types/Database/LimitMax';
import URL from 'Common/Types/API/URL';
import { DASHBOARD_API_URL } from 'CommonUI/src/Config';
import DisabledWarning from '../../../Components/Monitor/DisabledWarning';
import { ButtonStyleType } from 'CommonUI/src/Components/Button/Button';
import Modal, { ModalWidth } from 'CommonUI/src/Components/Modal/Modal';
const MonitorProbes: FunctionComponent<PageComponentProps> = (
_props: PageComponentProps
): ReactElement => {
const modelId: ObjectID = Navigation.getLastParamAsObjectID(1);
const [showViewLogsModal, setShowViewLogsModal] = useState<boolean>(false);
const [logs, setLogs] = useState<string>('');
const [isLoading, setIsLoading] = useState<boolean>(true);
const [error, setError] = useState<string>('');
@@ -168,6 +171,33 @@ const MonitorProbes: FunctionComponent<PageComponentProps> = (
'No probes found for this resource. However, you can add some probes to monitor this resource.'
}
viewPageRoute={Navigation.getCurrentRoute()}
selectMoreFields={{
lastMonitoringLog: true,
}}
actionButtons={[
{
title: 'View Logs',
buttonStyleType: ButtonStyleType.NORMAL,
icon: IconProp.List,
onClick: async (
item: JSONObject,
onCompleteAction: Function
) => {
setLogs(
item['lastMonitoringLog']
? JSON.stringify(
item['lastMonitoringLog'],
null,
2
)
: 'Not monitored yet'
);
setShowViewLogsModal(true);
onCompleteAction();
},
},
]}
formFields={[
{
field: {
@@ -279,6 +309,25 @@ const MonitorProbes: FunctionComponent<PageComponentProps> = (
>
<DisabledWarning monitorId={modelId} />
{getPageContent()}
{showViewLogsModal && (
<Modal
title={'Monitoring Logs'}
description="Here are the latest monitoring log for this resource."
isLoading={false}
modalWidth={ModalWidth.Large}
onSubmit={() => {
setShowViewLogsModal(false);
}}
submitButtonText={'Close'}
submitButtonStyleType={ButtonStyleType.NORMAL}
>
<div className="text-gray-500 mt-5 text-sm h-96 overflow-y-auto overflow-x-hidden p-5 border-gray-50 border border-2 bg-gray-100 rounded">
{logs.split('\n').map((log: string, i: number) => {
return <div key={i}>{log}</div>;
})}
</div>
</Modal>
)}
</ModelPage>
);
};

View File

@@ -18,6 +18,7 @@ import IconProp from 'Common/Types/Icon/IconProp';
import EnableDocumentation from 'Common/Types/Model/EnableDocumentation';
import Monitor from './Monitor';
import Probe from './Probe';
import { JSONObject } from 'Common/Types/JSON';
@EnableDocumentation()
@TenantColumn('projectId')
@@ -445,4 +446,26 @@ export default class MonitorProbe extends AccessControlModel {
default: true,
})
public isEnabled?: boolean = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.CanReadMonitorProbe,
],
update: [],
})
@TableColumn({
isDefaultValueColumn: false,
required: false,
type: TableColumnType.JSON,
})
@Column({
type: ColumnType.JSON,
nullable: true,
unique: false,
})
public lastMonitoringLog?: JSONObject = undefined;
}

View File

@@ -15,6 +15,7 @@ import WebsiteMonitor, {
import ApiMonitor, { APIResponse } from './MonitorTypes/ApiMonitor';
import JSONFunctions from 'Common/Types/JSONFunctions';
import logger from 'CommonServer/Utils/Logger';
import ProbeUtil from '../Probe';
export default class MonitorUtil {
public static async probeMonitor(
@@ -69,6 +70,7 @@ export default class MonitorUtil {
const result: ProbeMonitorResponse = {
monitorStepId: monitorStep.id,
monitorId: monitor.id!,
probeId: ProbeUtil.getProbeId(),
};
if (!monitorStep.data || !monitorStep.data?.monitorDestination) {

17
Probe/Utils/Probe.ts Normal file
View File

@@ -0,0 +1,17 @@
import LocalCache from 'CommonServer/Infrastructure/LocalCache';
import ObjectID from 'Common/Types/ObjectID';
import BadDataException from 'Common/Types/Exception/BadDataException';
export default class ProbeUtil {
public static getProbeId(): ObjectID {
const id: string | undefined =
LocalCache.getString('PROBE', 'PROBE_ID') ||
process.env['PROBE_ID'];
if (!id) {
throw new BadDataException('Probe ID not found');
}
return new ObjectID(id);
}
}

View File

@@ -1,14 +1,12 @@
import { JSONObject } from 'Common/Types/JSON';
import LocalCache from 'CommonServer/Infrastructure/LocalCache';
import ProbeUtil from './Probe';
import { PROBE_KEY } from '../Config';
export default class ProbeAPIRequest {
public static getDefaultRequestBody(): JSONObject {
return {
probeKey: PROBE_KEY,
probeId:
LocalCache.getString('PROBE', 'PROBE_ID') ||
process.env['PROBE_ID'],
probeId: ProbeUtil.getProbeId().toString(),
};
}
}

View File

@@ -21,6 +21,9 @@ import Monitor from 'Model/Models/Monitor';
import MonitorStatusTimeline from 'Model/Models/MonitorStatusTimeline';
import ObjectID from 'Common/Types/ObjectID';
import { JSONObject } from 'Common/Types/JSON';
import MonitorProbeService from 'CommonServer/Services/MonitorProbeService';
import OneUptimeDate from 'Common/Types/Date';
import MonitorProbe from 'Model/Models/MonitorProbe';
export default class ProbeMonitorResponseService {
public static async processProbeResponse(
@@ -50,6 +53,46 @@ export default class ProbeMonitorResponseService {
throw new BadDataException('Monitor not found');
}
// save the last log to MonitorProbe.
// get last log. We do this because there are many monitoring steps and we need to store those.
const monitorProbe: MonitorProbe | null =
await MonitorProbeService.findOneBy({
query: {
monitorId: monitor.id!,
probeId: probeMonitorResponse.probeId!,
},
select: {
lastMonitoringLog: true,
},
props: {
isRoot: true,
},
});
if (!monitorProbe) {
throw new BadDataException('Probe is not assigned to this monitor');
}
await MonitorProbeService.updateOneBy({
query: {
monitorId: monitor.id!,
probeId: probeMonitorResponse.probeId!,
},
data: {
lastMonitoringLog: {
...(monitorProbe.lastMonitoringLog || {}),
[probeMonitorResponse.monitorStepId.toString()]: {
...JSON.parse(JSON.stringify(probeMonitorResponse)),
monitoredAt: OneUptimeDate.getCurrentDate(),
},
} as any,
},
props: {
isRoot: true,
},
});
// save data to Clickhouse.
const monitorSteps: MonitorSteps = monitor.monitorSteps!;