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

Logging support #402

Closed
wants to merge 4 commits into from
Closed

Logging support #402

wants to merge 4 commits into from

Conversation

Slava
Copy link
Contributor

@Slava Slava commented Jun 9, 2016

This PR builds on top of #396 and provides a new option logFn that is called by GraphQL.js with the following events:

  • Resolver starts execution
  • Resolver finished execution
  • Resolver threw an error
  • Resolver's subtree starts execution (for a compound type)
  • Resolver's subtree finished execution
  • Resolver's subtree threw an error

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.

@Slava
Copy link
Contributor Author

Slava commented Jun 9, 2016

The tests pass locally. Can anyone please help me to understand why eslint complains about Flowtype type hints on Travis CI?

@leebyron
Copy link
Contributor

This is pretty sweet, I'm not sure why the flow errors are being introduced.

@leebyron
Copy link
Contributor

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.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.02%) to 98.31% when pulling abf51ce on meteor:logging-support into 572bbda on graphql:master.

@leebyron
Copy link
Contributor

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?

@leebyron
Copy link
Contributor

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.

@Slava
Copy link
Contributor Author

Slava commented Jun 10, 2016

Yeah, I am working on rebasing it. Will update soon.

@Slava
Copy link
Contributor Author

Slava commented Jun 10, 2016

@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.

@leebyron
Copy link
Contributor

The eslint errors look relevant actually. In a follow-up I unified the names of variables such that exePath is now path.

@Slava
Copy link
Contributor Author

Slava commented Jun 10, 2016

@leebyron oh, sorry! Seems like I didn't fetch after your follow ups second time. Should be good now.

@KyleAMathews
Copy link

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?

@Slava
Copy link
Contributor Author

Slava commented Jun 11, 2016

@KyleAMathews no, info is not the same as the context object, it is the same as the info object passed to a resolver function.

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.

@helfer
Copy link
Contributor

helfer commented Jun 11, 2016

@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.

@KyleAMathews
Copy link

@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.

Been hoping for this for a while :-) thanks @Slava! #109

@Slava
Copy link
Contributor Author

Slava commented Jun 17, 2016

The tests are failing on Travis because the eslint plugin was borked in recent versions (around 6 days ago). The workaround is to force a different version of the eslint plugin:

npm install babylon@6.8.0

@ghost ghost added the CLA Signed label Jul 12, 2016
Allows graphql to log whenever the executor enters or leaves a subtree;
an error occurs; or a resolver starts/ends execution.
@Slava
Copy link
Contributor Author

Slava commented Jul 13, 2016

@leebyron rebased the PR!

@ghost ghost added the CLA Signed label Jul 14, 2016
@leebyron
Copy link
Contributor

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.

@ghost ghost added the CLA Signed label Jul 21, 2016
@Slava
Copy link
Contributor Author

Slava commented Jul 21, 2016

@leebyron thanks for talking another look at this! I will be happy to look at alternative API designs.

@sibelius
Copy link

sibelius commented Mar 23, 2017

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 ?

@baer
Copy link
Contributor

baer commented Apr 20, 2017

@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.

@sibelius
Copy link

@baer what would be the benefit of "natively" approach from the apollo optics approach?

@baer
Copy link
Contributor

baer commented Apr 25, 2017

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.

Copy link
Contributor

@baer baer left a 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 = {
Copy link
Contributor

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',
Copy link
Contributor

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
Copy link
Contributor

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?

@chrisregnier
Copy link

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.

@BadgerBadgerBadgerBadger

Any updates on this?

@prantlf
Copy link

prantlf commented Aug 17, 2019

An example using the instrumentation could implement an output compatible with https://github.com/apollographql/apollo-tracing.

@langpavel
Copy link
Contributor

I'm heavily for logging support 👍 but honestly,
I'm don't understand code changes introduced.
This PR looks like bigger and interesting goal…
Please, explain all in details and then this may be accepted.

@vanthome
Copy link

This is desperately needed for debugging scenarios where single field resolvers fail not on the root level. Will this make it in?

@IvanGoncharov IvanGoncharov deleted the branch graphql:master January 27, 2021 11:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.