backlib 0.4.0-SNAPSHOT.1 → 0.5.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,13 @@
1
+ /**
2
+ * Tracker to determine if a decorator is on the leaf"est" method of the class hierarchy.
3
+ */
4
+ export declare function newLeafTracer(): LeafTracer;
5
+ /**
6
+ * Ter
7
+ */
8
+ declare class LeafTracer {
9
+ private dic;
10
+ /** Returns true if this method is the leaf most method annotatio traced by this tracer */
11
+ trace(objectClass: Function, targetClass: Function, propertyKey: string): boolean;
12
+ }
13
+ export {};
@@ -0,0 +1,6 @@
1
+ export { newLeafTracer } from './decorator-leaf-tracer.js';
2
+ export { FileWriter } from './log-file-writer.js';
3
+ export type { FileNameProvider, FileWriterOptions, OnFileCompleted, RecordSerializer } from './log-file-writer.js';
4
+ export { BaseLog } from './log.js';
5
+ export type { LogOptions, LogWriter } from './log.js';
6
+ export { prompt } from './utils.js';
package/dist/index.js CHANGED
@@ -1,5 +1,9 @@
1
- export * from './decorator-leaf-tracer.js';
2
- export * from './fs.js';
3
- export * from './log.js';
4
- export * from './utils.js';
1
+ // Export decorator constructs
2
+ export { newLeafTracer } from './decorator-leaf-tracer.js';
3
+ // Export log-file-writer constructs
4
+ export { FileWriter } from './log-file-writer.js';
5
+ // Export log constructs
6
+ export { BaseLog } from './log.js';
7
+ // Export utils constructs
8
+ export { prompt } from './utils.js';
5
9
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC;AAC3C,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8BAA8B;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,oCAAoC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAElD,wBAAwB;AACxB,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAEnC,0BAA0B;AAC1B,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,38 @@
1
+ import { LogWriter } from './index.js';
2
+ export declare type OnFileCompleted = (file: string) => Promise<void>;
3
+ export declare type FileNameProvider = (rev: number) => string;
4
+ /** Record serializer to string, which will be appended to the file. If null, record will be skipped */
5
+ export declare type RecordSerializer<R> = (rec: R) => string | null;
6
+ export interface FileWriterOptions<R> {
7
+ /** Local directory in which the logs files will be saved */
8
+ dir: string;
9
+ /** maxCount of record before file is uploaded to destination */
10
+ maxCount: number;
11
+ /** max time (in seconds) before file is uploaded to destination (which ever comes first with maxCount) */
12
+ maxTime: number;
13
+ /** Optional fileName generator for the new log file name (MUST BE UNIQUE for this dir) */
14
+ fileNameProvider?: FileNameProvider;
15
+ recordSerializer?: RecordSerializer<R>;
16
+ onFileCompleted?: OnFileCompleted;
17
+ }
18
+ export declare class FileWriter<R> implements LogWriter<R> {
19
+ private dir;
20
+ private maxCount;
21
+ private maxTime;
22
+ private fileNameProvider;
23
+ private recordSerializer;
24
+ private onFileCompleted?;
25
+ private _init;
26
+ private _rev;
27
+ private count;
28
+ private nextUpload;
29
+ private lastUpload?;
30
+ private file?;
31
+ constructor(opts: FileWriterOptions<R>);
32
+ private init;
33
+ /** Update the revision file */
34
+ private rev;
35
+ /** IMPLEMENTATION of the FileWriter interface */
36
+ writeRec(rec: R): Promise<void>;
37
+ private endFile;
38
+ }
@@ -0,0 +1,100 @@
1
+ import { pathExists } from 'fs-aux';
2
+ import { appendFile, mkdir, rename } from 'fs/promises';
3
+ import * as Path from "path";
4
+ import { isString } from 'utils-min';
5
+ export class FileWriter {
6
+ constructor(opts) {
7
+ this._init = false;
8
+ this._rev = 0;
9
+ this.count = 0;
10
+ this.nextUpload = null; // null means nothing scheduled
11
+ this.maxCount = opts.maxCount;
12
+ this.maxTime = opts.maxTime;
13
+ this.dir = opts.dir;
14
+ this.fileNameProvider = opts.fileNameProvider ?? defaultFileNameProvider;
15
+ this.onFileCompleted = opts.onFileCompleted;
16
+ this.recordSerializer = opts.recordSerializer ?? defaultSerializer;
17
+ }
18
+ async init() {
19
+ if (!this._init) {
20
+ await mkdir(this.dir, { recursive: true });
21
+ this.rev();
22
+ this._init = true;
23
+ }
24
+ }
25
+ /** Update the revision file */
26
+ rev() {
27
+ this.count = 0;
28
+ this._rev = this._rev + 1;
29
+ const fileName = this.fileNameProvider(this._rev);
30
+ this.file = Path.join(this.dir, fileName);
31
+ }
32
+ /** IMPLEMENTATION of the FileWriter interface */
33
+ async writeRec(rec) {
34
+ if (!this._init) {
35
+ await this.init();
36
+ }
37
+ const str = this.recordSerializer(rec);
38
+ if (str != null) {
39
+ const strWithNl = str + '\n'; // add the new line
40
+ await appendFile(this.file, strWithNl);
41
+ }
42
+ // add count
43
+ this.count = this.count + 1;
44
+ // if we are above the count, we upload
45
+ if (this.count > this.maxCount) {
46
+ await this.endFile();
47
+ }
48
+ // if still below the count, but do not have this.nextUpload, schedule one
49
+ else if (this.nextUpload === null) {
50
+ const maxTimeMs = this.maxTime * 1000; // in ms
51
+ const nextUpload = Date.now() + maxTimeMs;
52
+ this.nextUpload = nextUpload;
53
+ setTimeout(async () => {
54
+ // perform only if this.nextUpload match the scheduled nextUpload (otherwise, was already processed and this schedule is outdated)
55
+ if (this.nextUpload === nextUpload) {
56
+ await this.endFile();
57
+ }
58
+ }, maxTimeMs);
59
+ }
60
+ }
61
+ async endFile() {
62
+ const file = this.file;
63
+ // we rev just before to make sure other logs will happen on new files
64
+ this.rev();
65
+ try {
66
+ const exists = await pathExists(file);
67
+ if (exists) {
68
+ if (this.onFileCompleted) {
69
+ try {
70
+ await this.onFileCompleted(file);
71
+ }
72
+ catch (ex) {
73
+ console.log(`LOG PROCESSING ERROR - processing file '${file}' caused the following error: ${ex}`);
74
+ }
75
+ }
76
+ }
77
+ else {
78
+ console.log(`LOG PROCESSING REMOVE ERROR - cannot be processed file '${file}' does not exists anymore`);
79
+ }
80
+ }
81
+ // Note: note sure we need this global catch now.
82
+ catch (ex) {
83
+ console.log(`LOG PROCESSING - logger.processLogFile - cannot upload to big query ${file}, ${ex.message}`);
84
+ await rename(file, file + '.error');
85
+ }
86
+ this.count = 0;
87
+ this.lastUpload = Date.now();
88
+ this.nextUpload = null;
89
+ }
90
+ }
91
+ /** default serializer */
92
+ function defaultSerializer(rec) {
93
+ return isString(rec) ? rec : JSON.stringify(rec);
94
+ }
95
+ function defaultFileNameProvider(rev) {
96
+ const date = new Date().toISOString().replace(/[T:.]/g, "-").slice(0, -1);
97
+ const revStr = `${rev}`.padStart(5, '0');
98
+ return `log-file-${date}-${revStr}.log`;
99
+ }
100
+ //# sourceMappingURL=log-file-writer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log-file-writer.js","sourceRoot":"","sources":["../src/log-file-writer.ts"],"names":[],"mappings":"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"}
package/dist/log.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ export interface LogOptions<R> {
2
+ writers: LogWriter<R>[];
3
+ }
4
+ /**
5
+ * Base Log class that handle the log management logic and call `writer.writeRec` on the registered logWriters
6
+ */
7
+ export declare class BaseLog<R> {
8
+ #private;
9
+ constructor(opts: LogOptions<R>);
10
+ log(rec: R): Promise<void>;
11
+ }
12
+ export interface LogWriter<R> {
13
+ writeRec?(rec: R): Promise<void>;
14
+ }
package/dist/log.js CHANGED
@@ -1,119 +1,37 @@
1
- import * as Path from 'path';
2
- import { isString } from 'utils-min';
3
- import { glob, saferRemove } from './fs.js';
4
- const { pathExists, mkdirs, appendFile, rename } = (await import('fs-extra')).default;
1
+ // const { pathExists, mkdirs, appendFile, rename } = (await import('fs-extra')).default;
2
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
+ if (kind === "m") throw new TypeError("Private method is not writable");
4
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
6
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
+ };
8
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
10
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
+ };
13
+ var _BaseLog_logWriters;
5
14
  /**
6
- * Base Log class that handle the base log management logic.
15
+ * Base Log class that handle the log management logic and call `writer.writeRec` on the registered logWriters
7
16
  */
8
17
  export class BaseLog {
9
18
  constructor(opts) {
10
- this.logWriters = [];
11
- this.logWriters = [...opts.writers];
19
+ _BaseLog_logWriters.set(this, []);
20
+ __classPrivateFieldSet(this, _BaseLog_logWriters, [...opts.writers], "f");
12
21
  }
13
22
  async log(rec) {
14
- //
15
- for (const writer of this.logWriters) {
23
+ for (const writer of __classPrivateFieldGet(this, _BaseLog_logWriters, "f")) {
16
24
  if (writer.writeRec) {
17
25
  try {
18
26
  await writer.writeRec(rec);
19
27
  }
20
28
  catch (ex) {
21
29
  // here log console.log, no choise
22
- console.log(`LOG ERROR - Log exception when writeRec on logWriter ${writer.name}. ${ex}`);
30
+ console.log(`ERROR - BACKLIB - Log exception when calling writeRec on logWriter ${writer}. ${ex}`);
23
31
  }
24
32
  }
25
33
  }
26
34
  }
27
35
  }
28
- export class FileLogWriter {
29
- constructor(opts) {
30
- this._init = false;
31
- this._rev = 0;
32
- this.count = 0;
33
- this.nextUpload = null; // null means nothing scheduled
34
- this.name = opts.name;
35
- this.maxCount = opts.maxCount;
36
- this.maxTime = opts.maxTime;
37
- this.dir = opts.dir;
38
- this.fileProcessor = opts.fileProcessor;
39
- this.recordSerializer = opts.recordSerializer ?? defaultSerializer;
40
- }
41
- async init() {
42
- if (!this._init) {
43
- await mkdirs(this.dir);
44
- // delete the logs dir if exit
45
- const oldLogFiles = await glob(this.dir + `${this.name}*.log`);
46
- await saferRemove(oldLogFiles);
47
- console.log('Deleted old log files', oldLogFiles);
48
- this.rev();
49
- this._init = true;
50
- }
51
- }
52
- /** Update the revision file */
53
- rev() {
54
- this.count = 0;
55
- this._rev = this._rev + 1;
56
- const suffix = `${this._rev}`.padStart(5, '0');
57
- this.file = Path.join(this.dir, `${this.name}-${suffix}.log`);
58
- }
59
- /** IMPLEMENTATION of the FileWriter interface */
60
- async writeRec(rec) {
61
- if (!this._init) {
62
- await this.init();
63
- }
64
- // TODO: Need to move this outside of the generic log implementation (we do this because bigquery expect info to be string, since it can be dynamic)
65
- // NOTE: In fact, this whole file write and upload, should be part of a FileLogWriter, and we just treat it as above (perhaps in the BigQueryLogWriter extends FileLogWriter)
66
- const str = this.recordSerializer(rec);
67
- if (str != null) {
68
- await appendFile(this.file, str);
69
- }
70
- // add count
71
- this.count = this.count + 1;
72
- // if we are above the count, we upload
73
- if (this.count > this.maxCount) {
74
- await this.endFile();
75
- }
76
- // if still below the count, but do not have this.nextUpload, schedule one
77
- else if (this.nextUpload === null) {
78
- const maxTimeMs = this.maxTime * 1000; // in ms
79
- const nextUpload = Date.now() + maxTimeMs;
80
- this.nextUpload = nextUpload;
81
- setTimeout(async () => {
82
- // perform only if this.nextUpload match the scheduled nextUpload (otherwise, was already processed and this schedule is outdated)
83
- if (this.nextUpload === nextUpload) {
84
- await this.endFile();
85
- }
86
- }, maxTimeMs);
87
- }
88
- }
89
- async endFile() {
90
- const file = this.file;
91
- // we rev just before to make sure other logs will happen on new files
92
- this.rev();
93
- try {
94
- const exists = await pathExists(file);
95
- if (exists) {
96
- if (this.fileProcessor) {
97
- await this.fileProcessor(file);
98
- }
99
- await saferRemove(file);
100
- }
101
- else {
102
- console.log(`CODE ERROR - can't upload to big query ${file} does not exists`);
103
- }
104
- }
105
- catch (ex) {
106
- console.log(`ERROR - logger.processLogFile - cannot upload to big query ${file}, ${ex.message}`);
107
- await rename(file, file + '.error');
108
- }
109
- this.count = 0;
110
- this.lastUpload = Date.now();
111
- this.nextUpload = null;
112
- }
113
- }
114
- /** default serializer */
115
- function defaultSerializer(rec) {
116
- return isString(rec) ? rec : JSON.stringify(rec);
117
- }
118
- //#endregion ---------- /FileLogWriter ----------
36
+ _BaseLog_logWriters = new WeakMap();
119
37
  //# sourceMappingURL=log.js.map
package/dist/log.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"log.js","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"log.js","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AACA,yFAAyF;;;;;;;;;;;;;AAQzF;;GAEG;AACH,MAAM,OAAO,OAAO;IAGnB,YAAY,IAAmB;QAF/B,8BAA8B,EAAE,EAAC;QAGhC,uBAAA,IAAI,uBAAe,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,MAAA,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAM;QAEf,KAAK,MAAM,MAAM,IAAI,uBAAA,IAAI,2BAAY,EAAE;YACtC,IAAI,MAAM,CAAC,QAAQ,EAAE;gBACpB,IAAI;oBACH,MAAM,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;iBAC3B;gBAAC,OAAO,EAAE,EAAE;oBACZ,kCAAkC;oBAClC,OAAO,CAAC,GAAG,CAAC,sEAAsE,MAAM,KAAK,EAAE,EAAE,CAAC,CAAC;iBACnG;aACD;SACD;IAEF,CAAC;CAED"}
@@ -0,0 +1,2 @@
1
+ /** Promise a message and return the trimmed entered value */
2
+ export declare function prompt(message: string): Promise<unknown>;
package/package.json CHANGED
@@ -1,36 +1,30 @@
1
1
  {
2
2
  "name": "backlib",
3
3
  "type": "module",
4
- "version": "0.4.0-SNAPSHOT.1",
4
+ "version": "0.5.0",
5
5
  "description": "Minimalist library for backend services",
6
6
  "main": "dist/index.js",
7
- "typings": "dist/index.d.ts",
8
7
  "engines": {
9
- "node": ">=14.4"
8
+ "node": ">=16.13"
10
9
  },
11
10
  "repository": {
12
11
  "type": "git",
13
12
  "url": "https://github.com/britesnow/node-backlib.git"
14
13
  },
15
14
  "scripts": {
16
- "test": "TS_NODE_FILES=true ./node_modules/.bin/mocha -p ./test/tsconfig.json ./test/spec/*.spec.ts",
17
- "testw": "TS_NODE_FILES=true ./node_modules/.bin/mocha -p ./test/tsconfig.json ./test/spec/*.spec.ts --watch --watch-files './test/**/*.ts','./src/**/*.ts'",
15
+ "test": "echo 'no test for now'",
18
16
  "prepare": "node_modules/.bin/rimraf ./dist && ./node_modules/.bin/tsc"
19
17
  },
20
18
  "author": "jeremy.chone@gmail.com",
21
19
  "license": "MIT",
22
20
  "dependencies": {
23
- "fast-glob": "^3.2.11",
24
- "fs-extra": "^10.0.0",
21
+ "fs-aux": "^0.1.0",
25
22
  "utils-min": "^0.2.0"
26
23
  },
27
24
  "devDependencies": {
28
- "@types/fs-extra": "^9.0.13",
29
- "@types/mocha": "^9.1.0",
30
- "mocha": "^9.1.4",
25
+ "@types/node": "^17.0.10",
31
26
  "rimraf": "^3.0.2",
32
- "ts-node-dev": "^1.1.8",
33
- "typescript": "^4.5.4"
27
+ "typescript": "^4.5.5"
34
28
  },
35
29
  "files": [
36
30
  "src/",
package/src/index.ts CHANGED
@@ -1,5 +1,19 @@
1
- export * from './decorator-leaf-tracer.js';
2
- export * from './fs.js';
3
- export * from './log.js';
4
- export * from './utils.js';
1
+ // Export decorator constructs
2
+ export { newLeafTracer } from './decorator-leaf-tracer.js';
3
+ // Export log-file-writer constructs
4
+ export { FileWriter } from './log-file-writer.js';
5
+ export type { FileNameProvider, FileWriterOptions, OnFileCompleted, RecordSerializer } from './log-file-writer.js';
6
+ // Export log constructs
7
+ export { BaseLog } from './log.js';
8
+ export type { LogOptions, LogWriter } from './log.js';
9
+ // Export utils constructs
10
+ export { prompt } from './utils.js';
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
5
19
 
@@ -0,0 +1,155 @@
1
+ import { pathExists } from 'fs-aux';
2
+ import { appendFile, mkdir, rename } from 'fs/promises';
3
+ import * as Path from "path";
4
+ import { isString } from 'utils-min';
5
+ import { LogWriter } from './index.js';
6
+
7
+ export type OnFileCompleted = (file: string) => Promise<void>;
8
+ export type FileNameProvider = (rev: number) => string;
9
+
10
+ /** Record serializer to string, which will be appended to the file. If null, record will be skipped */
11
+ export type RecordSerializer<R> = (rec: R) => string | null;
12
+
13
+ export interface FileWriterOptions<R> {
14
+ /** Local directory in which the logs files will be saved */
15
+ dir: string;
16
+ /** maxCount of record before file is uploaded to destination */
17
+ maxCount: number;
18
+ /** max time (in seconds) before file is uploaded to destination (which ever comes first with maxCount) */
19
+ maxTime: number;
20
+
21
+ /** Optional fileName generator for the new log file name (MUST BE UNIQUE for this dir) */
22
+ fileNameProvider?: FileNameProvider,
23
+ /* Optional recordSerializer to file. By default, JSON.serializer() (new line json) */
24
+ recordSerializer?: RecordSerializer<R>;
25
+ /* Call when a log file is completed (i.e., new entries will go to another file) */
26
+ onFileCompleted?: OnFileCompleted;
27
+
28
+ }
29
+
30
+ export class FileWriter<R> implements LogWriter<R> {
31
+ private dir: string;
32
+ private maxCount: number;
33
+ private maxTime: number;
34
+
35
+ private fileNameProvider: FileNameProvider;
36
+ private recordSerializer: RecordSerializer<R>;
37
+ private onFileCompleted?: OnFileCompleted;
38
+
39
+ private _init = false;
40
+ private _rev = 0;
41
+ private count = 0;
42
+ private nextUpload: number | null = null; // null means nothing scheduled
43
+ private lastUpload?: number;
44
+
45
+ private file?: string;
46
+
47
+ constructor(opts: FileWriterOptions<R>) {
48
+ this.maxCount = opts.maxCount;
49
+ this.maxTime = opts.maxTime;
50
+ this.dir = opts.dir;
51
+ this.fileNameProvider = opts.fileNameProvider ?? defaultFileNameProvider;
52
+ this.onFileCompleted = opts.onFileCompleted;
53
+ this.recordSerializer = opts.recordSerializer ?? defaultSerializer;
54
+ }
55
+
56
+ private async init() {
57
+ if (!this._init) {
58
+ await mkdir(this.dir, { recursive: true });
59
+ this.rev();
60
+ this._init = true;
61
+ }
62
+ }
63
+
64
+ /** Update the revision file */
65
+ private rev() {
66
+ this.count = 0;
67
+ this._rev = this._rev + 1;
68
+
69
+ const fileName = this.fileNameProvider(this._rev);
70
+
71
+ this.file = Path.join(this.dir, fileName)
72
+ }
73
+
74
+
75
+ /** IMPLEMENTATION of the FileWriter interface */
76
+ async writeRec(rec: R) {
77
+
78
+ if (!this._init) {
79
+ await this.init();
80
+ }
81
+
82
+ const str = this.recordSerializer(rec);
83
+ if (str != null) {
84
+ const strWithNl = str + '\n'; // add the new line
85
+ await appendFile(this.file!, strWithNl);
86
+ }
87
+
88
+ // add count
89
+ this.count = this.count + 1;
90
+
91
+ // if we are above the count, we upload
92
+ if (this.count > this.maxCount) {
93
+ await this.endFile();
94
+ }
95
+ // if still below the count, but do not have this.nextUpload, schedule one
96
+ else if (this.nextUpload === null) {
97
+ const maxTimeMs = this.maxTime * 1000; // in ms
98
+
99
+ const nextUpload = Date.now() + maxTimeMs;
100
+ this.nextUpload = nextUpload;
101
+
102
+ setTimeout(async () => {
103
+ // perform only if this.nextUpload match the scheduled nextUpload (otherwise, was already processed and this schedule is outdated)
104
+ if (this.nextUpload === nextUpload) {
105
+ await this.endFile();
106
+ }
107
+ }, maxTimeMs);
108
+ }
109
+
110
+ }
111
+
112
+ private async endFile() {
113
+ const file = this.file!;
114
+ // we rev just before to make sure other logs will happen on new files
115
+ this.rev();
116
+
117
+ try {
118
+ const exists = await pathExists(file);
119
+ if (exists) {
120
+ if (this.onFileCompleted) {
121
+ try {
122
+ await this.onFileCompleted(file);
123
+ } catch (ex: any) {
124
+ console.log(`LOG PROCESSING ERROR - processing file '${file}' caused the following error: ${ex}`);
125
+ }
126
+ }
127
+ } else {
128
+ console.log(`LOG PROCESSING REMOVE ERROR - cannot be processed file '${file}' does not exists anymore`);
129
+ }
130
+
131
+ }
132
+ // Note: note sure we need this global catch now.
133
+ catch (ex: any) {
134
+ console.log(`LOG PROCESSING - logger.processLogFile - cannot upload to big query ${file}, ${ex.message}`);
135
+ await rename(file, file + '.error');
136
+ }
137
+
138
+ this.count = 0;
139
+ this.lastUpload = Date.now();
140
+ this.nextUpload = null;
141
+ }
142
+
143
+ }
144
+
145
+ /** default serializer */
146
+
147
+ function defaultSerializer<R>(rec: R): string {
148
+ return isString(rec) ? rec : JSON.stringify(rec);
149
+ }
150
+
151
+ function defaultFileNameProvider(rev: number): string {
152
+ const date = new Date().toISOString().replace(/[T:.]/g, "-").slice(0, -1);
153
+ const revStr = `${rev}`.padStart(5, '0');
154
+ return `log-file-${date}-${revStr}.log`;
155
+ }
package/src/log.ts CHANGED
@@ -1,34 +1,31 @@
1
- import * as Path from 'path';
2
- import { isString } from 'utils-min';
3
- import { glob, saferRemove } from './fs.js';
4
- const { pathExists, mkdirs, appendFile, rename } = (await import('fs-extra')).default;
1
+
2
+ // const { pathExists, mkdirs, appendFile, rename } = (await import('fs-extra')).default;
5
3
 
6
4
 
7
5
  //#region ---------- BaseLog ----------
8
- interface LogOptions<R> {
6
+ export interface LogOptions<R> {
9
7
  writers: LogWriter<R>[]
10
8
  }
11
9
 
12
10
  /**
13
- * Base Log class that handle the base log management logic.
11
+ * Base Log class that handle the log management logic and call `writer.writeRec` on the registered logWriters
14
12
  */
15
13
  export class BaseLog<R> {
16
- private logWriters: LogWriter<R>[] = [];
14
+ #logWriters: LogWriter<R>[] = [];
17
15
 
18
16
  constructor(opts: LogOptions<R>) {
19
- this.logWriters = [...opts.writers];
17
+ this.#logWriters = [...opts.writers];
20
18
  }
21
19
 
22
20
  async log(rec: R) {
23
21
 
24
- //
25
- for (const writer of this.logWriters) {
22
+ for (const writer of this.#logWriters) {
26
23
  if (writer.writeRec) {
27
24
  try {
28
25
  await writer.writeRec(rec);
29
26
  } catch (ex) {
30
27
  // here log console.log, no choise
31
- console.log(`LOG ERROR - Log exception when writeRec on logWriter ${writer.name}. ${ex}`);
28
+ console.log(`ERROR - BACKLIB - Log exception when calling writeRec on logWriter ${writer}. ${ex}`);
32
29
  }
33
30
  }
34
31
  }
@@ -39,160 +36,6 @@ export class BaseLog<R> {
39
36
  //#endregion ---------- /BaseLog ----------
40
37
 
41
38
  export interface LogWriter<R> {
42
- readonly name: string;
43
39
  writeRec?(rec: R): Promise<void>
44
40
  }
45
41
 
46
-
47
- //#region ---------- FileLogWriter ----------
48
- /** processing file, if return true, then file will be assumed to be full processed, and will be deleted */
49
- export type FileProcessor = (file: string) => Promise<boolean>;
50
-
51
- /** Record serializer to string, which will be appended to the file. If null, record will be skipped */
52
- export type RecordSerializer<R> = (rec: R) => string | null;
53
-
54
- interface FileLogWriterOptions<R> {
55
- /** name of the logWriter, will be used as prefix */
56
- name: string;
57
- /** Local directory in which the logs files will be saved */
58
- dir: string;
59
- /** maxCount of record before file is uploaded to destination */
60
- maxCount: number;
61
- /** max time (in ms) before file is uploaded to destination (which ever comes first with maxCount) */
62
- maxTime: number;
63
-
64
- /* Mehod to process the file (.e.g., upload to bucket, bigquery, ...) */
65
- fileProcessor?: FileProcessor;
66
-
67
- /* Optional recordSerializer to file. By default, JSON.serializer() (new line json) */
68
- recordSerializer?: RecordSerializer<R>;
69
- }
70
-
71
-
72
-
73
- export class FileLogWriter<R> implements LogWriter<R> {
74
-
75
- readonly name: string;
76
- private dir: string;
77
- private maxCount: number;
78
- private maxTime: number;
79
- private fileProcessor?: FileProcessor;
80
- private recordSerializer: RecordSerializer<R>;
81
-
82
- private _init = false;
83
- private _rev = 0;
84
- private count = 0;
85
- private nextUpload: number | null = null; // null means nothing scheduled
86
- private lastUpload?: number;
87
-
88
- private file?: string;
89
-
90
-
91
- constructor(opts: FileLogWriterOptions<R>) {
92
- this.name = opts.name;
93
- this.maxCount = opts.maxCount;
94
- this.maxTime = opts.maxTime;
95
- this.dir = opts.dir;
96
- this.fileProcessor = opts.fileProcessor;
97
- this.recordSerializer = opts.recordSerializer ?? defaultSerializer;
98
- }
99
-
100
- private async init() {
101
- if (!this._init) {
102
- await mkdirs(this.dir);
103
-
104
- // delete the logs dir if exit
105
- const oldLogFiles = await glob(this.dir + `${this.name}*.log`);
106
- await saferRemove(oldLogFiles);
107
- console.log('Deleted old log files', oldLogFiles);
108
-
109
- this.rev();
110
-
111
- this._init = true;
112
- }
113
-
114
- }
115
-
116
- /** Update the revision file */
117
- private rev() {
118
- this.count = 0;
119
- this._rev = this._rev + 1;
120
- const suffix = `${this._rev}`.padStart(5, '0');
121
- this.file = Path.join(this.dir, `${this.name}-${suffix}.log`)
122
- }
123
-
124
-
125
- /** IMPLEMENTATION of the FileWriter interface */
126
- async writeRec(rec: R) {
127
-
128
- if (!this._init) {
129
- await this.init();
130
- }
131
-
132
- // TODO: Need to move this outside of the generic log implementation (we do this because bigquery expect info to be string, since it can be dynamic)
133
- // NOTE: In fact, this whole file write and upload, should be part of a FileLogWriter, and we just treat it as above (perhaps in the BigQueryLogWriter extends FileLogWriter)
134
- const str = this.recordSerializer(rec);
135
- if (str != null) {
136
- await appendFile(this.file!, str);
137
- }
138
-
139
- // add count
140
- this.count = this.count + 1;
141
-
142
- // if we are above the count, we upload
143
- if (this.count > this.maxCount) {
144
- await this.endFile();
145
- }
146
- // if still below the count, but do not have this.nextUpload, schedule one
147
- else if (this.nextUpload === null) {
148
- const maxTimeMs = this.maxTime * 1000; // in ms
149
-
150
- const nextUpload = Date.now() + maxTimeMs;
151
- this.nextUpload = nextUpload;
152
-
153
- setTimeout(async () => {
154
- // perform only if this.nextUpload match the scheduled nextUpload (otherwise, was already processed and this schedule is outdated)
155
- if (this.nextUpload === nextUpload) {
156
- await this.endFile();
157
- }
158
- }, maxTimeMs);
159
- }
160
-
161
- }
162
-
163
- private async endFile() {
164
- const file = this.file!;
165
- // we rev just before to make sure other logs will happen on new files
166
- this.rev();
167
-
168
- try {
169
- const exists = await pathExists(file);
170
- if (exists) {
171
- if (this.fileProcessor) {
172
- await this.fileProcessor(file);
173
- }
174
- await saferRemove(file);
175
- } else {
176
- console.log(`CODE ERROR - can't upload to big query ${file} does not exists`);
177
- }
178
-
179
- } catch (ex: any) {
180
- console.log(`ERROR - logger.processLogFile - cannot upload to big query ${file}, ${ex.message}`);
181
- await rename(file, file + '.error');
182
- }
183
-
184
- this.count = 0;
185
- this.lastUpload = Date.now();
186
- this.nextUpload = null;
187
- }
188
-
189
- }
190
-
191
- /** default serializer */
192
-
193
- function defaultSerializer<R>(rec: R): string {
194
- return isString(rec) ? rec : JSON.stringify(rec);
195
- }
196
-
197
- //#endregion ---------- /FileLogWriter ----------
198
-
package/dist/fs.js DELETED
@@ -1,70 +0,0 @@
1
- import FastGlob from 'fast-glob';
2
- import { join as pathJoin, resolve as pathResolve } from 'path';
3
- import { asArray } from 'utils-min';
4
- const { pathExists, remove } = (await import('fs-extra')).default;
5
- /**
6
- * Simplified and sorted glob function (using fast-glob) for one or more pattern from current directory or a optional cwd one.
7
- *
8
- * Note 1: The result will be sorted by natural directory/subdir/filename order (as a would a recursive walk)
9
- * Note 2: When `cwd` in options, it is added to the file path i.e. `pathJoin(cwd, path)`
10
- *
11
- * @returns always sorted result return Promise<string[]>
12
- */
13
- export async function glob(pattern, cwdOrFastGlobOptions) {
14
- let opts = undefined;
15
- if (cwdOrFastGlobOptions != null) {
16
- opts = (typeof cwdOrFastGlobOptions === 'string') ? { cwd: cwdOrFastGlobOptions } : cwdOrFastGlobOptions;
17
- }
18
- const result = await FastGlob(pattern, opts);
19
- const cwd = (opts) ? opts.cwd : undefined;
20
- const list = result.map(path => {
21
- return (cwd) ? pathJoin(cwd, path) : path;
22
- });
23
- return list.sort(globCompare);
24
- }
25
- /** Remove one or more files. Resolved the number of names removed */
26
- export async function saferRemove(names, cwd) {
27
- const baseDir = (cwd) ? pathResolve(cwd) : pathResolve('./');
28
- let removedNames = [];
29
- for (const name of asArray(names)) {
30
- const fullPath = pathJoin(baseDir, name);
31
- if (!fullPath.startsWith(baseDir)) {
32
- throw new Error(`Path to be removed does not look safe (nothing done): ${fullPath}\n\tCause: Does not belong to ${baseDir}`);
33
- }
34
- const exists = await pathExists(fullPath);
35
- if (exists) {
36
- await remove(fullPath);
37
- removedNames.push(name);
38
- }
39
- }
40
- return removedNames;
41
- }
42
- //#region ---------- Utils ----------
43
- function globCompare(a, b) {
44
- const aPathIdxs = pathIndexes(a);
45
- const bPathIdxs = pathIndexes(b);
46
- const minIdx = Math.min(aPathIdxs.length, bPathIdxs.length) - 1;
47
- const aMinPath = a.substring(0, aPathIdxs[minIdx]);
48
- const bMinPath = b.substring(0, bPathIdxs[minIdx]);
49
- // if the common path is the same, and the path depth is different, then, the shortest one come first;
50
- if ((aMinPath === bMinPath) && (aPathIdxs.length !== bPathIdxs.length)) {
51
- return (aPathIdxs.length < bPathIdxs.length) ? -1 : 1;
52
- }
53
- // otherwise, we do a normal compare
54
- return (a < b) ? -1 : 1;
55
- }
56
- function pathIndexes(fullPath) {
57
- const idxs = [];
58
- const l = fullPath.length;
59
- for (let i = 0; i < l; i++) {
60
- if (fullPath[i] === '/') {
61
- idxs.push(i);
62
- }
63
- }
64
- return idxs;
65
- }
66
- // function asArray(names: string | string[]) {
67
- // return (names instanceof Array) ? names : [names];
68
- // }
69
- //#endregion ---------- /Utils ----------
70
- //# sourceMappingURL=fs.js.map
package/dist/fs.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"fs.js","sourceRoot":"","sources":["../src/fs.ts"],"names":[],"mappings":"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"}
package/src/fs.ts DELETED
@@ -1,84 +0,0 @@
1
- import FastGlob, { Options } from 'fast-glob';
2
- import { join as pathJoin, resolve as pathResolve } from 'path';
3
- import { asArray } from 'utils-min';
4
- const { pathExists, remove } = (await import('fs-extra')).default;
5
-
6
- /**
7
- * Simplified and sorted glob function (using fast-glob) for one or more pattern from current directory or a optional cwd one.
8
- *
9
- * Note 1: The result will be sorted by natural directory/subdir/filename order (as a would a recursive walk)
10
- * Note 2: When `cwd` in options, it is added to the file path i.e. `pathJoin(cwd, path)`
11
- *
12
- * @returns always sorted result return Promise<string[]>
13
- */
14
- export async function glob(pattern: string | string[], cwdOrFastGlobOptions?: string | Options): Promise<string[]> {
15
- let opts: Options | undefined = undefined;
16
-
17
- if (cwdOrFastGlobOptions != null) {
18
- opts = (typeof cwdOrFastGlobOptions === 'string') ? { cwd: cwdOrFastGlobOptions } : cwdOrFastGlobOptions;
19
- }
20
-
21
- const result = await FastGlob(pattern, opts);
22
- const cwd = (opts) ? opts.cwd : undefined;
23
- const list = result.map(path => {
24
- return (cwd) ? pathJoin(cwd, path) : path;
25
- });
26
- return list.sort(globCompare);
27
- }
28
-
29
- /** Remove one or more files. Resolved the number of names removed */
30
- export async function saferRemove(names: string | string[], cwd?: string): Promise<string[]> {
31
- const baseDir = (cwd) ? pathResolve(cwd) : pathResolve('./');
32
- let removedNames: string[] = [];
33
-
34
- for (const name of asArray(names)) {
35
- const fullPath = pathJoin(baseDir, name);
36
- if (!fullPath.startsWith(baseDir)) {
37
- throw new Error(`Path to be removed does not look safe (nothing done): ${fullPath}\n\tCause: Does not belong to ${baseDir}`);
38
- }
39
- const exists = await pathExists(fullPath);
40
- if (exists) {
41
- await remove(fullPath);
42
- removedNames.push(name);
43
- }
44
- }
45
- return removedNames;
46
- }
47
-
48
-
49
- //#region ---------- Utils ----------
50
- function globCompare(a: string, b: string) {
51
- const aPathIdxs = pathIndexes(a);
52
- const bPathIdxs = pathIndexes(b);
53
- const minIdx = Math.min(aPathIdxs.length, bPathIdxs.length) - 1;
54
- const aMinPath = a.substring(0, aPathIdxs[minIdx]);
55
- const bMinPath = b.substring(0, bPathIdxs[minIdx]);
56
-
57
- // if the common path is the same, and the path depth is different, then, the shortest one come first;
58
- if ((aMinPath === bMinPath) && (aPathIdxs.length !== bPathIdxs.length)) {
59
- return (aPathIdxs.length < bPathIdxs.length) ? -1 : 1;
60
- }
61
-
62
- // otherwise, we do a normal compare
63
- return (a < b) ? -1 : 1;
64
-
65
-
66
- }
67
-
68
- function pathIndexes(fullPath: string): number[] {
69
- const idxs: number[] = [];
70
-
71
- const l = fullPath.length;
72
- for (let i = 0; i < l; i++) {
73
- if (fullPath[i] === '/') {
74
- idxs.push(i);
75
- }
76
- }
77
-
78
- return idxs;
79
- }
80
-
81
- // function asArray(names: string | string[]) {
82
- // return (names instanceof Array) ? names : [names];
83
- // }
84
- //#endregion ---------- /Utils ----------