What are your favorite Grails debugging tricks? [closed]
Asked Answered
R

9

56

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?

Rancho answered 11/2, 2009 at 12:40 Comment(2)
Best question in recent memory...Plowman
@MichaelEaster seconded. It's really a shame that SO considers these kinds of KB articles as not constructive. They are as constructive as they come and the answer voting system provides an excellent system to collectively sort the tips from most to least useful, much more so than in a wiki-style KB.Furfuraceous
M
35

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.

Misapprehend answered 16/2, 2009 at 2:8 Comment(3)
"t/myController/myAction?showSource" Does this work in production mode? :)Obryan
Sorry nope - only development mode, not test or productionMisapprehend
I never really understood why save wouldn't failOnError by default -- I find that it is a completely unexpected behavior for every developer I know, that's why I want to grails.gorm.failOnError=true first thing when starting a new project.Lovegrass
H
16
  • 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.

Hypotenuse answered 16/2, 2009 at 21:19 Comment(2)
is saveClosure(x) really that groovy? Surely it'd be nicer to override the save() method?Chloride
Don't bother with p6spy any more. Use Spring Insight. It has all the functionality of p6spy without the configuration issues. Plus you can use it for performance tuning. Check out Peter Ledbrook's video. youtube.com/watch?v=rR-8EMKzduAExplicative
L
14

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:

Lacedaemonian answered 20/2, 2009 at 19:41 Comment(4)
I'm glad to see we finally have a silver bullet.Rancho
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.Halverson
Simon & the WTF response to writing tests... You could try adding logging to your tests.Explicative
@Halverson debugging is a lot easier when you can test one component at a time instead of the entire application.Blackfoot
S
7

To log exceptions with GrailsUtil.

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

More info about sanitize.

Sappington answered 12/10, 2010 at 18:27 Comment(0)
P
5

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
Plowman answered 2/1, 2010 at 1:48 Comment(0)
G
5

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

Grafton answered 5/12, 2011 at 16:22 Comment(0)
B
4

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

Breve answered 30/5, 2011 at 15:11 Comment(0)
A
2

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

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

Austral answered 12/2, 2009 at 0:36 Comment(0)
C
1

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

Cicisbeo answered 18/2, 2009 at 4:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.