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

Add tracing of request events + mention LOG_LEVEL in README #315

Merged
merged 8 commits into from
May 15, 2024

Conversation

sebsto
Copy link
Contributor

@sebsto sebsto commented Dec 17, 2023

This PR adds a logger.trace() in LambdaRuntime just before calling user's handler.
It allows to see in the trace logs a string representation of the event passed by the AWS Service.

Motivation:

When there is no or poor documentation of the event passed to a lambda function, is it super hard to figure out the exact Codable struct to implement for the LambdaHandler.

With other programming languages, such as Python or Typescript, developers typically add a print(event) or console.log(event) at the start of the lambda handler function to figure out the exact type of data to deal with.

When using the Swift AWS Lambda Runtime, it's not possible because the bytes received are encoded to a struct before the user's code is called.

When there is a mismatch between the received bytes and the Codable struct, the user is left with very few information to figure out what was wrong. The encode() error messages are cryptic. They don't provide enough information to debug this type of error.

Modifications:

I added one line in LambdaRuntime to trace a string representation of the request before calling the user's handler.

            logger.trace("Request", metadata: ["bytes": .string(String(buffer: bytes))])

I also modified the README to document the use of the LOG_LEVEL environemnt variable.
While I was modifying the README, I noticed there was no mention of local test, I therefore added a small section about local testing (which is a subject related to this PR, hence a single PR for the two new sections in the README)

Result:

When invoking the runtime with LOG_LEVEL=trace, we receive information such as

Build complete! (15.97s)
2023-12-16T18:43:33-0500 info LocalLambdaServer : [AWSLambdaRuntimeCore] LocalLambdaServer started and listening on 127.0.0.1:7000, receiving events on /invoke
2023-12-16T18:43:33-0500 info Lambda : [AWSLambdaRuntimeCore] lambda runtime starting with LambdaConfiguration
  General(logLevel: trace))
  Lifecycle(id: 345519645762583, maxTimes: 0, stopSignal: TERM)
  RuntimeEngine(ip: 127.0.0.1, port: 7000, requestTimeout: nil
2023-12-16T18:43:33-0500 debug Lambda : lifecycleId=345519645762583 [AWSLambdaRuntimeCore] initializing lambda
2023-12-16T18:43:33-0500 debug Lambda : lifecycleIteration=0 [AWSLambdaRuntimeCore] lambda invocation sequence starting
2023-12-16T18:43:33-0500 debug Lambda : lifecycleIteration=0 [AWSLambdaRuntimeCore] requesting work from lambda runtime engine using /2018-06-01/runtime/invocation/next
2023-12-16T18:44:18-0500 debug Lambda : lifecycleIteration=0 [AWSLambdaRuntimeCore] sending invocation to lambda handler
2023-12-16T18:44:18-0500 trace Lambda : bytes={	"rawQueryString": "",	"headers": {		"host": "b2k1t8fon7.execute-api.us-east-1.amazonaws.com",	"x-forwarded-port": "443",		"content-length": "0",		"x-amzn-trace-id": "Root=1-6571d134-63dbe8ee21efa87555d59265",		"x-forwarded-for": "191.95.148.219",		"x-forwarded-proto": "https",		"accept": "*/*",		"user-agent": "curl/8.1.2"	},	"requestContext": {		"apiId": "b2k1t8fon7",		"http": {			"sourceIp": "191.95.148.219",			"userAgent": "curl/8.1.2",	"method": "POST",			"path": "/liveness/create",			"protocol": "HTTP/1.1"		},		"timeEpoch": 1701957940365,		"domainPrefix": "b2k1t8fon7",		"accountId": "012345678912",		"time": "07/Dec/2023:14:05:40 +0000",		"stage": "$default",		"domainName": "b2k1t8fon7.execute-api.us-east-1.amazonaws.com",		"requestId": "Pk2gOia2IAMEPOw=",		"authorizer": {		"iam": {				"accessKey": "ASIA-redacted",				"accountId": "012345678912",				"callerId": "AIDA-redacted",				"cognitoIdentity": null,				"principalOrgId": "aws:PrincipalOrgID",				"userArn": "arn:aws:iam::012345678912:user/sst",				"userId": "AIDA-redacted"			}		}	},	"isBase64Encoded": false,	"version": "2.0",	"routeKey": "$default",	"rawPath": "/liveness/create"} lifecycleIteration=0 [AWSLambdaRuntimeCore] Request
2023-12-16T18:44:21-0500 debug Lambda : lifecycleIteration=0 [AWSLambdaRuntimeCore] reporting results to lambda runtime engine using /2018-06-01/runtime/invocation/345564850687750/response
2023-12-16T18:44:21-0500 debug Lambda : lifecycleIteration=0 [AWSLambdaRuntimeCore] lambda invocation sequence completed successfully
2023-12-16T18:44:21-0500 debug Lambda : lifecycleIteration=1 [AWSLambdaRuntimeCore] lambda invocation sequence starting
2023-12-16T18:44:21-0500 debug Lambda : lifecycleIteration=1 [AWSLambdaRuntimeCore] requesting work from lambda runtime engine using /2018-06-01/runtime/invocation/next

Comment on lines 77 to 78
logger.trace("Request", metadata: ["bytes": .string(String(buffer: bytes))])
logger.debug("sending invocation to lambda handler")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should combine those two. debug is the right level imo.
What happens with payloads that aren't string representable. IIRC I can send any payload to lambda.

Copy link
Contributor Author

@sebsto sebsto Dec 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @fabianfett for the feedback.

I think we should combine those two. debug is the right level imo.

I like the idea of separating debug output from tracing. Trace adds more info, potentially long data trace, which is the case here.
But maybe it's a matter of personal preference.
I can see user preferences for having just the debug logs, while some others want to have the trace level for a limited period of time.
Given the potentially sensitive nature of the data contained in the payload (such as JWT tokens or PII), I think it is a good idea to decouple the debug and trace levels in this case.

What happens with payloads that aren't string representable

According to the AWS Lambda service API documentation, the payload is always a string
https://docs.aws.amazon.com/lambda/latest/dg/API_Invoke.html

Furthermore, the max payload size for synchronous invocations is 6Mb. They wont be huge payloads passed as input to the swift AWS Lambda Runtime.
https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can also do something as follow if we want to eliminate the two logging statements

if logger.logLevel <= .trace {
    logger.trace("...")
  } else {
    logger.debug("...")
  }
}

Copy link
Contributor

@tomerd tomerd Dec 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Furthermore, the max payload size for synchronous invocations is 6Mb

perhaps we want to trim it?

Copy link
Contributor

@MahdiBM MahdiBM left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly some nits 🙂

readme.md Outdated Show resolved Hide resolved
readme.md Outdated Show resolved Hide resolved
readme.md Outdated Show resolved Hide resolved
readme.md Show resolved Hide resolved
readme.md Outdated Show resolved Hide resolved
readme.md Outdated Show resolved Hide resolved
sebsto and others added 5 commits December 18, 2023 08:20
Co-authored-by: Mahdi Bahrami <github@mahdibm.com>
Co-authored-by: Mahdi Bahrami <github@mahdibm.com>
Co-authored-by: Mahdi Bahrami <github@mahdibm.com>
Co-authored-by: Mahdi Bahrami <github@mahdibm.com>
@sebsto
Copy link
Contributor Author

sebsto commented Dec 18, 2023

@tomerd wdyt ?
(probably worth to squash the commits :-) )

@sebsto
Copy link
Contributor Author

sebsto commented Dec 18, 2023

Thank you @MahdiBM for your suggestions - they are all in.

@sebsto
Copy link
Contributor Author

sebsto commented Apr 15, 2024

@tomerd should we reconsider merging this one too ?

@tomerd
Copy link
Contributor

tomerd commented Apr 15, 2024

@sebsto
Copy link
Contributor Author

sebsto commented Apr 15, 2024

@tomerd done.
I merged the two statements with a if logger.logLevel <= .trace as you suggested and I sliced the buffer to print only the first Kb

// when log level is trace or lower, print the first Kb of the payload
if logger.logLevel <= .trace, let buffer = bytes.getSlice(at: 0, length: max(bytes.readableBytes, 1024)) {
logger.trace("sending invocation to lambda handler",
metadata: ["1024 first bytes": .string(String(buffer: buffer))])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect String(buffer: buffer) may fail if buffer is not a string, please make sure it does not crash

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the AWS Lambda service API documentation, the payload is always a string
https://docs.aws.amazon.com/lambda/latest/dg/API_Invoke.html

@sebsto
Copy link
Contributor Author

sebsto commented May 13, 2024

Hello @tomerd Ar ewe good to merge with this last comment ? Or you still prefer code to enforce the content type ?

According to the AWS Lambda service API documentation, the payload is always a string
https://docs.aws.amazon.com/lambda/latest/dg/API_Invoke.html

@sebsto sebsto merged commit 2219782 into swift-server:main May 15, 2024
7 of 8 checks passed
@sebsto sebsto deleted the sebsto/request_tracing branch May 15, 2024 07:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants