mirror of
https://github.com/logto-io/logto.git
synced 2025-01-06 20:40:08 -05:00
feat(core): log error body (#1065)
This commit is contained in:
parent
b2b71898d3
commit
2ba11215ed
3 changed files with 77 additions and 30 deletions
|
@ -1,5 +1,7 @@
|
|||
import { LogPayload, LogResult } from '@logto/schemas';
|
||||
import i18next from 'i18next';
|
||||
|
||||
import RequestError from '@/errors/RequestError';
|
||||
import { insertLog } from '@/queries/log';
|
||||
import { createContextWithRouteParameters } from '@/utils/test-utils';
|
||||
|
||||
|
@ -21,7 +23,7 @@ jest.mock('nanoid', () => ({
|
|||
describe('koaLog middleware', () => {
|
||||
const insertLogMock = insertLog as jest.Mock;
|
||||
const type = 'SignInUsernamePassword';
|
||||
const payload: LogPayload = {
|
||||
const mockPayload: LogPayload = {
|
||||
userId: 'foo',
|
||||
username: 'Bar',
|
||||
};
|
||||
|
@ -34,7 +36,7 @@ describe('koaLog middleware', () => {
|
|||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('insert log with success response', async () => {
|
||||
it('should insert a success log when next() does not throw an error', async () => {
|
||||
const ctx: WithLogContext<ReturnType<typeof createContextWithRouteParameters>> = {
|
||||
...createContextWithRouteParameters({ headers: { 'user-agent': userAgent } }),
|
||||
// Bypass middleware context type assert
|
||||
|
@ -44,7 +46,7 @@ describe('koaLog middleware', () => {
|
|||
ctx.request.ip = ip;
|
||||
|
||||
const next = async () => {
|
||||
ctx.log(type, payload);
|
||||
ctx.log(type, mockPayload);
|
||||
};
|
||||
await koaLog()(ctx, next);
|
||||
|
||||
|
@ -52,7 +54,7 @@ describe('koaLog middleware', () => {
|
|||
id: nanoIdMock,
|
||||
type,
|
||||
payload: {
|
||||
...payload,
|
||||
...mockPayload,
|
||||
result: LogResult.Success,
|
||||
ip,
|
||||
userAgent,
|
||||
|
@ -60,33 +62,70 @@ describe('koaLog middleware', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should insert log with failed result if next throws error', async () => {
|
||||
const ctx: WithLogContext<ReturnType<typeof createContextWithRouteParameters>> = {
|
||||
...createContextWithRouteParameters({ headers: { 'user-agent': userAgent } }),
|
||||
// Bypass middleware context type assert
|
||||
addLogContext,
|
||||
log,
|
||||
};
|
||||
ctx.request.ip = ip;
|
||||
describe('should insert an error log with the error message when next() throws an error', () => {
|
||||
it('should log with error message when next throws a normal Error', async () => {
|
||||
const ctx: WithLogContext<ReturnType<typeof createContextWithRouteParameters>> = {
|
||||
...createContextWithRouteParameters({ headers: { 'user-agent': userAgent } }),
|
||||
// Bypass middleware context type assert
|
||||
addLogContext,
|
||||
log,
|
||||
};
|
||||
ctx.request.ip = ip;
|
||||
|
||||
const error = new Error('next error');
|
||||
const message = 'Normal error';
|
||||
const error = new Error(message);
|
||||
|
||||
const next = async () => {
|
||||
ctx.log(type, payload);
|
||||
throw error;
|
||||
};
|
||||
await expect(koaLog()(ctx, next)).rejects.toMatchError(error);
|
||||
const next = async () => {
|
||||
ctx.log(type, mockPayload);
|
||||
throw error;
|
||||
};
|
||||
await expect(koaLog()(ctx, next)).rejects.toMatchError(error);
|
||||
|
||||
expect(insertLogMock).toBeCalledWith({
|
||||
id: nanoIdMock,
|
||||
type,
|
||||
payload: {
|
||||
...payload,
|
||||
result: LogResult.Error,
|
||||
error: String(error),
|
||||
ip,
|
||||
userAgent,
|
||||
},
|
||||
expect(insertLogMock).toBeCalledWith({
|
||||
id: nanoIdMock,
|
||||
type,
|
||||
payload: {
|
||||
...mockPayload,
|
||||
result: LogResult.Error,
|
||||
error: { message: `Error: ${message}` },
|
||||
ip,
|
||||
userAgent,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should insert an error log with the error body when next() throws a RequestError', async () => {
|
||||
const ctx: WithLogContext<ReturnType<typeof createContextWithRouteParameters>> = {
|
||||
...createContextWithRouteParameters({ headers: { 'user-agent': userAgent } }),
|
||||
// Bypass middleware context type assert
|
||||
addLogContext,
|
||||
log,
|
||||
};
|
||||
ctx.request.ip = ip;
|
||||
|
||||
const message = 'Error message';
|
||||
jest.spyOn(i18next, 't').mockReturnValueOnce(message); // Used in
|
||||
const code = 'connector.general';
|
||||
const data = { foo: 'bar', num: 123 };
|
||||
const error = new RequestError(code, data);
|
||||
|
||||
const next = async () => {
|
||||
ctx.log(type, mockPayload);
|
||||
throw error;
|
||||
};
|
||||
await expect(koaLog()(ctx, next)).rejects.toMatchError(error);
|
||||
|
||||
expect(insertLogMock).toBeCalledWith({
|
||||
id: nanoIdMock,
|
||||
type,
|
||||
payload: {
|
||||
...mockPayload,
|
||||
result: LogResult.Error,
|
||||
error: { message, code, data },
|
||||
ip,
|
||||
userAgent,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,8 +2,10 @@ import { BaseLogPayload, LogPayload, LogPayloads, LogResult, LogType } from '@lo
|
|||
import deepmerge from 'deepmerge';
|
||||
import { MiddlewareType } from 'koa';
|
||||
import { IRouterParamContext } from 'koa-router';
|
||||
import pick from 'lodash.pick';
|
||||
import { nanoid } from 'nanoid';
|
||||
|
||||
import RequestError from '@/errors/RequestError';
|
||||
import { insertLog } from '@/queries/log';
|
||||
|
||||
type MergeLog = <T extends LogType>(type: T, payload: LogPayloads[T]) => void;
|
||||
|
@ -90,7 +92,13 @@ export default function koaLog<
|
|||
try {
|
||||
await next();
|
||||
} catch (error: unknown) {
|
||||
logger.set({ result: LogResult.Error, error: String(error) });
|
||||
logger.set({
|
||||
result: LogResult.Error,
|
||||
error:
|
||||
error instanceof RequestError
|
||||
? pick(error, 'message', 'code', 'data')
|
||||
: { message: String(error) },
|
||||
});
|
||||
throw error;
|
||||
} finally {
|
||||
await logger.save();
|
||||
|
|
|
@ -7,7 +7,7 @@ export enum LogResult {
|
|||
|
||||
export interface BaseLogPayload {
|
||||
result?: LogResult;
|
||||
error?: string;
|
||||
error?: Record<string, unknown>;
|
||||
ip?: string;
|
||||
userAgent?: string;
|
||||
applicationId?: string;
|
||||
|
|
Loading…
Reference in a new issue