-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Logging support #402
Logging support #402
Conversation
The tests pass locally. Can anyone please help me to understand why eslint complains about Flowtype type hints on Travis CI? |
This is pretty sweet, I'm not sure why the flow errors are being introduced. |
Ah, it looks like when the flow version was recently upgraded, it started catching new errors which are live in the library. I'll investigate. |
It's a bit hard to review this with the stacked PRs (I really wish Github had better support for this), but now that the previous has landed, perhaps you can rebase this? |
I also landed a few minor follow-ups after your PR, so hopefully that doesn't cause too much rebase pain. Sorry for that in advance. |
Yeah, I am working on rebasing it. Will update soon. |
@leebyron I updated the PR. Similar to the previous time, the eslint errors on Travis CI don't make much sense to me. Tests pass locally. |
The eslint errors look relevant actually. In a follow-up I unified the names of variables such that |
@leebyron oh, sorry! Seems like I didn't fetch after your follow ups second time. Should be good now. |
Is info the same thing as the context object? Or will it be possible to pass a request or transaction id that gets passed to this logFn? |
@KyleAMathews no, info is not the same as the context object, it is the same as the In the code as it is now, there is no way to do it unless you pass a log function that is aware of some higher-level context. |
@KyleAMathews There may be several requests processed concurrently at any given time, so I think the best way to do what you ask for is to create a new log function for every request, and generate the ID when you create that function. That's what I've been doing for tracing and timing executions in apollo server. |
@helfer ah that sounds very doable. I implemented http://lightstep.com/ recently (which is 💯 ) and manually instumented some really important flows but this will let me instrument everything which is really exciting. |
The tests are failing on Travis because the
|
Allows graphql to log whenever the executor enters or leaves a subtree; an error occurs; or a resolver starts/ends execution.
@leebyron rebased the PR! |
Thank you, @Slava! I haven't forgotten about this and want to give it proper focus very soon. Just for some context as to how I'd like to approach this, I'd like to take a step back and propose a handful of alternative logging APIs and have a discussion around the pros/cons of each. |
@leebyron thanks for talking another look at this! I will be happy to look at alternative API designs. |
I think this should be outside graphql-js core take a look on these resources: graphql-compose/graphql-perf#2 you can wrap all your resolvers to do logging and performance metrics on them what do you think @leebyron ? |
@sibelius Instrumenting resolvers can be really helpful but there are certain metrics, like knowing if a deprecated field is still being called by clients, that could be more easily accessed if GraphQL exposed some instrumentation APIs. For example, the Apollo Optics project accomplishes this by walking the schema and instrumenting it field by field which would be made much easier if GraphQL just supported instrumentation natively. |
@baer what would be the benefit of "natively" approach from the apollo optics approach? |
Instrumenting field-by-field in the way they have to do it is not terribly graceful. It would be an improvement if GraphQl provided a generic way to add higher order functions to all resolvers since in this case things like performance, timings, and API usage data is so foundational that it may not make sense for this to be implemented in user-land. |
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 PR is 👏. I'd love to help move it forward if you'd like another set of hands.
* of patent rights can be found in the PATENTS file in the same directory. | ||
*/ | ||
|
||
export const TAG = { |
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.
What's the purpose of logging RESOLVER_ERROR
or SUBTREE_ERROR
if you've already got the exeContext.errors
Array?
*/ | ||
|
||
export const TAG = { | ||
SUBTREE_START: 'SUBTREE_START', |
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.
Isn't a SUBTREE
just a non-leaf node? I don't understand what the reason to log here is since the RESOLVER
will log immediately after. Seems redundant but I'm probably missing something.
*/ | ||
export function graphql( | ||
schema: GraphQLSchema, | ||
requestString: string, | ||
rootValue?: mixed, | ||
contextValue?: mixed, | ||
variableValues?: ?{[key: string]: mixed}, | ||
operationName?: ?string | ||
operationName?: ?string, | ||
logFn?: (tag: string, payload: mixed, info: mixed) => void |
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.
It seems like there are two main cases that this PR will need to account for:
- Timing information
- Inspection of poorly performing resolvers
Timings
Since the logging of *_START
and *_END
are separate, in order to measure resolver timings similar to what Apollo Optics does, this function should probably guarantee either a key like query:<path>:<fieldName>
or enough information about a resolver to construct one. I see that it accepts path (in some places) already, but that is likely not quite enough, especially if multiple queries are running at once.
Inspection
My guess is that it will eventually be useful to folks to log all sorts of information about poorly performing resolvers, much more than just the path or key. For example, if an Array resolver has a search/filter argument, it would be helpful to know which filters are running slowly. Rather than a payload and info of type mixed
, why not just pass the same or similar objects to what a resolver function receives?
This looks fantastic! One thing I'd suggest though is that the terminology seems a little odd. I think 'logFn' is a little specific and makes me think I should be providing a logger. I'd suggest something like 'onSignal' since firing signals seems more appropriate for the purpose here. |
Any updates on this? |
An example using the instrumentation could implement an output compatible with https://github.com/apollographql/apollo-tracing. |
I'm heavily for logging support 👍 but honestly, |
This is desperately needed for debugging scenarios where single field resolvers fail not on the root level. Will this make it in? |
This PR builds on top of #396 and provides a new option
logFn
that is called by GraphQL.js with the following events:This logging allows external users of the
graphql
module to tap into the execution time-line for profiling or logging/debugging.Note that this is not intended to be a logging system available from resolver's code. For that, we expect users of the library to pass their logging mechanism through the
context
property.