-
Notifications
You must be signed in to change notification settings - Fork 0
/
logging.ts
299 lines (262 loc) · 9.2 KB
/
logging.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
/**
* Logging environment, used by the rest of the code. By default, no logging occurs; the user can set his/her own
* logging environment. This module also includes a logger to create a recursive log in YAML.
*
* The "breakpoints", and related logging information, follow the [suggestions](https://www.w3.org/TR/rdf-canon/#typo-conventions) in the RDFC-1.0 specification.
*
* This module was useful for the development of the code, and may become useful for future
* maintenance. For "everyday" usage this module can be safely ignored.
*
* @copyright Ivan Herman 2023
*
* @packageDocumentation
*/
import * as yaml from 'yaml';
import { NDegreeHashResult, BNodeToQuads, quadToNquad, HashToBNodes, Hash } from './common';
/**
* Logging severity levels (following the usual practice, although the full hierarchy is not used)
* @enum
*/
export enum LogLevels {
error,
warn,
info,
debug
};
/**
* And individual log item.
*/
export interface LogItem {
[index: string]: string | string[] | Map<string, string> | boolean | LogItem | LogItem[];
}
export type Log = Map<string, LogItem>;
/**
* Very simple Logger interface, to be used in the code.
*
* Implementations should follow the usual interpretation of log severity levels. E.g., if
* the Logger is set up with severity level of, say, `LogLevels.info`, then the messages to `debug` should be ignored. If the
* level is set to `LogLevels.warn`, then only warning and debugging messages should be recorded/displayed, etc.
*
* For each call the arguments are:
* - log_point: the identification of the log point, related to the spec (in practice, this should be identical to the `id` value of the respective HTML element in the specification).
* - position: short description of the position of the log. The string may be empty (i.e., ""), in which case it will be ignored.
* - otherData: the 'real' log information.
*
*/
export interface Logger {
level: LogLevels;
/**
* Debug level log.
*
* @param log_point
* @param position
* @param otherData
*/
debug(log_point: string, position: string, ...otherData: LogItem[]): void;
/**
* Warning level log.
*
* @param log_point
* @param position
* @param otherData
*/
warn(log_point: string, position: string, ...otherData: LogItem[]): void;
/**
* Error level log.
*
* @param log_point
* @param position
* @param otherData
*/
error(log_point: string, position: string, ...otherData: LogItem[]): void;
/**
* Information level log.
*
* @param log_point
* @param position
* @param otherData
*/
info(log_point: string, position: string, ...otherData: LogItem[]): void;
/**
* Entry point for a increase in stack level. This is issued at each function entry except the top level, and at some, more complex, cycles.
* Needed if the logger instance intends to create recursive logs or if the structure is complex.
* @param label - identification of the position in the code
* @param extra_info - possible extra information on the level increase
* @param
*/
push(label: string, extra_info?: string, ...otherData: LogItem[]): void;
/**
* Counterpart of the {@link push} method.
*/
pop(): void;
/**
* Accessor to the (readonly) log;
*/
get log(): string;
}
/**
* A default, no-operation logger instance, used by default.
* All methods are empty, ie, all messages are lost, and the final log is an empty string.
*/
class DefaultLogger implements Logger {
protected _level: LogLevels;
constructor() { };
debug(_log_point: string, _position: string, ..._otherData: LogItem[]): void { };
warn(_log_point: string, _position: string, ..._otherData: LogItem[]): void { };
error(_log_point: string, _position: string, ..._otherData: LogItem[]): void { };
info(_log_point: string, _position: string, ..._otherData: LogItem[]): void { };
push(_label: string, _extra_info?: string, ..._otherData: LogItem[]): void { };
pop(): void { };
get log(): string {
return '';
}
set level(l: LogLevels) { this._level = l; }
get level(): LogLevels { return this._level; }
}
/**
* Simple logger, storing the individual log messages as an array of {@link LogItem} objects. The logger
* follows the recommendations on severity levels as described in {@link Logger}.
*
* The "current" log is an array of {@link LogItem} instances, filled by subsequent logger calls.
* In case of a call to `push` this instance is pushed on an internal stack and a new array is created.
*
* The final log can be retrieved either in a YAML format using the {@link getFinalLog} method.
*
* By default, the logger level is set to `LogLevels.info`.
*/
class YamlLogger extends DefaultLogger {
private top_log: LogItem = {};
private current_log: LogItem[];
private log_stack: LogItem[][] = [];
constructor() {
super();
const ca_level: LogItem[] = [];
this.top_log["ca"] = ca_level;
this.current_log = ca_level;
}
private emitMessage(mtype: "debug" | "info" | "warn" | "error", log_id: string, position: string, extras: LogItem[]): void {
const item: LogItem = {};
if (position !== '') {
item["log point"] = mtype === "info" ? `${position}` : `[${mtype}] ${position}`;
}
if (extras.length !== 0) {
item["with"] = extras;
}
const full_item: LogItem = {};
full_item[log_id] = item;
this.current_log.push(full_item);
}
debug(log_id: string, position: string, ...extras: LogItem[]): void {
if (this._level >= LogLevels.debug) this.emitMessage("debug", log_id, position, extras);
}
info(log_id: string, position: string, ...extras: LogItem[]): void {
if (this._level >= LogLevels.info) this.emitMessage("info", log_id, position, extras);
}
warn(log_id: string, position: string, ...extras: LogItem[]): void {
if (this._level >= LogLevels.warn) this.emitMessage("warn", log_id, position, extras);
}
error(log_id: string, position: string, ...extras: LogItem[]): void {
if (this._level >= LogLevels.error) this.emitMessage("error", log_id, position, extras);
}
push(label: string, extra_info?: string, ...extras: LogItem[]): void {
const new_level: LogItem[] = [];
const new_level_ref: LogItem = {};
new_level_ref[label] = new_level;
if (extra_info && extra_info !== "") {
new_level.push({
"push info": extra_info
});
}
if (extras.length !== 0) {
new_level.push({
"with": extras
});
}
this.current_log.push(new_level_ref);
this.log_stack.push(this.current_log);
this.current_log = new_level;
}
pop(): void {
this.current_log = this.log_stack.pop();
}
get log(): string {
return yaml.stringify(this.top_log, { aliasDuplicateObjects: false });
}
}
/**
* Logger factory: to create new instances of a logger.
*/
export class LoggerFactory {
static DEFAULT_LOGGER = "DefaultLogger";
static #logger_protos: { [key: string]: Logger; } = {
"YamlLogger": new YamlLogger(),
"DefaultLogger": new DefaultLogger(),
};
/**
*
* @param id Identification string for a new logger type.
* @param level
* @returns new logger instance.
*/
static createLogger(id: string = LoggerFactory.DEFAULT_LOGGER, level: LogLevels = LogLevels.debug): Logger | undefined {
if (id in LoggerFactory.#logger_protos) {
const new_logger: Logger = Object.create(LoggerFactory.#logger_protos[id]);
new_logger.level = level;
return new_logger;
} else {
console.error(`>>> Not found logger ${id}`);
return undefined;
}
}
/**
* List of available logger types.
*/
static loggerTypes(): string[] {
const retval: string[] = [];
for (const key in LoggerFactory.#logger_protos) {
retval.push(key);
}
return retval;
}
}
/**
* Return a log item version of a `BNodeToQuads` instance, used to build up a full log message.
*
* @param bntq
* @returns
*/
export function bntqToLogItem(bntq: BNodeToQuads): LogItem {
const bntnq: LogItem = {};
for (const bn in bntq) {
bntnq[bn] = bntq[bn].map(quadToNquad);
}
return bntnq;
}
/**
* Return a log item version of an `NDegreeHashResult` instance, used to build up a full log message.
*
* @param ndhrs
* @returns
*/
export function ndhrToLogItem(ndhrs: NDegreeHashResult[]): LogItem[] {
return ndhrs.map((ndhr: NDegreeHashResult): LogItem => {
return {
"hash": ndhr.hash,
"issuer": ndhr.issuer.toLogItem()
};
});
}
/**
* Return a log item version of an `HashToBNodes` instance, used to build up a full log message.
*
* @param htbn
* @returns
*/
export function htbnToLogItem(htbn: HashToBNodes): LogItem[] {
return Object.keys(htbn).map((index: Hash): LogItem => {
return {
"hash": index,
"bnodes": htbn[index]
};
});
}