(Quick Reference)

7 Creating logs for services - Reference Documentation

Authors: Alejandro GarcĂ­a Granados

Version: 0.4

7 Creating logs for services

Auditing and logging is one of the most important things you must consider when building web applications. Grails injects the log object for you in every artifact (such as domain classes, controllers and services), but the Spring Aspect Oriented Programming approach tells you that it is not a clean way to manage the logging. We strongly recommend the use of Spring AOP for this task.

Sometimes, it is difficult to configure AOP in Grails. There are a lot of blog entries suggesting you how to do it, but currently there isn't a standard approach. With this in mind, Optimus plugin lets you generate the corresponding AOP logs for every service method.

You can achieve this by executing the create-logs command:

grails create-logs [domainClass]
Suppose you have a class named mypackage.Person. With the create-logs command, you will get the following log files:
  • src/groovy/mypackage/aop/PersonServiceList.groovy
  • src/groovy/mypackage/aop/PersonServiceCreate.groovy
  • src/groovy/mypackage/aop/PersonServiceUpdate.groovy
  • src/groovy/mypackage/aop/PersonServiceGet.groovy
  • src/groovy/mypackage/aop/PersonServiceDelete.groovy

You can generate each file with the following commands:

FileCommand
src/groovy/mypackage/aop/PersonServiceList.groovycreate-service-list-log
src/groovy/mypackage/aop/PersonServiceCreate.groovycreate-service-create-log
src/groovy/mypackage/aop/PersonServiceUpdate.groovycreate-service-update-log
src/groovy/mypackage/aop/PersonServiceGet.groovycreate-service-get-log
src/groovy/mypackage/aop/PersonServiceDelete.groovycreate-service-delete-log
If we open the src/groovy/mypackage/aop/PersonServiceList.groovy file, we will find something like this:
package mypackage.aop

import org.aspectj.lang.annotation.AfterReturning import org.aspectj.lang.annotation.AfterThrowing import org.aspectj.lang.annotation.Aspect import org.aspectj.lang.annotation.Before import org.aspectj.lang.annotation.Pointcut

import org.springframework.stereotype.Component

@Component @Aspect class PersonServiceList {

@Pointcut( value='execution(java.util.Map com.company.app.PersonService.list(..)) && bean(personService) && args(params)', argNames='params') public void list( Map params ) {}

@Before('list(params)') void before( Map params ) { log.info( "Begins request: ${params}" ) }

@AfterReturning( pointcut='list(java.util.Map)', returning='map') void afterReturning( Map map ) { log.info( "End of request: ${map}" ) }

@AfterThrowing( pointcut='list(java.util.Map)', throwing='e' ) void afterThrowing( Exception e ) { log.info("Error in request: ${e.class.simpleName} - ${e.message}) }

}

There are some annotations that does the magic:
  • Component. Tells Spring that this class should be considered as Spring bean. It is configured via grails-app/conf/Config.groovy in the grails.spring.bean.packages property (the plugin does it for you).
  • Aspect. Indicates that the class must be managed as an aspect.
  • Pointcut. Indicates the method to be logged.
  • Before. Indicates the method to be executed before the method indicated in the Pointcut annotation is invoked.
  • AfterReturning. Indicates the method to be executed after the method indicated in the Pointcut annotation returns a value.
  • AfterThrowing. Indicates the method to be executed if the method indicated in the Pointcut annotation throws an exception.

We strongly recommend you to read the Spring AOP documentation for a further explanation.
When you run the application and execute some actions, the logs will appear in the output console and they will be written in the log/activity file:

Listing items:

yyyy-MM-dd HH:mm:ss [thread] INFO  aop.PersonServiceList  - Begins request: [action:content, controller:person]
yyyy-MM-dd HH:mm:ss [thread] INFO  aop.PersonServiceList  - End of request: [items:[], total:0]
Creating items:
yyyy-MM-dd HH:mm:ss [thread] INFO  aop.PersonServiceCreate  - Begins request: com.company.app.Person : (unsaved)
yyyy-MM-dd HH:mm:ss [thread] INFO  aop.PersonServiceCreate  - End of request
Getting items:
yyyy-MM-dd HH:mm:ss [thread] INFO  aop.PersonServiceGet  - Begins request:1
yyyy-MM-dd HH:mm:ss [thread] INFO  aop.PersonServiceGet  - End of request: com.company.app.Person : 1
Updating items:
yyyy-MM-dd HH:mm:ss [thread] INFO  aop.PersonServiceUpdate  - Begins request: com.company.app.Person : 1
yyyy-MM-dd HH:mm:ss [thread] INFO  aop.PersonServiceUpdate  - End of request
Deleting items:
yyyy-MM-dd HH:mm:ss [thread] INFO  aop.PersonServiceDelete  - Begins request:com.company.app.Person : 1
yyyy-MM-dd HH:mm:ss [thread] INFO  aop.PersonServiceDelete  - End of request
The default output is poor friendly (It uses the toString default method). You can edit the log output according to your own needings.