Skip to content

Commit 8e92c4e

Browse files
Merge pull request #4679 from NativeScript/vladimirov/fix-logger-again
fix: logger fails to print null objects
2 parents 812d980 + 6256958 commit 8e92c4e

File tree

2 files changed

+102
-12
lines changed

2 files changed

+102
-12
lines changed

lib/common/logger/logger.ts

+20-11
Original file line numberDiff line numberDiff line change
@@ -154,31 +154,40 @@ export class Logger implements ILogger {
154154
}
155155

156156
private getLogOptionsForMessage(data: any[]): { data: any[], [key: string]: any } {
157-
const opts = _.keys(LoggerConfigData);
157+
const loggerOptionKeys = _.keys(LoggerConfigData);
158+
const dataToCheck = data.filter(el => {
159+
// objects created with Object.create(null) do not have `hasOwnProperty` function
160+
if (!!el && typeof el === "object" && el.hasOwnProperty && typeof el.hasOwnProperty === "function") {
161+
for (const key of loggerOptionKeys) {
162+
if (el.hasOwnProperty(key)) {
163+
// include only the elements which have one of the keys we've specified as logger options
164+
return true;
165+
}
166+
}
167+
}
158168

159-
const result: any = {};
160-
const cleanedData = _.cloneDeep(data);
169+
return false;
170+
});
161171

162-
// objects created with Object.create(null) do not have `hasOwnProperty` function
163-
const dataToCheck = data.filter(el => typeof el === "object" && el.hasOwnProperty && typeof el.hasOwnProperty === "function");
172+
const result: any = {
173+
data: _.difference(data, dataToCheck)
174+
};
164175

165176
for (const element of dataToCheck) {
166-
if (opts.length === 0) {
177+
if (loggerOptionKeys.length === 0) {
167178
break;
168179
}
169180

170-
const remainingOpts = _.cloneDeep(opts);
181+
const remainingOpts = _.cloneDeep(loggerOptionKeys);
171182
for (const prop of remainingOpts) {
172183
const hasProp = element && element.hasOwnProperty(prop);
173184
if (hasProp) {
174-
opts.splice(opts.indexOf(prop), 1);
185+
loggerOptionKeys.splice(loggerOptionKeys.indexOf(prop), 1);
175186
result[prop] = element[prop];
176-
cleanedData.splice(cleanedData.indexOf(element), 1);
177187
}
178188
}
179189
}
180190

181-
result.data = cleanedData;
182191
return result;
183192
}
184193

@@ -195,7 +204,7 @@ export class Logger implements ILogger {
195204
/*******************************************************************************************
196205
* Metods below are deprecated. Delete them in 6.0.0 release: *
197206
* Present only for backwards compatibility as some plugins (nativescript-plugin-firebase) *
198-
* use these methods in their hooks *
207+
* use these methods in their hooks *
199208
*******************************************************************************************/
200209

201210
out(...args: any[]): void {

lib/common/test/unit-tests/logger.ts

+82-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { Yok } from "../../yok";
22
import { Logger } from "../../logger/logger";
33
import * as path from "path";
4+
import * as util from "util";
45
import { assert } from "chai";
56
import * as fileSystemFile from "../../file-system";
7+
import { LoggerConfigData } from "../../../constants";
68

79
const passwordReplacement = "*******";
810
const debugTrace = ["debug", "trace"];
@@ -33,7 +35,11 @@ describe("logger", () => {
3335
fs = testInjector.resolve("fs");
3436
outputs = {
3537
debug: "",
36-
trace: ""
38+
trace: "",
39+
info: "",
40+
error: "",
41+
context: {},
42+
removedContext: {}
3743
};
3844

3945
const log4jsLogger = {
@@ -42,6 +48,18 @@ describe("logger", () => {
4248
},
4349
trace: (...args: string[]) => {
4450
outputs.trace += args.join("");
51+
},
52+
info: (...args: string[]) => {
53+
outputs.info += util.format.apply(null, args);
54+
},
55+
error: (...args: string[]) => {
56+
outputs.error += util.format.apply(null, args);
57+
},
58+
addContext(key: string, value: any): void {
59+
outputs.context[key] = value;
60+
},
61+
removeContext(key: string): void {
62+
outputs.removedContext[key] = true;
4563
}
4664
};
4765

@@ -139,4 +157,67 @@ describe("logger", () => {
139157
assert.deepEqual(outputs.trace, `${request}${requestBody}`, "logger.trace should not obfuscate body of request unless it is towards api/itmstransporter");
140158
});
141159
});
160+
161+
describe("info", () => {
162+
[undefined, null, false, "string value", 42, { obj: 1 }, ["string value 1", "string value 2"]].forEach(value => {
163+
it(`handles formatted message with '${value}' value in one of the args`, () => {
164+
logger.info("test %s", value);
165+
assert.equal(outputs.info, `test ${value}`);
166+
assert.deepEqual(outputs.context, {}, "Nothing should be added to logger context.");
167+
assert.deepEqual(outputs.removedContext, {}, "Removed context should be empty.");
168+
});
169+
170+
it(`handles formatted message with '${value}' value in one of the args and additional values passed to context`, () => {
171+
logger.info("test %s", value, { [LoggerConfigData.skipNewLine]: true });
172+
assert.equal(outputs.info, `test ${value}`);
173+
assert.deepEqual(outputs.context, { [LoggerConfigData.skipNewLine]: true }, `${LoggerConfigData.skipNewLine} should be set with value true.`);
174+
assert.deepEqual(outputs.removedContext, { [LoggerConfigData.skipNewLine]: true }, `Removed context should contain ${LoggerConfigData.skipNewLine}`);
175+
});
176+
});
177+
178+
it("sets correct context when multiple logger options are passed in one object", () => {
179+
logger.info("test", { [LoggerConfigData.skipNewLine]: true, [LoggerConfigData.useStderr]: true });
180+
assert.equal(outputs.info, "test");
181+
assert.deepEqual(outputs.context, { [LoggerConfigData.skipNewLine]: true, [LoggerConfigData.useStderr]: true });
182+
assert.deepEqual(outputs.removedContext, { [LoggerConfigData.skipNewLine]: true, [LoggerConfigData.useStderr]: true });
183+
});
184+
185+
it("sets correct context when multiple logger options are passed in multiple objects", () => {
186+
logger.info({ [LoggerConfigData.useStderr]: true }, "test", { [LoggerConfigData.skipNewLine]: true });
187+
assert.equal(outputs.info, "test");
188+
assert.deepEqual(outputs.context, { [LoggerConfigData.skipNewLine]: true, [LoggerConfigData.useStderr]: true });
189+
assert.deepEqual(outputs.removedContext, { [LoggerConfigData.skipNewLine]: true, [LoggerConfigData.useStderr]: true });
190+
});
191+
});
192+
193+
describe("printMarkdown", () => {
194+
it(`passes markdown formatted text to log4js.info method`, () => {
195+
logger.printMarkdown("header text\n==");
196+
assert.isTrue(outputs.info.indexOf("# header text") !== -1);
197+
});
198+
199+
it(`passes skipNewLine to log4js context`, () => {
200+
logger.printMarkdown("`coloured` text");
201+
assert.isTrue(outputs.info.indexOf("coloured") !== -1);
202+
assert.deepEqual(outputs.context, { [LoggerConfigData.skipNewLine]: true }, `${LoggerConfigData.skipNewLine} should be set with value true.`);
203+
assert.deepEqual(outputs.removedContext, { [LoggerConfigData.skipNewLine]: true }, `Removed context should contain ${LoggerConfigData.skipNewLine}`);
204+
});
205+
});
206+
207+
describe("error", () => {
208+
const errorMessage = "this is error message";
209+
it(`passes useStderr to log4js context`, () => {
210+
logger.error(errorMessage);
211+
assert.equal(outputs.error, errorMessage);
212+
assert.deepEqual(outputs.context, { [LoggerConfigData.useStderr]: true }, `${LoggerConfigData.useStderr} should be set with value true.`);
213+
assert.deepEqual(outputs.removedContext, { [LoggerConfigData.useStderr]: true }, `Removed context should contain ${LoggerConfigData.useStderr}`);
214+
});
215+
216+
it(`allows overwrite of useStderr`, () => {
217+
logger.error(errorMessage, { [LoggerConfigData.useStderr]: false });
218+
assert.equal(outputs.error, errorMessage);
219+
assert.deepEqual(outputs.context, { [LoggerConfigData.useStderr]: false }, `${LoggerConfigData.useStderr} should be set with value false.`);
220+
assert.deepEqual(outputs.removedContext, { [LoggerConfigData.useStderr]: true }, `Removed context should contain ${LoggerConfigData.useStderr}`);
221+
});
222+
});
142223
});

0 commit comments

Comments
 (0)