Skip to content

Commit 4447c3b

Browse files
andrii-i3coins
andauthored
Emit telemetry events on success and failure of the Create Job, Create Job Definition, Create Job from Job Definition hooks (#472) (#475)
* add job/job definition creation event success or failure logging * fix create-job logging format * log error message and httpStatusCode when error occurs * Update src/index.tsx * Update src/context.ts * Update src/context.ts * log string for logDetails, not whole error * catch errors as unknown, not Error * Make generic error message more user-frinedly and actionable per @JasonWeill --------- Co-authored-by: Piyush Jain <[email protected]>
1 parent 60b8018 commit 4447c3b

File tree

12 files changed

+106
-38
lines changed

12 files changed

+106
-38
lines changed

src/components/advanced-table/advanced-table.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { Scheduler } from '../../handler';
1313
import { AdvancedTableHeader } from './advanced-table-header';
1414
import { useTranslator } from '../../hooks';
1515
import { Alert } from '@mui/material';
16+
import { getErrorMessage } from '../../util/errors';
1617

1718
const PAGE_SIZE = 25;
1819

@@ -102,8 +103,9 @@ export function AdvancedTable<
102103
setNextToken(payload?.next_token);
103104
setTotalCount(payload?.total_count);
104105
})
105-
.catch((e: Error) => {
106-
setDisplayError(e.message);
106+
.catch((e: unknown) => {
107+
const message = getErrorMessage(e);
108+
setDisplayError(message);
107109
});
108110
};
109111

@@ -152,8 +154,9 @@ export function AdvancedTable<
152154
setPage(newPage);
153155
setMaxPage(newPage);
154156
})
155-
.catch((e: Error) => {
156-
setDisplayError(e.message);
157+
.catch((e: unknown) => {
158+
const message = getErrorMessage(e);
159+
setDisplayError(message);
157160
});
158161
};
159162

src/components/job-definition-row.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { Scheduler, SchedulerService } from '../handler';
1616
import { useEventLogger, useTranslator } from '../hooks';
1717
import { TranslationBundle } from '@jupyterlab/translation';
1818
import { ConfirmDeleteButton } from './confirm-buttons';
19+
import { getErrorMessage } from '../util/errors';
1920

2021
function CreatedAt(props: {
2122
job: Scheduler.IDescribeJobDefinition;
@@ -120,8 +121,9 @@ export function buildJobDefinitionRow(
120121
.then(_ => {
121122
forceReload();
122123
})
123-
.catch((error: Error) => {
124-
handleApiError(error.message);
124+
.catch((e: unknown) => {
125+
const message = getErrorMessage(e);
126+
handleApiError(message);
125127
});
126128
}}
127129
/>
@@ -134,8 +136,9 @@ export function buildJobDefinitionRow(
134136
.then(_ => {
135137
forceReload();
136138
})
137-
.catch((error: Error) => {
138-
handleApiError(error.message);
139+
.catch((e: unknown) => {
140+
const message = getErrorMessage(e);
141+
handleApiError(message);
139142
});
140143
}}
141144
/>
@@ -148,8 +151,9 @@ export function buildJobDefinitionRow(
148151
.then(_ => {
149152
deleteRow(jobDef.job_definition_id);
150153
})
151-
.catch((error: Error) => {
152-
handleApiError(error.message);
154+
.catch((e: unknown) => {
155+
const message = getErrorMessage(e);
156+
handleApiError(message);
153157
});
154158
}}
155159
/>

src/components/job-row.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { ICreateJobModel } from '../model';
1010
import DownloadIcon from '@mui/icons-material/Download';
1111
import StopIcon from '@mui/icons-material/Stop';
1212
import { IconButton, Stack, Link, TableCell, TableRow } from '@mui/material';
13+
import { getErrorMessage } from '../util/errors';
1314

1415
function StopButton(props: {
1516
job: Scheduler.IDescribeJob;
@@ -106,8 +107,9 @@ function DownloadFilesButton(props: DownloadFilesButtonProps) {
106107
props.reload();
107108
})
108109
)
109-
.catch((e: Error) => {
110-
props.setDisplayError(e.message);
110+
.catch((e: unknown) => {
111+
const message = getErrorMessage(e);
112+
props.setDisplayError(message);
111113
});
112114
}}
113115
>
@@ -178,7 +180,10 @@ export function buildJobRow(
178180
id: job.job_id
179181
})
180182
.then(_ => deleteRow(job.job_id))
181-
.catch((e: Error) => setDisplayError(e.message));
183+
.catch((e: unknown) => {
184+
const message = getErrorMessage(e);
185+
setDisplayError(message);
186+
});
182187
}}
183188
/>
184189
<StopButton
@@ -189,7 +194,10 @@ export function buildJobRow(
189194
.execute(CommandIDs.stopJob, {
190195
id: job.job_id
191196
})
192-
.catch((e: Error) => setDisplayError(e.message));
197+
.catch((e: unknown) => {
198+
const message = getErrorMessage(e);
199+
setDisplayError(message);
200+
});
193201
}}
194202
/>
195203
</Stack>

src/context.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import { ITranslator, nullTranslator } from '@jupyterlab/translation';
22
import React from 'react';
33

4-
export type Logger = (eventName: string) => void;
5-
export const LogContext = React.createContext<Logger>((eventName: string) => {
6-
/*noop*/
7-
});
4+
export type Logger = (eventName: string, eventDetail?: string) => void;
5+
export const LogContext = React.createContext<Logger>(
6+
(eventName: string, eventDetail?: string) => {
7+
/*noop*/
8+
}
9+
);
810

911
// Context to be overridden with JupyterLab context
1012
const TranslatorContext = React.createContext<ITranslator>(nullTranslator);

src/index.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ export namespace CommandIDs {
4141
export const NotebookJobsPanelId = 'notebook-jobs-panel';
4242
export { Scheduler } from './tokens';
4343

44+
type EventLog = {
45+
body: { name: string; detail?: string };
46+
timestamp: Date;
47+
};
48+
4449
/**
4550
* Initialization data for the jupyterlab-scheduler extension.
4651
*/
@@ -178,16 +183,21 @@ async function activatePlugin(
178183
let mainAreaWidget: MainAreaWidget<NotebookJobsPanel> | undefined;
179184
let jobsPanel: NotebookJobsPanel | undefined;
180185

181-
const eventLogger: Scheduler.EventLogger = eventName => {
186+
const eventLogger: Scheduler.EventLogger = (eventName, eventDetail) => {
182187
if (!eventName) {
183188
return;
184189
}
185-
const eventLog = {
190+
const eventLog: EventLog = {
186191
body: {
187192
name: `org.jupyter.jupyter-scheduler.${eventName}`
188193
},
189194
timestamp: new Date()
190195
};
196+
197+
if (eventDetail) {
198+
eventLog.body.detail = eventDetail;
199+
}
200+
191201
telemetryHandler(eventLog).then();
192202
};
193203

src/mainviews/create-job-from-definition.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { Alert, Button, CircularProgress } from '@mui/material';
1313
import { Box, Stack } from '@mui/system';
1414

1515
import { LabeledValue } from '../components/labeled-value';
16+
import { getErrorMessage } from '../util/errors';
1617

1718
export interface ICreateJobFromDefinitionProps {
1819
model: ICreateJobModel;
@@ -139,17 +140,20 @@ export function CreateJobFromDefinition(
139140
createJobFromDefinitionModel
140141
)
141142
.then(response => {
143+
log('create-job-from-definition.create-job.success');
142144
// Switch to the list view with "Job List" active
143145
props.showListView(
144146
JobsView.ListJobs,
145147
response.job_id,
146148
props.model.jobName
147149
);
148150
})
149-
.catch((error: Error) => {
151+
.catch((e: unknown) => {
152+
const detail = getErrorMessage(e);
153+
log('create-job-from-definition.create-job.failure', detail);
150154
props.handleModelChange({
151155
...props.model,
152-
createError: error.message,
156+
createError: detail,
153157
createInProgress: false
154158
});
155159
});
@@ -240,7 +244,7 @@ export function CreateJobFromDefinition(
240244
<Button
241245
variant="contained"
242246
onClick={(e: React.MouseEvent) => {
243-
log('create-job-from-definition.create');
247+
log('create-job-from-definition.create-job');
244248
submitCreateJobRequest(e);
245249
return false;
246250
}}

src/mainviews/create-job.tsx

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import {
4141
} from '@mui/material';
4242

4343
import { Box, Stack } from '@mui/system';
44+
import { getErrorMessage } from '../util/errors';
4445

4546
export interface ICreateJobProps {
4647
model: ICreateJobModel;
@@ -335,13 +336,16 @@ export function CreateJob(props: ICreateJobProps): JSX.Element {
335336
api
336337
.createJob(jobOptions)
337338
.then(response => {
339+
log('create-job.create-job.success');
338340
// Switch to the list view with "Job List" active
339341
props.showListView(JobsView.ListJobs, response.job_id, jobOptions.name);
340342
})
341-
.catch((error: Error) => {
343+
.catch((e: unknown) => {
344+
const detail = getErrorMessage(e);
345+
log('create-job.create-job.failure', detail);
342346
props.handleModelChange({
343347
...props.model,
344-
createError: error.message,
348+
createError: detail,
345349
createInProgress: false
346350
});
347351
});
@@ -382,17 +386,20 @@ export function CreateJob(props: ICreateJobProps): JSX.Element {
382386
api
383387
.createJobDefinition(jobDefinitionOptions)
384388
.then(response => {
389+
log('create-job.create-job-definition.success');
385390
// Switch to the list view with "Job Definition List" active
386391
props.showListView(
387392
JobsView.ListJobDefinitions,
388393
response.job_definition_id,
389394
jobDefinitionOptions.name
390395
);
391396
})
392-
.catch((error: Error) => {
397+
.catch((e: unknown) => {
398+
const detail = getErrorMessage(e);
399+
log('create-job.create-job-definition.failure', detail);
393400
props.handleModelChange({
394401
...props.model,
395-
createError: error.message,
402+
createError: detail,
396403
createInProgress: false
397404
});
398405
});
@@ -591,7 +598,11 @@ export function CreateJob(props: ICreateJobProps): JSX.Element {
591598
<Button
592599
variant="contained"
593600
onClick={(e: React.MouseEvent) => {
594-
log('create-job.create');
601+
const eventType =
602+
props.model.createType === 'Job'
603+
? 'create-job'
604+
: 'create-job-definition';
605+
log(`create-job.${eventType}`);
595606
submitForm(e);
596607
return false;
597608
}}

src/mainviews/detail-view/job-definition.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {
3131
import { Scheduler as SchedulerTokens } from '../../tokens';
3232

3333
import { timestampLocalize } from './job-detail';
34+
import { getErrorMessage } from '../../util/errors';
3435

3536
export interface IJobDefinitionProps {
3637
app: JupyterFrontEnd;
@@ -79,21 +80,30 @@ export function JobDefinition(props: IJobDefinitionProps): JSX.Element {
7980
const handleDeleteJobDefinition = async () => {
8081
ss.deleteJobDefinition(model.definitionId ?? '')
8182
.then(_ => props.setJobsView(JobsView.ListJobDefinitions))
82-
.catch((e: Error) => setDisplayError(e.message));
83+
.catch((e: unknown) => {
84+
const message = getErrorMessage(e);
85+
setDisplayError(message);
86+
});
8387
};
8488

8589
const pauseJobDefinition = async () => {
8690
setDisplayError(null);
8791
ss.pauseJobDefinition(model.definitionId)
8892
.then(_ => props.refresh())
89-
.catch((e: Error) => setDisplayError(e.message));
93+
.catch((e: unknown) => {
94+
const message = getErrorMessage(e);
95+
setDisplayError(message);
96+
});
9097
};
9198

9299
const resumeJobDefinition = async () => {
93100
setDisplayError(null);
94101
ss.resumeJobDefinition(model.definitionId)
95102
.then(_ => props.refresh())
96-
.catch((e: Error) => setDisplayError(e.message));
103+
.catch((e: unknown) => {
104+
const message = getErrorMessage(e);
105+
setDisplayError(message);
106+
});
97107
};
98108

99109
const runJobDefinition = () => {

src/mainviews/detail-view/job-detail.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import {
3838
ILabeledValueProps,
3939
LabeledValue
4040
} from '../../components/labeled-value';
41+
import { getErrorMessage } from '../../util/errors';
4142

4243
export interface IJobDetailProps {
4344
app: JupyterFrontEnd;
@@ -103,7 +104,10 @@ export function JobDetail(props: IJobDetailProps): JSX.Element {
103104
setDisplayError(null);
104105
ss.deleteJob(props.model?.jobId ?? '')
105106
.then(_ => props.setJobsView(JobsView.ListJobs))
106-
.catch((e: Error) => setDisplayError(e.message));
107+
.catch((e: unknown) => {
108+
const message = getErrorMessage(e);
109+
setDisplayError(message);
110+
});
107111
};
108112

109113
const handleStopJob = async () => {
@@ -114,7 +118,10 @@ export function JobDetail(props: IJobDetailProps): JSX.Element {
114118
id: props.model?.jobId
115119
})
116120
.then(_ => props.handleModelChange())
117-
.catch((e: Error) => setDisplayError(e.message));
121+
.catch((e: unknown) => {
122+
const message = getErrorMessage(e);
123+
setDisplayError(message);
124+
});
118125
};
119126

120127
const downloadFiles = async () => {
@@ -130,8 +137,9 @@ export function JobDetail(props: IJobDetailProps): JSX.Element {
130137
props.handleModelChange().then(_ => setDownloading(false))
131138
);
132139
})
133-
.catch((e: Error) => {
134-
setDisplayError(e.message);
140+
.catch((e: unknown) => {
141+
const message = getErrorMessage(e);
142+
setDisplayError(message);
135143
setDownloading(false);
136144
});
137145
};

src/mainviews/edit-job-definition.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { Scheduler } from '../tokens';
2121
import { InputFileSnapshot } from '../components/input-file-snapshot';
2222
import { LabeledValue } from '../components/labeled-value';
2323
import { timestampLocalize } from './detail-view/job-detail';
24+
import { getErrorMessage } from '../util/errors';
2425

2526
export type EditJobDefinitionProps = {
2627
model: IUpdateJobDefinitionModel;
@@ -77,9 +78,10 @@ function EditJobDefinitionBody(props: EditJobDefinitionProps): JSX.Element {
7778
.then(() => {
7879
props.showJobDefinitionDetail(props.model.definitionId);
7980
})
80-
.catch((e: Error) => {
81+
.catch((e: unknown) => {
8182
setSaving(false);
82-
setDisplayError(e.message);
83+
const message = getErrorMessage(e);
84+
setDisplayError(message);
8385
});
8486
};
8587

0 commit comments

Comments
 (0)