What are your favorite Grails debugging tricks?

DebuggingGrails

Debugging Problem Overview


Grails can be a bit of a bear to debug with its long stack dumps. Getting to the source of the problem can be tricky. I've gotten burned a few times in the BootStrap.groovy doing "def foo = new Foo(a: a, b: b).save()", for example. What are your favorite tricks for debugging Grails apps?

Debugging Solutions


Solution 1 - Debugging

Some general tips:

  • Clear stacktrace.log, do grails run-app, then open stacktrace.log in a viewer (I prefer less stacktrace.log on linux)... once in your viewer, search for .groovy and .gsp... that generally brings you to what you actually care about.
  • When a stacktrace refers to a line number in a GSP file, you should open that view in a browser with ?showSource in the query string, i.e. http://localhost:8080/myProject/myController/myAction?showSource... this shows the compiled GSP source, and all GSP line numbers in the stacktrace refer to the compiled GSP, not the actual GSP source
  • Always, always, always surround your saves with at least some minimal error handling.

Example:

try {
    if(!someDomainObject.save()) {
        throw new Exception ("Save failed")
    } 
} catch(Exception e) {
    println e.toString()
    // This will at least tell you what is wrong with
    // the instance you are trying to save
    someDomainObject.errors.allErrors.each {error ->
        println error.toString()
    }
}

Beyond that, a lot of it just comes down to recognizing stacktraces and error messages... a lot of the time, Grails is incredibly unhelpful in the error messages it gives you, but you can learn to recognize patterns, like the following:

  • Some of the hardest errors to make sense of are because you didn't run grails clean or grails upgrade... to avoid these problems, I always use the following on the command line to run grails: grails clean; yes | grails upgrade; grails run-app
  • If the error has to do with duplicate definitions of a class, make sure that you declare the package the class belongs to at the top of the class's file
  • If the error has to do with schema metadata, connection, socket, or anything like that, make sure your database connector is in lib/, make sure your permissions are correct both in DataSource.groovy and in the database for username, password, and host, and make sure that you know the ins and outs of your connector's version (i.e. mysql connector version 5.1.X has a weird issue with aliases that may require you to set useOldAliasMetadataBehavior=true on the url in DataSource.groovy)

And so on. There are a lot of patterns to learn to recognize.

Solution 2 - Debugging

  • To add to Chris King's suggestion on save, I wrote a reusable closure:

      Closure saveClosure = { domainObj ->
           if(domainObj.save())
               println "Domain Object $domainObj Saved"
           else
           {
                   println "Errors Found During Save of $domainObj!"
                   println domainObj.errors.allErrors.each {
                       println it.defaultMessage
                   }
           }
        }
    

Then you can just use it everywhere and it will take care of error reporting:

  def book = new Book(authorName:"Mark Twain")
  saveClosure(book)
  • Additionally, I use the debug plugin - it allows extra logging, and I added tag to the bottom of my main - that gives me a view of all the variables in session / request.

  • Runtime Logging plugin allows to enable logging at runtime.

  • While writing this answer, P6SPY plugin also seems like it could be useful - it logs all statements your app makes against the database by acting as a proxy.

  • Grails Console is also useful. I use it to interactively poke around and experiment with some code, which also comes in handy during debugging.

  • And of course, being able to step through Debugger is sweet. I switched to IntelliJ IDEA since it has the best Grails / Groovy support.

Solution 3 - Debugging

I once asked an experienced groovy developer about how he effectively debugged his applications. His answer:

> I write tests!

And he has a very good point: If your code has sufficient unit and integration tests, you will hardly ever need to debug anything. Plus you get to say smug things like that to your fellow developers...

For Grails:

Solution 4 - Debugging

To log exceptions with GrailsUtil.

try{
   ...
}catch (Exception e){
   log.error("some message", GrailsUtil.sanitize(e))
   ...
}

More info about sanitize.

Solution 5 - Debugging

I'm not sure if this can be done out-of-the-box, but in webapps I find it useful to have a "who am I?" facility in the various view files.

The idea is to emit a message into the rendered HTML, to identify the fragment. This is especially true when I am encountering an app for the first time.

In Grails, I do this with a custom tag. For example, consider list.gsp for a Student:

<g:debug msg="student list" />

Here is the code:

class MiscTagLib {
    def debug = { map ->
        if (grailsApplication.config.grails.views.debug.mode == true) {
            def msg = map['msg']
            out << "<h2>${msg}</h2><br/>"
        }
    }
}

The key is that you can leave those tags in there, if desired, as they only appear in when the mode is enabled in Config.groovy:

grails.views.debug.mode=true

Solution 6 - Debugging

adding this code To the Bootsrap.groovy:init will overwrite the save method and execute some other code as well, printing out error messages in this case.

class BootStrap {

    def grailsApplication

    def init = {servletContext ->
 
        grailsApplication.domainClasses.each { clazz ->
            clazz.clazz.get(-1)

            def gormSave = clazz.metaClass.getMetaMethod('save')

            clazz.metaClass.save = {->

                def savedInstance = gormSave.invoke(delegate)
                if (!savedInstance) {
                    delegate.errors.each {
                        println it
                    }
                }
                savedInstance
            }

            def gormSaveMap = clazz.metaClass.getMetaMethod('save', Map)
            clazz.metaClass.save = { Map m ->
                def savedInstance = gormSaveMap.invoke(delegate, m)
                if (!savedInstance) {
                    delegate.errors.each {
                        println it
                    }
                }
                savedInstance

            }

            def gormSaveBoolean = clazz.metaClass.getMetaMethod('save', Boolean)
            clazz.metaClass.save = { Boolean b ->
                def savedInstance = gormSaveBoolean.invoke(delegate, b)
                if (!savedInstance) {
                    delegate.errors.each {
                        println it
                    }
                }
                savedInstance


            }
        }
...
}

hope that helps someone :)

(i know its not really DRY)

ref: http://grails.1312388.n4.nabble.com/How-to-override-save-method-on-domain-class-td3021424.html

Solution 7 - Debugging

Looking at the source code! This has saved me so many times now! And now that the code is hosted at GitHub it's easier than ever. Just press "t" and start typing to find the class that you're looking for!

http://github.com/grails/grails-core

Solution 8 - Debugging

Here's some tricks collected by @groovymag from Grails people in twitter:

http://blog.groovymag.com/2009/02/groovygrails-debugging/

Solution 9 - Debugging

For simple applications I use println statement.It is very very easy trick.For complex applications use debug mode in intellij idea.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionanonView Question on Stackoverflow
Solution 1 - DebuggingChris KingView Answer on Stackoverflow
Solution 2 - DebuggingJean BarmashView Answer on Stackoverflow
Solution 3 - Debuggingmycro.beView Answer on Stackoverflow
Solution 4 - Debugginguser2427View Answer on Stackoverflow
Solution 5 - DebuggingMichael EasterView Answer on Stackoverflow
Solution 6 - Debuggingnils petersohnView Answer on Stackoverflow
Solution 7 - DebuggingKimbleView Answer on Stackoverflow
Solution 8 - DebuggingchanwitView Answer on Stackoverflow
Solution 9 - DebuggingDonXView Answer on Stackoverflow