diff --git a/x-pack/plugins/reporting/common/types/index.ts b/x-pack/plugins/reporting/common/types/index.ts index 4ccd11f8522e6..55da2cc366390 100644 --- a/x-pack/plugins/reporting/common/types/index.ts +++ b/x-pack/plugins/reporting/common/types/index.ts @@ -36,6 +36,7 @@ export interface ReportOutput extends TaskRunResult { export interface TaskRunResult { content_type: string | null; csv_contains_formulas?: boolean; + csv_rows?: number; max_size_reached?: boolean; warnings?: string[]; } @@ -130,6 +131,7 @@ export interface JobSummary { title: ReportSource['payload']['title']; maxSizeReached: TaskRunResult['max_size_reached']; csvContainsFormulas: TaskRunResult['csv_contains_formulas']; + csvRows: TaskRunResult['csv_rows']; } export interface JobSummarySet { diff --git a/x-pack/plugins/reporting/public/lib/__snapshots__/stream_handler.test.ts.snap b/x-pack/plugins/reporting/public/lib/__snapshots__/stream_handler.test.ts.snap index 46b5556827070..3d326844fedf7 100644 --- a/x-pack/plugins/reporting/public/lib/__snapshots__/stream_handler.test.ts.snap +++ b/x-pack/plugins/reporting/public/lib/__snapshots__/stream_handler.test.ts.snap @@ -5,16 +5,27 @@ Object { "completed": Array [ Object { "csvContainsFormulas": false, + "csvRows": undefined, "id": "job-source-mock1", "jobtype": undefined, "maxSizeReached": false, "status": "completed", "title": "specimen", }, + Object { + "csvContainsFormulas": true, + "csvRows": 42000000, + "id": "job-source-mock4", + "jobtype": undefined, + "maxSizeReached": false, + "status": "completed", + "title": "specimen", + }, ], "failed": Array [ Object { "csvContainsFormulas": false, + "csvRows": undefined, "id": "job-source-mock2", "jobtype": undefined, "maxSizeReached": false, diff --git a/x-pack/plugins/reporting/public/lib/job.tsx b/x-pack/plugins/reporting/public/lib/job.tsx index 5cae9108f57ac..d9f501ecd1418 100644 --- a/x-pack/plugins/reporting/public/lib/job.tsx +++ b/x-pack/plugins/reporting/public/lib/job.tsx @@ -55,6 +55,7 @@ export class Job { public size?: ReportOutput['size']; public content_type?: TaskRunResult['content_type']; public csv_contains_formulas?: TaskRunResult['csv_contains_formulas']; + public csv_rows?: TaskRunResult['csv_rows']; public max_size_reached?: TaskRunResult['max_size_reached']; public warnings?: TaskRunResult['warnings']; @@ -87,6 +88,7 @@ export class Job { this.isDeprecated = report.payload.isDeprecated || false; this.spaceId = report.payload.spaceId; this.csv_contains_formulas = report.output?.csv_contains_formulas; + this.csv_rows = report.output?.csv_rows; this.max_size_reached = report.output?.max_size_reached; this.warnings = report.output?.warnings; this.locatorParams = (report.payload as BaseParamsV2).locatorParams; diff --git a/x-pack/plugins/reporting/public/lib/stream_handler.test.ts b/x-pack/plugins/reporting/public/lib/stream_handler.test.ts index 78742af7fe879..2caa1b70fe162 100644 --- a/x-pack/plugins/reporting/public/lib/stream_handler.test.ts +++ b/x-pack/plugins/reporting/public/lib/stream_handler.test.ts @@ -24,6 +24,7 @@ const mockJobsFound: Job[] = [ { id: 'job-source-mock1', status: 'completed', output: { csv_contains_formulas: false, max_size_reached: false }, payload: { title: 'specimen' } }, { id: 'job-source-mock2', status: 'failed', output: { csv_contains_formulas: false, max_size_reached: false }, payload: { title: 'specimen' } }, { id: 'job-source-mock3', status: 'pending', output: { csv_contains_formulas: false, max_size_reached: false }, payload: { title: 'specimen' } }, + { id: 'job-source-mock4', status: 'completed', output: { csv_contains_formulas: true, csv_rows: 42000000, max_size_reached: false }, payload: { title: 'specimen' } }, ].map((j) => new Job(j as ReportApiJSON)); // prettier-ignore const coreSetup = coreMock.createSetup(); @@ -74,6 +75,7 @@ describe('stream handler', () => { 'job-source-mock1', 'job-source-mock2', 'job-source-mock3', + 'job-source-mock4', ]); findJobs.subscribe((data) => { diff --git a/x-pack/plugins/reporting/public/lib/stream_handler.ts b/x-pack/plugins/reporting/public/lib/stream_handler.ts index e9645f3bb8735..03f4fcd30a618 100644 --- a/x-pack/plugins/reporting/public/lib/stream_handler.ts +++ b/x-pack/plugins/reporting/public/lib/stream_handler.ts @@ -33,6 +33,7 @@ function getReportStatus(src: Job): JobSummary { jobtype: src.prettyJobTypeName ?? src.jobtype, maxSizeReached: src.max_size_reached, csvContainsFormulas: src.csv_contains_formulas, + csvRows: src.csv_rows, }; } diff --git a/x-pack/plugins/reporting/public/management/components/report_info_flyout_content.tsx b/x-pack/plugins/reporting/public/management/components/report_info_flyout_content.tsx index 03ad5ff033e18..e73fc5ab54e33 100644 --- a/x-pack/plugins/reporting/public/management/components/report_info_flyout_content.tsx +++ b/x-pack/plugins/reporting/public/management/components/report_info_flyout_content.tsx @@ -50,6 +50,7 @@ export const ReportInfoFlyoutContent: FunctionComponent = ({ info }) => { const formatDate = createDateFormatter(uiSettings.get('dateFormat'), timezone); + const hasCsvRows = info.csv_rows != null; const hasScreenshot = USES_HEADLESS_JOB_TYPES.includes(info.jobtype); const outputInfo = [ @@ -94,6 +95,12 @@ export const ReportInfoFlyoutContent: FunctionComponent = ({ info }) => { }), description: info.size?.toString() || NA, }, + hasCsvRows && { + title: i18n.translate('xpack.reporting.listing.infoPanel.csvRows', { + defaultMessage: 'CSV rows', + }), + description: info.csv_rows?.toString() || NA, + }, hasScreenshot && { title: i18n.translate('xpack.reporting.listing.infoPanel.dimensionsInfoHeight', { diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts index 9088423159cf0..f0d7db2260f27 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts @@ -397,6 +397,7 @@ export class CsvGenerator { return { content_type: CONTENT_TYPE_CSV, csv_contains_formulas: this.csvContainsFormulas && !escapeFormulaValues, + csv_rows: this.csvRowCount, max_size_reached: this.maxSizeReached, warnings, }; diff --git a/x-pack/plugins/reporting/server/lib/event_logger/logger.test.ts b/x-pack/plugins/reporting/server/lib/event_logger/logger.test.ts index 9a1c282a01a59..10b7d1278183f 100644 --- a/x-pack/plugins/reporting/server/lib/event_logger/logger.test.ts +++ b/x-pack/plugins/reporting/server/lib/event_logger/logger.test.ts @@ -116,7 +116,7 @@ describe('Event Logger', () => { jest.spyOn(logger.completionLogger, 'stopTiming'); logger.logExecutionStart(); - const result = logger.logExecutionComplete({ byteSize: 444 }); + const result = logger.logExecutionComplete({ byteSize: 444, csvRows: 440000 }); expect([result.event, result.kibana.reporting, result.message]).toMatchInlineSnapshot(` Array [ Object { @@ -125,6 +125,7 @@ describe('Event Logger', () => { Object { "actionType": "execute-complete", "byteSize": 444, + "csvRows": 440000, "id": "12348", "jobType": "csv", }, diff --git a/x-pack/plugins/reporting/server/lib/event_logger/logger.ts b/x-pack/plugins/reporting/server/lib/event_logger/logger.ts index ccdee24d1879e..a54f69eff3582 100644 --- a/x-pack/plugins/reporting/server/lib/event_logger/logger.ts +++ b/x-pack/plugins/reporting/server/lib/event_logger/logger.ts @@ -18,7 +18,6 @@ import { ErrorAction, ExecuteError, FailedReport, - ReportingAction, SavedReport, ScheduledRetry, ScheduledTask, @@ -28,6 +27,7 @@ import { /** @internal */ export interface ExecutionCompleteMetrics { byteSize: number; + csvRows?: number; } export interface IReportingEventLogger { @@ -36,21 +36,21 @@ export interface IReportingEventLogger { stopTiming(): void; } +export interface BaseEvent { + event: { timezone: string }; + kibana: { + reporting: { id?: string; jobType: string }; + task?: { id: string }; + }; + user?: { name: string }; +} + /** @internal */ export function reportingEventLoggerFactory(logger: LevelLogger) { const genericLogger = new EcsLogAdapter(logger, { event: { provider: PLUGIN_ID } }); return class ReportingEventLogger { - readonly eventObj: { - event: { - timezone: string; - }; - kibana: { - reporting: ReportingAction['kibana']['reporting']; - task?: { id: string }; - }; - user?: { name: string }; - }; + readonly eventObj: BaseEvent; readonly report: IReport; readonly task?: { id: string }; @@ -102,13 +102,13 @@ export function reportingEventLoggerFactory(logger: LevelLogger) { return event; } - logExecutionComplete({ byteSize }: ExecutionCompleteMetrics): CompletedExecution { + logExecutionComplete({ byteSize, csvRows }: ExecutionCompleteMetrics): CompletedExecution { const message = `completed ${this.report.jobtype} execution`; this.completionLogger.stopTiming(); const event = deepMerge( { message, - kibana: { reporting: { actionType: ActionType.EXECUTE_COMPLETE, byteSize } }, + kibana: { reporting: { actionType: ActionType.EXECUTE_COMPLETE, byteSize, csvRows } }, } as Partial, this.eventObj ); diff --git a/x-pack/plugins/reporting/server/lib/event_logger/types.ts b/x-pack/plugins/reporting/server/lib/event_logger/types.ts index 3ae06dfdb4775..cc3ee25813128 100644 --- a/x-pack/plugins/reporting/server/lib/event_logger/types.ts +++ b/x-pack/plugins/reporting/server/lib/event_logger/types.ts @@ -8,19 +8,21 @@ import { LogMeta } from 'src/core/server'; import { ActionType } from './'; -interface ActionBase { +export interface ReportingAction extends LogMeta { event: { timezone: string; }; message: string; kibana: { reporting: { - actionType?: A; + actionType: A; id?: string; // "immediate download" exports have no ID jobType: string; byteSize?: number; + csvRows?: number; }; - } & { task?: { id?: string } }; + task?: { id?: string }; + }; user?: { name: string }; } @@ -31,8 +33,6 @@ export interface ErrorAction { type?: string; } -export type ReportingAction = ActionBase & LogMeta; - export type ScheduledTask = ReportingAction; export type StartedExecution = ReportingAction; export type CompletedExecution = ReportingAction; diff --git a/x-pack/plugins/reporting/server/lib/tasks/execute_report.ts b/x-pack/plugins/reporting/server/lib/tasks/execute_report.ts index dbd11943f70e4..c566a07c3e6b2 100644 --- a/x-pack/plugins/reporting/server/lib/tasks/execute_report.ts +++ b/x-pack/plugins/reporting/server/lib/tasks/execute_report.ts @@ -221,6 +221,7 @@ export class ExecuteReportTask implements ReportingTask { docOutput.content_type = output.content_type || unknownMime; docOutput.max_size_reached = output.max_size_reached; docOutput.csv_contains_formulas = output.csv_contains_formulas; + docOutput.csv_rows = output.csv_rows; docOutput.size = output.size; docOutput.warnings = output.warnings && output.warnings.length > 0 ? output.warnings : undefined; @@ -363,7 +364,10 @@ export class ExecuteReportTask implements ReportingTask { report._seq_no = stream.getSeqNo()!; report._primary_term = stream.getPrimaryTerm()!; - eventLog.logExecutionComplete({ byteSize: stream.bytesWritten }); + eventLog.logExecutionComplete({ + byteSize: stream.bytesWritten, + csvRows: output?.csv_rows, + }); if (output) { this.logger.debug(`Job output size: ${stream.bytesWritten} bytes.`); diff --git a/x-pack/plugins/reporting/server/routes/generate/csv_searchsource_immediate.ts b/x-pack/plugins/reporting/server/routes/generate/csv_searchsource_immediate.ts index bbc2aaf27375a..364ceea3fa001 100644 --- a/x-pack/plugins/reporting/server/routes/generate/csv_searchsource_immediate.ts +++ b/x-pack/plugins/reporting/server/routes/generate/csv_searchsource_immediate.ts @@ -81,14 +81,17 @@ export function registerGenerateCsvFromSavedObjectImmediate( try { eventLog.logExecutionStart(); const taskPromise = runTaskFn(null, req.body, context, stream, req) - .then(() => { + .then((output) => { logger.info(`Job output size: ${stream.bytesWritten} bytes.`); if (!stream.bytesWritten) { logger.warn('CSV Job Execution created empty content result'); } - eventLog.logExecutionComplete({ byteSize: stream.bytesWritten }); + eventLog.logExecutionComplete({ + byteSize: stream.bytesWritten, + csvRows: output.csv_rows, + }); }) .finally(() => stream.end()); diff --git a/x-pack/plugins/reporting/server/routes/lib/get_document_payload.test.ts b/x-pack/plugins/reporting/server/routes/lib/get_document_payload.test.ts index d2ed0b86e2cce..7904946892905 100644 --- a/x-pack/plugins/reporting/server/routes/lib/get_document_payload.test.ts +++ b/x-pack/plugins/reporting/server/routes/lib/get_document_payload.test.ts @@ -76,6 +76,7 @@ describe('getDocumentPayload', () => { output: { content_type: 'text/csv', csv_contains_formulas: true, + csv_rows: 42000000, max_size_reached: true, size: 1024, },