This file is indexed.

/usr/share/doc/liblogback-java/manual/groovy.html is in liblogback-java-doc 1:1.0.0-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    <title>Chapter12: Groovy configuration</title>
    
    <link rel="stylesheet" type="text/css" href="../css/common.css" />
    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen" />
    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print" />
    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen" />

  </head>
  <body onload="prettyPrint()">

    <script type="text/javascript">prefix='../'</script>
    <script type="text/javascript" src="../js/prettify.js"></script>
    <script src="../templates/header.js" type="text/javascript"></script>
    <script type="text/javascript" src="../js/dsl.js"></script>
    <div id="left">
      <noscript>Please turn on Javascript to view this menu</noscript>
      <script src="../templates/left.js" type="text/javascript"></script>
    </div>
    <div id="right">
      <script src="menu.js" type="text/javascript"></script>
    </div>
    <div id="content" class="chapter">
      
      <h1>Chapter 12: Groovy Configuration</h1>
      
      <div class="quote">
      <p><em>It is better to be a human being dissatisfied than a pig
      satisfied; better to be a Socrates dissatisfied than a fool
      satisfied. And if the fool or the pig thinks otherwise, it is
      because they have no experience of the better part.
      </em>
      </p>
      <p>&mdash;JOHN STUART MILL, <em>Utilitarianism</em></p>
    </div>
    <script src="../templates/creative.js" type="text/javascript"></script>


    <p>Domain-specific languages or DSLs are rather pervasive. The
    XML-based logback configuration can be viewed as a DSL
    instance. By the very nature of XML, XML-based configuration files
    are quite verbose and rather bulky. Moreover, a relatively large
    body of code in logback, namely Joran, is dedicated to processing
    these XML-based configuration files. Joran supports nifty features
    such as variable substitution, conditional processing and
    on-the-fly extensibility. However, not only is Joran a complex
    beast, the user-experience it provides can be described as
    unsatisfactory or at the very least unintuitive.
    </p>

    <p>The Groovy-based DSL described in this chapter aims to be
    consistent, intuitive, and powerful. Everything you can do using XML in
    configuration files, you can do in Groovy with a much shorter
    syntax. To help you migrate to Groovy style configuration, we have
    developed a <a
    href="http://logback.qos.ch/translator/asGroovy.html">tool to
    automatically migrate your existing <em>logback.xml</em> files to
    <em>logback.groovy</em></a>.
    </p>


    <h2>General philosophy</h2>
    
    <p>As a general rule, <em>logback.groovy</em> files are Groovy
    programs. And since Groovy is a super-set of Java, whatever
    configuration actions you can perform in Java, you can do the same
    within a <em>logback.groovy</em> file. However, since configuring
    logback programmatically using Java syntax can be cumbersome, we
    added a few logback-specific extensions to make your life
    easier. We try hard to limit the number of logback-specific
    syntactic extensions to an absolute minimum. If you are already
    familiar with Groovy, you should be able to read, understand and
    even write your own <em>logback.groovy</em> files with great
    ease. Those unfamiliar with Groovy should still find
    <em>logback.groovy</em> syntax much more comfortable to use than
    <em>logback.xml</em>.
    </p>

    <p>Given that <em>logback.groovy</em> files are Groovy programs
    with minimal logback-specific extensions, <em>all</em> the usual
    groovy constructs such as class imports, variable definitions,
    evaluation of $&#123;..&#125; expressions contained in strings
    (GStrings), and if-else statements are available in
    <em>logback.groovy</em> files.</p>

    <h2>Extensions specific to <em>logback.groovy</em></h2>

    <p><span class="green">Essentially, <em>Logback.groovy</em> syntax
    consists of half a dozen methods described next; in the reverse
    order of their customary appearance. </span>Strictly speaking, the
    order of invocation of these methods does NOT matter, with one
    exception: appenders MUST be defined before they can be attached
    to a logger.</p>

    <!-- ========================================================== -->

    <h3> &#8226; <span class="code">root(Level level, List&lt;String> appenderNames = [])</span></h3>

    <p>The <code>root</code> method can be used to set the level of
    the root logger. As an optional second argument of type
    <code>List&lt;String></code>, can be used to attach previously
    defined appenders by name. If you do not specify the list of
    appender names, then an empty list is assumed. In Groovy, an empty
    list is denoted by <code>[]</code>.</p>

    <p>To set the level of the root logger to WARN, you would write:</p>

    <pre class="prettyprint source">import static ch.qos.logback.classic.Level.WARN

root(WARN)</pre>

    <p>To set the level of the root logger to INFO, and attach 
    appenders named "CONSOLE" and "FILE" to root, you would write:</p>

    <pre class="prettyprint source">import static ch.qos.logback.classic.Level.INFO

root(INFO, ["CONSOLE", "FILE"])</pre>

    <p>In the previous example, it is assumed that the appenders named
    "CONSOLE" and "FILE" were already defined. Defining appenders will
    be discussed shortly.
    </p>

    <!-- ========================================================== -->

    <h3>&#8226; <span class="code">logger(String name, Level level, List&lt;String> appenderNames = [], <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Boolean additivity = null)</span></h3>

    <p>The <code>logger()</code> method takes four arguments, of which
    the last two are optional. The first argument is the name of the
    logger to configure. The second argument is the level of the
    designated logger. Setting the level of a logger to
    <code>null</code> forces it to <a
    href="architecture.html#effectiveLevel">inherit its level</a> from
    its nearest ancestor with an assigned level. The third argument of
    type <code>List&lt;String></code> is optional and defaults to an
    empty list if omitted. The appender names in the list are attached
    to the designated logger. The fourth argument of type
    <code>Boolean</code> is also optional and controls the <a
    href="architecture.html#additivity">additivity flag</a>. If
    omitted, it defaults to <code>null</code>.
    </p> 

    <p>For example, the following script sets the level of the
    "com.foo" logger to INFO.</p>

       <pre class="prettyprint source">import static ch.qos.logback.classic.Level.INFO

logger("com.foo", INFO)</pre>

    <p>The next script sets the level of the "com.foo" logger to
    DEBUG, and attaches the appender named "CONSOLE" to it.</p>

  <pre class="prettyprint source">import static ch.qos.logback.classic.Level.DEBUG

logger("com.foo", DEBUG, ["CONSOLE"])</pre>
    
   <p>The next script is similiar to the previous one, except that it
   also sets the the additivity flag of the "com.foo" logger to
   false.</p>

  <pre class="prettyprint source">import static ch.qos.logback.classic.Level.DEBUG

logger("com.foo", DEBUG, ["CONSOLE"], false)</pre>


    <!-- ========================================================== -->
    <h3>&#8226; <span class="code">appender(String name, Class clazz, Closure closure = null)</span></h3>

    <p>The appender method takes the name of the appender being
    configured as its first argument. The second mandatory argument is
    the class of the appender to instantiate. The third argument is a
    closure containing further configuration instructions. If omitted,
    it defaults to null.</p>

    <p>Most appenders require properties to be set and sub-components
    to be injected to function properly. Properties are set using the
    '=' operator (assignment). Sub-components are injected by invoking
    a method named after the property and passing that method the
    class to instantiate as an argument. This convention can be
    applied recursively to configure properties as well as
    sub-components of any appender sub-component. This approach is at
    the heart of <em>logback.groovy</em> scripts and is probably the
    only convention that needs learning.</p>
    
    <p>For example, the following script instantiates a
    <code>FileAppender</code> named "FILE", setting its <span
    class="option">file</span> property to "testFile.log" and its
    <span class="option">append</span> property to false. An encoder
    of type <code>PatternLayoutEncoder</code> is injected into the
    appender. The pattern property of the encoder is set to "%level
    %logger - %msg%n". The appender is then attached to the root
    logger.</p>

    <pre class="prettyprint source">import ch.qos.logback.classic.encoder.PatternLayoutEncoder 
import ch.qos.logback.core.FileAppender

import static ch.qos.logback.classic.Level.DEBUG

appender("FILE", FileAppender) {
  file = "testFile.log"
  append = true
  encoder(PatternLayoutEncoder) {
    pattern = "%level %logger - %msg%n"
  }
}

root(DEBUG, ["FILE"])</pre>

    <p>
    </p>

    
    <!-- ========================================================== -->        
    <h3>&#8226; <span class="code">timestamp(String datePattern, long timeReference = -1)</span></h3>

    <p>The <code>timestamp()</code> method method returns a string
    corresponding to the <code>timeReference</code> parameter
    formatted according to the <code>datePattern</code> parameter. The
    <code>datePattern</code> parameter should follow the conventions
    defined by <a
    href="http://java.sun.com/j2se/1.4.2/docs/api/java/text/SimpleDateFormat.html">SimpleDateFormat</a>. If
    the <code>timeReference</code> value is unspecified, it defaults
    to -1, in which case current time, that is time when the
    configuration file is parsed, is used as the time
    reference. Depending on the circumstances, occasion, you might
    wish to use <code>context.birthTime</code> as the time reference.
    </p>

    <p>In the next example, the <code>bySecond</code> variable is
    assigned the current time in the "yyyyMMdd'T'HHmmss" format. The
    "bySecond" variable is then used to define the value of the <span
    class="option">file</span> property.
    </p>

<pre class="prettyprint source">import
ch.qos.logback.classic.encoder.PatternLayoutEncoder import
ch.qos.logback.core.FileAppender

import static ch.qos.logback.classic.Level.DEBUG

<b>def bySecond = timestamp("yyyyMMdd'T'HHmmss")</b>

appender("FILE", FileAppender) {
  <b>file = "log-${bySecond}.txt"</b>
  encoder(PatternLayoutEncoder) {
    pattern = "%logger{35} - %msg%n"
  }
}
root(DEBUG, ["FILE"])</pre>

    <!-- ========================================================== -->        
    <h3>&#8226; <span class="code">conversionRule(String conversionWord, Class converterClass)</span></h3>

    <p>After creating your own <a
    href="layouts.html#customConversionSpecifier">conversion
    specifier</a>, you need to inform logback of its existence. Here
    is a sample logback.groovy file which instructs logback to use
    MySampleConverter whenever the <code>%sample</code> conversion
    word is encountered.
    </p>

    <pre class="prettyprint source">
import ch.qos.logback.classic.encoder.PatternLayoutEncoder
import ch.qos.logback.core.ConsoleAppender
import chapters.layouts.MySampleConverter

import static ch.qos.logback.classic.Level.DEBUG

conversionRule("sample", MySampleConverter)
appender("STDOUT", ConsoleAppender) {
  encoder(PatternLayoutEncoder) {
    pattern = "%-4relative [%thread] %<b>sample</b> - %msg%n"
  }
}
root(DEBUG, ["STDOUT"])</pre>

   <!-- ========================================================== -->
   <h3>&#8226; <span class="code">scan(String scanPeriod = null)</span></h3>

    <p>Invoking the scan() method instructs logback to periodically
    scan the logback.groovy file for changes. Whenever a change is
    detected, the <em>logback.groovy</em> file is reloaded.</p>

    <pre class="prettyprint source">scan()</pre>

    <p>By default, the configuration file will be scanned for changes
    once every minute. You can specify a different scanning period by
    passing a "scanPeriod" string value. Values can be specified in
    units of milliseconds, seconds, minutes or hours. Here is an
    example:
    </p>

    <pre class="prettyprint source">scan("30 seconds")</pre>
    
    <p>If no unit of time is specified, then the unit of time is
    assumed to be milliseconds, which is usually inappropriate. If you
    change the default scanning period, do not forget to specify a
    time unit. For additional details on how scanning works, please
    refer to the <a href="configuration.html#autoScan">section on
    automatic reloading</a>.
    </p>
    
    <!-- ========================================================== -->
  
    <h3>&#8226; <span class="code">statusListener(Class listenerClass)</span></h3>

    <p>You can add a status listener by invoking the
    <code>statusListener</code> method and passing a listener class as
    an argument. Here is an example:</p>

    <pre class="prettyprint source">import ch.qos.logback.core.status.OnConsoleStatusListener

<b>// We highly recommended that you always add a status listener just</b>
<b>// after the last import statement and before all other statements</b>
<b>statusListener(OnConsoleStatusListener)</b></pre>
 
    <p><a href="configuration.html#statusListener">Status listeners</a> were described in an earlier
    chapter.</p>



    <!-- ========================================================== -->

    <h2>Internal DSL, i.e. it's all groovy baby!</h2>

    <p>The <em>logback.groovy</em> is an internal DSL meaning that its
    contents are executed as a Groovy script. Thus, all the usual
    Groovy constructs such as class imports, GString, variable
    definitions, evaluation of $&#123;..&#125; expressions contained
    within strings (GStrings), if-else statements are all available in
    logback.groovy files. In the following discussion, we will present
    typical uses of these Groovy constructs in <em>logback.groovy</em>
    files.
    </p>


    <h3>Variable definitions and GStrings</h3>

    <p>You can define variables anywhere within a
    <em>logback.groovy</em> file, then use the variable within a
    GString. Here is an example.</p>

    <pre class="prettyprint source">import ch.qos.logback.classic.encoder.PatternLayoutEncoder
import ch.qos.logback.core.FileAppender

import static ch.qos.logback.classic.Level.DEBUG


// define the USER_HOME variable setting its value 
// to that of the "user.home" system property
<b>def USER_HOME = System.getProperty("user.home")</b>

appender("FILE", FileAppender) {
  // make use of the USER_HOME variable
  <b>file = "${USER_HOME}/myApp.log"</b>
  encoder(PatternLayoutEncoder) {
    pattern = "%msg%n"
  }
}
root(DEBUG, ["FILE"])</pre>


    <h3>Printing on the console</h3>

    <p>You can invoke Groovy's <code>println</code> method to print on
    the console. Here is an example.</p>

    <pre class="prettyprint source">import ch.qos.logback.classic.encoder.PatternLayoutEncoder
import ch.qos.logback.core.FileAppender

import static ch.qos.logback.classic.Level.DEBUG

def USER_HOME = System.getProperty("user.home");
<b>println "USER_HOME=${USER_HOME}"</b>

appender("FILE", FileAppender) {
  <b>println "Setting [file] property to [${USER_HOME}/myApp.log]"</b>
  file = "${USER_HOME}/myApp.log"  
  encoder(PatternLayoutEncoder) {
    pattern = "%msg%n"
  }
}
root(DEBUG, ["FILE"])</pre>


   <h3>Automatically exported fields</h3>

   <h4><a name="hostname" href="#hostname">'hostname' variable</a></h4>

   <p>The 'hostname' variable contains the name of the current
   host. However, due to scoping rules that the authors cannot fully
   explain, the 'hostname' variable is available only at the topmost
   scope but not in nested scopes. The next example should get the
   point across.
   </p>

 <pre class="prettyprint source">import ch.qos.logback.classic.encoder.PatternLayoutEncoder
import ch.qos.logback.core.ConsoleAppender

import static ch.qos.logback.classic.Level.DEBUG

// will print "hostname is x" where x is the current host's name
println "Hostname is ${hostname}"

appender("STDOUT", ConsoleAppender) {
  <b>// will print "hostname is null"</b>
  <b>println "Hostname is ${hostname}" </b>
}</pre>

   <p>If you wish to have the hostname variable be seen in all scopes,
   you need to define another variable and assign it the value of
   'hostname' as shown next.</p>

 <pre class="prettyprint source">import ch.qos.logback.classic.encoder.PatternLayoutEncoder
import ch.qos.logback.core.ConsoleAppender

import static ch.qos.logback.classic.Level.DEBUG

// define HOSTNAME by assigning it hostname
def HOSTNAME=hostname
// will print "hostname is x" where x is the current host's name
println "Hostname is ${HOSTNAME}"

appender("STDOUT", ConsoleAppender) {
  // will print "hostname is x" where x is the current host's name
  println "Hostname is ${HOSTNAME}" 
}</pre>


   <h3>Everything is context aware with a reference to the current
   context</h3>

   <p>The execution of the <em>logback.groovy</em> script is done
   within the scope of a <a
   href="../xref/ch/qos/logback/core/spi/ContextAware.html">ContextAware</a>
   object. Thus, the current context is always accessible using the
   '<code>context</code>' variable and you can invoke
   <code>addInfo</code>(), <code>addWarn</code>() and
   <code>addError</code>() methods to send status messages to the
   context's <code>StatusManager</code>.</p>

   <pre class="prettyprint source">import ch.qos.logback.classic.encoder.PatternLayoutEncoder
import ch.qos.logback.core.FileAppender
import ch.qos.logback.core.status.OnConsoleStatusListener

import static ch.qos.logback.classic.Level.DEBUG

// always a good idea to add an on console status listener
statusListener(OnConsoleStatusListener)

// set the context's name to wombat
<b>context.name = "wombat"</b>
// add a status message regarding context's name
<b>addInfo("Context name has been set to ${context.name}")</b>

def USER_HOME = System.getProperty("user.home");
// add a status message regarding USER_HOME
<b>addInfo("USER_HOME=${USER_HOME}")</b>

appender("FILE", FileAppender) {
  // add a status message regarding the file property
  <b>addInfo("Setting [file] property to [${USER_HOME}/myApp.log]")</b>
  file = "${USER_HOME}/myApp.log"  
  encoder(PatternLayoutEncoder) {
    pattern = "%msg%n"
  }
}
root(DEBUG, ["FILE"])</pre>


   <h3>Conditional configuration</h3>
   
   <p>Given that Groovy is a fully-fledged programming language,
   conditional statements allow for a single <em>logback.groovy</em>
   file to adapt to various environments such as development, testing
   or production.</p>

   <p>In the next script, a console appender is activated on hosts
   other than pixie or orion, our production machines. Note that the
   output directory of the rolling file appender also depends on the
   host.</p>
   
   <pre class="prettyprint source">import ch.qos.logback.classic.encoder.PatternLayoutEncoder
import ch.qos.logback.core.rolling.RollingFileAppender
import ch.qos.logback.core.rolling.TimeBasedRollingPolicy
import ch.qos.logback.core.ConsoleAppender
import ch.qos.logback.core.status.OnConsoleStatusListener

import static ch.qos.logback.classic.Level.INFO

// always a good idea to add an on console status listener
statusListener(OnConsoleStatusListener)

def appenderList = ["ROLLING"]
def WEBAPP_DIR = "."
def consoleAppender = true;

// does hostname match pixie or orion?
if (hostname =~ /pixie|orion/) {
  WEBAPP_DIR = "/opt/myapp"     
  consoleAppender = false   
} else {
  appenderList.add("CONSOLE")
}

if (consoleAppender) {
  appender("CONSOLE", ConsoleAppender) {
    encoder(PatternLayoutEncoder) {
      pattern = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
    }
  }
}

appender("ROLLING", RollingFileAppender) {
  encoder(PatternLayoutEncoder) {
    Pattern = "%d %level %thread %mdc %logger - %m%n"
  }
  rollingPolicy(TimeBasedRollingPolicy) {
    FileNamePattern = "${WEBAPP_DIR}/log/translator-%d{yyyy-MM}.zip"
  }
}

root(INFO, appenderList)</pre>




    <script src="../templates/footer.js" type="text/javascript"></script>

    </div>
  </body>
</html>