Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to import console module #130

Closed
Tracked by #251
dreamorosi opened this issue Feb 12, 2024 · 8 comments
Closed
Tracked by #251

Unable to import console module #130

dreamorosi opened this issue Feb 12, 2024 · 8 comments
Assignees
Labels
documentation Improvements or additions to documentation enhancement New feature or request

Comments

@dreamorosi
Copy link
Member

Hi, we have received a report from a customer that LLRT is not compatible with Powertools for AWS Lambda (TypeScript) (aws-powertools/powertools-lambda-typescript#2050.

From an initial test we were able to narrow down the issue to the usage of the console module. Below a code snippet that results in an exception:

import { Console } from "node:console";

const con = new Console({ stdout: process.stdout, stderr: process.stderr });

export const handler = async (event: any) => {
  con.log("Hello, world!");
  return "Hello, world!";
};

Full error below:

  24-02-12T15:15:47.833Z        n/a     ERROR   ReferenceError: Error resolving module 'console' from '/var/task/index.mjs'
} stackTrace: [ '' ]or resolving module 'console' from '/var/task/index.mjs'',
INIT_REPORT Init Duration: 40.23 ms     Phase: init     Status: error   Error Type: Runtime.ExitError
  24-02-12T15:15:48.350Z        n/a     ERROR   ReferenceError: Error resolving module 'console' from '/var/task/index.mjs'
} stackTrace: [ '' ]or resolving module 'console' from '/var/task/index.mjs'',
INIT_REPORT Init Duration: 523.69 ms    Phase: invoke   Status: error   Error Type: Runtime.ExitError
START RequestId: 36e99bfc-806a-4326-963b-354ee212c545 Version: $LATEST
END RequestId: 36e99bfc-806a-4326-963b-354ee212c545
REPORT RequestId: 36e99bfc-806a-4326-963b-354ee212c545  Duration: 551.40 ms     Billed Duration: 551 ms Memory Size: 128 MB     Max Memory Used: 21 MB  Status: error      Error Type: Runtime.ExitError

The console module is listed as supported in the compatibility matrix in your readme but doesn't appear in the API docs document.

Any chance that you could confirm whether the module is expected to work? If not, we'd like to raise a feature request for it to be eventually implemented on behalf of this customer.

@richarddavison richarddavison added bug Something isn't working enhancement New feature or request documentation Improvements or additions to documentation and removed bug Something isn't working labels Feb 12, 2024
@richarddavison
Copy link
Contributor

Thanks for the detailed report! Appreciate it!

console is currently not exportable and cannot be instantiated.
There are also a couple of properties on process that are currently not implemented like process.stdout and process.stderr.

In it's current state, console is only available globally and will only emit logs to stdout and errors to stderr.
However, this should be fixed in documentation.

Can the customer work around the issue by providing a shim for the console module (during bundling) which simply forwards to console methods?

@dreamorosi
Copy link
Member Author

dreamorosi commented Feb 12, 2024

Hi @richarddavison, thanks for the quick reply.

As it stands we (Powertools) rely on instantiating our own Console object because we need more control over the logging process than the one offered using the global console object.

In Node.js managed runtimes AWS Lambda patches the object to inject request specific info into logs (i.e. request ID and timestamp) as well as to offer features like Advanced Logging Control. Using the global object yields logs that look like this:

2022-04-08T10:58:44.811Z	b6511ee9-4873-404e-b60c-a23cb45d3ff2	INFO {"cold_start":false,"function_arn":"arn:aws:lambda:eu-west-2:12345:function:test","function_memory_size":1024,"function_name":"test","function_request_id":"b6511ee9-4873-404e-b60c-a23cb45d3ff2","level":"INFO","message":"test message","service":"test","timestamp":"2022-04-08T10:58:44.811Z"}

But our customers, including those who process their logs outside of CloudWatch, have requested that we emit logs like this:

{"cold_start":false,"function_arn":"arn:aws:lambda:eu-west-2:12345:function:test","function_memory_size":1024,"function_name":"test","function_request_id":"b6511ee9-4873-404e-b60c-a23cb45d3ff2","level":"INFO","message":"test message","service":"test","timestamp":"2022-04-08T10:58:44.811Z"}

So in response to this requirement we implemented this feature almost 2 years ago (aws-powertools/powertools-lambda-typescript#747).

I understand that at least for now the console object cannot be imported directly, however it'd be great if in the future this is supported provided there's customer demand.

@richarddavison
Copy link
Contributor

Hi @richarddavison, thanks for the quick reply.

As it stands we (Powertools) rely on instantiating our own Console object because we need more control over the logging process than the one offered using the global console object.

In Node.js managed runtimes AWS Lambda patches the object to inject request specific info into logs (i.e. request ID and timestamp) as well as to offer features like Advanced Logging Control. Using the global object yields logs that look like this:

2022-04-08T10:58:44.811Z	b6511ee9-4873-404e-b60c-a23cb45d3ff2	INFO {"cold_start":false,"function_arn":"arn:aws:lambda:eu-west-2:12345:function:test","function_memory_size":1024,"function_name":"test","function_request_id":"b6511ee9-4873-404e-b60c-a23cb45d3ff2","level":"INFO","message":"test message","service":"test","timestamp":"2022-04-08T10:58:44.811Z"}

But our customers, including those who process their logs outside of CloudWatch, have requested that we emit logs like this:

{"cold_start":false,"function_arn":"arn:aws:lambda:eu-west-2:12345:function:test","function_memory_size":1024,"function_name":"test","function_request_id":"b6511ee9-4873-404e-b60c-a23cb45d3ff2","level":"INFO","message":"test message","service":"test","timestamp":"2022-04-08T10:58:44.811Z"}

So in response to this requirement we implemented this feature almost 2 years ago (aws-powertools/powertools-lambda-typescript#747).

I understand that at least for now the console object cannot be imported directly, however it'd be great if in the future this is supported provided there's customer demand.

Ah I see, thanks for the clarification. In LLRT we don't patch the console object from JS, but do it in rust. There is a special LAMBDA_MODE being applied if _HANDLER env is present. That also deals with line endings so logs look better in CW.

if AWS_LAMBDA_MODE.load(Ordering::Relaxed) {

I see a couple of paths forward here:

  1. Expose a console class that can be imported and created, just like node. This however would require read and write streams as inputs which is a bit out of scope here. Or we can opt for a simpler implementation that differs from Node.js
  2. Expose printError on global scope. There is already a print that writes to stdout and a console.__format() that returns a string rather than prints to stdout/stderr. Then override the console prototype.
  3. Disable "metadata" for console output. Just applies line endings when in LAMBDA_MODE, does nothing with the output.

@dreamorosi
Copy link
Member Author

Thanks for the clear answer.

Given the scope of the project, I think the first option with a simpler implementation would be the best for us, but I understand we are not the only stakeholders here.

From a strictly Lambda-specific point of view, having working read/write streams for logs is not really needed, so having a simple Console class that uses the same signature, but then redirects the outputs wherever you're already redirecting them would be enough to increase compatibility for any code that uses said object.

@nerdoza
Copy link

nerdoza commented Feb 13, 2024

Can the customer work around the issue by providing a shim for the console module (during bundling) which simply forwards to console methods?

What is the recommendation to polyfill missing APIs like node:console and node:url?

@richarddavison
Copy link
Contributor

richarddavison commented Feb 13, 2024

Can the customer work around the issue by providing a shim for the console module (during bundling) which simply forwards to console methods?

What is the recommendation to polyfill missing APIs like node:console and node:url?

both console and URL are available in global scope:
llrt -e 'console.log(new URL("http://www.google.com"))'

See here for examples:

describe("URL class", () => {

There are a few browser specific APIs not implemented:
url.domainToASCII(domain)
url.domainToUnicode(domain)
url.fileURLToPath(url)
url.format(URL[, options])
url.pathToFileURL(path)
url.urlToHttpOptions(url)

@floydspace
Copy link
Contributor

I can relate, it is what I do to workaround the console issue.

diff --git a/lib/Logger.js b/lib/Logger.js
index c08d5a871ff20a7d3cbd9c387747761a85af25b9..a4e1bf90fa52dc5b0e1874637ba34abaab0a902a 100644
--- a/lib/Logger.js
+++ b/lib/Logger.js
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
 Object.defineProperty(exports, "__esModule", { value: true });
 exports.Logger = void 0;
 const node_crypto_1 = require("node:crypto");
-const node_console_1 = require("node:console");
+// const node_console_1 = require("node:console");
 const node_util_1 = require("node:util");
 const commons_1 = require("@aws-lambda-powertools/commons");
 const formatter_1 = require("./formatter");
@@ -692,15 +692,15 @@ class Logger extends commons_1.Utility {
      * @returns {void}
      */
     setConsole() {
-        if (!this.getEnvVarsService().isDevMode()) {
-            this.console = new node_console_1.Console({
-                stdout: process.stdout,
-                stderr: process.stderr,
-            });
-        }
-        else {
+        // if (!this.getEnvVarsService().isDevMode()) {
+        //     this.console = new node_console_1.Console({
+        //         stdout: process.stdout,
+        //         stderr: process.stderr,
+        //     });
+        // }
+        // else {
             this.console = console;
-        }
+        // }
     }
     /**
      * Sets the Logger's customer config service instance, which will be used

@fredbonin
Copy link
Contributor

Implemented :

Expose a console class that can be imported and created, just like node. This however would require read and write streams as inputs which is a bit out of scope here. Or we can opt for a simpler implementation that differs from Node.js

but using the simpler implementation that differs from Node.js for now.

This fixes the console issue but there are issues with node:crypto that I will look at next (ref #294)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

5 participants