Comparing version 1.0.2 to 2.0.0
# Changelog | ||
### 2.0.0 (2022-08-22) | ||
- Breaking | ||
- Restructure Error props inside `err` prop including `fullStack` and `shortStack`. | ||
- Fixes | ||
- Include all props in `Error` object | ||
- Upgrade dependencies | ||
### 1.0.2 (2021.08.28) | ||
- Fixes | ||
- Make log level a required param | ||
### 1.0.1 (2021.08.27) | ||
- Fixes | ||
- Fix typing and error object handling bug | ||
### 1.0.0 (2021-08-25) | ||
@@ -4,0 +22,0 @@ |
@@ -1,30 +0,2 @@ | ||
import { Request, Response } from 'express'; | ||
declare const levels: readonly ["trace", "info", "warn", "error", "fatal", "security"]; | ||
export declare type LevelType = typeof levels[number]; | ||
export interface LogPresets extends Record<string, unknown> { | ||
module?: string; | ||
trackId?: string; | ||
} | ||
export interface LaLogOptions { | ||
addTrackId?: boolean; | ||
moduleName?: string; | ||
presets?: LogPresets; | ||
serviceName?: string; | ||
isTransient?: boolean; | ||
} | ||
export declare type ParseReqIn = Request & { | ||
user?: unknown; | ||
}; | ||
export declare type ParseReqOut = Pick<ParseReqIn, 'body' | 'headers' | 'method' | 'params' | 'path' | 'query' | 'url' | 'user'>; | ||
export interface LogData extends Record<string, unknown> { | ||
err?: Error; | ||
msg?: string; | ||
req?: ParseReqIn; | ||
} | ||
export interface ResponseWrapper { | ||
res: Response; | ||
code: number; | ||
} | ||
export declare type LogFunction = (logData: LogData, response?: ResponseWrapper) => Promise<any>; | ||
export declare type TimeLogFunction = (label: string, level: LevelType, extraLogDat?: LogData) => Promise<any>; | ||
import { LaLogOptions, LevelType, LogData, LogFunction, LogPresets, ParseReqIn, ParseReqOut, ResponseWrapper, TimeLogFunction } from './local-types'; | ||
export default class Logger { | ||
@@ -81,2 +53,1 @@ isTransient: boolean; | ||
} | ||
export {}; |
@@ -17,8 +17,8 @@ "use strict"; | ||
const loggly_wrapper_1 = require("./loggly-wrapper"); | ||
const levels = ['trace', 'info', 'warn', 'error', 'fatal', 'security']; | ||
const errorLevel = levels.indexOf('error'); | ||
const local_types_1 = require("./local-types"); | ||
const errorLevel = local_types_1.levels.indexOf('error'); | ||
const getInitialLogLevel = () => { | ||
const laLogLevel = process.env.LALOG_LEVEL; | ||
if (levels.includes(laLogLevel)) { | ||
return levels.indexOf(laLogLevel); | ||
if (local_types_1.levels.includes(laLogLevel)) { | ||
return local_types_1.levels.indexOf(laLogLevel); | ||
} | ||
@@ -41,8 +41,8 @@ return errorLevel; | ||
// Previously this was setup using a loop but Typescript couldn't type it | ||
this.trace = this.write.bind(this, levels.indexOf('trace')); | ||
this.info = this.write.bind(this, levels.indexOf('info')); | ||
this.warn = this.write.bind(this, levels.indexOf('warn')); | ||
this.error = this.write.bind(this, levels.indexOf('error')); | ||
this.fatal = this.write.bind(this, levels.indexOf('fatal')); | ||
this.security = this.write.bind(this, levels.indexOf('security')); | ||
this.trace = this.write.bind(this, local_types_1.levels.indexOf('trace')); | ||
this.info = this.write.bind(this, local_types_1.levels.indexOf('info')); | ||
this.warn = this.write.bind(this, local_types_1.levels.indexOf('warn')); | ||
this.error = this.write.bind(this, local_types_1.levels.indexOf('error')); | ||
this.fatal = this.write.bind(this, local_types_1.levels.indexOf('fatal')); | ||
this.security = this.write.bind(this, local_types_1.levels.indexOf('security')); | ||
this.timers = {}; | ||
@@ -67,3 +67,3 @@ /** | ||
static allLevels() { | ||
return levels; | ||
return local_types_1.levels; | ||
} | ||
@@ -74,3 +74,3 @@ /** | ||
static getLevel() { | ||
return levels[currentLevelIndex]; | ||
return local_types_1.levels[currentLevelIndex]; | ||
} | ||
@@ -87,3 +87,3 @@ /** | ||
} | ||
const newLevelIndex = levels.indexOf(newLevelName); | ||
const newLevelIndex = local_types_1.levels.indexOf(newLevelName); | ||
if (newLevelIndex >= 0) { | ||
@@ -121,3 +121,3 @@ currentLevelIndex = newLevelIndex; | ||
writeTimeEnd(label, level, extraLogDat) { | ||
const levelIndex = levels.indexOf(level); | ||
const levelIndex = local_types_1.levels.indexOf(level); | ||
const extraLogData = extraLogDat || {}; | ||
@@ -138,3 +138,2 @@ const time = this.timers[label]; | ||
async write(levelIndex, logData, response) { | ||
var _a; | ||
if (!(0, utils_1.isObject)(logData)) { | ||
@@ -146,3 +145,3 @@ // eslint-disable-next-line no-console | ||
const { req } = logData, rest = __rest(logData, ["req"]); | ||
const logObj = Object.assign(Object.assign({}, this.presets), rest); | ||
let logObj = Object.assign(Object.assign({}, this.presets), rest); | ||
if (response) { | ||
@@ -172,28 +171,7 @@ // If the response object has been included with the call then it means we need to | ||
if (levelIndex >= currentLevelIndex || this.isTransient) { | ||
logObj.level = levels[levelIndex]; | ||
logObj.level = local_types_1.levels[levelIndex]; | ||
if (levelIndex >= errorLevel && !logObj.err) { | ||
logObj.err = new Error(); | ||
} | ||
if (logObj.err) { | ||
if (!logObj.err.stack) { | ||
// This will happen if we manually created an err prop - it might not have a stack prop | ||
// `stack` is a non standard property on the Error object so it can be undefined | ||
// which is why we have to provide the ??. | ||
// Ignoring the line for code coverage for now because we're going to have to | ||
// mock new Error() or extract this line into a local method that can be mocked | ||
// which would add an extra frame to the stack which I don't want. | ||
/* istanbul ignore next */ | ||
logObj.err.stack = (_a = new Error().stack) !== null && _a !== void 0 ? _a : '<no error stack>'; | ||
} | ||
logObj.fullStack = logObj.err.stack.split('\n').slice(1); | ||
/** | ||
* Checks if string includes node_modules | ||
*/ | ||
const hasNodeModules = (i) => !i.includes('/node_modules/'); | ||
logObj.shortStack = logObj.fullStack.filter(hasNodeModules); | ||
if (!logObj.msg) { | ||
logObj.msg = logObj.err.message; | ||
} | ||
delete logObj.err; | ||
} | ||
logObj = (0, utils_1.enrichError)(logObj); | ||
if (req) { | ||
@@ -200,0 +178,0 @@ logObj.req = Logger.parseReq(req); |
@@ -0,3 +1,46 @@ | ||
import { Request, Response } from 'express'; | ||
export declare type BetterOmit<T, K extends PropertyKey> = { | ||
[P in keyof T as Exclude<P, K>]: T[P]; | ||
}; | ||
export declare const levels: readonly ["trace", "info", "warn", "error", "fatal", "security"]; | ||
export declare type LevelType = typeof levels[number]; | ||
export interface LogPresets extends Record<string, unknown> { | ||
module?: string; | ||
trackId?: string; | ||
} | ||
export interface LaLogOptions { | ||
addTrackId?: boolean; | ||
moduleName?: string; | ||
presets?: LogPresets; | ||
serviceName?: string; | ||
isTransient?: boolean; | ||
} | ||
export declare type ParseReqIn = Request & { | ||
user?: unknown; | ||
}; | ||
export declare type ParseReqOut = Pick<ParseReqIn, 'body' | 'headers' | 'method' | 'params' | 'path' | 'query' | 'url' | 'user'>; | ||
export interface LogData extends Record<string, unknown> { | ||
err?: Error; | ||
msg?: string; | ||
req?: ParseReqIn; | ||
} | ||
export interface LogDataOut extends BetterOmit<LogData, 'req'>, LogPresets { | ||
/** | ||
* The Stack property from the Error object split into lines. | ||
*/ | ||
fullStack?: string[]; | ||
/** | ||
* Created from the fullStack by removing lines containing node_modules | ||
*/ | ||
shortStack?: string[]; | ||
req?: ParseReqOut; | ||
} | ||
export interface ResponseWrapper { | ||
res: Response; | ||
code: number; | ||
} | ||
export declare type LogFunction = (logData: LogData, response?: ResponseWrapper) => Promise<any>; | ||
export declare type TimeLogFunction = (label: string, level: LevelType, extraLogDat?: LogData) => Promise<any>; | ||
export declare type logDataEnriched = Omit<LogDataOut, 'err'> & { | ||
err: Record<string, unknown>; | ||
}; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.levels = void 0; | ||
exports.levels = ['trace', 'info', 'warn', 'error', 'fatal', 'security']; | ||
//# sourceMappingURL=local-types.js.map |
@@ -68,3 +68,3 @@ "use strict"; | ||
} | ||
const body = logObj.map(utils_1.safeJsonStringify).join('\n'); | ||
const body = logObj.map((logEntry) => (0, utils_1.safeJsonStringify)(logEntry)).join('\n'); | ||
return log(Object.assign(Object.assign({}, options), { logObj: body }), true); | ||
@@ -71,0 +71,0 @@ }; |
@@ -1,2 +0,8 @@ | ||
export declare const isObject: (obj?: Record<string, unknown> | null | undefined) => boolean; | ||
import { logDataEnriched, LogDataOut } from './local-types'; | ||
export declare const isObject: (obj?: Record<string, unknown> | null) => boolean; | ||
export declare const safeJsonStringify: (obj: Record<string, unknown>) => string; | ||
/** | ||
* If the body has an err prop and its type is Error then find all properties in it | ||
* and spread them out. | ||
*/ | ||
export declare const enrichError: (body: LogDataOut) => logDataEnriched | LogDataOut; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.safeJsonStringify = exports.isObject = void 0; | ||
exports.enrichError = exports.safeJsonStringify = exports.isObject = void 0; | ||
const isObject = (obj) => !!obj && obj.toString() === '[object Object]' && !Array.isArray(obj); | ||
@@ -22,2 +22,32 @@ exports.isObject = isObject; | ||
exports.safeJsonStringify = safeJsonStringify; | ||
const hasNodeModules = (i) => !i.includes('/node_modules/'); | ||
/** | ||
* If the body has an err prop and its type is Error then find all properties in it | ||
* and spread them out. | ||
*/ | ||
const enrichError = (body) => { | ||
var _a, _b; | ||
const { err } = body; | ||
if (err) { | ||
const stack = err.stack | ||
? err.stack | ||
: /* istanbul ignore next */ (_a = new Error().stack) !== null && _a !== void 0 ? _a : '<no error stack>'; | ||
const fullStack = stack.split('\n').slice(1); | ||
const shortStack = fullStack.filter(hasNodeModules); | ||
let err2 = {}; | ||
if (err instanceof Error) { | ||
err2 = Object.entries(err).reduce((acc, [k, v]) => { | ||
acc[k] = v; | ||
return acc; | ||
}, {}); | ||
/* istanbul ignore next */ | ||
err2.name = (_b = err.name) !== null && _b !== void 0 ? _b : '<none>'; | ||
err2.message = err.message; | ||
} | ||
return Object.assign(Object.assign({}, body), { err: Object.assign({ fullStack, | ||
shortStack }, err2) }); | ||
} | ||
return body; | ||
}; | ||
exports.enrichError = enrichError; | ||
//# sourceMappingURL=utils.js.map |
import { v4 } from 'uuid'; | ||
import { Request, Response } from 'express'; | ||
import { isObject } from './utils'; | ||
import { enrichError, isObject } from './utils'; | ||
@@ -9,58 +8,7 @@ import { | ||
} from './loggly-wrapper'; | ||
import { BetterOmit } from './local-types'; | ||
import { | ||
LaLogOptions, levels, LevelType, LogData, logDataEnriched, LogDataOut, LogFunction, | ||
LogPresets, ParseReqIn, ParseReqOut, ResponseWrapper, TimeLogFunction, | ||
} from './local-types'; | ||
const levels = ['trace', 'info', 'warn', 'error', 'fatal', 'security'] as const; | ||
export type LevelType = typeof levels[number]; | ||
export interface LogPresets extends Record<string, unknown> { | ||
module?: string; | ||
trackId?: string; | ||
} | ||
export interface LaLogOptions { | ||
addTrackId?: boolean; | ||
moduleName?: string; | ||
presets?: LogPresets; | ||
serviceName?: string; | ||
isTransient?: boolean; | ||
} | ||
export type ParseReqIn = Request & { user?: unknown }; | ||
export type ParseReqOut = Pick<ParseReqIn, 'body' | | ||
'headers' | | ||
'method' | | ||
'params' | | ||
'path' | | ||
'query' | | ||
'url' | | ||
'user'>; | ||
export interface LogData extends Record<string, unknown> { | ||
err?: Error; | ||
msg?: string; | ||
req?: ParseReqIn; | ||
} | ||
interface LogDataOut extends BetterOmit<LogData, 'req'>, LogPresets { | ||
/** | ||
* The Stack property from the Error object split into lines. | ||
*/ | ||
fullStack?: string[]; | ||
/** | ||
* Created from the fullStack by removing lines containing node_modules | ||
*/ | ||
shortStack?: string[]; | ||
req?: ParseReqOut; | ||
} | ||
export interface ResponseWrapper { | ||
res: Response; | ||
code: number; | ||
} | ||
export type LogFunction = (logData: LogData, response?: ResponseWrapper) => Promise<any>; | ||
export type TimeLogFunction = ( | ||
label: string, level: LevelType, extraLogDat?: LogData, | ||
) => Promise<any>; | ||
const errorLevel = levels.indexOf('error'); | ||
@@ -249,3 +197,3 @@ | ||
const { req, ...rest } = logData; | ||
const logObj: LogDataOut = { ...this.presets, ...rest }; | ||
let logObj: logDataEnriched | LogDataOut = { ...this.presets, ...rest }; | ||
@@ -284,24 +232,3 @@ if (response) { | ||
if (logObj.err) { | ||
if (!logObj.err.stack) { | ||
// This will happen if we manually created an err prop - it might not have a stack prop | ||
// `stack` is a non standard property on the Error object so it can be undefined | ||
// which is why we have to provide the ??. | ||
// Ignoring the line for code coverage for now because we're going to have to | ||
// mock new Error() or extract this line into a local method that can be mocked | ||
// which would add an extra frame to the stack which I don't want. | ||
/* istanbul ignore next */ | ||
logObj.err.stack = new Error().stack ?? '<no error stack>'; | ||
} | ||
logObj.fullStack = logObj.err.stack.split('\n').slice(1); | ||
/** | ||
* Checks if string includes node_modules | ||
*/ | ||
const hasNodeModules = (i: string): boolean => !i.includes('/node_modules/'); | ||
logObj.shortStack = logObj.fullStack.filter(hasNodeModules); | ||
if (!logObj.msg) { | ||
logObj.msg = logObj.err.message; | ||
} | ||
delete logObj.err; | ||
} | ||
logObj = enrichError(logObj); | ||
@@ -308,0 +235,0 @@ if (req) { |
@@ -0,2 +1,62 @@ | ||
import { Request, Response } from 'express'; | ||
export type BetterOmit<T, K extends PropertyKey> = | ||
{ [P in keyof T as Exclude<P, K>]: T[P] }; | ||
export const levels = ['trace', 'info', 'warn', 'error', 'fatal', 'security'] as const; | ||
export type LevelType = typeof levels[number]; | ||
export interface LogPresets extends Record<string, unknown> { | ||
module?: string; | ||
trackId?: string; | ||
} | ||
export interface LaLogOptions { | ||
addTrackId?: boolean; | ||
moduleName?: string; | ||
presets?: LogPresets; | ||
serviceName?: string; | ||
isTransient?: boolean; | ||
} | ||
export type ParseReqIn = Request & { user?: unknown }; | ||
export type ParseReqOut = Pick<ParseReqIn, 'body' | | ||
'headers' | | ||
'method' | | ||
'params' | | ||
'path' | | ||
'query' | | ||
'url' | | ||
'user'>; | ||
export interface LogData extends Record<string, unknown> { | ||
err?: Error; | ||
msg?: string; | ||
req?: ParseReqIn; | ||
} | ||
export interface LogDataOut extends BetterOmit<LogData, 'req'>, LogPresets { | ||
/** | ||
* The Stack property from the Error object split into lines. | ||
*/ | ||
fullStack?: string[]; | ||
/** | ||
* Created from the fullStack by removing lines containing node_modules | ||
*/ | ||
shortStack?: string[]; | ||
req?: ParseReqOut; | ||
} | ||
export interface ResponseWrapper { | ||
res: Response; | ||
code: number; | ||
} | ||
export type LogFunction = (logData: LogData, response?: ResponseWrapper) => Promise<any>; | ||
export type TimeLogFunction = ( | ||
label: string, level: LevelType, extraLogDat?: LogData, | ||
) => Promise<any>; | ||
export type logDataEnriched = Omit<LogDataOut, 'err'> & { | ||
err: Record<string, unknown> | ||
} |
@@ -10,5 +10,3 @@ import fetch, { RequestInit, Response } from 'node-fetch'; | ||
const log = async ( | ||
options: LogOptions, bulk: boolean, | ||
): Promise<Record<string, unknown>> => { | ||
const log = async (options: LogOptions, bulk: boolean): Promise<Record<string, unknown>> => { | ||
const { | ||
@@ -103,3 +101,3 @@ tag, | ||
const body = logObj.map(safeJsonStringify).join('\n'); | ||
const body = logObj.map((logEntry) => safeJsonStringify(logEntry)).join('\n'); | ||
return log({ | ||
@@ -106,0 +104,0 @@ ...options, |
@@ -0,1 +1,3 @@ | ||
import { logDataEnriched, LogDataOut } from './local-types'; | ||
export const isObject = ( | ||
@@ -25,1 +27,40 @@ obj?: Record<string, unknown> | null, | ||
): string => JSON.stringify(obj, getCircularReplacer()); | ||
const hasNodeModules = (i: string): boolean => !i.includes('/node_modules/'); | ||
/** | ||
* If the body has an err prop and its type is Error then find all properties in it | ||
* and spread them out. | ||
*/ | ||
export const enrichError = (body: LogDataOut): logDataEnriched | LogDataOut => { | ||
const { err } = body; | ||
if (err) { | ||
const stack = err.stack | ||
? err.stack | ||
: /* istanbul ignore next */ new Error().stack ?? '<no error stack>'; | ||
const fullStack = stack.split('\n').slice(1); | ||
const shortStack = fullStack.filter(hasNodeModules); | ||
let err2: Record<string, unknown> = {}; | ||
if (err instanceof Error) { | ||
err2 = Object.entries(err).reduce((acc, [k, v]) => { | ||
acc[k] = v; | ||
return acc; | ||
}, {} as Record<string, unknown>); | ||
/* istanbul ignore next */ | ||
err2.name = err.name ?? '<none>'; | ||
err2.message = err.message; | ||
} | ||
return { | ||
...body, | ||
err: { | ||
fullStack, | ||
shortStack, | ||
...err2, | ||
}, | ||
}; | ||
} | ||
return body; | ||
}; |
@@ -13,18 +13,18 @@ { | ||
"@types/express": "4.17.13", | ||
"@types/jest": "27.0.1", | ||
"@types/node": "16.7.4", | ||
"@types/node-fetch": "2.5.12", | ||
"@types/uuid": "8.3.1", | ||
"@typescript-eslint/eslint-plugin": "4.29.3", | ||
"@typescript-eslint/parser": "4.29.3", | ||
"eslint": "7.32.0", | ||
"eslint-config-airbnb-base": "14.2.1", | ||
"eslint-plugin-import": "2.24.2", | ||
"eslint-plugin-jest": "24.4.0", | ||
"eslint-plugin-security": "1.4.0", | ||
"@types/jest": "27.5.0", | ||
"@types/node": "18.7.9", | ||
"@types/node-fetch": "2.6.1", | ||
"@types/uuid": "8.3.4", | ||
"@typescript-eslint/eslint-plugin": "5.34.0", | ||
"@typescript-eslint/parser": "5.34.0", | ||
"eslint": "8.22.0", | ||
"eslint-config-airbnb-base": "15.0.0", | ||
"eslint-plugin-import": "2.26.0", | ||
"eslint-plugin-jest": "26.8.7", | ||
"eslint-plugin-security": "1.5.0", | ||
"eslint-plugin-sort-keys-fix": "1.1.2", | ||
"jest": "27.1.0", | ||
"jest": "27.5.1", | ||
"pre-commit": "1.2.2", | ||
"ts-jest": "27.0.5", | ||
"typescript": "4.4.2" | ||
"ts-jest": "27.1.4", | ||
"typescript": "4.7.4" | ||
}, | ||
@@ -58,3 +58,3 @@ "homepage": "https://github.com/guyellis/lalog#readme", | ||
"types": "dist/index.d.ts", | ||
"version": "1.0.2" | ||
"version": "2.0.0" | ||
} |
@@ -8,3 +8,3 @@ { | ||
"incremental": true, | ||
"lib": ["es2019"], | ||
"lib": ["es2019", "dom"], | ||
"module": "commonjs", | ||
@@ -11,0 +11,0 @@ "noEmit": false, |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
75581
974