bajo 2.0.2 → 2.1.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.
package/class/app.js CHANGED
@@ -15,10 +15,12 @@ import dayjs from 'dayjs'
15
15
  import utc from 'dayjs/plugin/utc.js'
16
16
  import customParseFormat from 'dayjs/plugin/customParseFormat.js'
17
17
  import localizedFormat from 'dayjs/plugin/localizedFormat.js'
18
+ import weekOfYear from 'dayjs/plugin/weekOfYear.js'
18
19
 
19
20
  dayjs.extend(utc)
20
21
  dayjs.extend(customParseFormat)
21
22
  dayjs.extend(localizedFormat)
23
+ dayjs.extend(weekOfYear)
22
24
 
23
25
  const { isPlainObject, get, reverse, map, isString, last, without, keys } = lodash
24
26
  let unknownLangWarning = false
@@ -42,11 +42,18 @@ const defConfig = {
42
42
  env: 'dev',
43
43
  log: {
44
44
  timeTaken: false,
45
- dateFormat: 'YYYY-MM-DDTHH:MM:ss.SSS[Z]',
46
- localDate: false,
45
+ dateFormat: 'YYYY-MM-DDTHH:mm:ss.SSS',
46
+ useUtc: false,
47
47
  pretty: false,
48
48
  applet: false,
49
- traceHook: false
49
+ traceHook: false,
50
+ save: false,
51
+ rotation: {
52
+ cycle: 'none', // none, daily, weekly, monthly
53
+ compressOld: true,
54
+ byPlugin: false,
55
+ retain: 5
56
+ }
50
57
  },
51
58
  lang: Intl.DateTimeFormat().resolvedOptions().lang ?? 'en-US',
52
59
  intl: {
@@ -109,12 +116,9 @@ export async function buildBaseConfig () {
109
116
  set(this, 'dir.base', this.app.dir)
110
117
  const path = currentLoc(import.meta).dir + '/../..'
111
118
  set(this, 'dir.pkg', this.resolvePath(path))
119
+ if (get(this, 'config.dir.data')) set(this, 'dir.data', this.config.dir.data)
112
120
  if (!get(this, 'dir.data')) set(this, 'dir.data', `${this.dir.base}/data`)
113
121
  this.dir.data = this.resolvePath(this.dir.data)
114
- if (!fs.existsSync(this.dir.data)) {
115
- console.log('Data directory (%s) doesn\'t exist yet', this.dir.data)
116
- process.exit(1)
117
- }
118
122
  fs.ensureDirSync(`${this.dir.data}/config`)
119
123
  if (!this.dir.tmp) {
120
124
  this.dir.tmp = `${this.resolvePath(os.tmpdir())}/${this.ns}`
@@ -222,6 +226,7 @@ export async function buildExtConfig () {
222
226
  if (!this.config.log.applet) this.config.log.level = 'silent'
223
227
  this.config.exitHandler = false
224
228
  }
229
+ this.log.trace('dataDir%s', this.dir.data)
225
230
  this.log.debug('configHandlers%s', this.join(exts))
226
231
  }
227
232
 
package/class/misc/log.js CHANGED
@@ -1,11 +1,8 @@
1
1
  import os from 'os'
2
- import lodash from 'lodash'
3
- import dayjs from 'dayjs'
4
- import fs from 'fs-extra'
5
2
  import logLevels from '../../lib/log-levels.js'
6
3
  import chalk from 'chalk'
7
-
8
- const { isEmpty, without, merge, get } = lodash
4
+ import { stripVTControlCharacters } from 'node:util'
5
+ import dayjs from 'dayjs'
9
6
 
10
7
  /**
11
8
  * Log output in stringified JSON format. Returned when app run in ```prod``` environment
@@ -53,12 +50,9 @@ class Log {
53
50
  */
54
51
  this.app = app
55
52
 
56
- /**
57
- * Date format to use in {@link https://day.js.org/docs/en/parse/string-format|dayjs} format. See {@tutorial config} for more info.
58
- * @type {string}
59
- */
60
- const { dateFormat } = this.app.bajo.config.log ?? {}
61
- this.dateFormat = dateFormat ?? 'YYYY-MM-DDTHH:mm:ss.SSS'
53
+ const { fs } = this.app.lib
54
+ this.logDir = `${this.app.bajo.dir.data}/log`
55
+ if (this.app.bajo.config.log.save) fs.ensureDirSync(this.logDir)
62
56
  }
63
57
 
64
58
  /**
@@ -77,9 +71,12 @@ class Log {
77
71
  * @see TLogJson
78
72
  */
79
73
  formatMsg = (level, prefix, ...params) => {
74
+ const { dayjs } = this.app.lib
75
+ const { isEmpty, merge, without } = this.app.lib._
76
+
80
77
  if (this.app.bajo.config.log.level === 'silent') return
81
78
  if (!this.app.bajo.isLogInRange(level)) return
82
- const pretty = this.app.bajo.config.log.pretty
79
+ const { useUtc, timeTaken, dateFormat, pretty } = this.app.bajo.config.log
83
80
  let [data, msg, ...args] = params
84
81
  if (typeof data === 'string') {
85
82
  args.unshift(msg)
@@ -93,11 +90,10 @@ class Log {
93
90
  }
94
91
  msg = this.app.t(prefix, msg, ...args)
95
92
  let text
96
- const dt = new Date()
93
+ const dt = dayjs()
97
94
  let diff = null
98
- const timeTaken = !!get(this, 'app.bajo.config.log.timeTaken')
99
95
  if (timeTaken) {
100
- const delta = dayjs(dt).diff(this.app.runAt, 'ms')
96
+ const delta = dt.diff(this.app.runAt, 'ms')
101
97
  diff = delta - this.lastDelta
102
98
  this.lastDelta = delta
103
99
  }
@@ -107,11 +103,8 @@ class Log {
107
103
  if (timeTaken) merge(json, { timeTaken: diff })
108
104
  text = JSON.stringify(json)
109
105
  } else {
110
- let dateFormat = get(this, 'app.bajo.config.log.dateFormat', this.dateFormat).replaceAll('[Z]', '')
111
- const localDate = get(this, 'app.bajo.config.log.localDate', false)
112
- let date = dayjs(dt)
113
- if (!localDate) date = date.utc()
114
- if (!(dateFormat.includes('L') || dateFormat.includes('l'))) dateFormat += '[Z]'
106
+ let date = dt.clone()
107
+ if (useUtc) date = dayjs.utc(dt)
115
108
  date = date.format(dateFormat)
116
109
  let tdate = pretty ? chalk.cyan(date) : `[${date}]`
117
110
  if (timeTaken) {
@@ -124,11 +117,61 @@ class Log {
124
117
  if (!isEmpty(data)) text += '\n' + JSON.stringify(data)
125
118
  }
126
119
  console.log(text)
127
- if (this.app.bajo.config.log.save) {
128
- fs.ensureDirSync(`${this.app.bajo.dir.data}/log`)
129
- // TODO: log write, rotation, etc
130
- }
131
120
  if (data instanceof Error && level === 'trace') console.error(data)
121
+ if (this.app.bajo.config.log.save) this.save(text, prefix)
122
+ }
123
+
124
+ /**
125
+ * Calculate pattern used for log rotation
126
+ *
127
+ * @method
128
+ * @param {boolean} isPrev - If true, calculate previous rotation pattern
129
+ * @returns {string} Calculated pattern
130
+ */
131
+ getRotationPattern = (isPrev) => {
132
+ const { dayjs } = this.app.lib
133
+ const { cycle } = this.app.bajo.config.log.rotation
134
+ if (cycle === 'none') return
135
+ let pattern
136
+ const now = dayjs()
137
+ switch (cycle) {
138
+ case 'monthly': {
139
+ const dt = isPrev ? now.subtract(1, 'month') : now
140
+ pattern = dt.format('YYYY-MM')
141
+ break
142
+ }
143
+ case 'weekly': {
144
+ const dt = isPrev ? now.subtract(1, 'week') : now
145
+ pattern = dt.format(`YYYY-W${dt.week()}`)
146
+ break
147
+ }
148
+ case 'daily': {
149
+ const dt = isPrev ? now.subtract(1, 'day') : now
150
+ pattern = dt.format('YYYY-MM-DD')
151
+ break
152
+ }
153
+ }
154
+ return pattern
155
+ }
156
+
157
+ /**
158
+ * Save log to file in {dataDir}/log
159
+ *
160
+ * @method
161
+ * @param {string} text - Log message to save
162
+ * @param {string} prefix - Use prefix as basename. Defaults to 'bajo'
163
+ */
164
+ save = (text, prefix = 'bajo') => {
165
+ const { fs } = this.app.lib
166
+ let fname = this.app.bajo.config.log.rotation.byPlugin ? prefix : 'bajo'
167
+ let file = `${this.logDir}/${fname}.log`
168
+ const content = stripVTControlCharacters(text)
169
+ const pattern = this.getRotationPattern()
170
+ if (pattern) {
171
+ file = `${this.logDir}/${fname}.${pattern}.log`
172
+ }
173
+ fs.appendFileSync(file, `${content}\n`, 'utf8')
174
+ // TODO: symlink bajo.log to target
132
175
  }
133
176
 
134
177
  /**
@@ -22,6 +22,7 @@
22
22
  "appRunningAsApplet": "App runs in applet mode",
23
23
  "setupBootOrder": "Setup boot order",
24
24
  "configHandlers%s": "Config handlers: %s",
25
+ "dataDir%s": "Data dir: %s",
25
26
  "exited": "Exited",
26
27
  "appShutdown": "App shutdown",
27
28
  "appletModeActivated": "Applet mode activated",
@@ -22,6 +22,7 @@
22
22
  "appRunningAsApplet": "App berjalan di mode applet",
23
23
  "setupBootOrder": "Atur urutan booting",
24
24
  "configHandlers%s": "Pengatur konfigurasi: %s",
25
+ "dataDir%s": "Dir data: %s",
25
26
  "exited": "Exited",
26
27
  "appShutdown": "Aplikasi ditutup",
27
28
  "appletModeActivated": "Mode applet diaktifkan",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bajo",
3
- "version": "2.0.2",
3
+ "version": "2.1.0",
4
4
  "description": "The ultimate framework for whipping up massive apps in no time",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/wiki/CONFIG.md CHANGED
@@ -7,9 +7,15 @@ The following table shows the default app settings. To change these to suit your
7
7
  | ```log``` | ```object``` | | |
8
8
  |     ```dateFormat``` | ```string``` | ```YYYY-MM-DDTHH:MM:ss.SSS[Z]```| See [dayjs string & format](https://day.js.org/docs/en/parse/string-format) for more info |
9
9
  |     ```timeTaken``` | ```boolean``` | ```false```| Show time taken from previous activity in ms |
10
- |     ```localDate``` | ```boolean``` | ```false```| Use local date, defaults: UTC |
10
+ |     ```useUtc``` | ```boolean``` | ```false```| Use UTC, defaults: local date/time |
11
11
  |     ```pretty``` | ```boolean``` | ```false```| Colorful, pretty styling |
12
12
  |     ```applet``` | ```boolean``` | ```false```| Show log even in applet mode |
13
+ |     ```save``` | ```boolean``` | ```false```| Save log in '{dataDir}/log' |
14
+ |     ```rotation``` | ```object``` | | Log rotation config if ```save``` is true |
15
+ |         ```cycle``` | ```string``` | ```none``` | Available values: ```none```, ```daily```, ```weekly```, ```monthly``` |
16
+ |         ```compressOld``` | ```boolean``` | ```false``` | Set to ```true``` to compress old logs |
17
+ |         ```byPlugin``` | ```boolean``` | ```false``` | Split log by plugin's name |
18
+ |         ```retain``` | ```integer``` | ```5``` | How many old logs will be kept/retained |
13
19
  | ```lang``` | ```string``` | Auto detected | Valid language code e.g: 'en-US', 'id', etc. |
14
20
  | ```intl``` | ```object``` | | Internationalization settings |
15
21
  |     ```supported``` | ```array``` | ```['en-US', 'id']``` | Supported languages |