The question is lacking a very important detail: Why?
If you want this for debugging, then debug.sethook is probably the more fitting option. As Paul Kulchenko pointed out, debug.getinfo might return the wrong function name, but it also returns the function object itself, which you could use to look up a canonical name in a table. This could also double as a filter for what functions to monitor:
local watch = {}
debug.sethook(function()
local info = debug.getinfo(2)
if watch[info.func] then
print(watch[info.func], "was called")
-- This *should* not cause a recursion, as
-- the hook *should* be disabled inside the hook
-- callback.
end
end, "c")
-- Somewhere else in your code
watch[print] = "print"
print("foobar")
However, if you intend to use this for anything other than debugging, you should avoid debug.sethook whenever possible, as it may mess with other libraries that rely on debug hooks (like luacov to name an example); in this case, it'd be better to replace the function:
do local p = print
function print(...)
p("Print was called!")
return p(...)
end
end
but keep in mind that you shouldn't change the behavior of ahy functions or you might break code somewhere else. This also includes the runtime; if you make a very performant function do lots of heavy lifting, that might completely break performance in unexpected ways.
Also keep in mind that some libraries localize global functions that they expect to use. While this is generally very bad practice, there seems to be a myth that this magically makes Lua code faster (there's a reason for this myth, but in most instances, it's not true); this means your replacement has to happen before any of those libraries are loaded.
For that same reason, by the way, you shouldn't usually localize globals unless you know you need a persisteny reference to the original function as is the case with the p variable in the example above.