llrt
llrt copied to clipboard
Unable to import `console` module
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.
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?
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.
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.
https://github.com/awslabs/llrt/blob/ee750d18feae0dc577184dd46effd6c9f3be808b/src/console.rs#L584
I see a couple of paths forward here:
- 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
- Expose
printError
on global scope. There is already aprint
that writes to stdout and aconsole.__format()
that returns a string rather than prints to stdout/stderr. Then override the console prototype. - Disable "metadata" for console output. Just applies line endings when in LAMBDA_MODE, does nothing with the output.
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.
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
?
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
andnode:url
?
both console and URL are available in global scope:
llrt -e 'console.log(new URL("http://www.google.com"))'
See here for examples: https://github.com/awslabs/llrt/blob/b01b7aab8a8182b05be57cfa297235250e58817f/tests/http.test.ts#L99
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)
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
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)