views:

2274

answers:

8

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?

+1  A: 

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

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

chanwit
+7  A: 

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, i.e.:
    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.

Chris King
+6  A: 
  • 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.

Jean Barmash
A: 

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

BlackPanther
+4  A: 

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:

mycro.be
I'm glad to see we finally have a silver bullet.
WTF? So how did he debug the code when his tests failed? Or did his code never fail his tests? In which case was he only testing what he knew would work? This is a very weird thing to say and strikes me as tautological. Not to mention smug as you rightly say.
Simon
A: 

This is a question for Jean Barmash with respect to his saveDomain closure. In what file in Grails would I put that closure such that it can be used everywhere? I put it in Bootstrap.groovy and I'm only able to use it there? Thanks.

Brian Doyle
+1  A: 

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
Michael Easter
A: 

To log exceptions with GrailsUtil.

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

More info about sanitize.

damian