ionelmc / python-hunter

Hunter is a flexible code tracing toolkit.
https://python-hunter.readthedocs.io/
BSD 2-Clause "Simplified" License
796 stars 46 forks source link

Json output of trace #38

Open talwrii opened 6 years ago

talwrii commented 6 years ago

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:

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.

ionelmc commented 6 years ago

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 commented 6 years ago

And a cookbook entry with your usecase of course :)

talwrii commented 6 years ago

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.

ionelmc commented 6 years ago

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))