The project I'm working on has a lot of telemetry and debugging mechanisms built in that must be available in release, but MUST NOT be used by most parts of the system. I'd like to build a macro, to be called at the top of such methods, that would throw an exception if the method had not been called from one of a very limited group of modules.
For example, I have a debug module that I can use to manipulate system state from the c开发者_运维技巧onsole, and I have a web tool that allows admins to inspect and change the system. Some of the methods that are designed for their consumption (but are implemented in modules all over the system) are fine for occasional use, but would be crippling if someone made the mistake of calling them as part of normal operation.
EDIT: I've tried the lists:keysearch suggestion, below, and it works for any module that I can explicitly name, which is probably the best I'm ever going to manage. I have modules like web_foo, web_bar, web_foo_other, web_yippie_ki_yea_mf to match that I'm falling back on regexps for.
I can do something like the following. (I've not compiled this. I'm hoping that any answers you give will prevent me from ever putting this into production. =] )
try
%gotta do this to get the stacktrace:
throw(a)
catch
_ ->
Stack = erlang:get_stacktrace(),
{_, RegExp} = re:compile("webmanager", [multiline]),
case Match = re:run(Stack, RegExp) of
nomatch ->
throw(im_sorry_dave__im_afraid_i_cant_do_that);
{match, _} ->
ok
end
end
Since Stack is a list of tuples representing the method calls, you can use lists:keysearch(webmanager, 1, Stack)
to check if the module is in the stack trace.
From your description I can't see why not to isolate operations support functions and debugging ones behind different facade modules or processes. Those processes code could restrict callers based on their attributes, for example registered name (global, local, gproc) or node name.
-module(adm_facade).
-export([register_admin_mode_proc/0,
unregister_admin_mode_proc/0,
adm1/1
]).
register_admin_mode_proc() ->
register(admin_mode_proc, self()).
unregister_admin_mode_proc() ->
unregister(admin_mode_proc).
adm(X) ->
check_adm(),
some_other_module:adm(X).
check_adm() ->
Caller = self(),
case erlang:whereis(admin_mode_proc) of
Caller -> ok;
_ -> erlang:error({access_denied, Caller})
end.
Of course this limits admin functions to a single caller, but you can use gproc or your own registrar.
精彩评论