-
Notifications
You must be signed in to change notification settings - Fork 21
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
feat: make RequestLoggingMiddleware non-experimental and call toString on MiddlewareMessages #1268
Conversation
this.requestId, | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access | ||
request.constructor.name, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this used to just print MiddlewareMessage
rather than the request type
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there are probably a few other places in the middleware where calling constructorName()
will be more useful but haven't tracked them all down yet
return this._grpcMessage.constructor.name; | ||
} | ||
|
||
// Note: APIs that use streaming interceptors (e.g. GetBatch and SetBatch) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there were no middleware messages shown for GetBatch or SetBatch when I tested locally
const request = this._grpcMessage as cache.cache_client._SetRequest; | ||
return `SetRequest with key "${TEXT_DECODER.decode( | ||
request.cache_key | ||
)}" and value "${TEXT_DECODER.decode(request.cache_body)}" and ttl ${ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
request.cache_body
could contain megabytes of data. We used a truncateString()
utility method in the response classes to limit the amount of output from toString()
. I'm guessing in a situation where someone is using a middleware logger they're going to want to see absolutely everything, but I thought it might be worth discussion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that's a good callout. I think for now let's just leave it the way Anita has it (logging the whole item) and see if we get feedback.
request.cache_key | ||
)}" and value "${TEXT_DECODER.decode( | ||
request.cache_body | ||
)}" and condition "${request.condition}"`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is missing the ttl_milliseconds
. I think it might also be worthwhile to log the condition value here for requests that require one like present_and_not_equal
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
confirmed conditions are logged as strings
[2024-05-13T19:26:52.702Z] DEBUG (Momento: RequestLoggingMiddleware): Logging middleware: request 14 onRequestBody: request type: _SetIfRequest, request size: 44, details: {"key":"test-key","value":"test-field","condition":"present_and_not_equal","ttl_milliseconds":60000}
const request = this._grpcMessage as cache.cache_client._SetRequest; | ||
return `SetRequest with key "${TEXT_DECODER.decode( | ||
request.cache_key | ||
)}" and value "${TEXT_DECODER.decode(request.cache_body)}" and ttl ${ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that's a good callout. I think for now let's just leave it the way Anita has it (logging the whole item) and see if we get feedback.
|
||
// Note: APIs that use streaming interceptors (e.g. GetBatch and SetBatch) | ||
// will not see these debug messages | ||
toString(): string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thank you so much for doing all of this! i have a few suggestions, i apologize for not discussing this with you in more depth before you started on the ticket. If any of these seem too onerous, they are not urgent, and I can add a commit and some point to help out with the tedious parts. :)
- For detailed logs like this it's really valuable to structure the log output as JSON or some other format that is easy to parse programmatically. So, I know it's going to be repetitive, but I'd probably define an
interface
for each one, likeGetRequestLogFormat
, and then the implementation should create an instance of the interface and then callJSON.stringify
on it. - JSON.stringify might fail on binary data so we should test that briefly before doing all of the rest of the work, and if it does, decide what we want to do about it. I have a few ideas we can discuss if it happens.
- We will need a big switch statement no matter what, but I think it would be best if each case in the switch just called a single function to do the conversion for the given request/response type, so that we have a separate function that does the actual work for each conversion.
- It would be really awesome to have a unit test that would assert that we have defined the necessary conversion functions for all of the possible types. kinda like what we are doing here:
client-sdk-javascript/packages/core/test/unit/clients/imomentocache-interface.test.ts
Lines 15 to 19 in 0831824
const cacheInterfaceMembers = cacheInterface .getMethods() .map(m => m.getName()) .sort(); console.log(`Cache interface members: ${cacheInterfaceMembers.join(',')}`);
I know the list above is a lot of tedious changes so feel free to approach it incrementally in between other tasks, or ask for volunteers to help with some subset of the types, etc. (I am very willing to contribute!)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the feedback! And my bad for not checking in before diving in first haha 😅
Sounds like a good plan for revisions. It looks like JSON.stringify
would treat binary data like a list:
const binaryData = Uint8Array.of(108, 111, 108);
console.log(JSON.stringify({ data: binaryData }));
// prints '{"data":{"0":108,"1":111,"2":108}}'
But I think we'll be able to get around this using the TextDecoder when creating an instance of each *RequestLogFormat
interface:
console.log(JSON.stringify({ data: TEXT_DECODER.decode(binaryData) }));
// prints '{"data":"lol"}'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sounds good. TEXT_DECODER.decode might throw an error if the bytes aren't utf-8 though, so we might have to figure out what we want to do for those cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and no worries re: checking in, I should have put a note in the ticket description.
Current state of the PR:
|
Example output file from running docs examples snippets locally: |
…g on MiddlewareMessages
13050d3
to
3ff5131
Compare
rebased, now will pick up from where anita left off. |
25ced9e
to
8500cfc
Compare
I think I'm done with this for now. It's not perfect and probably warrants some discussion on Monday but I think it's probably a reasonable direction. |
// Note: APIs that use streaming interceptors (e.g. GetBatch and SetBatch) | ||
// will not see these debug messages | ||
toLogFormat(): RequestLog { | ||
const requestToLogConverter = CacheRequestToLogInterfaceConverter.get( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ooh much nicer than the giant switch statement
}); | ||
|
||
describe('supported cache request types', () => { | ||
it('should have a converter for all known cache request types', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🙌
Spent some time playing around with this pr this morning, I think it looks good. I really like that last test that was added to ensure we have logging for all request types. I wonder if it would be good in our canaries/integration tests to enable this middleware so we have it being tested across all of our apis in an environment on a regular cadence. One nit that I saw was the jsdoc for the class
Still has comments about how it is experimental.
Once we remove this text blurb I am on this pr |
@bruuuuuuuce I removed that part in cdf6b28 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addresses https://github.com/momentohq/dev-eco-issue-tracker/issues/811
Adds a
toString
method toMiddlewareMessage
that prints extra details forCacheClient
andLeaderboardClient
requests (as those are the only two with middlewares enabled for now).Affects only the NodeJS SDK, similar changes can be made in web sdk as desired.
Snippet of what the logs look like now:
I've also got a full output file of log statements from running the doc example snippets file with this middleware enabled, just let me know if you want it!