• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

node-opcua / node-opcua / 23974043205

04 Apr 2026 07:17AM UTC coverage: 92.589% (+0.01%) from 92.576%
23974043205

push

github

erossignon
chore: fix Mocha.Suite.settimeout misused

18408 of 21832 branches covered (84.32%)

161708 of 174651 relevant lines covered (92.59%)

461089.77 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

74.06
/packages/node-opcua-debug/source/make_loggers.ts
1
/**
1✔
2
 * @module node-opcua-debug
1✔
3
 */
1✔
4
// tslint:disable:no-console
1✔
5
import { EventEmitter } from "node:events";
1✔
6
import { format } from "node:util";
1✔
7
import chalk from "chalk";
1✔
8

1✔
9
const debugFlags: { [id: string]: boolean } = {};
1✔
10

1✔
11
const _process = typeof process === "object" ? process : { env: {} as Record<string, string> };
1!
12
const sTraceFlag = _process.env && (_process.env.DEBUG as string);
1✔
13

1✔
14
export enum LogLevel {
17✔
15
    Emergency = 0,
17✔
16
    Alert = 1,
17✔
17
    Critic = 2,
17✔
18
    Error = 3,
17✔
19
    Warning = 4,
17✔
20
    Notice = 5,
17✔
21
    Info = 6,
17✔
22
    Debug = 7
17✔
23
}
17✔
24

1✔
25
// c8 ignore next
1✔
26
const _activateDebug = false;
1✔
27
if (_process.env && _activateDebug) {
1!
28
    // this code can be activated to help detecting
×
29
    // when a external module overwrite one of the
×
30
    // environment variable that we may be using as well.
×
31
    const old = { ..._process.env };
×
32
    const handler = {
×
33
        get: (_obj: unknown, prop: string) => old[prop],
×
34
        set: (_obj: unknown, prop: string, value: unknown) => {
×
35
            console.log(`setting process.env = prop ${prop}`);
×
36
            old[prop] = value as string;
×
37
            return true;
×
38
        }
×
39
    };
×
40
    _process.env = (new Proxy(old, handler)) as Record<string, string>;
×
41
}
×
42
const maxLines =
1✔
43
    _process.env?.NODEOPCUA_DEBUG_MAXLINE_PER_MESSAGE
1✔
44
        ? parseInt(_process.env.NODEOPCUA_DEBUG_MAXLINE_PER_MESSAGE, 10)
1!
45
        : 25;
1✔
46
let g_logLevel: LogLevel = process.env.NODEOPCUA_LOG_LEVEL
1✔
47
    ? (parseInt(process.env.NODEOPCUA_LOG_LEVEL, 10) as LogLevel)
1!
48
    : LogLevel.Warning;
1✔
49

1✔
50
export function setLogLevel(level: LogLevel): void {
×
51
    g_logLevel = level;
×
52
}
×
53

1✔
54
function extractBasename(name: string): string {
2,186✔
55
    if (!name) {
2,186!
56
        return "";
×
57
    }
×
58
    // return basename(name).replace(/\.(js|ts)$/, "");
2,186✔
59
    return name.replace(/(.*[\\|/])?/g, "").replace(/\.(js|ts)$/, "");
2,186✔
60
}
2,186✔
61

1✔
62
function w(str: string, l: number): string {
2,768✔
63
    return str.padEnd(l, " ").substring(0, l);
2,768✔
64
}
2,768✔
65

1✔
66
interface Context {
1✔
67
    filename: string;
1✔
68
    callerline: number;
1✔
69
}
1✔
70

1✔
71
const contextCounter: Record<string, number> = {};
1✔
72
const increaseCounter = (context: Context) => {
1✔
73
    const { filename, callerline } = context;
1,487✔
74
    const key = `${filename}:${callerline}};`;
1,487✔
75
    const bucket = contextCounter[key];
1,487✔
76
    if (!bucket) {
1,487✔
77
        contextCounter[key] = 1;
201✔
78
        return 1;
201✔
79
    }
201✔
80
    contextCounter[key] = contextCounter[key] + 1;
1,286✔
81
    return contextCounter[key];
1,286✔
82
};
1,286✔
83

1✔
84
const threshold = 100;
1✔
85

1✔
86
type PrintFunc = (data?: unknown, ...argN: unknown[]) => void;
1✔
87
const loggers = {
1✔
88
    errorLogger: (context: Context, ...args: [unknown, ...unknown[]]) => {
1✔
89
        const occurrenceCount = increaseCounter(context);
251✔
90
        if (occurrenceCount > threshold) {
251!
91
            return;
×
92
        }
×
93
        const output = dump(context, "E", args);
251✔
94
        messageLogger.emit("errorMessage", output);
251✔
95
        if (occurrenceCount === threshold) {
251!
96
            dump(context, "E", [`This error occurred more than ${threshold} times, no more error will be logged for this context`]);
×
97
            return;
×
98
        }
×
99
    },
1✔
100
    warningLogger: (context: Context, ...args: [unknown, ...unknown[]]) => {
1✔
101
        const occurrenceCount = increaseCounter(context);
1,236✔
102
        if (occurrenceCount > threshold) {
1,236✔
103
            return;
572✔
104
        }
572✔
105
        const output = dump(context, "W", args);
664✔
106
        messageLogger.emit("warningMessage", output);
664✔
107
        if (occurrenceCount === threshold) {
1,236✔
108
            dump(context, "W", [
2✔
109
                `This warning occurred more than ${threshold} times, no more warning will be logged for this context`
2✔
110
            ]);
2✔
111
            return;
2✔
112
        }
2✔
113
    },
1✔
114
    traceLogger: (context: Context, ...args: [unknown, ...unknown[]]) => {
1✔
115
        dump(context, "T", args);
×
116
    },
1✔
117
    debugLogger: (context: Context, ...args: [unknown, ...unknown[]]) => {
1✔
118
        dump(context, "D", args);
×
119
    }
×
120
};
1✔
121

1✔
122
export function setDebugLogger(log: PrintFunc): void {
×
123
    loggers.debugLogger = log;
×
124
}
×
125
export function setWarningLogger(log: PrintFunc): void {
×
126
    loggers.warningLogger = log;
×
127
}
×
128
export function setErrorLogger(log: PrintFunc): void {
×
129
    loggers.errorLogger = log;
×
130
}
×
131
export function setTraceLogger(log: PrintFunc): void {
×
132
    loggers.traceLogger = log;
×
133
}
×
134
export function setDebugFlag(scriptFullPath: string, flag: boolean): void {
×
135
    const filename = extractBasename(scriptFullPath);
×
136
    if (sTraceFlag && sTraceFlag.length > 1 && flag) {
×
137
        const decoratedFilename = chalk.yellow(w(filename, 60));
×
138
        loggers.debugLogger(
×
139
            {
×
140
                filename: __filename,
×
141
                callerline: -1
×
142
            },
×
143
            " Setting debug for ",
×
144
            decoratedFilename,
×
145
            " to ",
×
146
            (flag ? chalk.cyan : chalk.red)(flag.toString(), sTraceFlag)
×
147
        );
×
148
        g_logLevel = LogLevel.Debug;
×
149
    }
×
150
    debugFlags[filename] = flag;
×
151
}
×
152

1✔
153
export function checkDebugFlag(scriptFullPath: string): boolean {
314✔
154
    const filename = extractBasename(scriptFullPath);
314✔
155
    let doDebug: boolean = debugFlags[filename];
314✔
156
    if (sTraceFlag && !Object.hasOwn(debugFlags, filename)) {
314!
157
        doDebug = sTraceFlag.indexOf(filename) >= 0 || sTraceFlag.indexOf("ALL") >= 0;
×
158
        setDebugFlag(filename, doDebug);
×
159
    }
×
160
    return doDebug;
314✔
161
}
314✔
162

1✔
163
/**
1✔
164
 * file_line return a 51 character string
1✔
165
 * @param filename
1✔
166
 * @param callerLine
1✔
167
 */
1✔
168
function file_line(mode: "E" | "D" | "W" | "T", filename: string, callerLine: number): string {
917✔
169
    const d = new Date().toISOString().substring(11);
917✔
170
    if (mode === "T") {
917!
171
        return chalk.bgGreenBright.white(`${w(d, 14)}:${w(filename, 30)}:${w(callerLine.toString(), 5)}`);
×
172
    } else if (mode === "W") {
917✔
173
        return chalk.bgCyan.white(`${w(d, 14)}:${w(filename, 30)}:${w(callerLine.toString(), 5)}`);
666✔
174
    } else if (mode === "D") {
917!
175
        return chalk.bgWhite.cyan(`${w(d, 14)}:${w(filename, 30)}:${w(callerLine.toString(), 5)}`);
×
176
    } else {
251✔
177
        return chalk.bgRed.white(`${w(d, 14)}:${w(filename, 30)}:${w(callerLine.toString(), 5)}`);
251✔
178
    }
251✔
179
}
917✔
180

1✔
181
const continuation = w(" ...                                                            ", 51);
1✔
182

1✔
183
function getCallerContext(level: number) {
1,487✔
184
    const stack: string = new Error("").stack || "";
1,487!
185
    // caller line number
1,487✔
186
    const l: string[] = stack.split("\n")[level].split(":");
1,487✔
187
    const callerline: number = parseInt(l[l.length - 2], 10);
1,487✔
188
    const filename: string = extractBasename(l[l.length - 3]);
1,487✔
189
    return { filename, callerline };
1,487✔
190
}
1,487✔
191

1✔
192
function dump(ctx: Context, mode: "E" | "D" | "W" | "T", args1: [unknown?, ...unknown[]]) {
917✔
193
    const a2 = Object.values(args1) as [string, ...string[]];
917✔
194
    const output = format(...a2);
917✔
195
    const { filename, callerline } = ctx;
917✔
196
    let a1 = [file_line(mode, filename, callerline)];
917✔
197
    let i = 0;
917✔
198
    for (const line of output.split("\n")) {
917✔
199
        const lineArguments = ([] as string[]).concat(a1, [line]);
1,610✔
200
        // eslint-disable-next-line prefer-spread
1,610✔
201
        console.log(...lineArguments);
1,610✔
202
        a1 = [continuation];
1,610✔
203
        i = i + 1;
1,610✔
204
        if (i > maxLines) {
1,610✔
205
            const a3 = a1.concat([` .... TRUNCATED ..... (NODEOPCUA_DEBUG_MAXLINE_PER_MESSAGE=${maxLines}`]);
2✔
206
            // eslint-disable-next-line prefer-spread
2✔
207
            console.log(...a3);
2✔
208
            break;
2✔
209
        }
2✔
210
    }
1,610✔
211
    return output;
917✔
212
}
917✔
213

1✔
214
export class MessageLogger extends EventEmitter {
1✔
215
    public on(eventName: "warningMessage" | "errorMessage", eventHandler: () => void): this {
1✔
216
        return super.on(eventName, eventHandler);
6✔
217
    }
6✔
218
}
1✔
219
export const messageLogger = new MessageLogger();
1✔
220

1✔
221
/**
1✔
222

1✔
223
 * @param scriptFullPath:string
1✔
224
 * @return returns a  debugLog function that will write message to the console
1✔
225
 * if the DEBUG environment variable indicates that the provided source file shall display debug trace
1✔
226
 *
1✔
227
 */
1✔
228
export function make_debugLog(scriptFullPath: string): (...arg: unknown[]) => void {
385✔
229
    const filename = extractBasename(scriptFullPath);
385✔
230
    function debugLogFunc(...args: [unknown?, ...unknown[]]) {
385✔
231
        if (debugFlags[filename] && g_logLevel >= LogLevel.Debug) {
386,952!
232
            const ctxt = getCallerContext(3);
×
233
            loggers.debugLogger(ctxt, ...args);
×
234
        }
×
235
    }
386,952✔
236
    return debugLogFunc;
385✔
237
}
385✔
238

1✔
239
function errorLogFunc(...args: [unknown?, ...unknown[]]) {
251✔
240
    if (g_logLevel >= LogLevel.Error) {
251✔
241
        const ctxt = getCallerContext(3);
251✔
242
        loggers.errorLogger(ctxt, ...args);
251✔
243
    }
251✔
244
}
251✔
245

1✔
246
export function make_errorLog(_context: string): PrintFunc {
202✔
247
    return errorLogFunc;
202✔
248
}
202✔
249

1✔
250
function warningLogFunc(...args: [unknown?, ...unknown[]]) {
1,236✔
251
    if (g_logLevel >= LogLevel.Warning) {
1,236✔
252
        const ctxt = getCallerContext(3);
1,236✔
253
        loggers.warningLogger(ctxt, ...args);
1,236✔
254
    }
1,236✔
255
}
1,236✔
256
export function make_warningLog(_context: string): PrintFunc {
294✔
257
    return warningLogFunc;
294✔
258
}
294✔
259

1✔
260
function traceLogFunc(...args: [unknown?, ...unknown[]]) {
×
261
    const ctxt = getCallerContext(3);
×
262
    loggers.traceLogger(ctxt, ...args);
×
263
}
×
264
export function make_traceLog(_context: string): PrintFunc {
10✔
265
    return traceLogFunc;
10✔
266
}
10✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc