python-hunter icon indicating copy to clipboard operation
python-hunter copied to clipboard

Json output of trace

Open talwrii opened this issue 7 years ago • 5 comments

talwrii avatar May 23 '18 17:05 talwrii

One method of debugging can be to run code two ways and compare the logging output.

Such comparison is made a lot easier if the trace itself is machine-readable. This can do things like:

  • Deal with call re-ordering
  • Allow a more fuzzy type of comparison (e.g. values that change between runs)

There are no doubt other uses of a "trace as data" rather than a "trace a something to be read by a human".

As far as I can tell, this is not already implemented. (1, 2)

It looks relatively straight-forward to implement using the pluggable actions system and would consist of merely replace stream.write calls to stream.write(json.dumps).

Given this, there is not necessarily a strong argument for this feature being part of this tool, other than advertising, convenience and feature completeness. I suspect whether this should be implemented here (if it should be implemented) or in another project is probably largely a question of the maintainers project goals and aesthetics.

talwrii avatar May 23 '18 17:05 talwrii

From my perspective hunter's event object is only missing a fast way to get a dictionary with all the data (the pure python Event has a __dict__ but the cython one doesn't).

That would reduce your code to hunter.trace(action=lambda event: json.dump(event.as_dict(), fd=stream)). So an as_dict method on the event object that materializes all the fields.

ionelmc avatar May 23 '18 18:05 ionelmc

And a cookbook entry with your usecase of course :)

ionelmc avatar May 23 '18 18:05 ionelmc

only missing a fast way to get a dictionary with all the data

This seems like a neat, and generic way of doing things. Just thinking this logic through

Json formatting = dict formatting + json_serialisation
Do the dict formatting on the event object itself, turn the action into a lambda

I guess the whole functional "the interface is a function call" composibility thing is what makes this easier. It's kind of reminiscent of "fat model / thin controller", although a generalisation of this principle is split generic code out into function + a few layers as possible.

From a documentation point of view, you might still like to have

JsonAction = lambda stream: lambda event: json.dump(event.as_dict(), fd=stream)

because people sometimes prefer reading the source code to reading documentation.

The only downside of this approach is that it effectively fixes the format of event since as_dict() becomes de facto serialisation code. I read a book on enterprise bus design that went on upon decoupling serialisation from internal logic for loose coupling. On the other hand... I get the impression that the types of event isn't going to change much.

talwrii avatar May 23 '18 23:05 talwrii

I suppose there could be a builtin action for this, I'll think about it ...

But what interests me more is what do you do with this json, do you have some sort of json differ? Do you already have something implemented?

PS. You can do it right now with a bit of boilerplate, eg:

hunter.trace(action=lambda event: json.dump({
  key, getatttr(event, key) for key in (
    'function', 'module', 'lineno', 'stdlib', 'arg', 'kind', 'filename', 'source',
    'threadname', 'threadid', 'depth', 'calls',
  )
}), fd=stream))

ionelmc avatar May 24 '18 09:05 ionelmc