arjan
Hi,
I am writing the Elixir integration for AppSignal, an application metrics solution. As of such, I am looking for a way to decorate functions in a developer-friendly way, so that these functions are automagically wrapped with library calls to measure the time it took to execute them (and send that info off to the backend).
As instrumenting functions is a common task while analyzing an application’s performance I want to minimize the amount of work the developer has to do to add instrumentation. As of such I have found two ways to do the instrumentation, both of which have its drawbacks:
1 - an instrumented do .... end block, in which you define the functions example here. Drawback of this method is that inside the block, everything is indented one level deeper, causing all the code to change while you have in fact just added 2 lines of code;making merging code changes harder;
2 - replace def foo() by def_instrument foo() (or similar) and use Kernel.def (like suggested here); the downside of this method is that it just “feels” weird to not read def, plus editor syntax highlighting breaks.
Are there any alternatives to tackle this? Ideally my solution would be a (Python / Java) decorator kind of syntax like this:
@instrumented
def foo(bar) do
...
But I am not sure that this is technically possible. I would love some input from the community on this!
cheers,
Arjan
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #blog-post
- #elixir-ls
- #elixirconf-us
- #ai
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming











Showing Posts 1 to 10- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
OvermindDL1
I’d probably just make something that the user could use just via:
In your use you can hook all the functions that are listed by the tuple of the funName and funArity and use a macro to hook the def’s and instrument the ones that match the list. That seems like the easiest way to hook that. Could do an
:allor so for the funs to say to do everythingOr you could do something like this if you want to mark ‘at’ the function site:
Where that would basically do the same thing but you’d walk the ast looking for that attribute than an immediately following function declaration or so.
It would be entirely possible for someone to make a generic ‘function decorator’ macro module that works like that, could do things like
@decorate SomeModule.functionWrapperor so before the functions. Unsure if anyone’s made one yet, but eh?arjan
Yes this latest idea was how I envisioned implementing it, walking the AST looking for module attributes next to a def. However, I dont know where I should hook in to,
@before_compiledoes not allow you to alter the AST, iirc.. and__using__only allows you to add code to the end of the module.OvermindDL1
What about the
@on_definitionhook?You can always hook
defitself within the module with a macro and have it either call to the normal def or do special things based on information too (that would work well with the first style especially), or at least using it for the second style and renaming the function (adding a NONINSTRUMENTED_ or so to everything) then setting up trampolines with instrumenting or not at the end based on the annotations.arjan
Yes, overriding
defwith a macro seems possible however I still don’t know how to get the@attributes preceding thedef, because I’m not sure there is a way to get the entire AST tree in some kind of hook and alter it.I’m starting to lean to your first suggestion:
NobbZ
Use a generic callback for def, when it gets called, check if
@instrumentedis set as a module-attribute, if so, do your stuff and unset it. If it is not set just do nothing.There are some handy functions in the
Module-module to work with module attributes ((get|put|delete|register)_attribute/*)arjan
Thanks. But I guess I also need to check its position in the AST - basically misuse module attributes to check whether an attribute occurs just before a
def.Maybe if I put the decorators inside the function as the first thing, I can get away with just overriding
def:NobbZ
Why do you need it to be the very last thing before the function or the very first in it? If you are too strict, it will be impossible to compose such annotations.
Also, why should it matter if one writes variant A or B?
A:
B:
Of course you can exchange
@docwith@specor any other function level attribute or combinations thereof.PS: AFAIK you cant do just an empty attribute you need to assign something to it.
arjan
It doesnt need to be strictly the last attribute before the def but the attribute should be scoped to the function somehow, e.g. some defs need instrumentation, others dont:
I know about attribute definitions needing definition but inside the
__using__the attribute can get defined.NobbZ
You have missed some of my points.
When you define
@instrumentedin the using, it will get replaced by that value in the sourcecode on every occurence, this can make your sourcecode invalid.My proposal was to explicitely set it before functions, maybe specifying some extra stuff (similar to ExUnits
@tags). And unset it in the callback.arjan
Ah! That makes sense suddenly.
So @instrumented “blablah” just before a
def(which is an overridden macro); and then the macro does its AST magic, and unsets the attribute. Neat! I’ll try that.