errorlog
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import chalk from 'chalk';
|
||||
import { isEmpty, prettyJson } from '../../util/prettyJson.js';
|
||||
import { writeErrorLog } from '../../util/errorLog.js';
|
||||
|
||||
function logField(label, value, { skipEmpty = false } = {}) {
|
||||
if (skipEmpty && isEmpty(value)) return;
|
||||
@@ -56,6 +57,60 @@ export const loggerMiddleware = (req, res, next) => {
|
||||
}
|
||||
logField('body', req.body ?? {}, { skipEmpty: true });
|
||||
logField('response', getBody());
|
||||
|
||||
// Dump full request/response/error details to a log file on 5xx.
|
||||
const statusCode = res.statusCode;
|
||||
if (statusCode >= 500) {
|
||||
const body = getBody();
|
||||
writeErrorLog(
|
||||
`${req.method} ${req.originalUrl} ${statusCode}`,
|
||||
{
|
||||
request: {
|
||||
method: req.method,
|
||||
url: req.originalUrl,
|
||||
headers: req.headers,
|
||||
params: req.params,
|
||||
query: req.query,
|
||||
},
|
||||
body: req.body,
|
||||
files: Array.isArray(req.files)
|
||||
? req.files.map((f) => ({
|
||||
fieldname: f.fieldname,
|
||||
originalname: f.originalname,
|
||||
mimetype: f.mimetype,
|
||||
size: f.size,
|
||||
encoding: f.encoding,
|
||||
bufferHeadHex: f.buffer
|
||||
? [...f.buffer.subarray(0, 32)]
|
||||
.map((b) => b.toString(16).padStart(2, '0'))
|
||||
.join(' ')
|
||||
: null,
|
||||
}))
|
||||
: undefined,
|
||||
response: {
|
||||
status: statusCode,
|
||||
body,
|
||||
},
|
||||
error: res.locals?.error
|
||||
? {
|
||||
message: res.locals.error.message,
|
||||
code: res.locals.error.code,
|
||||
number: res.locals.error.number,
|
||||
state: res.locals.error.state,
|
||||
class: res.locals.error.class,
|
||||
lineNumber: res.locals.error.lineNumber,
|
||||
procName: res.locals.error.procName,
|
||||
stack: res.locals.error.stack,
|
||||
precedingErrors: res.locals.error.precedingErrors?.map((e) => ({
|
||||
message: e.message,
|
||||
number: e.number,
|
||||
state: e.state,
|
||||
})),
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
next();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import multer from 'multer';
|
||||
import * as db from '../../db/delivery.js';
|
||||
import { writeErrorLog } from '../../util/errorLog.js';
|
||||
|
||||
// Shared memory storage for delivery attachments (max 50 MB)
|
||||
const multerUpload = multer({
|
||||
@@ -105,6 +106,51 @@ function requireSelectedCompany(req) {
|
||||
return { companyId };
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug logging: dump per-file details before handing them to the store.
|
||||
* Needed to diagnose MSSQL TDS errors on @content ("Data type 0xA5 has an
|
||||
* invalid data length or metadata length").
|
||||
*/
|
||||
function logDeliveryFiles(files, label) {
|
||||
if (!Array.isArray(files) || files.length === 0) {
|
||||
console.log(`[delivery diag] ${label}: no files`);
|
||||
return;
|
||||
}
|
||||
files.forEach((f, idx) => {
|
||||
const content = f?.content;
|
||||
const isBuf = Buffer.isBuffer(content);
|
||||
const hex = isBuf
|
||||
? [...content.subarray(0, 8)].map((b) => b.toString(16).padStart(2, '0')).join(' ')
|
||||
: null;
|
||||
console.log(
|
||||
`[delivery diag] ${label} file[${idx}]:` +
|
||||
` name=${JSON.stringify(f?.name)}` +
|
||||
` mimetype=${JSON.stringify(f?.mimetype)}` +
|
||||
` size=${JSON.stringify(f?.size)}` +
|
||||
` contentType=${isBuf ? 'Buffer' : typeof content}` +
|
||||
` contentLength=${isBuf ? content.length : typeof content === 'string' ? content.length : 'n/a'}` +
|
||||
(hex !== null ? ` headHex=${hex}` : '') +
|
||||
(typeof content === 'string' ? ` head=${JSON.stringify(content.slice(0, 16))}` : '')
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug logging: dump everything tedious/mssql puts on a SQL error.
|
||||
*/
|
||||
function logStoreError(label, error) {
|
||||
console.error(`[delivery diag] ${label} store error:`);
|
||||
console.error(` message: ${error.message}`);
|
||||
console.error(` code: ${error.code} number: ${error.number} state: ${error.state} class: ${error.class}`);
|
||||
console.error(` lineNumber: ${error.lineNumber} serverName: ${error.serverName} procName: ${error.procName}`);
|
||||
if (error.precedingErrors?.length) {
|
||||
error.precedingErrors.forEach((e, i) => {
|
||||
console.error(` precedingError[${i}]: ${e.message} (number=${e.number} state=${e.state})`);
|
||||
});
|
||||
}
|
||||
console.error(error.stack);
|
||||
}
|
||||
|
||||
function deliveryErrorStatus(error) {
|
||||
if (error.message === 'No company selected') return 400;
|
||||
if (error.message === 'Not authorized') return 403;
|
||||
@@ -293,6 +339,7 @@ export function register(app, basePath = '') {
|
||||
const deliveries = await db.list(req.user.id);
|
||||
res.status(200).json({ deliveries });
|
||||
} catch (error) {
|
||||
res.locals.error = error;
|
||||
const status = deliveryErrorStatus(error);
|
||||
if (status === 500) console.error('List deliveries failed:', error);
|
||||
res.status(status).json({ error: error.message });
|
||||
@@ -307,6 +354,7 @@ export function register(app, basePath = '') {
|
||||
|
||||
try {
|
||||
const { externalNumber, date, supplierId, lines, files } = parseDeliveryPayload(req);
|
||||
logDeliveryFiles(files, 'create');
|
||||
const delivery = await db.create(req.user.id, {
|
||||
externalNumber,
|
||||
date,
|
||||
@@ -316,8 +364,34 @@ export function register(app, basePath = '') {
|
||||
});
|
||||
res.status(201).json({ delivery });
|
||||
} catch (error) {
|
||||
res.locals.error = error;
|
||||
const status = deliveryErrorStatus(error);
|
||||
if (status === 500) console.error('Create delivery failed:', error);
|
||||
if (status === 500) {
|
||||
logStoreError('Create delivery failed', error);
|
||||
// Dump the full parsed payload (including file metadata) to a log file.
|
||||
try {
|
||||
const { externalNumber, date, supplierId, lines, files } = parseDeliveryPayload(req);
|
||||
writeErrorLog(`POST ${basePath}/delivery 500 (create delivery)`, {
|
||||
payload: { externalNumber, date, supplierId, lines },
|
||||
files: files?.map((f) => ({
|
||||
name: f.name,
|
||||
mimetype: f.mimetype,
|
||||
size: f.size,
|
||||
meta: f.meta,
|
||||
})),
|
||||
error: {
|
||||
message: error.message,
|
||||
code: error.code,
|
||||
number: error.number,
|
||||
state: error.state,
|
||||
class: error.class,
|
||||
stack: error.stack,
|
||||
},
|
||||
});
|
||||
} catch (logErr) {
|
||||
console.error('[delivery diag] Failed to dump payload:', logErr.message);
|
||||
}
|
||||
}
|
||||
res.status(status).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
@@ -526,6 +600,7 @@ export function register(app, basePath = '') {
|
||||
const delivery = await db.getById(req.user.id, id);
|
||||
res.status(200).json({ delivery });
|
||||
} catch (error) {
|
||||
res.locals.error = error;
|
||||
const status = deliveryErrorStatus(error);
|
||||
if (status === 500) console.error('Get delivery failed:', error);
|
||||
res.status(status).json({ error: error.message });
|
||||
@@ -562,11 +637,15 @@ export function register(app, basePath = '') {
|
||||
patch.files = parsed.files;
|
||||
}
|
||||
|
||||
logDeliveryFiles(patch.files, `update ${id}`);
|
||||
const delivery = await db.update(req.user.id, id, patch);
|
||||
res.status(200).json({ delivery });
|
||||
} catch (error) {
|
||||
res.locals.error = error;
|
||||
const status = deliveryErrorStatus(error);
|
||||
if (status === 500) console.error('Update delivery failed:', error);
|
||||
if (status === 500) {
|
||||
logStoreError('Update delivery failed', error);
|
||||
}
|
||||
res.status(status).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
@@ -586,6 +665,7 @@ export function register(app, basePath = '') {
|
||||
await db.remove(req.user.id, id);
|
||||
res.sendStatus(204);
|
||||
} catch (error) {
|
||||
res.locals.error = error;
|
||||
const status = deliveryErrorStatus(error);
|
||||
if (status === 500) console.error('Delete delivery failed:', error);
|
||||
res.status(status).json({ error: error.message });
|
||||
@@ -729,6 +809,7 @@ export function register(app, basePath = '') {
|
||||
return res.status(200).send(buffer);
|
||||
}
|
||||
} catch (error) {
|
||||
res.locals.error = error;
|
||||
const status = deliveryErrorStatus(error);
|
||||
if (status === 500) console.error('Get delivery file failed:', error);
|
||||
res.status(status).json({ error: error.message });
|
||||
|
||||
Reference in New Issue
Block a user