Sending log messages from Grails BootStrap.groovy and plugin descriptors - grails

When I was introducing the Fixture module into my Grails application, I had trouble finding out how to send log messages from the application's main BootStrap.groovy and from the initialization code of my plugins.

I use the following log4j config in Config.groovy
log4j = {
appenders {
console name: 'consoleAppender', layout: pattern(conversionPattern: '%d{dd-MM-yyyy HH:mm:ss,SSS} %5p %c{2} - %m%n')
}
root {
// define the root logger's level and appenders, these will be inherited by all other loggers
error 'consoleAppender'
}
// change the default log level for classes in our app to DEBUG
def packageRoot = 'com.example.myapp'
def appNamespaces = [
packageRoot,
"grails.app.conf.$packageRoot",
"grails.app.filters.$packageRoot",
"grails.app.taglib.$packageRoot",
"grails.app.services.$packageRoot",
"grails.app.controllers.$packageRoot",
"grails.app.domain.$packageRoot",
"grails.app.conf.BootStrap"
]
// statements from the app should be logged at DEBUG level
appNamespaces.each { debug it }
}
The only change you should need to make is to set packageRoot to the root package of your app. The name/namespace of the logger that is assigned to BootStrap.groovy is grails.app.conf.BootStrap, so including this in appNamespaces ensures that it will log at the default level for the application (debug in the example above).
You don't have to do anything to get a logger instance in BootStrap.groovy, one is already provided by Grails with the name log, e.g.
class BootStrap {
def init = { servletContext ->
log.debug 'hello bootstrap'
}
}

In Grails 2.2.4:
The "log" logger is injected into the application's main BootStrap.groovy and into the plugin's descriptor (e.g.: FooGrailsPlugin.groovy)
The logger in the app's BootStrap.groovy has a name like "grails.app.BootStrap" so by enabling the appending of the "grails.app" logger in the configuration will allow displaying the messages sent through this logger.
The logger in the plugin descriptors has no package prefix, and named exactly as the descriptor class but without the groovy extension. E.g.: "FooGrailsPlugin", so it is not so easy to enable the log messages by the default injected logger. It doesn't help if you add a package definition into the top of plugin descriptor, it will not be used in the composition of the name of the logger.
Naturally, you can manually define a logger in the plugin descriptor (using a package name according to your needs) like this:
private static final log = LogFactory.getLog("yourapp.foo.FooGrailsPlugin")
After this, you can enable the "yourapp.foo" logger in the application and you will see the messages sent through the plugin descriptor's manually defined logger.

Related

Grails doesn't log plugins

I cannot make the standard logging mechanism work for plugins without the use of the #Slf4j or #CompileStatic annotations.
The main application's standard logging is functioning properly.
I can't figure out why it doesn't work without the annotation.
grailsVersion=4.1.1, groovyVersion=2.5.14
logbook.groovy is including the packages:
List<String> debug = [
'grails.app',
'foo', // My application and some plugins
'boo', // only grails plugins
]
debug.each {
logger(it, DEBUG, ['FILE'], false)
}

grails 3 - Update grails config during Plugin "doWithApplicationContext"

I've got a plugin that, during startup, reads some properties from the applications Config file, creates some domain objects and then needs to update the configuration with some additional information. However, it seems that the config object available during doWithApplicationContext is not the actual grailsApplication.config object.
For instance, attempting to do something straightforward in the MyPluginGrailsPlugin.groovy file like:
void doWithApplicationContext() {
grailsApplication.config.put('test', 'testValue')
}
does not update the config.
If this plugin is incldued in an application, at any point after startup, grailsApplication.config.getProperty('test') will return null.
How does one go about updating the config map during plugin startup?
NOTE: In grails 2, this used to work.
With this code snippet in MyPluginGrailsPlugin.groovy's doWithApplicationContext, new properties were successfully added into the application's config object.
ConfigObject myConfigObject = new ConfigSlurper().parse(props)
PropertySource propertySource = new MapPropertySource('grails.plugins.myPlugin', [:] << myConfigObject)
def propertySources = grailsApplication.mainContext.environment.propertySources
propertySources.addFirst propertySource
As an additional note: in doWithApplicationContext in my plugin, changing the config object like this worked in Grails 2 and no longer works in Grails 3.
grailsApplication.config.merge(myConfigObject)
grailsApplication.configChanged()

How can I reload the message updates in the messages.properties using Grails Localization plugin?

In Grails Localization plugin documentation, it says:
If you distribute your finished application in the form of a war file, then unless the target application server unzips the war file on installation, the localizations plugin will be unable to automatically load (or subsequently 'import') the properties files from within the war file.
In order to walk around to reload all the message updates using war, I use the following code in BootStrap.groovy.
import org.grails.plugins.localization.Localization
class BootStrap {
def init = { servletContext ->
Localization.reload()
}
}
This code does help me that whenever the app deploys to a Tomcat server, the message updates will apply. But as more and more releases get deployed, the localization table's id number increases automatically.
So I tried "truncate table" in my code to resolve the issue. In BootStrap.groovy:
import org.grails.plugins.localization.Localization
class BootStrap {
def sessionFactory
def init = { servletContext ->
sessionFactory.getCurrentSession().createSQLQuery('truncate table localization').executeUpdate()
Localization.load()
}
}
Using this code leads the run-app fails, because sometime, the truncate table happens in the middle of the load process, not before!
Did you try to execute it as a thread and force waiting for the end of that process?
Localization plugin has a LocalizationsBootStrap.groovy. Writing code in BootStrap.groovy leads to a race between two of the BootStrap files.
We can just do nothing in the BootStrap.groovy,but override the LocalizationsBootStrap.groovy by createing it under conf:
import org.grails.plugins.localization.*
class LocalizationsBootStrap {
def sessionFactory
def init = { servletContext ->
sessionFactory.getCurrentSession().createSQLQuery('truncate table localization').executeUpdate()
Localization.load()
}
}

logging my application's classes at DEBUG level, all others at WARN

I've configured my Grails app to read the log4j config from /conf/log4j.properties file instead of the more usual DSL in Config.groovy, by adding the following Spring bean:
log4jConfigurer(MethodInvokingFactoryBean) {
targetClass = "org.springframework.util.Log4jConfigurer"
targetMethod = "initLogging"
arguments = ["/conf/log4j.properties", 1000 * 60] // 2nd arg is refresh interval in ms
}
My goal is to log all the classes in the app itself at the DEBUG level, and all others at the WARN level. /conf/log4j.properties contains the following:
log4j.logger.com.myapp=DEBUG, CONSOLE
log4j.logger.grails.app=DEBUG, CONSOLE
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%-5p myapp %c{3} %d{HH:mm:ss,SSS} %t : %m%n
It seems the namespace com.myapp is used for regular classes in my app (e.g. those under src/groovy), whereas the namespace grails.app is used for Grails artefacts (controllers, services, taglibs, etc.). However the grails.app namespace also includes artefacts from plugins, which I don't want to log at the DEBUG level.
Is there a way to enable DEBUG logging only for the classes in my application?
You append your package unto the grails.app.controllers to get just your application.
info 'grails.app.controllers.mypackage'
Adding the following solved the problem
log4j.logger.grails.app.conf.com.myapp=DEBUG, CONSOLE
log4j.logger.grails.app.filters.com.myapp=DEBUG, CONSOLE
log4j.logger.grails.app.taglib.com.myapp=DEBUG, CONSOLE
log4j.logger.grails.app.services.com.myapp=DEBUG, CONSOLE
log4j.logger.grails.app.controllers.com.myapp=DEBUG, CONSOLE
log4j.logger.grails.app.domain.com.myapp=DEBUG, CONSOLE
Presumably if a Grails app has other types of artefacts that you want to log at the DEBUG level, an additional logger should be configured for each one.

Grails conversionPattern change at runtime

Using a standard log4j configuration for my grails app, with a custom conversion pattern like that :
log4j = {
appenders {
console name:'stdout', layout:pattern(conversionPattern: '[%-7p][%d{dd/MM/yyyy HH:mm:ss,SSS}] %C %m%n')
}
root {
warn 'stdout'
additivity = true
}
error 'org.grails.plugins.springsecurity'
error 'org.codehaus.groovy.grails.web.servlet' // controllers
// ...
warn 'org.mortbay.log',
'org.apache.tomcat',
'org.apache.tomcat.util.digester'
debug 'grails.app'
}
My grails app start as expected .. with the good conversionPattern ... but only during few log lines ... to finally fallback to the default grails conversionPattern ... :-/
Any idea ?
I don't code in Grails but I do know log4j very well.
On the surface, it seems you need to inspect those lines that aren't formatted as expected. Chances are, they are not caught by the logger that uses your stdout appender.
From what I can piece together, it looks to me like maybe your warning logger is the only one that uses your stdout appender. Meaning anything other than warnings would not format as expected. Further, it's also possible that loggers present in your libraries catch some log statements but not others.
Basically, the best way to solve this is to modify your pattern to give you good information on your loggers (I would suggest replacing the %C pattern, which is very slow to execute, with %c to see the exact category that was used by the logger). Then, look at the differences between what's properly formatted and everything else.
Do they have a common level (info, debug, error, etc)?
Do they stem from the same package?
Are they 3rd party library calls?
Figure out what they have in common and you will find your error.

Resources