@trojs/logger 1.0.1 → 2.0.1

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.
package/package.json CHANGED
@@ -1,80 +1,72 @@
1
1
  {
2
- "name": "@trojs/logger",
3
- "description": "Winston logger for TroJS",
4
- "version": "1.0.1",
5
- "author": {
6
- "name": "Pieter Wigboldus",
7
- "url": "https://trojs.org/"
8
- },
9
- "license": "MIT",
10
- "scripts": {
11
- "lint": "eslint",
12
- "lint:report": "eslint src/*.js -f json -o report.json",
13
- "lint:fix": "eslint --fix",
14
- "test": "c8 node --test",
15
- "cpd": "node_modules/jscpd/bin/jscpd src",
16
- "vulnerabilities": "npm audit --omit=dev"
17
- },
18
- "type": "module",
19
- "files": [
20
- "src/logger.js",
21
- "src/models/logger.js",
22
- "src/models/schemas/logger.js",
23
- "src/models/enums/level.js",
24
- "src/models/mutators/logger.js",
25
- "src/loggers/index.js",
26
- "src/loggers/console.js",
27
- "src/loggers/sentry.js",
28
- "src/loggers/combined-file.js",
29
- "src/loggers/error-file.js",
30
- "src/helpers/stackdriver.js"
31
- ],
32
- "main": "src/logger.js",
33
- "devDependencies": {
34
- "@eslint/js": "^9.15.0",
35
- "@stylistic/eslint-plugin": "^2.11.0",
36
- "@stylistic/eslint-plugin-js": "^2.11.0",
37
- "@trojs/error": "^4.0.0",
38
- "c8": "^10.0.0",
39
- "eslint": "^9.15.0",
40
- "eslint-config-prettier": "^10.0.0",
41
- "eslint-plugin-import": "^2.31.0",
42
- "eslint-plugin-jsdoc": "^50.5.0",
43
- "eslint-plugin-n": "^17.14.0",
44
- "eslint-plugin-prettier": "^5.2.1",
45
- "eslint-plugin-promise": "^7.1.0",
46
- "eslint-plugin-sonarjs": "^2.0.4",
47
- "globals": "^15.12.0",
48
- "jscpd": "^4.0.0",
49
- "prettier": "^3.3.3"
50
- },
51
- "repository": {
52
- "type": "git",
53
- "url": "https://github.com/trojs/logger"
54
- },
55
- "engines": {
56
- "node": ">= 20"
57
- },
58
- "keywords": [
59
- "javascript",
60
- "sentry",
61
- "logging",
62
- "error"
63
- ],
64
- "dependencies": {
65
- "@sentry/node": "^8.0.0",
66
- "@trojs/enum": "^4.0.0",
67
- "@trojs/mutator": "^1.0.0",
68
- "@trojs/objects": "^9.0.1",
69
- "compression": "^1.7.4",
70
- "cors": "^2.8.5",
71
- "dotenv": "^16.0.3",
72
- "winston": "^3.13.0",
73
- "winston-transport": "^4.5.0",
74
- "winston-transport-sentry-node": "^3.0.0"
75
- },
76
- "funding": {
77
- "type": "github",
78
- "url": "https://github.com/sponsors/w3nl"
79
- }
2
+ "name": "@trojs/logger",
3
+ "description": "Winston logger for TroJS",
4
+ "version": "2.0.1",
5
+ "author": {
6
+ "name": "Pieter Wigboldus",
7
+ "url": "https://trojs.org/"
8
+ },
9
+ "license": "MIT",
10
+ "scripts": {
11
+ "lint": "eslint",
12
+ "lint:report": "eslint src/*.js -f json -o report.json",
13
+ "lint:fix": "eslint --fix",
14
+ "test": "node --test --experimental-test-coverage --test-reporter=spec --test-reporter=lcov --test-reporter-destination=stdout --test-reporter-destination=./coverage/lcov.info",
15
+ "cpd": "node_modules/jscpd/bin/jscpd src",
16
+ "vulnerabilities": "npm audit --omit=dev"
17
+ },
18
+ "type": "module",
19
+ "files": [
20
+ "src/logger.js",
21
+ "src/models/logger.js",
22
+ "src/models/schemas/logger.js",
23
+ "src/models/enums/level.js",
24
+ "src/models/mutators/logger.js",
25
+ "src/loggers/index.js",
26
+ "src/loggers/console.js",
27
+ "src/loggers/sentry.js",
28
+ "src/loggers/combined-file.js",
29
+ "src/loggers/error-file.js",
30
+ "src/loggers/winston-transport-sentry-node.js",
31
+ "src/helpers/stackdriver.js",
32
+ "src/helpers/extended-error.js"
33
+ ],
34
+ "main": "src/logger.js",
35
+ "devDependencies": {
36
+ "@trojs/error": "^4.0.0",
37
+ "@trojs/lint": "^0.2.13",
38
+ "eslint": "^9.15.0",
39
+ "globals": "^16.0.0",
40
+ "jscpd": "^4.0.0",
41
+ "prettier": "^3.3.3"
42
+ },
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "https://github.com/trojs/logger"
46
+ },
47
+ "engines": {
48
+ "node": ">= 20"
49
+ },
50
+ "keywords": [
51
+ "javascript",
52
+ "sentry",
53
+ "logging",
54
+ "error"
55
+ ],
56
+ "dependencies": {
57
+ "@sentry/node": "^9.0.0",
58
+ "@trojs/enum": "^4.0.0",
59
+ "@trojs/mutator": "^1.0.0",
60
+ "@trojs/objects": "^9.0.1",
61
+ "compression": "^1.7.4",
62
+ "cors": "^2.8.5",
63
+ "dotenv": "^16.0.3",
64
+ "triple-beam": "^1.4.1",
65
+ "winston": "^3.13.0",
66
+ "winston-transport": "^4.5.0"
67
+ },
68
+ "funding": {
69
+ "type": "github",
70
+ "url": "https://github.com/sponsors/w3nl"
71
+ }
80
72
  }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Extended error class to handle additional error information.
3
+ */
4
+ export class ExtendedError extends Error {
5
+ /**
6
+ * @param {object} info - Error information.
7
+ */
8
+ constructor (info) {
9
+ super(info.message)
10
+
11
+ this.name = info.name || 'Error'
12
+ if (info.stack && typeof info.stack === 'string') {
13
+ this.stack = info.stack
14
+ }
15
+ }
16
+ }
@@ -1,32 +1,32 @@
1
1
  import os from 'node:os'
2
2
 
3
3
  const levelToSeverity = {
4
- trace: 'DEBUG',
5
- debug: 'DEBUG',
6
- info: 'INFO',
7
- warn: 'WARNING',
8
- error: 'ERROR',
9
- fatal: 'CRITICAL'
4
+ trace: 'DEBUG',
5
+ debug: 'DEBUG',
6
+ info: 'INFO',
7
+ warn: 'WARNING',
8
+ error: 'ERROR',
9
+ fatal: 'CRITICAL'
10
10
  }
11
11
 
12
12
  const levels = {
13
- trace: 10,
14
- debug: 20,
15
- info: 30,
16
- warn: 40,
17
- error: 50,
18
- fatal: 60
13
+ trace: 10,
14
+ debug: 20,
15
+ info: 30,
16
+ warn: 40,
17
+ error: 50,
18
+ fatal: 60
19
19
  }
20
20
 
21
- const stackdriver =
22
- ({ level, defaultLevel }) =>
23
- (info) => ({
24
- ...info,
25
- severity: levelToSeverity[level] || levelToSeverity[defaultLevel],
26
- level: levels[level] || levels[defaultLevel],
27
- time: Date.now(),
28
- pid: process.pid,
29
- hostname: os.hostname()
30
- })
21
+ const stackdriver
22
+ = ({ level, defaultLevel }) =>
23
+ (info) => ({
24
+ ...info,
25
+ severity: levelToSeverity[level] || levelToSeverity[defaultLevel],
26
+ level: levels[level] || levels[defaultLevel],
27
+ time: Date.now(),
28
+ pid: process.pid,
29
+ hostname: os.hostname()
30
+ })
31
31
 
32
32
  export default stackdriver
package/src/logger.js CHANGED
@@ -8,18 +8,18 @@ import makeLoggers from './loggers/index.js'
8
8
 
9
9
  /** @type {LoggerType[]} */
10
10
  const defaultLoggers = [
11
- {
12
- type: 'console'
13
- }
11
+ {
12
+ type: 'console'
13
+ }
14
14
  ]
15
15
 
16
16
  const levels = {
17
- fatal: 0,
18
- error: 1,
19
- warn: 2,
20
- info: 3,
21
- debug: 4,
22
- trace: 5
17
+ fatal: 0,
18
+ error: 1,
19
+ warn: 2,
20
+ info: 3,
21
+ debug: 4,
22
+ trace: 5
23
23
  }
24
24
 
25
25
  /**
@@ -31,12 +31,12 @@ const levels = {
31
31
  * @returns {winston.Logger}
32
32
  */
33
33
  export default ({ loggers = defaultLoggers, level = 'info', meta = {} }) => {
34
- const winstonLoggers = makeLoggers({ winston, loggers })
34
+ const winstonLoggers = makeLoggers({ winston, loggers })
35
35
 
36
- return winston.createLogger({
37
- level,
38
- levels,
39
- defaultMeta: meta,
40
- transports: winstonLoggers
41
- })
36
+ return winston.createLogger({
37
+ level,
38
+ levels,
39
+ defaultMeta: meta,
40
+ transports: winstonLoggers
41
+ })
42
42
  }
@@ -1,4 +1,4 @@
1
1
  export default ({ winston, logger }) =>
2
- new winston.transports.File({
3
- filename: logger?.location || 'combined.log'
4
- })
2
+ new winston.transports.File({
3
+ filename: logger?.location || 'combined.log'
4
+ })
@@ -1,22 +1,25 @@
1
1
  import stackDriver from '../helpers/stackdriver.js'
2
2
 
3
3
  export default ({ winston, logger }) => {
4
- const defaultLevel = 'trace'
4
+ const defaultLevel = 'trace'
5
5
 
6
- return new winston.transports.Console({
7
- level: logger?.level || defaultLevel,
8
- format:
6
+ const jsonFormatter = winston.format.combine(
7
+ winston.format.errors({ stack: logger?.debug ?? false }),
8
+ winston.format(
9
+ stackDriver({ level: logger?.level, defaultLevel })
10
+ )(),
11
+ winston.format.json()
12
+ )
13
+
14
+ const defaultFormatter = winston.format.combine(
15
+ winston.format.errors({ stack: logger?.debug ?? false }),
16
+ winston.format.simple()
17
+ )
18
+ return new winston.transports.Console({
19
+ level: logger?.level || defaultLevel,
20
+ format:
9
21
  logger.format === 'json'
10
- ? winston.format.combine(
11
- winston.format.errors({ stack: logger?.debug ?? false }),
12
- winston.format(
13
- stackDriver({ level: logger?.level, defaultLevel })
14
- )(),
15
- winston.format.json()
16
- )
17
- : winston.format.combine(
18
- winston.format.errors({ stack: logger?.debug ?? false }),
19
- winston.format.simple()
20
- )
21
- })
22
+ ? jsonFormatter
23
+ : defaultFormatter
24
+ })
22
25
  }
@@ -1,5 +1,5 @@
1
1
  export default ({ winston, logger }) =>
2
- new winston.transports.File({
3
- filename: logger?.location || 'error.log',
4
- level: logger?.level || 'error'
5
- })
2
+ new winston.transports.File({
3
+ filename: logger?.location || 'error.log',
4
+ level: logger?.level || 'error'
5
+ })
@@ -6,10 +6,10 @@ import makeConsoleLogger from './console.js'
6
6
  import Logger from '../models/logger.js'
7
7
 
8
8
  const winstonLoggers = {
9
- sentry: makeSentryLogger,
10
- errorFile: makeErrorFileLogger,
11
- combinedFile: makeCombinedFileLogger,
12
- console: makeConsoleLogger
9
+ sentry: makeSentryLogger,
10
+ errorFile: makeErrorFileLogger,
11
+ combinedFile: makeCombinedFileLogger,
12
+ console: makeConsoleLogger
13
13
  }
14
14
 
15
15
  /**
@@ -25,18 +25,18 @@ const winstonLoggers = {
25
25
  * @returns {TransportStream[]}
26
26
  */
27
27
  const makeLoggers = ({ winston, loggers }) =>
28
- Logger.createAll(loggers).map((logger) => {
29
- const loggerFn = winstonLoggers[logger.type]
30
- if (!loggerFn) {
31
- throw new Error(`Unknown logger type: ${logger.type}`)
32
- }
28
+ Logger.createAll(loggers).map((logger) => {
29
+ const loggerFn = winstonLoggers[logger.type]
30
+ if (!loggerFn) {
31
+ throw new Error(`Unknown logger type: ${logger.type}`)
32
+ }
33
33
 
34
- const opts = {
35
- logger,
36
- winston
37
- }
34
+ const opts = {
35
+ logger,
36
+ winston
37
+ }
38
38
 
39
- return loggerFn(opts)
40
- })
39
+ return loggerFn(opts)
40
+ })
41
41
 
42
42
  export default makeLoggers
@@ -1,20 +1,18 @@
1
- import SentryPackage from 'winston-transport-sentry-node'
2
-
3
- const Sentry = SentryPackage?.default || SentryPackage
1
+ import { SentryTransport } from './winston-transport-sentry-node.js'
4
2
 
5
3
  export default ({ logger }) => {
6
- const options = {
7
- sentry: {
8
- dsn: logger?.location,
9
- environment: logger?.environment || 'production',
10
- serverName: logger?.serverName || 'localhost',
11
- release: logger?.release || 'unknown',
12
- debug: logger?.debug || false,
13
- sampleRate: logger?.sampleRate || 1,
14
- tracesSampleRate: logger?.tracesSampleRate || 1
15
- },
16
- level: logger?.level || 'info'
17
- }
4
+ const options = {
5
+ sentry: {
6
+ dsn: logger?.location,
7
+ environment: logger?.environment || 'production',
8
+ serverName: logger?.serverName || 'localhost',
9
+ release: logger?.release || 'unknown',
10
+ debug: logger?.debug || false,
11
+ sampleRate: logger?.sampleRate || 1,
12
+ tracesSampleRate: logger?.tracesSampleRate || 1
13
+ },
14
+ level: logger?.level || 'info'
15
+ }
18
16
 
19
- return new Sentry(options)
17
+ return new SentryTransport(options)
20
18
  }
@@ -0,0 +1,194 @@
1
+ import * as Sentry from '@sentry/node'
2
+ import TransportStream from 'winston-transport'
3
+ import { LEVEL } from 'triple-beam'
4
+ import { ExtendedError } from '../helpers/extended-error.js'
5
+
6
+ /**
7
+ * @enum {string}
8
+ */
9
+ const SentrySeverity = {
10
+ Debug: 'debug',
11
+ Log: 'log',
12
+ Info: 'info',
13
+ Warning: 'warning',
14
+ Error: 'error',
15
+ Fatal: 'fatal'
16
+ }
17
+
18
+ /**
19
+ * @type {{[key: string]: string}}
20
+ */
21
+ const DEFAULT_LEVELS_MAP = {
22
+ silly: SentrySeverity.Debug,
23
+ verbose: SentrySeverity.Debug,
24
+ info: SentrySeverity.Info,
25
+ debug: SentrySeverity.Debug,
26
+ warn: SentrySeverity.Warning,
27
+ error: SentrySeverity.Error
28
+ }
29
+
30
+ /**
31
+ * @typedef {object} SentryTransportOptions
32
+ * @property {Sentry.NodeOptions} [sentry] - Sentry configuration options.
33
+ * @property {{[key: string]: string}} [levelsMap] - Custom levels map.
34
+ * @property {boolean} [skipSentryInit] - Whether to skip Sentry initialization.
35
+ * @property {boolean} [silent] - Whether to suppress logging.
36
+ */
37
+
38
+ /**
39
+ * @typedef {{[key: string]: string}} SeverityOptions
40
+ */
41
+
42
+ /**
43
+ * Custom Winston transport for Sentry.
44
+ */
45
+ export class SentryTransport extends TransportStream {
46
+ /**
47
+ * @type {boolean}
48
+ */
49
+ silent = false
50
+
51
+ /**
52
+ * @type {SeverityOptions}
53
+ */
54
+ levelsMap = {}
55
+
56
+ /**
57
+ * @param {SentryTransportOptions} [opts] - Transport options.
58
+ */
59
+ constructor (opts) {
60
+ super(opts)
61
+
62
+ this.levelsMap = this.setLevelsMap(opts && opts.levelsMap)
63
+ this.silent = (opts && opts.silent) || false
64
+
65
+ if (!opts || !opts.skipSentryInit) {
66
+ Sentry.init(SentryTransport.withDefaults((opts && opts.sentry) || {}))
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Logs the message to Sentry.
72
+ * @param {object} info - Log information.
73
+ * @param {Function} callback - Callback function.
74
+ * @returns {void}
75
+ */
76
+ log (info, callback) {
77
+ setImmediate(() => {
78
+ this.emit('logged', info)
79
+ })
80
+
81
+ if (this.silent) return callback()
82
+
83
+ const { message, tags, user, ...meta } = info
84
+ const winstonLevel = info[LEVEL]
85
+
86
+ const sentryLevel = this.levelsMap[winstonLevel]
87
+
88
+ return Sentry.withScope((scope) => {
89
+ if (tags !== undefined && SentryTransport.isObject(tags)) {
90
+ scope.setTags(tags)
91
+ }
92
+
93
+ scope.setExtras(meta)
94
+
95
+ if (user !== undefined && SentryTransport.isObject(user)) {
96
+ scope.setUser(user)
97
+ }
98
+
99
+ // Capturing Errors / Exceptions
100
+ if (SentryTransport.shouldLogException(sentryLevel)) {
101
+ const error
102
+ = Object.values(info).find((value) => value instanceof Error)
103
+ ?? new ExtendedError(info)
104
+ Sentry.captureException(error, { tags, level: sentryLevel })
105
+ } else {
106
+ // Capturing Messages
107
+ Sentry.captureMessage(message, sentryLevel)
108
+ }
109
+ })
110
+ }
111
+
112
+ /**
113
+ * Ends the transport and flushes Sentry.
114
+ * @param {...any} args - Arguments.
115
+ * @returns {this}
116
+ */
117
+ end (...args) {
118
+ // eslint-disable-next-line promise/catch-or-return
119
+ Sentry.flush().then(() => super.end(...args))
120
+ return this
121
+ }
122
+
123
+ /**
124
+ * @returns {typeof Sentry}
125
+ */
126
+ get sentry () {
127
+ return Sentry
128
+ }
129
+
130
+ /**
131
+ * Sets the levels map.
132
+ * @param {SeverityOptions} [options] - Custom levels map.
133
+ * @returns {SeverityOptions}
134
+ */
135
+ setLevelsMap (options) {
136
+ if (!options) {
137
+ return DEFAULT_LEVELS_MAP
138
+ }
139
+
140
+ const customLevelsMap = Object.keys(options).reduce(
141
+ (acc, winstonSeverity) => {
142
+ acc[winstonSeverity] = options[winstonSeverity]
143
+ return acc
144
+ },
145
+ {}
146
+ )
147
+
148
+ return {
149
+ ...DEFAULT_LEVELS_MAP,
150
+ ...customLevelsMap
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Merges default Sentry options with user-provided options.
156
+ * @param {Sentry.NodeOptions} options - Sentry options.
157
+ * @returns {Sentry.NodeOptions}
158
+ */
159
+ static withDefaults (options) {
160
+ return {
161
+ ...options,
162
+ dsn: (options && options.dsn) || process.env.SENTRY_DSN || '',
163
+ serverName:
164
+ (options && options.serverName) || 'winston-transport-sentry-node',
165
+ environment:
166
+ (options && options.environment)
167
+ || process.env.SENTRY_ENVIRONMENT
168
+ || process.env.NODE_ENV
169
+ || 'production',
170
+ debug: (options && options.debug) || !!process.env.SENTRY_DEBUG || false,
171
+ sampleRate: (options && options.sampleRate) || 1.0,
172
+ maxBreadcrumbs: (options && options.maxBreadcrumbs) || 100
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Checks if the given object is an object or function.
178
+ * @param {any} obj - Object to check.
179
+ * @returns {boolean}
180
+ */
181
+ static isObject (obj) {
182
+ const type = typeof obj
183
+ return type === 'function' || (type === 'object' && !!obj)
184
+ }
185
+
186
+ /**
187
+ * Determines if the given level should log an exception.
188
+ * @param {string} level - Sentry severity level.
189
+ * @returns {boolean}
190
+ */
191
+ static shouldLogException (level) {
192
+ return level === SentrySeverity.Fatal || level === SentrySeverity.Error
193
+ }
194
+ }
@@ -19,17 +19,17 @@ import { Enum } from '@trojs/enum'
19
19
  * @enum { LevelType }
20
20
  */
21
21
  class Level extends Enum {
22
- static info = 'info'
22
+ static info = 'info'
23
23
 
24
- static fatal = 'fatal'
24
+ static fatal = 'fatal'
25
25
 
26
- static error = 'error'
26
+ static error = 'error'
27
27
 
28
- static warn = 'warn'
28
+ static warn = 'warn'
29
29
 
30
- static debug = 'debug'
30
+ static debug = 'debug'
31
31
 
32
- static trace = 'trace'
32
+ static trace = 'trace'
33
33
  }
34
34
 
35
35
  export default Level
@@ -9,26 +9,26 @@ import LoggerMutator from './mutators/logger.js'
9
9
  const LoggerObject = Obj({ schema: loggerSchema })
10
10
 
11
11
  export default class Logger {
12
- /**
13
- * Create a logger object
14
- * @param {LoggerType} data
15
- * @returns {LoggerType}
16
- */
17
- static create(data) {
18
- const logger = LoggerMutator.create(data)
19
- return LoggerObject.create(logger)
20
- }
21
-
22
- /**
23
- * Create logger objects
24
- * @param {LoggerType[]} data
25
- * @returns {LoggerType[]}
26
- */
27
- static createAll(data) {
28
- if (!data || data.length === 0) {
29
- return []
30
- }
12
+ /**
13
+ * Create a logger object
14
+ * @param {LoggerType} data
15
+ * @returns {LoggerType}
16
+ */
17
+ static create (data) {
18
+ const logger = LoggerMutator.create(data)
19
+ return LoggerObject.create(logger)
20
+ }
31
21
 
32
- return data.map((loggerData) => Logger.create(loggerData))
22
+ /**
23
+ * Create logger objects
24
+ * @param {LoggerType[]} data
25
+ * @returns {LoggerType[]}
26
+ */
27
+ static createAll (data) {
28
+ if (!data || data.length === 0) {
29
+ return []
33
30
  }
31
+
32
+ return data.map((loggerData) => Logger.create(loggerData))
33
+ }
34
34
  }
@@ -6,9 +6,9 @@ import LevelEnum from '../enums/level.js'
6
6
  * @augments DefaultMutator
7
7
  */
8
8
  class Logger extends DefaultMutator {
9
- setLevelAttribute(level) {
10
- return LevelEnum.fromKey(level)
11
- }
9
+ setLevelAttribute (level) {
10
+ return LevelEnum.fromKey(level)
11
+ }
12
12
  }
13
13
 
14
14
  export default Logger
@@ -22,16 +22,16 @@ import Level from '../enums/level.js'
22
22
  */
23
23
 
24
24
  export default {
25
- type: String,
26
- 'location?': String,
27
- 'level?': Level,
28
- 'key?': String,
29
- 'credentials?': Object,
30
- 'environment?': String,
31
- 'serverName?': String,
32
- 'release?': String,
33
- 'debug?': Boolean,
34
- 'sampleRate?': Number,
35
- 'tracesSampleRate?': Number,
36
- 'format?': String
25
+ type: String,
26
+ 'location?': String,
27
+ 'level?': Level,
28
+ 'key?': String,
29
+ 'credentials?': Object,
30
+ 'environment?': String,
31
+ 'serverName?': String,
32
+ 'release?': String,
33
+ 'debug?': Boolean,
34
+ 'sampleRate?': Number,
35
+ 'tracesSampleRate?': Number,
36
+ 'format?': String
37
37
  }