Improve telemetry (#2604)

* add node positions in node graph

* add hover events

* add tag count in save event

* populate properties when default

* fix delete and enable node events

* add node and workflow exec events

* lint

* add node graph

* add node id
This commit is contained in:
Ahsan Virani
2022-01-07 17:14:59 +01:00
committed by GitHub
parent df412e9523
commit 57016624b8
14 changed files with 104 additions and 32 deletions

View File

@@ -1,19 +1,23 @@
/* eslint-disable import/no-cycle */
import { BinaryDataManager } from 'n8n-core';
import { IDataObject, IRun, TelemetryHelpers } from 'n8n-workflow';
import { IDataObject, INodeTypes, IRun, TelemetryHelpers } from 'n8n-workflow';
import {
IDiagnosticInfo,
IInternalHooksClass,
IPersonalizationSurveyAnswers,
IWorkflowBase,
IWorkflowDb,
} from '.';
import { Telemetry } from './telemetry';
export class InternalHooksClass implements IInternalHooksClass {
private versionCli: string;
constructor(private telemetry: Telemetry, versionCli: string) {
private nodeTypes: INodeTypes;
constructor(private telemetry: Telemetry, versionCli: string, nodeTypes: INodeTypes) {
this.versionCli = versionCli;
this.nodeTypes = nodeTypes;
}
async onServerStarted(
@@ -53,7 +57,7 @@ export class InternalHooksClass implements IInternalHooksClass {
}
async onWorkflowCreated(workflow: IWorkflowBase): Promise<void> {
const { nodeGraph } = TelemetryHelpers.generateNodesGraph(workflow);
const { nodeGraph } = TelemetryHelpers.generateNodesGraph(workflow, this.nodeTypes);
return this.telemetry.track('User created workflow', {
workflow_id: workflow.id,
node_graph: nodeGraph,
@@ -67,14 +71,15 @@ export class InternalHooksClass implements IInternalHooksClass {
});
}
async onWorkflowSaved(workflow: IWorkflowBase): Promise<void> {
const { nodeGraph } = TelemetryHelpers.generateNodesGraph(workflow);
async onWorkflowSaved(workflow: IWorkflowDb): Promise<void> {
const { nodeGraph } = TelemetryHelpers.generateNodesGraph(workflow, this.nodeTypes);
return this.telemetry.track('User saved workflow', {
workflow_id: workflow.id,
node_graph: nodeGraph,
node_graph_string: JSON.stringify(nodeGraph),
version_cli: this.versionCli,
num_tags: workflow.tags.length,
});
}
@@ -83,6 +88,7 @@ export class InternalHooksClass implements IInternalHooksClass {
workflow: IWorkflowBase,
runData?: IRun,
): Promise<void> {
const promises = [Promise.resolve()];
const properties: IDataObject = {
workflow_id: workflow.id,
is_manual: false,
@@ -91,11 +97,10 @@ export class InternalHooksClass implements IInternalHooksClass {
if (runData !== undefined) {
properties.execution_mode = runData.mode;
if (runData.mode === 'manual') {
properties.is_manual = true;
}
properties.success = !!runData.finished;
properties.is_manual = runData.mode === 'manual';
let nodeGraphResult;
if (!properties.success && runData?.data.resultData.error) {
properties.error_message = runData?.data.resultData.error.message;
@@ -115,7 +120,7 @@ export class InternalHooksClass implements IInternalHooksClass {
}
if (properties.is_manual) {
const nodeGraphResult = TelemetryHelpers.generateNodesGraph(workflow);
nodeGraphResult = TelemetryHelpers.generateNodesGraph(workflow, this.nodeTypes);
properties.node_graph = nodeGraphResult.nodeGraph;
properties.node_graph_string = JSON.stringify(nodeGraphResult.nodeGraph);
@@ -124,9 +129,51 @@ export class InternalHooksClass implements IInternalHooksClass {
}
}
}
if (properties.is_manual) {
if (!nodeGraphResult) {
nodeGraphResult = TelemetryHelpers.generateNodesGraph(workflow, this.nodeTypes);
}
const manualExecEventProperties = {
workflow_id: workflow.id,
status: properties.success ? 'success' : 'failed',
error_message: properties.error_message,
error_node_type: properties.error_node_type,
node_graph: properties.node_graph,
node_graph_string: properties.node_graph_string,
error_node_id: properties.error_node_id,
};
if (!manualExecEventProperties.node_graph) {
nodeGraphResult = TelemetryHelpers.generateNodesGraph(workflow, this.nodeTypes);
manualExecEventProperties.node_graph = nodeGraphResult.nodeGraph;
manualExecEventProperties.node_graph_string = JSON.stringify(
manualExecEventProperties.node_graph,
);
}
if (runData.data.startData?.destinationNode) {
promises.push(
this.telemetry.track('Manual node exec finished', {
...manualExecEventProperties,
node_type: TelemetryHelpers.getNodeTypeForName(
workflow,
runData.data.startData?.destinationNode,
)?.type,
node_id: nodeGraphResult.nameIndices[runData.data.startData?.destinationNode],
}),
);
} else {
promises.push(
this.telemetry.track('Manual workflow exec finished', manualExecEventProperties),
);
}
}
}
return Promise.all([
...promises,
BinaryDataManager.getInstance().persistBinaryDataForExecutionId(executionId),
this.telemetry.trackWorkflowExecution(properties),
]).then(() => {});

View File

@@ -1,4 +1,5 @@
/* eslint-disable import/no-cycle */
import { INodeTypes } from 'n8n-workflow';
import { InternalHooksClass } from './InternalHooks';
import { Telemetry } from './telemetry';
@@ -13,11 +14,12 @@ export class InternalHooksManager {
throw new Error('InternalHooks not initialized');
}
static init(instanceId: string, versionCli: string): InternalHooksClass {
static init(instanceId: string, versionCli: string, nodeTypes: INodeTypes): InternalHooksClass {
if (!this.internalHooksInstance) {
this.internalHooksInstance = new InternalHooksClass(
new Telemetry(instanceId, versionCli),
versionCli,
nodeTypes,
);
}

View File

@@ -891,7 +891,7 @@ class App {
}
await this.externalHooks.run('workflow.afterUpdate', [workflow]);
void InternalHooksManager.getInstance().onWorkflowSaved(workflow as IWorkflowBase);
void InternalHooksManager.getInstance().onWorkflowSaved(workflow);
if (workflow.active) {
// When the workflow is supposed to be active add it again

View File

@@ -145,7 +145,7 @@ export class WorkflowRunnerProcess {
const instanceId = (await UserSettings.prepareUserSettings()).instanceId ?? '';
const { cli } = await GenericHelpers.getVersions();
InternalHooksManager.init(instanceId, cli);
InternalHooksManager.init(instanceId, cli, nodeTypes);
const binaryDataConfig = config.get('binaryDataManager') as IBinaryDataConfig;
await BinaryDataManager.init(binaryDataConfig);