fullstack-build / tslog

📝 tslog - Universal Logger for TypeScript and JavaScript
https://tslog.js.org
MIT License
1.35k stars 63 forks source link

Bug: Cannot log URLs #247

Closed abirtley closed 1 year ago

abirtley commented 1 year ago

Describe the bug If you attempt to log a URL, you get a variety of errors, depending on whether the URL is a top level logged item, or a field inside a logged object. Either way, it fails.

To Reproduce Steps to reproduce the behavior:

import { Logger } from 'tslog';
process.version; //? 18.16.0
const logger = new Logger({});
logger.info(new URL('https://www.abc.def')); //? Cannot convert undefined or null to object
logger.info({ u: new URL('https://www.abc.def') }); //? TypeError: Cannot convert undefined or null to object at getPrototypeOf (<anonymous>) at URL.[nodejs.util.inspect.custom...

Expected behavior The URLs are logged without error.

Screenshots

image image

Additional context

Seems to be a reversion of https://github.com/fullstack-build/tslog/issues/93, but perhaps a different cause.

I have managed to hack around it with the following additions in BaseLogger.js (node_modules/tslog/dist/esm/BaseLogger.js). With this amended code, URL objects can be logged without issue.

    _recursiveCloneAndMaskValuesOfKeys(source, keys, seen = []) {
        if (seen.includes(source)) {
            return { ...source };
        }
        if (typeof source === "object" && source != null) {
            seen.push(source);
        }

// NEW FUNCTION isURL - JUST A PROOF OF CONCEPT REALLY
        function isUrl(candidate) {
            if (!candidate || typeof candidate !== 'object') {
                return false;
            }
            if (candidate.href !== undefined && candidate.hash !== undefined && candidate.pathname !== undefined) {
                return true;
            }
            return false;
        }

        return isBuffer(source)
            ? source
            : source instanceof Map
                ? new Map(source)
                : source instanceof Set
                    ? new Set(source)
                    : Array.isArray(source)
                        ? source.map((item) => this._recursiveCloneAndMaskValuesOfKeys(item, keys, seen))
                        : source instanceof Date
                            ? new Date(source.getTime())
                            : isError(source)
                                ? Object.getOwnPropertyNames(source).reduce((o, prop) => {
                                    o[prop] = keys.includes(this.settings?.maskValuesOfKeysCaseInsensitive !== true ? prop : prop.toLowerCase())
                                        ? this.settings.maskPlaceholder
                                        : this._recursiveCloneAndMaskValuesOfKeys(source[prop], keys, seen);
                                    return o;
                                }, this._cloneError(source))
                                // NEW CODE: IF THIS IS A URL, JUST RETURN IT AS A STRING. DO NOT RECURSE.
                                : isUrl(source)
                                ? source.toString()
                                : source != null && typeof source === "object"
                                    ? Object.getOwnPropertyNames(source).reduce((o, prop) => {
                                        o[prop] = keys.includes(this.settings?.maskValuesOfKeysCaseInsensitive !== true ? prop : prop.toLowerCase())
                                            ? this.settings.maskPlaceholder
                                            : this._recursiveCloneAndMaskValuesOfKeys(source[prop], keys, seen);
                                        return o;
                                    }, Object.create(Object.getPrototypeOf(source)))
                                    : ((source) => {
                                        this.settings?.maskValuesRegEx?.forEach((regEx) => {
                                            source = source?.toString()?.replace(regEx, this.settings.maskPlaceholder);
                                        });
                                        return source;
                                    })(source);

Node.js Version e.g. 18.16.0

OS incl. Version macOS 13.4.1 (c)

alexanderguy commented 1 year ago

I came here to report the same issue with tslog v4.8.2, on both OS X and Linux with two different versions of node. Running the above test case, here are the stack traces I'm seeing:

node:internal/url:668
        ObjectGetPrototypeOf(this[context]) !== URLContext.prototype) {
        ^

TypeError: Cannot convert undefined or null to object
    at getPrototypeOf (<anonymous>)
    at [nodejs.util.inspect.custom] (node:internal/url:668:9)
    at formatValue (node:internal/util/inspect:806:19)
    at inspect (node:internal/util/inspect:365:10)
    at formatWithOptionsInternal (node:internal/util/inspect:2273:40)
    at formatWithOptions (node:internal/util/inspect:2135:10)
    at transportFormatted (file://redacted/node_modules/tslog/dist/esm/runtime/nodejs/index.js:89:33)
    at Logger.log (file://redacted/node_modules/tslog/dist/esm/BaseLogger.js:104:19)
    at Logger.info (file://redacted/node_modules/tslog/dist/esm/index.js:20:22)
    at file://redacted/repro.mjs:4:8

Node.js v18.13.0

node:internal/url:815
    return this.#context.href;
                ^

TypeError: Cannot read private member #context from an object whose class did not declare it
    at get href [as href] (node:internal/url:815:17)
    at [nodejs.util.inspect.custom] (node:internal/url:756:21)
    at formatValue (node:internal/util/inspect:804:19)
    at inspect (node:internal/util/inspect:363:10)
    at formatWithOptionsInternal (node:internal/util/inspect:2297:40)
    at formatWithOptions (node:internal/util/inspect:2159:10)
    at transportFormatted (file://redacted/node_modules/.pnpm/tslog@4.8.2/node_modules/tslog/dist/esm/runtime/nodejs/index.js:89:33)
    at Logger.log (file://redacted/node_modules/.pnpm/tslog@4.8.2/node_modules/tslog/dist/esm/BaseLogger.js:104:19)
    at Logger.info (file://redacted/node_modules/.pnpm/tslog@4.8.2/node_modules/tslog/dist/esm/index.js:20:22)
    at file://redacted/repro.mjs:4:8

Node.js v20.2.0
terehov commented 1 year ago

It's solved in V4.9.0. Thanks for pointing it out.