refactor(core): Log binary data file write errors (no-changelog) (#7237)

This PR adds logging for binary data file write errors, to capture why
executions sometimes point to non-existing binary data files. See
[Sentry
error](https://n8nio.sentry.io/issues/4495134693/?alert_rule_id=14556563&alert_type=issue&notification_uuid=4b50a5da-6ae9-472e-9658-984cca824762&project=4503924908883968&referrer=slack).
This commit is contained in:
Iván Ovejero 2023-09-22 11:48:20 +02:00 committed by GitHub
parent c0df5cdfd4
commit e8e44f6b6e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 54 additions and 12 deletions

View file

@ -6,7 +6,8 @@ import type { Readable } from 'stream';
import type { BinaryMetadata } from 'n8n-workflow'; import type { BinaryMetadata } from 'n8n-workflow';
import { jsonParse } from 'n8n-workflow'; import { jsonParse } from 'n8n-workflow';
import type { IBinaryDataConfig, IBinaryDataManager } from '../Interfaces'; import { IBinaryDataConfig } from '../Interfaces';
import type { IBinaryDataManager } from '../Interfaces';
import { FileNotFoundError } from '../errors'; import { FileNotFoundError } from '../errors';
const executionExtractionRegexp = const executionExtractionRegexp =

View file

@ -1,11 +1,13 @@
import { readFile, stat } from 'fs/promises'; import { readFile, stat } from 'fs/promises';
import type { BinaryMetadata, IBinaryData, INodeExecutionData } from 'n8n-workflow'; import type { BinaryMetadata, INodeExecutionData } from 'n8n-workflow';
import prettyBytes from 'pretty-bytes'; import prettyBytes from 'pretty-bytes';
import type { Readable } from 'stream'; import type { Readable } from 'stream';
import { BINARY_ENCODING } from 'n8n-workflow'; import { BINARY_ENCODING, LoggerProxy as Logger, IBinaryData } from 'n8n-workflow';
import type { IBinaryDataConfig, IBinaryDataManager } from '../Interfaces'; import { IBinaryDataConfig } from '../Interfaces';
import type { IBinaryDataManager } from '../Interfaces';
import { BinaryDataFileSystem } from './FileSystem'; import { BinaryDataFileSystem } from './FileSystem';
import { binaryToBuffer } from './utils'; import { binaryToBuffer } from './utils';
import { LogCatch } from '../decorators/LogCatch.decorator';
export class BinaryDataManager { export class BinaryDataManager {
static instance: BinaryDataManager | undefined; static instance: BinaryDataManager | undefined;
@ -47,6 +49,7 @@ export class BinaryDataManager {
return BinaryDataManager.instance; return BinaryDataManager.instance;
} }
@LogCatch((error) => Logger.error('Failed to copy binary data file', { error }))
async copyBinaryFile( async copyBinaryFile(
binaryData: IBinaryData, binaryData: IBinaryData,
filePath: string, filePath: string,
@ -79,6 +82,7 @@ export class BinaryDataManager {
return binaryData; return binaryData;
} }
@LogCatch((error) => Logger.error('Failed to write binary data file', { error }))
async storeBinaryData( async storeBinaryData(
binaryData: IBinaryData, binaryData: IBinaryData,
input: Buffer | Readable, input: Buffer | Readable,
@ -162,6 +166,9 @@ export class BinaryDataManager {
} }
} }
@LogCatch((error) =>
Logger.error('Failed to copy all binary data files for execution', { error }),
)
async duplicateBinaryData( async duplicateBinaryData(
inputData: Array<INodeExecutionData[] | null>, inputData: Array<INodeExecutionData[] | null>,
executionId: string, executionId: string,

View file

@ -1,11 +1,11 @@
import type { import type { INode } from 'n8n-workflow';
INode, import {
Workflow,
INodeCredentials, INodeCredentials,
INodeParameters, INodeParameters,
INodeTypeNameVersion, INodeTypeNameVersion,
INodeTypes, INodeTypes,
} from 'n8n-workflow'; } from 'n8n-workflow';
import { Workflow } from 'n8n-workflow';
const TEMP_NODE_NAME = 'Temp-Node'; const TEMP_NODE_NAME = 'Temp-Node';
const TEMP_WORKFLOW_NAME = 'Temp-Workflow'; const TEMP_WORKFLOW_NAME = 'Temp-Workflow';

View file

@ -19,20 +19,23 @@ import type {
IPinData, IPinData,
IRun, IRun,
IRunData, IRunData,
IRunExecutionData,
ISourceData, ISourceData,
ITaskData, ITaskData,
ITaskDataConnections, ITaskDataConnections,
ITaskDataConnectionsSource, ITaskDataConnectionsSource,
IWaitingForExecution, IWaitingForExecution,
IWaitingForExecutionSource, IWaitingForExecutionSource,
IWorkflowExecuteAdditionalData,
NodeApiError, NodeApiError,
NodeOperationError, NodeOperationError,
Workflow, Workflow,
} from 'n8n-workflow';
import {
LoggerProxy as Logger,
WorkflowOperationError,
IRunExecutionData,
IWorkflowExecuteAdditionalData,
WorkflowExecuteMode, WorkflowExecuteMode,
} from 'n8n-workflow'; } from 'n8n-workflow';
import { LoggerProxy as Logger, WorkflowOperationError } from 'n8n-workflow';
import get from 'lodash/get'; import get from 'lodash/get';
import * as NodeExecuteFunctions from './NodeExecuteFunctions'; import * as NodeExecuteFunctions from './NodeExecuteFunctions';

View file

@ -0,0 +1,30 @@
/* eslint-disable @typescript-eslint/naming-convention */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
export const LogCatch = (logFn: (error: unknown) => void) => {
return (target: unknown, propertyKey: string, descriptor: PropertyDescriptor) => {
const originalMethod = descriptor.value;
descriptor.value = function (...args: unknown[]) {
try {
const result: unknown = originalMethod.apply(this, args);
if (result && result instanceof Promise) {
return result.catch((error: unknown) => {
logFn(error);
throw error;
});
}
return result;
} catch (error) {
logFn(error);
throw error;
}
};
return descriptor;
};
};

View file

@ -13,7 +13,6 @@ import type {
INode, INode,
INodeCredentialsDetails, INodeCredentialsDetails,
INodeType, INodeType,
INodeTypeData,
INodeTypes, INodeTypes,
IRun, IRun,
ITaskData, ITaskData,
@ -24,7 +23,7 @@ import type {
WorkflowTestData, WorkflowTestData,
} from 'n8n-workflow'; } from 'n8n-workflow';
import { ICredentialsHelper, NodeHelpers, WorkflowHooks } from 'n8n-workflow'; import { ICredentialsHelper, NodeHelpers, WorkflowHooks, INodeTypeData } from 'n8n-workflow';
import { Credentials } from '@/Credentials'; import { Credentials } from '@/Credentials';
import { predefinedNodesTypes } from './constants'; import { predefinedNodesTypes } from './constants';

View file

@ -7,6 +7,8 @@
"paths": { "paths": {
"@/*": ["./*"] "@/*": ["./*"]
}, },
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
// TODO: remove all options below this line // TODO: remove all options below this line
"useUnknownInCatchVariables": false "useUnknownInCatchVariables": false
}, },