Log4jConfig.groovy
001 /*
002  * Copyright 2004-2012 the original author or authors.
003  *
004  * Licensed under the Apache License, Version 2.0 (the "License");
005  * you may not use this file except in compliance with the License.
006  * You may obtain a copy of the License at
007  *
008  *      http://www.apache.org/licenses/LICENSE-2.0
009  *
010  * Unless required by applicable law or agreed to in writing, software
011  * distributed under the License is distributed on an "AS IS" BASIS,
012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013  * See the License for the specific language governing permissions and
014  * limitations under the License.
015  */
016 package org.codehaus.griffon.runtime.logging
017 
018 import griffon.util.Environment
019 import griffon.util.Metadata
020 import org.apache.log4j.helpers.LogLog
021 import org.apache.log4j.varia.NullAppender
022 import org.apache.log4j.xml.XMLLayout
023 import org.codehaus.groovy.runtime.InvokerHelper
024 import org.apache.log4j.*
025 
026 /**
027  * Encapsulates the configuration of Log4j.
028  *
029  @author Graeme Rocher (Grails 1.1)
030  @since 0.9.2
031  */
032 class Log4jConfig {
033     static final DEFAULT_PATTERN_LAYOUT = new PatternLayout(
034             conversionPattern: '%d [%t%-5p %c{2%x - %m%n')
035 
036     static final LAYOUTS = [xml: XMLLayout, html: HTMLLayout, simple: SimpleLayout, pattern: PatternLayout]
037     static final APPENDERS = ["null": NullAppender, console: ConsoleAppender,
038             file: FileAppender, rollingFile: RollingFileAppender,
039             event: GriffonApplicationEventAppender]
040 
041     private appenders = [:]
042 
043     def methodMissing(String name, args) {
044         if (APPENDERS.containsKey(name&& args) {
045             def constructorArgs = args[0instanceof Map ? args[0[:]
046             if (!constructorArgs.layout) {
047                 constructorArgs.layout = DEFAULT_PATTERN_LAYOUT
048             }
049             def appender = APPENDERS[name].newInstance()
050             InvokerHelper.setProperties appender, constructorArgs
051             if (appender.name) {
052                 appenders[appender.name= appender
053             else {
054                 LogLog.error "Appender of type $name doesn't define a name attribute, and hence is ignored."
055             }
056             appender.activateOptions()
057             return appenders[name]
058         }
059 
060         if (LAYOUTS.containsKey(name&& args) {
061             return LAYOUTS[name].newInstance(args[0])
062         }
063 
064         if (isCustomEnvironmentMethod(name, args)) {
065             return invokeCallable(args[0])
066         }
067 
068         LogLog.error "Method missing when configuring log4j: $name"
069     }
070 
071     private boolean isCustomEnvironmentMethod(String name, args) {
072         Environment.current == Environment.CUSTOM &&
073                 Environment.current.name == name &&
074                 args && (args[0instanceof Closure)
075     }
076 
077     def environments(Closure callable) {
078         invokeCallable(callable)
079     }
080 
081     private invokeCallable(Closure callable) {
082         callable.delegate = this
083         callable.resolveStrategy = Closure.DELEGATE_FIRST
084         callable.call()
085     }
086 
087     def development(Closure callable) {
088         if (Environment.current == Environment.DEVELOPMENT) {
089             invokeCallable(callable)
090         }
091     }
092 
093     def production(Closure callable) {
094         if (Environment.current == Environment.PRODUCTION) {
095             invokeCallable(callable)
096         }
097     }
098 
099     def test(Closure callable) {
100         if (Environment.current == Environment.TEST) {
101             invokeCallable(callable)
102         }
103     }
104 
105     def configure(Closure callable = {}) {
106 
107         Logger root = Logger.getRootLogger()
108 
109         callable.delegate = this
110         callable.resolveStrategy = Closure.DELEGATE_FIRST
111 
112         try {
113             callable.call(root)
114             for (appender in appenders.values()) root.addAppender appender
115 
116             if (!root.allAppenders.hasMoreElements()) {
117                 def consoleAppender = createConsoleAppender()
118                 root.setLevel Level.ERROR
119                 appenders['stdout'] = consoleAppender
120 
121                 error 'org.codehaus.griffon'
122 
123                 root.addAppender appenders['stdout']
124             }
125             /*
126             Logger logger = Logger.getLogger("StackTrace")
127             logger.additivity = false
128             def fileAppender = createFullstackTraceAppender()
129             if (!logger.allAppenders.hasMoreElements()) {
130                 logger.addAppender fileAppender
131             }
132             */
133         }
134         catch (Exception e) {
135             LogLog.error "WARNING: Exception occured configuring log4j logging: $e.message"
136         }
137     }
138 
139     private createConsoleAppender() {
140         def consoleAppender = new ConsoleAppender(layout: DEFAULT_PATTERN_LAYOUT, name: "stdout")
141         consoleAppender.activateOptions()
142         appenders.console = consoleAppender
143         return consoleAppender
144     }
145 
146     private createFullstackTraceAppender() {
147         if (appenders.stacktrace) {
148             return appenders.stacktrace
149         }
150 
151         def fileAppender = new FileAppender(layout: DEFAULT_PATTERN_LAYOUT, name: "stacktraceLog")
152         if (Environment.current == Environment.DEVELOPMENT) {
153             def targetDir = Metadata.current.getGriffonWorkingDir()
154             if (targetDirtargetDir.mkdirs()
155             fileAppender.file = targetDir ? "${targetDir.absolutePath}/stacktrace.log" "stacktrace.log"
156         }
157         else {
158             fileAppender.file = "stacktrace.log"
159         }
160         fileAppender.activateOptions()
161         appenders.stacktrace = fileAppender
162         return fileAppender
163     }
164 
165     Logger root(Closure c) {
166         def root = Logger.getRootLogger()
167 
168         if (c) {
169             c.delegate = new RootLog4jConfig(root, this)
170             c.resolveStrategy = Closure.DELEGATE_FIRST
171             c.call()
172         }
173 
174         return root
175     }
176 
177     def appenders(Closure callable) {
178         callable.delegate = this
179         callable.resolveStrategy = Closure.DELEGATE_FIRST
180         callable.call()
181     }
182 
183     def appender(Map name, Appender instance) {
184         if (name && instance) {
185             String appenderName = name.values().iterator().next()
186             instance.name = appenderName
187             appenders[appenderName= instance
188             instance.activateOptions()
189         }
190     }
191 
192     def appender(Appender instance) {
193         if (instance && instance.name) {
194             appenders[instance.name= instance
195             instance.activateOptions()
196         }
197         else {
198             LogLog.error "Appender [$instance] is null or does not define a name."
199         }
200     }
201 
202     def off(Map appenderAndPackages) {
203         setLogLevelForAppenderToPackageMap(appenderAndPackages, Level.OFF)
204     }
205 
206     def fatal(Map appenderAndPackages) {
207         setLogLevelForAppenderToPackageMap(appenderAndPackages, Level.FATAL)
208     }
209 
210     def error(Map appenderAndPackages) {
211         setLogLevelForAppenderToPackageMap(appenderAndPackages, Level.ERROR)
212     }
213 
214     def warn(Map appenderAndPackages) {
215         setLogLevelForAppenderToPackageMap(appenderAndPackages, Level.WARN)
216     }
217 
218     def info(Map appenderAndPackages) {
219         setLogLevelForAppenderToPackageMap(appenderAndPackages, Level.INFO)
220     }
221 
222     def debug(Map appenderAndPackages) {
223         setLogLevelForAppenderToPackageMap(appenderAndPackages, Level.DEBUG)
224     }
225 
226     def trace(Map appenderAndPackages) {
227         setLogLevelForAppenderToPackageMap(appenderAndPackages, Level.TRACE)
228     }
229 
230     def all(Map appenderAndPackages) {
231         setLogLevelForAppenderToPackageMap(appenderAndPackages, Level.ALL)
232     }
233 
234     private setLogLevelForAppenderToPackageMap(appenderAndPackages, Level level) {
235 
236         def additivity = appenderAndPackages.additivity != null ? appenderAndPackages.remove('additivity') true
237 
238         appenderAndPackages?.each appender, packages ->
239             eachLogger(packages) { Logger logger ->
240                 logger.level = level
241                 if (appenders[appender]) {
242                     logger.addAppender appenders[appender]
243                     logger.additivity = additivity
244                 }
245                 else {
246                     LogLog.error "Appender $appender not found configuring logger ${logger.getName()}"
247                 }
248             }
249         }
250     }
251 
252     def eachLogger(packages, Closure callable) {
253         if (packages instanceof String || packages instanceof GString) {
254             Logger logger = Logger.getLogger(packages)
255             callable(logger)
256         }
257         else {
258             for (p in packages) {
259                 p = p?.toString()
260                 if (p) {
261                     Logger logger = Logger.getLogger(p)
262                     callable(logger)
263                 }
264             }
265         }
266     }
267 
268     def off(Object[] packages) {
269         eachLogger(packages) { logger -> logger.level = Level.OFF }
270     }
271 
272     def fatal(Object[] packages) {
273         eachLogger(packages) { logger -> logger.level = Level.FATAL }
274     }
275 
276     def error(Object[] packages) {
277         eachLogger(packages) { logger -> logger.level = Level.ERROR }
278     }
279 
280     def warn(Object[] packages) {
281         eachLogger(packages) { logger -> logger.level = Level.WARN }
282     }
283 
284     def info(Object[] packages) {
285         eachLogger(packages) { logger -> logger.level = Level.INFO }
286     }
287 
288     def debug(Object[] packages) {
289         eachLogger(packages) { Logger logger -> logger.level = Level.DEBUG }
290     }
291 
292     def trace(Object[] packages) {
293         eachLogger(packages) { logger -> logger.level = Level.TRACE }
294     }
295 
296     def all(Object[] packages) {
297         eachLogger(packages) { logger -> logger.level = Level.ALL }
298     }
299 
300     def removeAppender(String name) {
301         Logger.getRootLogger().removeAppender name
302     }
303 }
304 
305 class RootLog4jConfig {
306     Logger root
307     Log4jConfig config
308 
309     def RootLog4jConfig(root, config) {
310         this.root = root
311         this.config = config
312     }
313 
314     def debug(Object[] appenders = null) {
315         setLevelAndAppender(Level.DEBUG, appenders)
316     }
317 
318     private setLevelAndAppender(Level level, Object[] appenders) {
319         root.level = level
320         for (appName in appenders) {
321             Appender app
322             if (appName instanceof Appender) {
323                 app = appName
324             else {
325                 app = config.appenders[appName?.toString()]
326             }
327             if (app) {
328                 root.addAppender app
329             }
330         }
331     }
332 
333     def info(Object[] appenders = null) {
334         setLevelAndAppender(Level.INFO, appenders)
335     }
336 
337     def warn(Object[] appenders = null) {
338         setLevelAndAppender(Level.WARN, appenders)
339     }
340 
341     def trace(Object[] appenders = null) {
342         setLevelAndAppender(Level.TRACE, appenders)
343     }
344 
345     def all(Object[] appenders = null) {
346         setLevelAndAppender(Level.ALL, appenders)
347     }
348 
349     def error(Object[] appenders = null) {
350         setLevelAndAppender(Level.ERROR, appenders)
351     }
352 
353     def fatal(Object[] appenders = null) {
354         setLevelAndAppender(Level.FATAL, appenders)
355     }
356 
357     def off(Object[] appenders = null) {
358         setLevelAndAppender(Level.OFF, appenders)
359     }
360 
361     void setProperty(String s, Object o) {
362         root."$s" = o
363     }
364 }