1 | import { fileURLToPath, URL } from 'url';
|
2 |
|
3 | import { LogLevel } from '@stryker-mutator/api/core';
|
4 | import log4js from 'log4js';
|
5 |
|
6 | import { netUtils } from '../utils/net-utils.js';
|
7 |
|
8 | import { LoggingClientContext } from './logging-client-context.js';
|
9 | import { minLevel } from './log-utils.js';
|
10 |
|
11 | const enum AppenderName {
|
12 | File = 'file',
|
13 | FilterLevelFile = 'filterLevelFile',
|
14 | FilterLog4jsCategoryFile = 'filterLog4jsCategoryFile',
|
15 | Console = 'console',
|
16 | FilterLevelConsole = 'filterLevelConsole',
|
17 | StripAnsi = 'stripAnsi',
|
18 | FilterLog4jsCategoryConsole = 'filterLog4jsCategoryConsole',
|
19 | All = 'all',
|
20 | Server = 'server',
|
21 | }
|
22 |
|
23 | const layouts: { color: log4js.PatternLayout; noColor: log4js.PatternLayout } = {
|
24 | color: {
|
25 | pattern: '%[%r (%z) %p %c%] %m',
|
26 | type: 'pattern',
|
27 | },
|
28 | noColor: {
|
29 | pattern: '%r (%z) %p %c %m',
|
30 | type: 'pattern',
|
31 | },
|
32 | };
|
33 |
|
34 | type AppendersConfiguration = Partial<Record<AppenderName, log4js.Appender>>;
|
35 |
|
36 | const LOG_FILE_NAME = 'stryker.log';
|
37 | export class LogConfigurator {
|
38 | private static createMainProcessAppenders(consoleLogLevel: LogLevel, fileLogLevel: LogLevel, allowConsoleColors: boolean): AppendersConfiguration {
|
39 |
|
40 | const multiAppender = {
|
41 | type: fileURLToPath(new URL('../cjs/logging/multi-appender.js', import.meta.url)),
|
42 | appenders: [AppenderName.FilterLevelConsole],
|
43 | };
|
44 |
|
45 | const consoleLayout = allowConsoleColors ? layouts.color : layouts.noColor;
|
46 |
|
47 | let allAppenders: AppendersConfiguration = {
|
48 | [AppenderName.Console]: { type: 'stdout', layout: consoleLayout },
|
49 |
|
50 | [AppenderName.FilterLog4jsCategoryConsole]: { type: 'categoryFilter', appender: AppenderName.Console, exclude: 'log4js' },
|
51 | [AppenderName.FilterLevelConsole]: { type: 'logLevelFilter', appender: AppenderName.FilterLog4jsCategoryConsole, level: consoleLogLevel },
|
52 | [AppenderName.All]: multiAppender,
|
53 | };
|
54 |
|
55 |
|
56 | if (fileLogLevel.toUpperCase() !== LogLevel.Off.toUpperCase()) {
|
57 |
|
58 | const stripAnsiAppender = {
|
59 | type: fileURLToPath(new URL('../cjs/logging/strip-ansi-appender.js', import.meta.url)),
|
60 | appender: AppenderName.File,
|
61 | };
|
62 | const fileAppender: log4js.FileAppender = { type: 'file', filename: LOG_FILE_NAME, layout: layouts.noColor };
|
63 | const filterLog4sCategory: log4js.CategoryFilterAppender = { type: 'categoryFilter', appender: AppenderName.StripAnsi, exclude: 'log4js' };
|
64 | const filterFileAppender: log4js.LogLevelFilterAppender = {
|
65 | type: 'logLevelFilter',
|
66 | appender: AppenderName.FilterLog4jsCategoryFile,
|
67 | level: fileLogLevel,
|
68 | };
|
69 |
|
70 |
|
71 |
|
72 | allAppenders = {
|
73 | ...allAppenders,
|
74 | [AppenderName.File]: fileAppender,
|
75 | [AppenderName.StripAnsi]: stripAnsiAppender,
|
76 | [AppenderName.FilterLog4jsCategoryFile]: filterLog4sCategory,
|
77 | [AppenderName.FilterLevelFile]: filterFileAppender,
|
78 | };
|
79 |
|
80 | multiAppender.appenders.push(AppenderName.FilterLevelFile);
|
81 | }
|
82 |
|
83 | return allAppenders;
|
84 | }
|
85 |
|
86 | private static createLog4jsConfig(defaultLogLevel: LogLevel, appenders: AppendersConfiguration): log4js.Configuration {
|
87 | return {
|
88 | appenders,
|
89 | categories: {
|
90 | default: {
|
91 | appenders: [AppenderName.All],
|
92 | level: defaultLogLevel,
|
93 | },
|
94 | },
|
95 | };
|
96 | }
|
97 |
|
98 | |
99 |
|
100 |
|
101 |
|
102 |
|
103 | public static configureMainProcess(
|
104 | consoleLogLevel: LogLevel = LogLevel.Information,
|
105 | fileLogLevel: LogLevel = LogLevel.Off,
|
106 | allowConsoleColors = true
|
107 | ): void {
|
108 | const appenders = this.createMainProcessAppenders(consoleLogLevel, fileLogLevel, allowConsoleColors);
|
109 | log4js.configure(this.createLog4jsConfig(minLevel(consoleLogLevel, fileLogLevel), appenders));
|
110 | }
|
111 |
|
112 | |
113 |
|
114 |
|
115 |
|
116 |
|
117 |
|
118 |
|
119 |
|
120 |
|
121 | public static async configureLoggingServer(
|
122 | consoleLogLevel: LogLevel,
|
123 | fileLogLevel: LogLevel,
|
124 | allowConsoleColors: boolean
|
125 | ): Promise<LoggingClientContext> {
|
126 | const loggerPort = await netUtils.getFreePort();
|
127 |
|
128 |
|
129 | const appenders = this.createMainProcessAppenders(consoleLogLevel, fileLogLevel, allowConsoleColors);
|
130 | const multiProcessAppender: log4js.MultiprocessAppender = {
|
131 | appender: AppenderName.All,
|
132 | loggerPort,
|
133 | mode: 'master',
|
134 | type: 'multiprocess',
|
135 | };
|
136 | appenders[AppenderName.Server] = multiProcessAppender;
|
137 | const defaultLogLevel = minLevel(consoleLogLevel, fileLogLevel);
|
138 | log4js.configure(this.createLog4jsConfig(defaultLogLevel, appenders));
|
139 |
|
140 | const context: LoggingClientContext = {
|
141 | level: defaultLogLevel,
|
142 | port: loggerPort,
|
143 | };
|
144 | return context;
|
145 | }
|
146 |
|
147 | |
148 |
|
149 |
|
150 |
|
151 |
|
152 | public static configureChildProcess(context: LoggingClientContext): void {
|
153 | const clientAppender: log4js.MultiprocessAppender = { type: 'multiprocess', mode: 'worker', loggerPort: context.port };
|
154 | const appenders: AppendersConfiguration = { [AppenderName.All]: clientAppender };
|
155 | log4js.configure(this.createLog4jsConfig(context.level, appenders));
|
156 | }
|
157 |
|
158 | public static shutdown(): Promise<void> {
|
159 | return new Promise((res, rej) => {
|
160 | log4js.shutdown((err) => {
|
161 | if (err) {
|
162 | rej(err);
|
163 | } else {
|
164 | res();
|
165 | }
|
166 | });
|
167 | });
|
168 | }
|
169 | }
|