nextrans-logger 0.2.20 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,25 @@
1
+ import { LoggerContract, LogMeta } from './logger.contract';
2
+ export interface CloudwatchOptions {
3
+ logGroupName: string;
4
+ logStreamName: string;
5
+ region: string;
6
+ credentials: {
7
+ accessKeyId: string;
8
+ secretAccessKey: string;
9
+ };
10
+ }
11
+ export declare class Cloudwatch implements LoggerContract {
12
+ private client;
13
+ private sequenceToken;
14
+ private DEFAULT_LOG_CONFIG;
15
+ constructor(options: CloudwatchOptions);
16
+ private _formatMessage;
17
+ private _send;
18
+ log(message: LogMeta): void;
19
+ info(message: LogMeta): void;
20
+ error(message: LogMeta): void;
21
+ fatal(message: LogMeta): void;
22
+ warn(message: LogMeta): void;
23
+ debug(message: LogMeta): void;
24
+ verbose(message: LogMeta): void;
25
+ }
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Cloudwatch = void 0;
4
+ const client_cloudwatch_logs_1 = require("@aws-sdk/client-cloudwatch-logs");
5
+ const crypto_1 = require("crypto");
6
+ class Cloudwatch {
7
+ client;
8
+ sequenceToken;
9
+ DEFAULT_LOG_CONFIG;
10
+ constructor(options) {
11
+ this.client = new client_cloudwatch_logs_1.CloudWatchLogsClient({
12
+ region: options.region,
13
+ credentials: {
14
+ accessKeyId: options.credentials.accessKeyId,
15
+ secretAccessKey: options.credentials.secretAccessKey,
16
+ },
17
+ });
18
+ if (options.logGroupName && options.logStreamName) {
19
+ this.DEFAULT_LOG_CONFIG = {
20
+ logGroupName: options.logGroupName,
21
+ logStreamName: options.logStreamName,
22
+ };
23
+ }
24
+ }
25
+ _formatMessage(message) {
26
+ let formattedMessage = `[${message.level.toUpperCase()}][${message?.host}] `;
27
+ return (formattedMessage +
28
+ message.message
29
+ .map(([type, value]) => value)
30
+ .filter((d) => d)
31
+ .join('\n '));
32
+ }
33
+ async _send(message, retry = true) {
34
+ if (!this.client || !this.DEFAULT_LOG_CONFIG)
35
+ return;
36
+ try {
37
+ const command = new client_cloudwatch_logs_1.PutLogEventsCommand({
38
+ ...this.DEFAULT_LOG_CONFIG,
39
+ logEvents: [
40
+ {
41
+ timestamp: Date.now(),
42
+ message: this._formatMessage(message),
43
+ },
44
+ ],
45
+ sequenceToken: this.sequenceToken ?? (0, crypto_1.randomUUID)(),
46
+ });
47
+ const response = await this.client.send(command, {
48
+ requestTimeout: 50,
49
+ abortSignal: AbortSignal.timeout(30000),
50
+ });
51
+ this.sequenceToken = response.nextSequenceToken;
52
+ }
53
+ catch (error) {
54
+ console.error('Error sending log to CloudWatch:', error);
55
+ if (error.name === 'InvalidSequenceTokenException' && retry) {
56
+ await this._send(message, false);
57
+ }
58
+ }
59
+ }
60
+ log(message) {
61
+ this._send(message);
62
+ }
63
+ info(message) {
64
+ this._send(message);
65
+ }
66
+ error(message) {
67
+ this._send(message);
68
+ }
69
+ fatal(message) {
70
+ this._send(message);
71
+ }
72
+ warn(message) {
73
+ this._send(message);
74
+ }
75
+ debug(message) {
76
+ // this._send(message);
77
+ }
78
+ verbose(message) {
79
+ // this._send(message);
80
+ }
81
+ }
82
+ exports.Cloudwatch = Cloudwatch;
@@ -0,0 +1,15 @@
1
+ export declare const sanitizeRequests: string[];
2
+ declare class DateUtil {
3
+ private date;
4
+ constructor(date?: Date | string | number);
5
+ utcOffset(offsetHourOrMinutes: number): DateUtil;
6
+ utc(): DateUtil;
7
+ isUtc(): boolean;
8
+ getTz(): number;
9
+ format(formatStr?: string): string;
10
+ toISOString(): string;
11
+ toString(): string;
12
+ toDate(): Date;
13
+ }
14
+ export declare const dateUtil: (date?: Date | string | number) => DateUtil;
15
+ export {};
package/dist/helper.js ADDED
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.dateUtil = exports.sanitizeRequests = void 0;
4
+ exports.sanitizeRequests = [
5
+ 'content',
6
+ 'html',
7
+ 'password',
8
+ 'accessToken',
9
+ 'refreshToken',
10
+ 'otp',
11
+ 'totp',
12
+ 'secret',
13
+ 'publicKey',
14
+ 'privateKey',
15
+ 'apiKey',
16
+ 'secretKey',
17
+ 'token',
18
+ 'authorization',
19
+ 'authToken',
20
+ 'apiKey',
21
+ 'secretKey',
22
+ 'pin',
23
+ 'secret',
24
+ 'permissions',
25
+ ];
26
+ class DateUtil {
27
+ date;
28
+ constructor(date) {
29
+ let parsedDate;
30
+ if (date instanceof Date) {
31
+ const isUTC = date.getTimezoneOffset() === 0;
32
+ if (isUTC) {
33
+ parsedDate = new Date(date);
34
+ }
35
+ else {
36
+ parsedDate = new Date(date.getTime() + date.getTimezoneOffset() * 60 * 1000);
37
+ }
38
+ }
39
+ else {
40
+ parsedDate = date ? new Date(date) : new Date();
41
+ }
42
+ if (isNaN(parsedDate.getTime())) {
43
+ throw new Error('Invalid date ' + date);
44
+ }
45
+ this.date = parsedDate;
46
+ }
47
+ utcOffset(offsetHourOrMinutes) {
48
+ if (offsetHourOrMinutes > 14 * 60 || offsetHourOrMinutes < -12 * 60) {
49
+ offsetHourOrMinutes = offsetHourOrMinutes * 60;
50
+ }
51
+ this.date = new Date(this.date.getTime() + offsetHourOrMinutes * 60 * 1000);
52
+ return this;
53
+ }
54
+ utc() {
55
+ const offset = this.date.getTimezoneOffset();
56
+ const utcTime = new Date(this.date.getTime() + offset * 60 * 1000);
57
+ const datePart = [
58
+ utcTime.getFullYear().toString().padStart(4, '0'),
59
+ (utcTime.getMonth() + 1).toString().padStart(2, '0'),
60
+ utcTime.getDate().toString().padStart(2, '0'),
61
+ ].join('-');
62
+ const timePart = [
63
+ utcTime.getHours().toString().padStart(2, '0'),
64
+ utcTime.getMinutes().toString().padStart(2, '0'),
65
+ utcTime.getSeconds().toString().padStart(2, '0'),
66
+ ].join(':');
67
+ const msPart = utcTime.getMilliseconds().toString().padStart(3, '0');
68
+ const isoString = `${datePart}T${timePart}.${msPart}Z`;
69
+ this.date = new Date(isoString);
70
+ return this;
71
+ }
72
+ isUtc() {
73
+ return this.date.getTimezoneOffset() === 0;
74
+ }
75
+ getTz() {
76
+ return -this.date.getTimezoneOffset() / 60;
77
+ }
78
+ format(formatStr = 'YYYY-MM-DD HH:mm:ss') {
79
+ const map = {
80
+ YYYY: this.date.getFullYear().toString(),
81
+ YY: this.date.getFullYear().toString().slice(-2),
82
+ MM: (this.date.getMonth() + 1).toString().padStart(2, '0'),
83
+ M: (this.date.getMonth() + 1).toString(),
84
+ DD: this.date.getDate().toString().padStart(2, '0'),
85
+ D: this.date.getDate().toString(),
86
+ HH: this.date.getHours().toString().padStart(2, '0'),
87
+ H: this.date.getHours().toString(),
88
+ mm: this.date.getMinutes().toString().padStart(2, '0'),
89
+ m: this.date.getMinutes().toString(),
90
+ ss: this.date.getSeconds().toString().padStart(2, '0'),
91
+ s: this.date.getSeconds().toString(),
92
+ SSS: this.date.getMilliseconds().toString().padStart(3, '0'),
93
+ Z: this.isUtc() ? 'Z' : `+${this.getTz().toString().padStart(2, '0')}:00`,
94
+ };
95
+ return formatStr.replace(/YYYY|YY|MM|M|DD|D|HH|H|mm|m|ss|s|SSS|Z/g, (matched) => map[matched] ?? matched);
96
+ }
97
+ toISOString() {
98
+ return this.format('YYYY-MM-DDTHH:mm:ss.SSSZ');
99
+ }
100
+ toString() {
101
+ return this.format('YYYY-MM-DD HH:mm:ss');
102
+ }
103
+ toDate() {
104
+ return this.date;
105
+ }
106
+ }
107
+ const dateUtil = (date) => new DateUtil(date);
108
+ exports.dateUtil = dateUtil;
@@ -0,0 +1,29 @@
1
+ import { CloudwatchOptions } from './cloudwatch';
2
+ import { LoggerContract, LogMessageType, LogMeta } from './logger.contract';
3
+ export interface LoggingOptions {
4
+ env: string;
5
+ enable: boolean;
6
+ slackWebhook?: string;
7
+ cloudwatchOption?: CloudwatchOptions;
8
+ }
9
+ export default class Logger implements LoggerContract {
10
+ private LOG_STACKS;
11
+ private readonly HOST;
12
+ private readonly ENV;
13
+ private cloudwatch;
14
+ constructor(options: LoggingOptions);
15
+ private _sanitizePolicyData;
16
+ private _formatBgLevel;
17
+ private _formatException;
18
+ private _parseMessage;
19
+ private _mapLog;
20
+ private _formatConsoleError;
21
+ private _console;
22
+ log(...args: LogMessageType[]): void;
23
+ info(...args: LogMessageType[]): void;
24
+ error(...args: LogMessageType[]): void;
25
+ fatal(...args: LogMessageType[]): void;
26
+ warn(...args: LogMessageType[]): void;
27
+ verbose(message: LogMeta): void;
28
+ debug(...args: LogMessageType[]): void;
29
+ }
package/dist/index.js ADDED
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const os_1 = __importDefault(require("os"));
7
+ const util_1 = require("util");
8
+ const cloudwatch_1 = require("./cloudwatch");
9
+ const helper_1 = require("./helper");
10
+ const logger_contract_1 = require("./logger.contract");
11
+ class Logger {
12
+ LOG_STACKS = 5;
13
+ HOST = os_1.default.hostname();
14
+ ENV = 'development';
15
+ cloudwatch;
16
+ constructor(options) {
17
+ this.ENV = options.env ?? this.ENV;
18
+ if (options.enable && options.cloudwatchOption) {
19
+ this.cloudwatch = new cloudwatch_1.Cloudwatch(options.cloudwatchOption);
20
+ }
21
+ }
22
+ _sanitizePolicyData(object) {
23
+ if (!object)
24
+ return undefined;
25
+ if (Array.isArray(object)) {
26
+ return object.map((d) => typeof d !== 'object' ? d : this._sanitizePolicyData(d));
27
+ }
28
+ else {
29
+ return Object.keys(object).reduce((acc, key) => {
30
+ let value = object[key];
31
+ if (helper_1.sanitizeRequests.includes(key)) {
32
+ // @ts-ignore
33
+ value = '***';
34
+ }
35
+ else if (typeof value === 'object') {
36
+ // @ts-ignore
37
+ value = this._sanitizePolicyData(value);
38
+ }
39
+ return { ...acc, [key]: value };
40
+ }, {});
41
+ }
42
+ }
43
+ _formatBgLevel(level, ...args) {
44
+ const msg = `[${args.map((d) => d?.trim()).join('][')}]`;
45
+ switch (level) {
46
+ case 'log':
47
+ return (0, logger_contract_1.logInfo)(msg);
48
+ case 'warn':
49
+ return (0, logger_contract_1.logWarn)(msg);
50
+ case 'error':
51
+ return (0, logger_contract_1.logError)(msg);
52
+ case 'fatal':
53
+ return (0, logger_contract_1.logError)(msg);
54
+ case 'debug':
55
+ return (0, logger_contract_1.logDebug)(msg);
56
+ default:
57
+ return (0, logger_contract_1.logInfo)(msg);
58
+ }
59
+ }
60
+ _formatException(error) {
61
+ let stacks = (error?.stack ?? '')
62
+ ?.split('\n')
63
+ .map((o) => o.trim().replace?.(error.name + ': ', ''));
64
+ if (!['development', 'local', 'staging'].includes(this.ENV.toLocaleLowerCase()))
65
+ stacks = stacks.slice(0, this.LOG_STACKS);
66
+ return (error?.name ?? 'UnknownErrorException') + ' ' + stacks.join(`\n`);
67
+ }
68
+ _parseMessage(arg) {
69
+ if (arg instanceof Error)
70
+ return ['error', this._formatException(arg)];
71
+ if (typeof arg === 'object')
72
+ return ['object', JSON.stringify(this._sanitizePolicyData(arg), null, 2)];
73
+ return ['string', arg?.toString()];
74
+ }
75
+ _mapLog(level, ...args) {
76
+ if (this.ENV?.toLowerCase() === 'test')
77
+ return;
78
+ const message = [];
79
+ for (const arg of args?.flat()?.flat()) {
80
+ if (!arg)
81
+ continue;
82
+ message.push(this._parseMessage(arg));
83
+ }
84
+ return {
85
+ level,
86
+ host: this.HOST,
87
+ env: this.ENV.toUpperCase(),
88
+ timestamp: (0, helper_1.dateUtil)().format('YYYY-MM-DD HH:mm:ss.SSS'),
89
+ message,
90
+ };
91
+ }
92
+ _formatConsoleError(error) {
93
+ return error
94
+ .split('\n')
95
+ .map((d) => d.endsWith('Exception')
96
+ ? d.replace(/(.*)Exception(.*)/, (0, logger_contract_1.logError)('$1Exception') + (0, logger_contract_1.logError)('$2'))
97
+ : (0, logger_contract_1.logError)(d))
98
+ .join('\n ');
99
+ }
100
+ _console(logMeta) {
101
+ const log = this._formatBgLevel(logMeta.level, logMeta.timestamp, this.HOST, logMeta.level.toUpperCase() === 'LOG'
102
+ ? 'INFO'
103
+ : logMeta.level.toUpperCase()) +
104
+ (0, util_1.styleText)('reset', ` `) +
105
+ logMeta.message
106
+ .map(([type, value]) => {
107
+ if (typeof value === 'function')
108
+ return;
109
+ if (type === 'error')
110
+ return (0, logger_contract_1.logError)(this._formatConsoleError(value));
111
+ if (type === 'object') {
112
+ return (0, logger_contract_1.logWarn)((0, logger_contract_1.logWarnBg)('Object') +
113
+ ' ' +
114
+ value.split('\n').map(logger_contract_1.logWarn).join('\n '));
115
+ }
116
+ return value;
117
+ })
118
+ .filter((d) => d)
119
+ .join('\n ');
120
+ return console.log(log);
121
+ }
122
+ log(...args) {
123
+ const logMeta = this._mapLog('log', ...args);
124
+ if (!logMeta)
125
+ return;
126
+ this._console(logMeta);
127
+ this.cloudwatch?.log(logMeta);
128
+ }
129
+ info(...args) {
130
+ const logMeta = this._mapLog('info', ...args);
131
+ if (!logMeta)
132
+ return;
133
+ this._console(logMeta);
134
+ this.cloudwatch?.info(logMeta);
135
+ }
136
+ error(...args) {
137
+ const logMeta = this._mapLog('error', ...args);
138
+ if (!logMeta)
139
+ return;
140
+ this._console(logMeta);
141
+ this.cloudwatch?.error(logMeta);
142
+ }
143
+ fatal(...args) {
144
+ const logMeta = this._mapLog('fatal', ...args);
145
+ if (!logMeta)
146
+ return;
147
+ this._console(logMeta);
148
+ this.cloudwatch?.fatal(logMeta);
149
+ }
150
+ warn(...args) {
151
+ const logMeta = this._mapLog('warn', ...args);
152
+ if (!logMeta)
153
+ return;
154
+ this._console(logMeta);
155
+ this.cloudwatch?.warn(logMeta);
156
+ }
157
+ verbose(message) {
158
+ throw new Error('Method not implemented.');
159
+ }
160
+ debug(...args) {
161
+ const logMeta = this._mapLog('debug', ...args);
162
+ if (!logMeta)
163
+ return;
164
+ this._console(logMeta);
165
+ this.cloudwatch?.debug(logMeta);
166
+ }
167
+ }
168
+ exports.default = Logger;
@@ -0,0 +1,26 @@
1
+ export type LogLevel = 'error' | 'log' | 'info' | 'warn' | 'debug' | 'fatal' | 'verbose';
2
+ export type LogMessageType = string | object | Error | undefined | null | number | boolean | unknown | any;
3
+ export interface LogMeta {
4
+ level: LogLevel;
5
+ env: string;
6
+ host: string;
7
+ timestamp: string;
8
+ message: ['error' | 'object' | 'string', string][];
9
+ }
10
+ export interface LoggerContract {
11
+ log(message: LogMeta): void;
12
+ info(message: LogMeta): void;
13
+ error(message: LogMeta): void;
14
+ fatal(message: LogMeta): void;
15
+ warn(message: LogMeta): void;
16
+ debug(message: LogMeta): void;
17
+ verbose(message: LogMeta): void;
18
+ }
19
+ export declare const logWarnBg: (log: string) => string;
20
+ export declare const logErrorBg: (log: string) => string;
21
+ export declare const logInfoBg: (log: string) => string;
22
+ export declare const logDebugBg: (log: string) => string;
23
+ export declare const logWarn: (log: string) => string;
24
+ export declare const logInfo: (log: string) => string;
25
+ export declare const logDebug: (log: string) => string;
26
+ export declare const logError: (log: string) => string;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.logError = exports.logDebug = exports.logInfo = exports.logWarn = exports.logDebugBg = exports.logInfoBg = exports.logErrorBg = exports.logWarnBg = void 0;
4
+ const util_1 = require("util");
5
+ const bgLog = (option, log) => {
6
+ return (0, util_1.styleText)('reset', (0, util_1.styleText)('bold', (0, util_1.styleText)(option, (0, util_1.styleText)('black', ` ${log} `))));
7
+ };
8
+ const logWarnBg = (log) => bgLog('bgYellow', log);
9
+ exports.logWarnBg = logWarnBg;
10
+ const logErrorBg = (log) => bgLog('bgRed', log);
11
+ exports.logErrorBg = logErrorBg;
12
+ const logInfoBg = (log) => bgLog('bgGreen', log);
13
+ exports.logInfoBg = logInfoBg;
14
+ const logDebugBg = (log) => bgLog('bgMagenta', log);
15
+ exports.logDebugBg = logDebugBg;
16
+ const logWarn = (log) => (0, util_1.styleText)('yellow', log);
17
+ exports.logWarn = logWarn;
18
+ const logInfo = (log) => (0, util_1.styleText)('green', log);
19
+ exports.logInfo = logInfo;
20
+ const logDebug = (log) => (0, util_1.styleText)('magenta', log);
21
+ exports.logDebug = logDebug;
22
+ const logError = (log) => (0, util_1.styleText)('red', log);
23
+ exports.logError = logError;
package/package.json CHANGED
@@ -1,46 +1,34 @@
1
1
  {
2
- "name": "nextrans-logger",
3
- "version": "0.2.20",
4
- "description": "Logging for nextrans app service",
5
- "main": "lib/index.cjs",
6
- "module": "lib/index.js",
7
- "types": "lib/index.d.ts",
8
- "scripts": {
9
- "build": "rm -fr lib/* && tsc -p tsconfig.json",
10
- "build:cjs": "tsc -p tsconfig-cjs.json",
11
- "move:cjs": "cp lib/cjs/index.js lib/index.cjs && rm -r lib/cjs",
12
- "test": "jest"
13
- },
14
- "author": "nextrans-team",
15
- "license": "ISC",
16
- "dependencies": {
17
- "@aws-sdk/client-cloudwatch-logs": "^3.347.0",
18
- "moment": "^2.29.4",
19
- "winston": "^3.8.2",
20
- "winston-cloudwatch": "^6.1.1",
21
- "winston-slack-webhook-transport": "^2.2.3"
22
- },
23
- "devDependencies": {
24
- "@types/jest": "^29.4.0",
25
- "@types/node": "^18.14.6",
26
- "dotenv": "^16.0.3",
27
- "jest": "^29.5.0",
28
- "ts-jest": "^29.0.5",
29
- "ts-node": "^10.9.1",
30
- "typescript": "^4.9.5"
31
- },
32
- "repository": {
33
- "type": "git",
34
- "url": "git+https://bitbucket.org/nexpay-nextrans/nextrans-logger.git"
35
- },
36
- "bugs": {
37
- "url": "https://bitbucket.org/nexpay-nextrans/nextrans-logger/issues"
38
- },
39
- "homepage": "https://bitbucket.org/nexpay-nextrans/nextrans-logger#readme",
40
- "exports": {
41
- ".": {
42
- "import": "./lib/index.js",
43
- "require": "./lib/index.cjs"
44
- }
45
- }
2
+ "name": "nextrans-logger",
3
+ "version": "0.3.0",
4
+ "description": "Logging for nextrans app service",
5
+ "type": "commonjs",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "scripts": {
9
+ "build": "rm -rf dist && tsc",
10
+ "test": "jest"
11
+ },
12
+ "author": "nextrans-team",
13
+ "license": "ISC",
14
+ "peerDependencies": {
15
+ "@aws-sdk/client-cloudwatch-logs": "^3.1055.0"
16
+ },
17
+ "devDependencies": {
18
+ "@aws-sdk/client-cloudwatch-logs": "^3.1055.0",
19
+ "@types/jest": "^29.4.0",
20
+ "@types/node": "^25.9.1",
21
+ "jest": "^29.5.0",
22
+ "ts-jest": "^29.0.5",
23
+ "ts-node": "^10.9.2",
24
+ "typescript": "^4.9.5"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://bitbucket.org/nexpay-nextrans/nextrans-logger.git"
29
+ },
30
+ "bugs": {
31
+ "url": "https://bitbucket.org/nexpay-nextrans/nextrans-logger/issues"
32
+ },
33
+ "homepage": "https://bitbucket.org/nexpay-nextrans/nextrans-logger#readme"
46
34
  }
@@ -1,6 +0,0 @@
1
- {
2
- "editor.tabSize": 2,
3
- "prettier.singleQuote": true,
4
- "javascript.preferences.quoteStyle": "single",
5
- "typescript.preferences.quoteStyle": "single"
6
- }