13

I'm using Groovy 1.8.4, trying to get the name of the enclosing function...

def myFunction() {
  println functionName??
}

I've tried delegate, this, owner, Groovy complains no such objects found.

I also tried the Java hack new Exception().getStackTrace()[0].getMethodName(), but that just prints newInstance0

Arturo Herrero
  • 12,772
  • 11
  • 42
  • 73
raffian
  • 31,267
  • 26
  • 103
  • 174
  • Might I ask what you're trying to accomplish with the method name? – Jarred Olson Mar 02 '12 at 21:39
  • Output tracing...we're building a POC, code is changing frequently, we've got a method that prints a header when it starts a new function, we want the name to come from the function itself instead of passing it as an argument in case the function name changes – raffian Mar 02 '12 at 21:55
  • There's an example in http://stackoverflow.com/a/9417763/190201 as well as how to get the current line number and file name. – ataylor Mar 02 '12 at 23:13
  • @ataylor oh man, that's nasty! I'll deal without it for now, thx – raffian Mar 03 '12 at 00:27

5 Answers5

18
import org.codehaus.groovy.runtime.StackTraceUtils

def getCurrentMethodName(){
  def marker = new Throwable()
  return StackTraceUtils.sanitize(marker).stackTrace[1].methodName
}

def helloFun(){
   println( getCurrentMethodName() )
}

helloFun()

output:

helloFun
Robert Fey
  • 1,747
  • 22
  • 23
  • 1
    when the getCurrentMethodName() is called in a closure, it will show doCall(). i.e. myArray.each { e -> println getCurrentMethodName(); } – TX T Feb 12 '16 at 15:20
  • 1
    Would the index within `stackTrace[1]` always be `1`? – cellepo Jan 22 '21 at 01:14
  • 1
    @cellepo, the "call stack" are all functions you are currently in (including the calling function (and the calling function of that one, etc.). The number indicates how far up the stack you want to go. Using 0 would return "getCurrentMethodName". Using 1 would return "helloFun". Using 2 would return the name of the function that called helloFun. etc. You can read up on what the "call stack" is on https://en.wikipedia.org/wiki/Call_stack – Robert Fey Jan 22 '21 at 10:17
  • 1
    Thanks. Yeah I know what the call stack is; I just wanted to double-check the index here. btw, I think you meant "down the call stack", as stacks are top-down. Despite how the call stack may be visualized or printed in an IDE or stack trace, calls on the stack other than the current call are below the current call, as the current call is on the top of the stack. – cellepo Jan 30 '21 at 03:11
16

How about groovy's StackTraceUtils.sanitize? Here's a quick example:

import org.codehaus.groovy.runtime.StackTraceUtils

class A {
  def methodX() {
    methodY()
  }

  def methodY() {
    methodZ()
  }

  def methodZ() {
    def marker = new Throwable()
    StackTraceUtils.sanitize(marker).stackTrace.eachWithIndex { e, i ->
        println "> $i ${e.toString().padRight(30)} ${e.methodName}"
    }
  }

}

new A().methodX()

The output of the above when pasted into a standalone script test.groovy is as follows:

$ groovy test.groovy 
> 0 A.methodZ(test.groovy:13)      methodZ
> 1 A.methodY(test.groovy:9)       methodY
> 2 A.methodX(test.groovy:5)       methodX
> 3 A$methodX.call(Unknown Source) call
> 4 test.run(test.groovy:21)       run

the sanitize method filters out all groovy internal mumbo jumbo from the trace and the clean trace together with ...stackTrace.find { } should give you a decent start.

Matias Bjarland
  • 161
  • 1
  • 2
  • How would I modify methodZ so it returns the immediate calling functions, that it only returns "1 A.methodY(test.groovy:9) methodY" – Chris F Feb 11 '19 at 19:18
4

You can get to it via the stacktrace, I've been able to get it via:

groovy:000> def foo() { println Thread.currentThread().stackTrace[10].methodName }
===> true
groovy:000> foo()
foo
groovy:000> class Foo {                                                             
groovy:001>   def bar() { println Thread.currentThread().stackTrace[10].methodName }
groovy:002> }
===> true
groovy:000> new Foo().bar()
bar
doelleri
  • 19,232
  • 5
  • 61
  • 65
  • 6
    yeah but you kind of have to guess the stacktrace depth :) and it's not guaranteed to stay constant with future groovy releases – Liviu T. Mar 02 '12 at 22:04
  • 1
    To build off of @doelerri s Code. You could do something like this: `Thread.currentThread().stackTrace.find {it.className.startsWith("com.project...")}.methodName`. There you can do whatever you feel comfortable with to ensure that if the stacktrace depth doesn't stay consistent then you'll be getting your code. – Jarred Olson Mar 02 '12 at 22:12
  • @JarredOlson Aren't these just hacks? Surprising Groovy does not offer such a feature – raffian Mar 02 '12 at 22:17
  • @RaffiM: Since it seems like Groovy doesn't, you could always submit a JIRA. – doelleri Mar 02 '12 at 22:43
  • @All, Groovy is simply dependent on Java. This is not something you can do directly from java itself without the exact same hack. – Glstunna Sep 19 '14 at 18:31
1
@CompileStatic
class LogUtils {
    // can be called the Groovy or Java way
    public static String getCurrentMethodName(){
        StackTraceElement[] stackTrace = StackTraceUtils.sanitize(new Throwable()).stackTrace
        stackTrace[2].methodName != 'jlrMethodInvoke' ? stackTrace[2].methodName : stackTrace[3].methodName
    }
}
Bertl
  • 605
  • 5
  • 10
0

Today there are several ways to do this.

See: https://www.baeldung.com/java-name-of-executing-method

In my case, what worked best was this appoach:

new Object(){}.getClass().getEnclosingMethod().getName();