log4 properties injection in grails Config.groovy - grails

I would like to inject log4j config stuff stored in a properties file in my Config.groovy.
Here is my properties file :
log.file=path/to/my/log
log.root.level=info
log.grails.app.level=info
No problem for file path EL syntax ${} but it doesn't work for levels as it is not strings. Here is the config.groovy :
appenders {
file name:'file', file:"${config.log.file}"
}
root {
${log.root.level} 'stdout', 'file'
}
Any advise ?

You have to read the properties file and convert it to a ConfigObject to use in Config.groovy.
log4j {
def props = new Properties()
new File("path/to/log.properties").withReader{
props.load(it)
}
def slurp = new ConfigSlurper().parse(props)
appenders {
file name:'file', file:"$slurp.log.file"
}
root {
"$slurp.log.root.level" 'stdout', 'file'
}
}
Refer this similar question.

If I'm reading the code of the Grails Log4J DSL parser correctly you should just be able to say
root {
"${config.config.log.root.level}" 'stdout', 'file'
}
or if that doesn't work then
root {
delegate."${config.config.log.root.level}" 'stdout', 'file'
}
Normally, within the log4j closure you have access to the complete grailsApplication.config (including options merged in from external configuration files) as the variable config but it looks like you need a second config in front of that when you're inside the root {} block.
Explanation - the closure delegate of the root closure is a RootLog4jConfig object, which has a property config pointing to the main Log4jConfig which is the delegate for the rest of the log4j closure, and that in turn has its own property config pointing to the parsed ConfigObject. I somehow doubt that this behaviour is deliberate, and it might be worth a JIRA to suggest that config should resolve to the same thing inside the root block as outside it.

Related

Jenkinsfile shared params in source control

I'm new to jenkins and inherited a bunch of declarative pipelines of unknown code quality. Each pipeline uses folder properties to set shared default param values. This puts essential variables outside of source control, which kills our PR process and our history for debugging. For example
//pipelineA/Jenkinsfile
pipeline {
parameters {
string name: 'important_variable', defaultValue: folderProperty('important_variable')
}
//etc
}
//pipelineB/Jenkinsfile
pipeline {
parameters {
string name: 'important_variable', defaultValue: folderProperty('important_variable')
}
//etc
}
Then in the root folder a property important_variable is set to "Hello World"
Is there a way to get this into source control either by setting the folder property to extract the variable from a yaml, or by using shared libraries?
Thank you for any help!
In case anyone reads this, we ended up:
Create a bootstrap.groovy file
This file MUST go in a /vars directory at the absolute top of your repo
Using the Jenkins UI we went to the pipeline's parent directory > config and created a shared library called config-lib that points at our repo and automatically exposes the bootstrap.groovy file methods as long as the file is in the right place
The bootstrap.groovy file has a call method that returns a map with key value pairs for our default parameters. This method has to be named call
In the Jenkinsfile for the pipeline we include the following two lines:
#Library("config-lib") _
config = bootstrap()
The library decorator (note it ends with _) imports the config-lib methods defined in the jenkins ui
The bootstrap function calls the call method from the bootstrap.groovy file in that config-lib library
in the Jenkinsfile use the config map to populate the param default values
pipeline {
parameters {
string name: 'foo', defaultValue: config.foo
}
And it's done.
This video helped immensely: https://youtu.be/Wj-weFEsTb0

Load an external properties file in grails

I am trying to load the properties file externally, by setting up the system environment.
In my config.groovy file,
println "Config file location --->" + System.getenv("SAM_ENV")
grails.config.locations = ["file:"+ System.getenv("SAM_ENV")]
I have set the system env SAM_ENV value as C:\test\config.properties.
When I try to run the application,I am getting the print value as
Config file location ---> C:\test\config.properties prints properly.
The problem is when I try to access the properties file in my controller as
print "PAGINATION1"+grailsApplication.config.PAGINATION1
the value of PAGINATION1 is not getting printed properly.
Can any one help me what configuration has to be done to access the external properties file in grails application.
Add the below line in config.groovy
grails.config.locations = [ "classpath:grails-app-config.properties"]
environments {
development {
grails.logging.jul.usebridge = true
grails.config.locations = ["file:C:\\conf\\externalfile.groovy"]
}
production {
grails.logging.jul.usebridge = false
grails.config.locations = ["file:/opt/config/externalfile.groovy"]
// TODO: grails.serverURL = "http://www.changeme.com"
}
}
If you want to access any property from external configuration(config.groovy) then just declare the property like
property = property value eg:(ImagePath = "C:\\Users\\Saved Pictures")
access it like grailsApplication.config."property"
eg:(grailsApplication.config.ImagePath)
NOTE: dont use def just a property and its value.
What you're looking for is extending the classpath, which you can achieve by adding a post compilation event in _Events.groovy. Try this:
eventCompileEnd = {
ant.copy(todir:classesDirPath) {
fileset(file:"C:\test\config.properties")
}}
You can find more help here

grails only my log

in my Config.groovy I have log4j configuration :
log4j = {
appenders {
file name:'connection', file: '/tmp/connection.log'
}
info "grails.app.controllers.myController"
root {
info 'connection'
}
how I can configure for getting only myController logs?
how I can configure for getting only myController logs?
You are currently setting the root logger to "info" level, thus the
info "grails.app.controllers.myController"
doesn't actually do anything (as this logger would inherit a level of info from the root anyway). If you want to disable all logging apart from that from myController then you need to set the root logger to "off" rather then info
root {
off 'connection'
}
If instead you want to put myController output into /tmp/connection.log and leave the other log messages on stdout as normal then you need
root {
info 'stdout'
}
info connection:'grails.app.controllers.myController', additivity:false
(or just leave out the root block entirely to get the default behaviour, which is the equivalent of saying root { error "stdout" }). The additivity:false tells the myController logger to log only to the appenders attached directly to it (your "connection" appender). Without this the messages would also go to the appenders inherited from the parent, i.e. the default "stdout" appender.
you should define appender specific loggers, e.g.
info connection: "grails.app.controllers.myController"
next you should remove your "connection" appender from the root block to avoid root logger inheritance.
for further information see the logging section in the grails documentation: http://grails.org/doc/latest/guide/conf.html#logging
try something like that:
log4j = {
def loggerPattern = '%d %-5p >> %m%n'
def errorClasses = [] // add more classes if needed
def infoClasses = ['grails.app.controllers.myController'] // add more classes if needed
def debugClasses = [] // add more classes if needed
appenders {
console name:'stdout', layout:pattern(conversionPattern: loggerPattern)
rollingFile name: "file", maxFileSize: 1024, file: "./tmp/logs/logger.log", layout:pattern(conversionPattern: loggerPattern)
}
error stdout: errorClasses, file: errorClasses
info stdout: infoClasses, file: infoClasses
debug stdout: debugClasses, file: debugClasses
}

Externalized grails.serverURL not accessible from Config.groovy

I have an application where the config is externalized. In Config.groovy, I'm updating
grails.config.locations=[file:/.../myapp-log4j.groovy, file:/.../myapp-config.properties]
That works fine for datasources and such. But later in Config.groovy, I have:
springws {
wsdl {
MyApp {
// In this case the wsdl will be available at <grails.serverURL>/services/v1/myapp/myapp-v1.wsdl
wsdlName= 'myapp-v1'
xsds= '/WEB-INF/myapp.xsd'
portTypeName = 'myappPort'
serviceName = 'myappService'
locationUri = "${grails.serverURL}/services/v1/myapp"
targetNamespace = 'http://www..../myapp/v1/definitions'
}
}
}
And ${grails.serverURL} contains [:] which is not what is in my config file. The config file contains (among the datasource details):
grails.serverURL=http://samiel:9011/xid
My guess would be that the updated grails.config.locations is only used after I return from Config.groovy.
So, what are my options to setup my web service details based on the externalized serverURL ?
This is what I get when I run your example (just confirming your starting postion):
def testExternalConfig() {
println "grails.serverURL: ${ConfigurationHolder.config.grails.serverURL}"
println "springws.wsdl.MyApp.locationUri ${ConfigurationHolder.config.springws.wsdl.MyApp.locationUri}"
}
--Output from testExternalConfig--
grails.serverURL: http://samiel:9011/xid
springws.wsdl.MyApp.locationUri http://localhost:8080/soGrails/services/v1/myapp
Like you said, Config.groovy does not see the value set in the external config. I believe that Grails processes external
configs after Config.groovy, and this test appears to confirm that. The logic being that you likely have external config file
values that you want to have precedence over config in war file.
Fix is to override the full property in myapp-config.properties:
grails.serverURL=http://samiel:9011/xid
springws.wsdl.MyApp.locationUri=http://samiel:9011/xid/services/v1/myapp
With that change I get this:
--Output from testExternalConfig--
grails.serverURL: http://samiel:9011/xid
springws.wsdl.MyApp.locationUri http://samiel:9011/xid/services/v1/myapp

Configuration of Grails plugin

I'm developing my first Grails plugin. It has to access a webservice. The Plugin will obviously need the webservice url. What is the best way to configure this without hardcoding it into the Groovy classes? It would be nice with different config for different environments.
You might want to Keep It Simple(tm). You may define the URL directly in Config.groovy -including per-environment settings- and access it from your plugin as needed using grailsApplication.config (in most cases) or a ConfigurationHolder.config object (See further details in the manual).
As an added bonus that setting may also be defined in standard Java property files or on other configuration files specified in grails.config.locations.
e.g. in Config.groovy
// This will be the default value...
myPlugin.url=http://somewhe.re/test/endpoint
environments {
production {
// ...except when running in production mode
myPlugin.url=http://somewhe.re/for-real/endpoint
}
}
later, in a service provided by your plugin
import org.codehaus.groovy.grails.commons.ConfigurationHolder
class MyPluginService {
def url = ConfigurationHolder.config.myPlugin.url
// ...
}
If its only a small (read: one item) config option, it might just be easier to slurp in a properties file. If there are some number of configuration options, and some of them should be dynamic, i would suggest doing what the Acegi Security plugin does - add a file to /grails-app/conf/plugin_name_config.groovy perhaps.
added bonus is that the user can execute groovy code to compute their configuration options (much better over using properties files), as well as being able to do different environments with ease.
check out http://groovy.codehaus.org/ConfigSlurper , which is what grails internally use to slurp configs like config.groovy.
//e.g. in /grails-app/conf/MyWebServicePluginConfig.groovy
somePluginName {
production {
property1 = "some string"
}
test {
property1 = "another"
}
}
//in your myWebServicePlugin.groovy file, perhaps in the doWithSpring closure
GroovyClassLoader classLoader = new GroovyClassLoader(getClass().getClassLoader())
ConfigObject config
try {
config = new ConfigSlurper().parse(classLoader.loadClass('MyWebServicePluginConfig'))
} catch (Exception e) {/*??handle or what? use default here?*/}
assert config.test.property1.equals("another") == true

Resources