Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Cases] Adding file telemetry #152968

Merged
merged 8 commits into from
Mar 24, 2023

Conversation

jonathan-buttner
Copy link
Contributor

@jonathan-buttner jonathan-buttner commented Mar 8, 2023

This PR adds telemetry for the attachment framework and files attachments by plugin and for all cases.

Issue: #151933

Notable changes:

  • We now send information for each registered attachment type
  • We send average file size
    • We leverage the files saved object to aggregate this information
    • We use an array type so that we don't have to hard code each attachment type, they are dynamically discovered instead
  • Stats for the attachments are done for all cases and broken down by plugin
Example telemetry payload
                        "cases": {
                            "cases": {
                                "all": {
                                    "total": 1,
                                    "daily": 1,
                                    "weekly": 1,
                                    "monthly": 1,
                                    "status": {
                                        "open": 1,
                                        "inProgress": 0,
                                        "closed": 0
                                    },
                                    "syncAlertsOn": 1,
                                    "syncAlertsOff": 0,
                                    "totalUsers": 1,
                                    "totalParticipants": 1,
                                    "totalTags": 1,
                                    "totalWithAlerts": 0,
                                    "totalWithConnectors": 0,
                                    "latestDates": {
                                        "createdAt": "2023-03-09T15:20:46.399Z",
                                        "updatedAt": "2023-03-09T15:21:02.399Z",
                                        "closedAt": null
                                    },
                                    "assignees": {
                                        "total": 0,
                                        "totalWithZero": 1,
                                        "totalWithAtLeastOne": 0
                                    },
                                    "attachmentFramework": {
                                        "externalAttachments": [
                                            {
                                                "type": ".files",
                                                "average": 4,
                                                "maxOnACase": 4,
                                                "total": 4
                                            }
                                        ],
                                        "persistableAttachments": [],
                                        "files": {
                                            "averageSize": {
                                                "value": 3
                                            },
                                            "average": 4,
                                            "maxOnACase": 4,
                                            "total": 4
                                        }
                                    }
                                },
                                "sec": {
                                    "total": 0,
                                    "daily": 0,
                                    "weekly": 0,
                                    "monthly": 0,
                                    "attachmentFramework": {
                                        "externalAttachments": [],
                                        "persistableAttachments": [],
                                        "files": {
                                            "average": 0,
                                            "averageSize": 0,
                                            "maxOnACase": 0,
                                            "total": 0
                                        }
                                    },
                                    "assignees": {
                                        "total": 0,
                                        "totalWithZero": 0,
                                        "totalWithAtLeastOne": 0
                                    }
                                },
                                "obs": {
                                    "total": 0,
                                    "daily": 0,
                                    "weekly": 0,
                                    "monthly": 0,
                                    "attachmentFramework": {
                                        "externalAttachments": [],
                                        "persistableAttachments": [],
                                        "files": {
                                            "average": 0,
                                            "averageSize": 0,
                                            "maxOnACase": 0,
                                            "total": 0
                                        }
                                    },
                                    "assignees": {
                                        "total": 0,
                                        "totalWithZero": 0,
                                        "totalWithAtLeastOne": 0
                                    }
                                },
                                "main": {
                                    "total": 1,
                                    "daily": 1,
                                    "weekly": 1,
                                    "monthly": 1,
                                    "attachmentFramework": {
                                        "externalAttachments": [
                                            {
                                                "type": ".files",
                                                "average": 4,
                                                "maxOnACase": 4,
                                                "total": 4
                                            }
                                        ],
                                        "persistableAttachments": [],
                                        "files": {
                                            "averageSize": {
                                                "value": 3
                                            },
                                            "average": 4,
                                            "maxOnACase": 4,
                                            "total": 4
                                        }
                                    },
                                    "assignees": {
                                        "total": 0,
                                        "totalWithZero": 1,
                                        "totalWithAtLeastOne": 0
                                    }
                                }
                            },
                            "userActions": {
                                "all": {
                                    "total": 5,
                                    "daily": 5,
                                    "weekly": 5,
                                    "monthly": 5,
                                    "maxOnACase": 5
                                }
                            },
                            "comments": {
                                "all": {
                                    "total": 0,
                                    "daily": 0,
                                    "weekly": 0,
                                    "monthly": 0,
                                    "maxOnACase": 0
                                }
                            },
                            "alerts": {
                                "all": {
                                    "total": 0,
                                    "daily": 0,
                                    "weekly": 0,
                                    "monthly": 0,
                                    "maxOnACase": 0
                                }
                            },
                            "connectors": {
                                "all": {
                                    "all": {
                                        "totalAttached": 0
                                    },
                                    "itsm": {
                                        "totalAttached": 0
                                    },
                                    "sir": {
                                        "totalAttached": 0
                                    },
                                    "jira": {
                                        "totalAttached": 0
                                    },
                                    "resilient": {
                                        "totalAttached": 0
                                    },
                                    "swimlane": {
                                        "totalAttached": 0
                                    },
                                    "maxAttachedToACase": 0
                                }
                            },
                            "pushes": {
                                "all": {
                                    "total": 0,
                                    "maxOnACase": 0
                                }
                            },
                            "configuration": {
                                "all": {
                                    "closure": {
                                        "manually": 0,
                                        "automatic": 0
                                    }
                                }
                            }
                        },

Testing

To test modify this file: https://github.com/elastic/kibana/blob/main/x-pack/plugins/cases/server/telemetry/schedule_telemetry_task.ts

With:

export const scheduleCasesTelemetryTask = (
  taskManager: TaskManagerStartContract,
  logger: Logger
) => {
  (async () => {
    await taskManager
      .ensureScheduled({
        id: CASES_TELEMETRY_TASK_NAME,
        taskType: CASES_TELEMETRY_TASK_NAME,
        schedule: {
          interval: `${MINUTES_ON_HALF_DAY}m`,
        },
        scope: ['cases'],
        params: {},
        state: {},
      })
      .catch((err) => {
        logger.debug(
          `Error scheduling cases task with ID ${CASES_TELEMETRY_TASK_NAME} and type ${CASES_TELEMETRY_TASK_NAME}. Received ${err.message}`
        );
      });

    await taskManager.runSoon(CASES_TELEMETRY_TASK_NAME);
  })();
};

This will cause the telemetry to be sent as soon as the server is restarted.

To generate files and attachments to add stats to the telemetry I created this python script: https://github.com/elastic/cases-files-generator

To retrieve the telemetry:

POST http://localhost:5601/api/telemetry/v2/clusters/_stats
{
   "refreshCache": true,
   "unencrypted": true
}

@jonathan-buttner jonathan-buttner added release_note:skip Skip the PR/issue when compiling release notes Team:ResponseOps Label for the ResponseOps team (formerly the Cases and Alerting teams) Feature:Cases Cases feature v8.8.0 labels Mar 8, 2023
/**
* This should only be used within telemetry
*/
export const OWNERS = [OBSERVABILITY_OWNER, SECURITY_SOLUTION_OWNER, GENERAL_CASES_OWNER] as const;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have these values in common now.

@@ -42,7 +43,7 @@ export const createCasesTelemetry = async ({
}: CreateCasesTelemetryArgs) => {
const getInternalSavedObjectClient = async (): Promise<ISavedObjectsRepository> => {
const [coreStart] = await core.getStartServices();
return coreStart.savedObjects.createInternalRepository(SAVED_OBJECT_TYPES);
return coreStart.savedObjects.createInternalRepository([...SAVED_OBJECT_TYPES, FILE_SO_TYPE]);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just calling this out, to retrieve the average file size we have to do an average aggregation over the file service's saved objects that contain a metadata field caseId. Hopefully it's ok that we're accessing the file service's saved object internals.

The advantage of doing it this way is that we don't need to duplicate the file size within case comments.

createdAt: savedObjects?.[0].saved_objects?.[0].attributes?.created_at ?? null,
updatedAt: savedObjects?.[1].saved_objects?.[0].attributes?.updated_at ?? null,
closedAt: savedObjects?.[2].saved_objects?.[0].attributes?.closed_at ?? null,
createdAt: savedObjects?.[0]?.saved_objects?.[0]?.attributes?.created_at ?? null,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the telemetry runs initially and there are no cases this was throwing an error

};
};

export const getCasesTelemetryData = async ({
savedObjectsClient,
logger,
}: CollectTelemetryDataParams): Promise<CasesTelemetry['cases']> => {
const byOwnerAggregationQuery = OWNERS.reduce(
try {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did some refactoring here, wrapped it all in a try/catch so we can more easily identify errors when we're developing additional telemetry and now doing a promise.all

createdAt: savedObjects?.[0].saved_objects?.[0].attributes?.created_at ?? null,
updatedAt: savedObjects?.[1].saved_objects?.[0].attributes?.updated_at ?? null,
closedAt: savedObjects?.[2].saved_objects?.[0].attributes?.closed_at ?? null,
createdAt: savedObjects?.[0]?.saved_objects?.[0]?.attributes?.created_at ?? '',
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prior to adding the ? this was causing an exception if saved_objects[0] was undefined which was skipping returning null. Now that this doesn't throw an error I'm getting test failures because a keyword field can't be mapped to null 🤷‍♂️ .

@jonathan-buttner jonathan-buttner marked this pull request as ready for review March 10, 2023 01:10
@jonathan-buttner jonathan-buttner requested review from a team as code owners March 10, 2023 01:10
@elasticmachine
Copy link
Contributor

Pinging @elastic/response-ops (Team:ResponseOps)

@elasticmachine
Copy link
Contributor

Pinging @elastic/response-ops-cases (Feature:Cases)

Copy link
Member

@afharo afharo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving to unblock. I added a nit, though.

persistableAttachments: attachmentRegistrySchema,
externalAttachments: attachmentRegistrySchema,
files: {
average: long,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: do we want to keep the decimals in the avg? If so, we might need to mark it as double or float. WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great question, I was thinking integers since I don't know if care about the precision that much for this telemetry. I'll ask my PM though 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked with my PM and we're going to keep them as longs.

@kibana-ci
Copy link
Collaborator

💚 Build Succeeded

Metrics [docs]

Unknown metric groups

ESLint disabled line counts

id before after diff
securitySolution 433 436 +3

Total ESLint disabled count

id before after diff
securitySolution 513 516 +3

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

Copy link
Member

@cnasikas cnasikas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thanks for the refactor. Much cleaner.

@jonathan-buttner jonathan-buttner merged commit f17fe1b into elastic:main Mar 24, 2023
@kibanamachine kibanamachine added the backport:skip This commit does not require backporting label Mar 24, 2023
@jonathan-buttner jonathan-buttner deleted the cases-files-telemetry branch March 24, 2023 19:43
@cnasikas cnasikas mentioned this pull request Apr 3, 2023
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport:skip This commit does not require backporting Feature:Cases Cases feature release_note:skip Skip the PR/issue when compiling release notes Team:ResponseOps Label for the ResponseOps team (formerly the Cases and Alerting teams) v8.8.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants