vinted-log4r 1.1.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +7 -0
  2. data/doc/content/contact.html +22 -0
  3. data/doc/content/contribute.html +21 -0
  4. data/doc/content/index.html +90 -0
  5. data/doc/content/license.html +56 -0
  6. data/doc/content/manual.html +449 -0
  7. data/doc/dev/README.developers +55 -0
  8. data/doc/dev/checklist +23 -0
  9. data/doc/dev/things-to-do +5 -0
  10. data/doc/images/log4r-logo.png +0 -0
  11. data/doc/images/logo2.png +0 -0
  12. data/doc/log4r.css +111 -0
  13. data/doc/rdoc-log4r.css +696 -0
  14. data/doc/templates/main.html +147 -0
  15. data/examples/README +19 -0
  16. data/examples/ancestors.rb +53 -0
  17. data/examples/chainsaw_settings.xml +7 -0
  18. data/examples/customlevels.rb +34 -0
  19. data/examples/filelog.rb +25 -0
  20. data/examples/fileroll.rb +40 -0
  21. data/examples/gmail.rb +30 -0
  22. data/examples/gmail.yaml +95 -0
  23. data/examples/log4r_yaml.yaml +0 -0
  24. data/examples/logclient.rb +25 -0
  25. data/examples/logserver.rb +18 -0
  26. data/examples/moderate.xml +29 -0
  27. data/examples/moderateconfig.rb +66 -0
  28. data/examples/myformatter.rb +23 -0
  29. data/examples/outofthebox.rb +21 -0
  30. data/examples/rdoc-gen +2 -0
  31. data/examples/rrconfig.xml +63 -0
  32. data/examples/rrsetup.rb +42 -0
  33. data/examples/simpleconfig.rb +39 -0
  34. data/examples/syslogcustom.rb +52 -0
  35. data/examples/xmlconfig.rb +25 -0
  36. data/examples/yaml.rb +30 -0
  37. data/lib/log4r.rb +17 -0
  38. data/lib/log4r/GDC.rb +41 -0
  39. data/lib/log4r/MDC.rb +59 -0
  40. data/lib/log4r/NDC.rb +86 -0
  41. data/lib/log4r/base.rb +74 -0
  42. data/lib/log4r/config.rb +9 -0
  43. data/lib/log4r/configurator.rb +224 -0
  44. data/lib/log4r/formatter/formatter.rb +105 -0
  45. data/lib/log4r/formatter/log4jxmlformatter.rb +65 -0
  46. data/lib/log4r/formatter/patternformatter.rb +145 -0
  47. data/lib/log4r/lib/drbloader.rb +52 -0
  48. data/lib/log4r/lib/xmlloader.rb +24 -0
  49. data/lib/log4r/logevent.rb +28 -0
  50. data/lib/log4r/logger.rb +216 -0
  51. data/lib/log4r/loggerfactory.rb +89 -0
  52. data/lib/log4r/logserver.rb +28 -0
  53. data/lib/log4r/outputter/consoleoutputters.rb +18 -0
  54. data/lib/log4r/outputter/datefileoutputter.rb +117 -0
  55. data/lib/log4r/outputter/emailoutputter.rb +143 -0
  56. data/lib/log4r/outputter/fileoutputter.rb +57 -0
  57. data/lib/log4r/outputter/iooutputter.rb +55 -0
  58. data/lib/log4r/outputter/outputter.rb +134 -0
  59. data/lib/log4r/outputter/outputterfactory.rb +60 -0
  60. data/lib/log4r/outputter/remoteoutputter.rb +40 -0
  61. data/lib/log4r/outputter/rollingfileoutputter.rb +234 -0
  62. data/lib/log4r/outputter/scribeoutputter.rb +37 -0
  63. data/lib/log4r/outputter/staticoutputter.rb +30 -0
  64. data/lib/log4r/outputter/syslogoutputter.rb +126 -0
  65. data/lib/log4r/outputter/udpoutputter.rb +53 -0
  66. data/lib/log4r/rdoc/GDC +14 -0
  67. data/lib/log4r/rdoc/MDC +16 -0
  68. data/lib/log4r/rdoc/NDC +41 -0
  69. data/lib/log4r/rdoc/configurator +243 -0
  70. data/lib/log4r/rdoc/emailoutputter +103 -0
  71. data/lib/log4r/rdoc/formatter +39 -0
  72. data/lib/log4r/rdoc/log4jxmlformatter +21 -0
  73. data/lib/log4r/rdoc/log4r +89 -0
  74. data/lib/log4r/rdoc/logger +175 -0
  75. data/lib/log4r/rdoc/logserver +85 -0
  76. data/lib/log4r/rdoc/outputter +108 -0
  77. data/lib/log4r/rdoc/patternformatter +128 -0
  78. data/lib/log4r/rdoc/scribeoutputter +16 -0
  79. data/lib/log4r/rdoc/syslogoutputter +29 -0
  80. data/lib/log4r/rdoc/win32eventoutputter +7 -0
  81. data/lib/log4r/rdoc/yamlconfigurator +20 -0
  82. data/lib/log4r/repository.rb +88 -0
  83. data/lib/log4r/staticlogger.rb +49 -0
  84. data/lib/log4r/version.rb +4 -0
  85. data/lib/log4r/yamlconfigurator.rb +198 -0
  86. data/tests/README +10 -0
  87. data/tests/testGDC.rb +24 -0
  88. data/tests/testMDC.rb +40 -0
  89. data/tests/testNDC.rb +25 -0
  90. data/tests/test_helper.rb +12 -0
  91. data/tests/testall.rb +6 -0
  92. data/tests/testbase.rb +48 -0
  93. data/tests/testchainsaw.rb +42 -0
  94. data/tests/testconf.xml +37 -0
  95. data/tests/testcustom.rb +30 -0
  96. data/tests/testformatter.rb +31 -0
  97. data/tests/testlogger.rb +200 -0
  98. data/tests/testoutputter.rb +146 -0
  99. data/tests/testpatternformatter.rb +76 -0
  100. data/tests/testthreads.rb +31 -0
  101. data/tests/testxmlconf.rb +48 -0
  102. data/tests/testyaml.rb +39 -0
  103. data/tests/testyaml_arrays.yaml +25 -0
  104. data/tests/testyaml_injection.yaml +22 -0
  105. metadata +193 -0
@@ -0,0 +1,4 @@
1
+ module Log4r
2
+ Log4rVersion = [1, 1, 11].join '.' # deprecate?
3
+ VERSION = Log4rVersion
4
+ end
@@ -0,0 +1,198 @@
1
+ # :include: rdoc/yamlconfigurator
2
+ #
3
+ # == Other Info
4
+ #
5
+ # Version: $Id$
6
+
7
+ require "log4r/logger"
8
+ require "log4r/outputter/staticoutputter"
9
+ require "log4r/logserver"
10
+ require "log4r/outputter/remoteoutputter"
11
+
12
+ require 'yaml'
13
+
14
+ module Log4r
15
+ # Gets raised when Configurator encounters bad YAML.
16
+ class ConfigError < Exception
17
+ end
18
+
19
+ # See log4r/yamlconfigurator.rb
20
+ class YamlConfigurator
21
+ @@params = Hash.new
22
+
23
+ # Get a parameter's value
24
+ def self.[](param); @@params[param] end
25
+ # Define a parameter with a value
26
+ def self.[]=(param, value); @@params[param] = value end
27
+
28
+
29
+ def self.custom_levels( levels)
30
+ return Logger.root if levels.size == 0
31
+ for i in 0...levels.size
32
+ name = levels[i].to_s
33
+ if name =~ /\s/ or name !~ /^[A-Z]/
34
+ raise TypeError, "#{name} is not a valid Ruby Constant name", caller
35
+ end
36
+ end
37
+ Log4r.define_levels *levels
38
+ end
39
+
40
+ # Given a filename, loads the YAML configuration for Log4r.
41
+ def self.load_yaml_file( filename)
42
+ actual_load( File.open( filename))
43
+ end
44
+
45
+ # You can load a String YAML configuration instead of a file.
46
+ def self.load_yaml_string( string)
47
+ actual_load( string)
48
+ end
49
+
50
+ #######
51
+ private
52
+ #######
53
+
54
+ def self.actual_load( yaml_docs)
55
+ log4r_config = nil
56
+ YAML.load_documents( yaml_docs){ |doc|
57
+ doc.has_key?( 'log4r_config') and log4r_config = doc['log4r_config'] and break
58
+ }
59
+ if log4r_config.nil?
60
+ raise ConfigError,
61
+ "Key 'log4r_config:' not defined in yaml documents", caller[1..-1]
62
+ end
63
+ decode_yaml( log4r_config)
64
+ end
65
+
66
+ def self.decode_yaml( cfg)
67
+ decode_pre_config( cfg['pre_config'])
68
+ cfg['outputters'].each{ |op| decode_outputter( op)}
69
+ cfg['loggers'].each{ |lo| decode_logger( lo)}
70
+ cfg['logserver'].each{ |lo| decode_logserver( lo)} unless cfg['logserver'].nil?
71
+ end
72
+
73
+ def self.decode_pre_config( pre)
74
+ return Logger.root if pre.nil?
75
+ decode_custom_levels( pre['custom_levels'])
76
+ global_config( pre['global'])
77
+ global_config( pre['root'])
78
+ decode_parameters( pre['parameters'])
79
+ end
80
+
81
+ def self.decode_custom_levels( levels)
82
+ return Logger.root if levels.nil?
83
+ begin custom_levels( levels)
84
+ rescue TypeError => te
85
+ raise ConfigError, te.message, caller[1..-4]
86
+ end
87
+ end
88
+
89
+ def self.global_config( e)
90
+ return if e.nil?
91
+ globlev = e['level']
92
+ return if globlev.nil?
93
+ lev = LNAMES.index(globlev) # find value in LNAMES
94
+ Log4rTools.validate_level(lev, 4) # choke on bad level
95
+ Logger.global.level = lev
96
+ end
97
+
98
+ def self.decode_parameters( params)
99
+ params.each{ |p| @@params[p['name']] = p['value']} unless params.nil?
100
+ end
101
+
102
+ def self.decode_outputter( op)
103
+ # fields
104
+ name = op['name']
105
+ type = op['type']
106
+ level = op['level']
107
+ only_at = op['only_at']
108
+ # validation
109
+ raise ConfigError, "Outputter missing name", caller[1..-3] if name.nil?
110
+ raise ConfigError, "Outputter missing type", caller[1..-3] if type.nil?
111
+ Log4rTools.validate_level(LNAMES.index(level)) unless level.nil?
112
+ only_levels = []
113
+ unless only_at.nil?
114
+ for lev in only_at
115
+ alev = LNAMES.index(lev)
116
+ Log4rTools.validate_level(alev, 3)
117
+ only_levels.push alev
118
+ end
119
+ end
120
+
121
+ formatter = decode_formatter( op['formatter'])
122
+
123
+ opts = {}
124
+ opts[:level] = LNAMES.index(level) unless level.nil?
125
+ opts[:formatter] = formatter unless formatter.nil?
126
+ opts.merge!(decode_hash_params(op))
127
+ begin
128
+ Outputter[name] = Log4r.const_get(type).new name, opts
129
+ rescue Exception => ae
130
+ raise ConfigError,
131
+ "Problem creating outputter: #{ae.message}", caller[1..-3]
132
+ end
133
+ Outputter[name].only_at( *only_levels) if only_levels.size > 0
134
+ Outputter[name]
135
+ end
136
+
137
+ def self.decode_formatter( fo)
138
+ return nil if fo.nil?
139
+ type = fo['type']
140
+ raise ConfigError, "Formatter missing type", caller[1..-4] if type.nil?
141
+ begin
142
+ return Log4r.const_get(type).new(decode_hash_params(fo))
143
+ rescue Exception => ae
144
+ raise ConfigError,
145
+ "Problem creating outputter: #{ae.message}", caller[1..-4]
146
+ end
147
+ end
148
+
149
+ ExcludeParams = %w{formatter level name type only_at}
150
+
151
+ # Does the fancy parameter to hash argument transformation
152
+ def self.decode_hash_params(ph)
153
+ case ph
154
+ when Hash
155
+ ph.inject({}){|a,(k,v)| a[k] = self.decode_hash_params(v); a}
156
+ when Array
157
+ ph.map{|v| self.decode_hash_params(v)}
158
+ when String
159
+ self.paramsub(ph)
160
+ else
161
+ ph
162
+ end
163
+ end
164
+
165
+ # Substitues any #{foo} in the YAML with Parameter['foo']
166
+ def self.paramsub(str)
167
+ @@params.each {|param, value|
168
+ str = str.sub("\#{#{param}}", value)
169
+ }
170
+ str
171
+ end
172
+
173
+ def self.decode_logger( lo)
174
+ l = Logger.new lo['name']
175
+ decode_logger_common( l, lo)
176
+ end
177
+
178
+ def self.decode_logserver( lo)
179
+ name = lo['name']
180
+ uri = lo['uri']
181
+ l = LogServer.new name, uri
182
+ decode_logger_common(l, lo)
183
+ end
184
+
185
+ def self.decode_logger_common( l, lo)
186
+ level = lo['level']
187
+ additive = lo['additive']
188
+ trace = lo['trace']
189
+ l.level = LNAMES.index( level) unless level.nil?
190
+ l.additive = additive unless additive.nil?
191
+ l.trace = trace unless trace.nil?
192
+ # and now for outputters
193
+ outs = lo['outputters']
194
+ outs.each {|n| l.add n.strip} unless outs.nil?
195
+ end
196
+ end
197
+ end
198
+
data/tests/README ADDED
@@ -0,0 +1,10 @@
1
+ The unit tests are currently out of date. The examples actually provide
2
+ a decent test suite for log4r. But the unit tests are still very important
3
+ because of bounds-checking and a quicker turnaround for bug discovery.
4
+ The unit files need to be converted to the new 'test/unit' paradigm.
5
+
6
+ Because Log4r dynamically defines constants according to user preferences,
7
+ the unit testing can't all be done in one instance of ruby. It is planned
8
+ to use popen to run each test that needs a clean ruby instance.
9
+
10
+ The logs/ directory is where these tests dump generated log files.
data/tests/testGDC.rb ADDED
@@ -0,0 +1,24 @@
1
+ require 'test_helper'
2
+
3
+ class TestGDC < TestCase
4
+ include Log4r
5
+
6
+ def test_gdc_default
7
+ assert(GDC.get() == "testGDC.rb", "Expected 'testGDC.rb' got '#{GDC.get()}'" )
8
+ end
9
+
10
+ def test_gdc_set
11
+ assert_nothing_raised() { GDC.set("testGDCset") }
12
+ assert(GDC.get() == "testGDCset", "Expected 'testGDCset' got '#{GDC.get()}'" )
13
+ end
14
+
15
+ def test_gdc_threaded
16
+ assert_nothing_raised() { GDC.set("testGDCset") }
17
+ t = Thread.new("test GDC thread") do |name|
18
+ assert_raise(RuntimeError) { GDC.set("somethingelse") }
19
+ end
20
+ t.join
21
+ assert(GDC.get() == "testGDCset", "Expected 'testGDCset' got '#{GDC.get()}'" )
22
+ end
23
+
24
+ end
data/tests/testMDC.rb ADDED
@@ -0,0 +1,40 @@
1
+ require 'test_helper'
2
+
3
+ class TestMDC < TestCase
4
+ include Log4r
5
+
6
+ def test_multithread_copy
7
+ Log4r::MDC.put("user","colbygk")
8
+ t = Thread.new("test first copy") do |name|
9
+ assert(Log4r::MDC.get("user") == "colbygk",
10
+ "Did not get back expected value, '#{MDC.get("user")}'")
11
+ Log4r::MDC.put("user","unique")
12
+ assert(Log4r::MDC.get("user") == "unique",
13
+ "Did not get back expected value, '#{MDC.get("user")}'")
14
+ end
15
+ t.join
16
+ assert(Log4r::MDC.get("user") == "colbygk",
17
+ "Did not get back expected value, '#{MDC.get("user")}'")
18
+ end
19
+
20
+ def test_MDCoutput
21
+ Log4r::MDC.put(:user, "symbol")
22
+ Log4r::MDC.put("string", "string")
23
+ Log4r::MDC.put(5, "number")
24
+ l = Logger.new 'test'
25
+ o = StdoutOutputter.new 'test'
26
+ l.add o
27
+ assert_nothing_raised {
28
+ f = PatternFormatter.new :pattern=> "%l user: %X{:user} %X{strng} %X{5}"
29
+ Outputter['test'].formatter = f
30
+ l.debug "And this?"
31
+ l.info "How's this?"
32
+ l.error "and a really freaking huge line which we hope will be trimmed?"
33
+ e = ArgumentError.new("something barfed")
34
+ e.set_backtrace Array.new(5, "trace junk at thisfile.rb 154")
35
+ l.fatal e
36
+ l.info [1, 3, 5]
37
+ }
38
+
39
+ end
40
+ end
data/tests/testNDC.rb ADDED
@@ -0,0 +1,25 @@
1
+ require 'test_helper'
2
+
3
+ class TestNDC < TestCase
4
+ include Log4r
5
+
6
+ def test_ndc_remove_push
7
+ NDC.remove()
8
+ NDC.push("ndc")
9
+ assert(Log4r::NDC.get() == "ndc", "Expected 'ndc' got '#{NDC.get()}'" )
10
+ NDC.push("ndc")
11
+ assert(Log4r::NDC.get() == "ndc ndc", "Expected 'ndc ndc' got '#{NDC.get()}'" )
12
+ end
13
+
14
+ def test_ndc_remove_push_clone_and_inherit
15
+ NDC.remove()
16
+ NDC.push("ndc")
17
+ NDC.push("ndc")
18
+ a = NDC.clone_stack()
19
+ NDC.remove()
20
+ assert(NDC.get() == "", "Expected '' got '#{NDC.get()}'" )
21
+ NDC.inherit(a)
22
+ assert(NDC.get() == "ndc ndc", "Expected 'ndc ndc' got '#{NDC.get()}'" )
23
+ end
24
+
25
+ end
@@ -0,0 +1,12 @@
1
+ $:.unshift(File.dirname(__FILE__))
2
+
3
+ require "test/unit"
4
+ require 'log4r'
5
+ require 'log4r/configurator'
6
+ require 'log4r/staticlogger'
7
+ require 'log4r/formatter/log4jxmlformatter'
8
+ require 'log4r/outputter/udpoutputter'
9
+ require 'log4r/outputter/consoleoutputters'
10
+ require 'log4r/yamlconfigurator'
11
+
12
+ include Test::Unit
data/tests/testall.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'test_helper'
2
+
3
+ # because constants are dynamically defined, some tests need to
4
+ # be opened in a fresh instance of Ruby, hence the popens
5
+
6
+ IO.popen("date") { |f| puts f.gets }
data/tests/testbase.rb ADDED
@@ -0,0 +1,48 @@
1
+ require 'test_helper'
2
+
3
+ class TestBase < TestCase
4
+ include Log4r
5
+
6
+ # check that LNAMES loads properly (it uses an eval to load)
7
+ def test_default_levels
8
+ Logger.root # doing this loads the default levels
9
+ assert_equal(ALL,0)
10
+ assert_equal(DEBUG,1)
11
+ assert_equal(INFO,2)
12
+ assert_equal(WARN,3)
13
+ assert_equal(ERROR,4)
14
+ assert_equal(FATAL,5)
15
+ assert_equal(OFF,6)
16
+ assert_equal(LEVELS, 7)
17
+ assert_equal(LNAMES.size, 7)
18
+ end
19
+ # check bad input and bounds for validate_level
20
+ def test_validate_level
21
+ 7.times{|i| assert_nothing_raised {Log4rTools.validate_level(i)} }
22
+ assert_raise(ArgumentError) {Log4rTools.validate_level(-1)}
23
+ assert_raise(ArgumentError) {Log4rTools.validate_level(LEVELS)}
24
+ assert_raise(ArgumentError) {Log4rTools.validate_level(String)}
25
+ assert_raise(ArgumentError) {Log4rTools.validate_level("bogus")}
26
+ end
27
+ # decode_bool turns a string 'true' into true and so on
28
+ def test_decode_bool
29
+ # when the key is a symbol :data
30
+ assert(Log4rTools.decode_bool({:data=> 'true'} ,:data,false) == true)
31
+ assert(Log4rTools.decode_bool({:data=> true} ,:data,false) == true)
32
+ assert(Log4rTools.decode_bool({:data=> 'false'} ,:data,true) == false)
33
+ assert(Log4rTools.decode_bool({:data=> false} ,:data,true) == false)
34
+ assert(Log4rTools.decode_bool({:data=> nil} ,:data,true) == true)
35
+ assert(Log4rTools.decode_bool({:data=> nil} ,:data,false) == false)
36
+ assert(Log4rTools.decode_bool({:data=> String} ,:data,true) == true)
37
+ assert(Log4rTools.decode_bool({:data=> String} ,:data,false) == false)
38
+ # now the key is a string 'data'
39
+ assert(Log4rTools.decode_bool({'data'=> 'true'} ,:data,false) == true)
40
+ assert(Log4rTools.decode_bool({'data'=> true} ,:data,false) == true)
41
+ assert(Log4rTools.decode_bool({'data'=> 'false'} ,:data,true) == false)
42
+ assert(Log4rTools.decode_bool({'data'=> false} ,:data,true) == false)
43
+ assert(Log4rTools.decode_bool({'data'=> nil} ,:data,true) == true)
44
+ assert(Log4rTools.decode_bool({'data'=> nil} ,:data,false) == false)
45
+ assert(Log4rTools.decode_bool({'data'=> String} ,:data,true) == true)
46
+ assert(Log4rTools.decode_bool({'data'=> String} ,:data,false) == false)
47
+ end
48
+ end
@@ -0,0 +1,42 @@
1
+ require 'test_helper'
2
+
3
+ include Log4r
4
+
5
+ log4r = Logger.new 'log4r'
6
+ log4r.trace = true
7
+ log4r.outputters = StdoutOutputter.new 'log4r'
8
+ log4r.level = ALL
9
+
10
+ formatter = Log4jXmlFormatter.new
11
+ outputter = UDPOutputter.new 'udp', :hostname => "localhost", :port => 8071
12
+ outputter.formatter = formatter
13
+
14
+ mylog = Logger.new 'mylog'
15
+ mylog.trace = true
16
+ mylog.outputters = [outputter]
17
+
18
+ # Log4r::Formatter throws when formatting
19
+ # an excpetion with a nil backtrace (line 73).
20
+ def get_exception(msg)
21
+ begin
22
+ raise msg
23
+ rescue Exception => e
24
+ e
25
+ end
26
+ end
27
+
28
+ NDC.push "saw test"
29
+
30
+ MDC.put "clientip", %q{10.33.33.33}
31
+
32
+ def do_log(log)
33
+ log.debug "This is a message with level DEBUG"
34
+ log.info "This is a message with level INFO"
35
+ log.warn "This is a message with level WARN"
36
+ log.error "This is a message with level ERROR"
37
+ log.fatal "This is a message with level FATAL"
38
+
39
+ log.fatal get_exception( "This is an exception" )
40
+ end
41
+
42
+ do_log(mylog)
@@ -0,0 +1,37 @@
1
+ <test>
2
+ <log4r_config>
3
+
4
+ <pre_config>
5
+ <custom_levels>Foo, Bar,Baz, Bing</custom_levels>
6
+ <global level="Foo"/>
7
+ <parameters>
8
+ <mypattern>[%l] %d %t - %m</mypattern>
9
+ </parameters>
10
+ <parameter name="datem" value="usec"/>
11
+ </pre_config>
12
+
13
+ <!-- level and formatter are optional, -->
14
+ <outputter name="SO" type="StdoutOutputter">
15
+ <level>Foo</level>
16
+ <formatter type="Log4r::PatternFormatter">
17
+ <pattern>%d %c %l&gt; %m</pattern>
18
+ <date_method>#{datem}</date_method>
19
+ </formatter>
20
+ </outputter>
21
+ <outputter name="SE" type="StderrOutputter" level="Baz">
22
+ <formatter type="PatternFormatter" pattern="#{mypattern}">
23
+ <date_pattern>%H:%S</date_pattern>
24
+ </formatter>
25
+ </outputter>
26
+ <outputter name="F" type="FileOutputter">
27
+ <filename>#{logpath}/junk/foo.log</filename>
28
+ <trunc>true</trunc>
29
+ <only_at>Foo, Bar, Bing</only_at>
30
+ </outputter>
31
+ <!-- optional level, additive and outputters -->
32
+ <logger name="first::second" level="Bar" additive="false">
33
+ <trace>true</trace>
34
+ <outputters>SO, SE, F, stdout, stderr</outputters>
35
+ </logger>
36
+ </log4r_config>
37
+ </test>