structured_log 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.travis.yml +5 -0
  4. data/CODE_OF_CONDUCT.md +74 -0
  5. data/Gemfile +6 -0
  6. data/Gemfile.lock +27 -0
  7. data/LICENSE +21 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +620 -0
  10. data/Rakefile +44 -0
  11. data/bin/console +14 -0
  12. data/bin/setup +8 -0
  13. data/images/array.jpg +0 -0
  14. data/images/attributes.png +0 -0
  15. data/images/comment.png +0 -0
  16. data/images/custom.png +0 -0
  17. data/images/data.png +0 -0
  18. data/images/exception.png +0 -0
  19. data/images/hash.png +0 -0
  20. data/images/nesting.jpg +0 -0
  21. data/images/potpourri.png +0 -0
  22. data/images/rescue.jpg +0 -0
  23. data/images/structured.png +0 -0
  24. data/images/text.jpg +0 -0
  25. data/images/time.png +0 -0
  26. data/lib/structured_log.rb +322 -0
  27. data/lib/structured_log/version.rb +3 -0
  28. data/readme_files/README.template.md +167 -0
  29. data/readme_files/logs/array.xml +10 -0
  30. data/readme_files/logs/attributes.xml +7 -0
  31. data/readme_files/logs/cdata.xml +12 -0
  32. data/readme_files/logs/comment.xml +5 -0
  33. data/readme_files/logs/custom_entry.xml +12 -0
  34. data/readme_files/logs/custom_section.xml +14 -0
  35. data/readme_files/logs/data.xml +20 -0
  36. data/readme_files/logs/exception.xml +14 -0
  37. data/readme_files/logs/hash.xml +10 -0
  38. data/readme_files/logs/potpourri.xml +13 -0
  39. data/readme_files/logs/potpourri_other.xml +20 -0
  40. data/readme_files/logs/potpourri_usual.xml +8 -0
  41. data/readme_files/logs/rescue.xml +28 -0
  42. data/readme_files/logs/sections.xml +7 -0
  43. data/readme_files/logs/text.xml +5 -0
  44. data/readme_files/logs/time.xml +17 -0
  45. data/readme_files/scripts/array.rb +6 -0
  46. data/readme_files/scripts/attributes.rb +8 -0
  47. data/readme_files/scripts/cdata.rb +17 -0
  48. data/readme_files/scripts/comment.rb +5 -0
  49. data/readme_files/scripts/custom_entry.rb +10 -0
  50. data/readme_files/scripts/custom_section.rb +15 -0
  51. data/readme_files/scripts/data.rb +16 -0
  52. data/readme_files/scripts/exception.rb +5 -0
  53. data/readme_files/scripts/hash.rb +11 -0
  54. data/readme_files/scripts/potpourri.rb +20 -0
  55. data/readme_files/scripts/potpourri_other.rb +17 -0
  56. data/readme_files/scripts/potpourri_usual.rb +12 -0
  57. data/readme_files/scripts/rescue.rb +12 -0
  58. data/readme_files/scripts/sections.rb +17 -0
  59. data/readme_files/scripts/text.rb +8 -0
  60. data/readme_files/scripts/time.rb +15 -0
  61. data/structured_log.gemspec +38 -0
  62. metadata +176 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c79556fbe921de0e1c558d92fb818e2092908840
4
+ data.tar.gz: ba191522d9991ee4760ffa4554a0b653b040a97d
5
+ SHA512:
6
+ metadata.gz: cd84786ce35b895802ca4ba41104ee01cbcb951e67cd5ac1a0bef6a935b12daba757f7f698975cf56fe6633e5d5e03f5fb2e41e85e57d2d24a6019a747f0cc15
7
+ data.tar.gz: 4783e18a64c266c1fa0587e18da3e53a0b7fb6c96fee0d2bec23ac1764adea28f210e7ffce310addfb138fde6c59d6cca599642c75d9200a67d55a25a1134c55
@@ -0,0 +1,9 @@
1
+ .idea
2
+ /.bundle/
3
+ /.yardoc
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.2.6
5
+ before_install: gem install bundler -v 1.16.1
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at BurdetteLamar@Yahoo.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in structured_log.gemspec
6
+ gemspec
@@ -0,0 +1,27 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ structured_log (0.9.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.3)
10
+ markdown_helper (2.0.0)
11
+ minitest (5.11.3)
12
+ rake (10.5.0)
13
+
14
+ PLATFORMS
15
+ x64-mingw32
16
+ x86-mingw32
17
+
18
+ DEPENDENCIES
19
+ bundler (~> 1.16.2)
20
+ diff-lcs (~> 1.3)
21
+ markdown_helper (~> 2.0.0)
22
+ minitest (~> 5.0)
23
+ rake (~> 10.0)
24
+ structured_log!
25
+
26
+ BUNDLED WITH
27
+ 1.16.2
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 Burdette Lamar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 burdettelamar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,620 @@
1
+ <!-- >>>>>> BEGIN GENERATED FILE (include): SOURCE readme_files/README.template.md -->
2
+ # Structured Log
3
+
4
+ <img src="images/structured.png" height="70" alt="Structured Log">
5
+
6
+ Class <code>StructuredLog</code> offers structured (as opposed to flat) logging. Nested sections (blocks) in Ruby code become nested XML elements in the log.
7
+
8
+ This sectioning allows you to group actions in your program, and that grouping carries over into the log.
9
+
10
+ Optionally, each section may include:
11
+ <ul>
12
+ <li>A timestamp.
13
+ <li>A duration.
14
+ <li>The ability to rescue and log an exception.
15
+ </ul>
16
+
17
+ And of course the class offers many ways to log data.
18
+
19
+ ## About the Examples
20
+
21
+ A working example is worth a thousand words (maybe).
22
+
23
+ Each of the following sections features an example Ruby program, followed by its output log.
24
+
25
+
26
+ ## Sections
27
+
28
+ ### Nested Sections
29
+ <img src="images/nesting.jpg" height="70" alt="Nesting">
30
+
31
+ Use nested sections to give structure to your log.
32
+
33
+ <!-- >>>>>> BEGIN INCLUDED FILE (ruby): SOURCE readme_files/scripts/sections.rb -->
34
+ ```sections.rb```:
35
+ ```ruby
36
+ require 'structured_log'
37
+
38
+ StructuredLog.open('sections.xml') do |log|
39
+ # Any code can be here.
40
+ log.section('Outer') do
41
+ # Any code can be here.
42
+ log.section('Mid') do
43
+ # Any code can be here.
44
+ log.section('Inner') do
45
+ # Any code can be here.
46
+ end
47
+ # Any code can be here.
48
+ end
49
+ # Any code can be here.
50
+ end
51
+ # Any code can be here.
52
+ end
53
+ ```
54
+ <!-- <<<<<< END INCLUDED FILE (ruby): SOURCE readme_files/scripts/sections.rb -->
55
+
56
+ <!-- >>>>>> BEGIN INCLUDED FILE (xml): SOURCE readme_files/logs/sections.xml -->
57
+ ```sections.xml```:
58
+ ```xml
59
+ <log>
60
+ <section name='Outer'>
61
+ <section name='Mid'>
62
+ <section name='Inner'/>
63
+ </section>
64
+ </section>
65
+ </log>
66
+ ```
67
+ <!-- <<<<<< END INCLUDED FILE (xml): SOURCE readme_files/logs/sections.xml -->
68
+
69
+ ### Text
70
+ <img src="images/text.jpg" height="70" alt="Text">
71
+
72
+ Add text to a <code>section</code> element by passing a string argument.
73
+
74
+ <!-- >>>>>> BEGIN INCLUDED FILE (ruby): SOURCE readme_files/scripts/text.rb -->
75
+ ```text.rb```:
76
+ ```ruby
77
+ require 'structured_log'
78
+
79
+ text = 'This section has text.'
80
+ StructuredLog.open('text.xml') do |log|
81
+ log.section('with_text', text) do
82
+ # Any code can be here.
83
+ end
84
+ end
85
+ ```
86
+ <!-- <<<<<< END INCLUDED FILE (ruby): SOURCE readme_files/scripts/text.rb -->
87
+
88
+ <!-- >>>>>> BEGIN INCLUDED FILE (xml): SOURCE readme_files/logs/text.xml -->
89
+ ```text.xml```:
90
+ ```xml
91
+ <log>
92
+ <section name='with_text'>
93
+ This section has text.
94
+ </section>
95
+ </log>
96
+ ```
97
+ <!-- <<<<<< END INCLUDED FILE (xml): SOURCE readme_files/logs/text.xml -->
98
+
99
+ ### Attributes
100
+ <img src="images/attributes.png" height="70" alt="Attributes">
101
+
102
+ Add attributes to a <code>section</code> element by passing a hash argument.
103
+
104
+ <!-- >>>>>> BEGIN INCLUDED FILE (ruby): SOURCE readme_files/scripts/attributes.rb -->
105
+ ```attributes.rb```:
106
+ ```ruby
107
+ require 'structured_log'
108
+
109
+ attributes = {:a => 0, :b => 1}
110
+ StructuredLog.open('attributes.xml') do |log|
111
+ log.section('with_attributes', attributes) do
112
+ log.comment('This section has attributes a and b.')
113
+ end
114
+ end
115
+ ```
116
+ <!-- <<<<<< END INCLUDED FILE (ruby): SOURCE readme_files/scripts/attributes.rb -->
117
+
118
+ <!-- >>>>>> BEGIN INCLUDED FILE (xml): SOURCE readme_files/logs/attributes.xml -->
119
+ ```attributes.xml```:
120
+ ```xml
121
+ <log>
122
+ <section name='with_attributes' a='0' b='1'>
123
+ <comment>
124
+ This section has attributes a and b.
125
+ </comment>
126
+ </section>
127
+ </log>
128
+ ```
129
+ <!-- <<<<<< END INCLUDED FILE (xml): SOURCE readme_files/logs/attributes.xml -->
130
+
131
+ ### Timestamps and Durations
132
+ <img src="images/time.png" height="70" alt="Time">
133
+
134
+ Add a timestamp or duration to a <code>section</code> element by passing a special symbol argument.
135
+
136
+ <!-- >>>>>> BEGIN INCLUDED FILE (ruby): SOURCE readme_files/scripts/time.rb -->
137
+ ```time.rb```:
138
+ ```ruby
139
+ require 'structured_log'
140
+
141
+ StructuredLog.open('time.xml') do |log|
142
+ log.section('Section with timestamp', :timestamp) do
143
+ log.comment('This section has a timestamp.')
144
+ end
145
+ log.section('Section with duration', :duration) do
146
+ log.comment('This section has a duration.')
147
+ sleep 1
148
+ end
149
+ log.section('Section with both', :duration, :timestamp) do
150
+ log.comment('This section has both.')
151
+ sleep 1
152
+ end
153
+ end
154
+ ```
155
+ <!-- <<<<<< END INCLUDED FILE (ruby): SOURCE readme_files/scripts/time.rb -->
156
+
157
+ <!-- >>>>>> BEGIN INCLUDED FILE (xml): SOURCE readme_files/logs/time.xml -->
158
+ ```time.xml```:
159
+ ```xml
160
+ <log>
161
+ <section name='Section with timestamp' timestamp='2018-06-09-Sat-11.38.13.373'>
162
+ <comment>
163
+ This section has a timestamp.
164
+ </comment>
165
+ </section>
166
+ <section name='Section with duration' duration_seconds='1.016'>
167
+ <comment>
168
+ This section has a duration.
169
+ </comment>
170
+ </section>
171
+ <section name='Section with both' timestamp='2018-06-09-Sat-11.38.14.389' duration_seconds='1.000'>
172
+ <comment>
173
+ This section has both.
174
+ </comment>
175
+ </section>
176
+ </log>
177
+ ```
178
+ <!-- <<<<<< END INCLUDED FILE (xml): SOURCE readme_files/logs/time.xml -->
179
+
180
+ ### Rescued Section
181
+ <img src="images/rescue.jpg" height="70" alt="Rescue">
182
+
183
+ Add rescuing to a <code>section</code> element by passing a special symbol argument.
184
+
185
+ For the rescued exception, the class, message, and backtrace are logged.
186
+
187
+ <!-- >>>>>> BEGIN INCLUDED FILE (ruby): SOURCE readme_files/scripts/rescue.rb -->
188
+ ```rescue.rb```:
189
+ ```ruby
190
+ require 'structured_log'
191
+
192
+ StructuredLog.open('rescue.xml') do |log|
193
+ log.section('Section with rescue', :rescue) do
194
+ log.comment('This section will terminate because of the failure.')
195
+ fail 'This exception will be rescued and logged.'
196
+ log.comment('This comment will not be reached.')
197
+ end
198
+ log.section('Another section') do
199
+ log.comment('This comment will be reached and logged, because of rescue above.')
200
+ end
201
+ end
202
+ ```
203
+ <!-- <<<<<< END INCLUDED FILE (ruby): SOURCE readme_files/scripts/rescue.rb -->
204
+
205
+ <!-- >>>>>> BEGIN INCLUDED FILE (xml): SOURCE readme_files/logs/rescue.xml -->
206
+ ```rescue.xml```:
207
+ ```xml
208
+ <log>
209
+ <section name='Section with rescue'>
210
+ <comment>
211
+ This section will terminate because of the failure.
212
+ </comment>
213
+ <rescued_exception timestamp='2018-06-09-Sat-11.38.12.889' class='RuntimeError'>
214
+ <message>
215
+ This exception will be rescued and logged.
216
+ </message>
217
+ <backtrace>
218
+ <![CDATA[
219
+ C:/Users/Burdette/Documents/GitHub/structured_log/readme_files/scripts/rescue.rb:6:in `block (2 levels) in <main>'
220
+ C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/structured_log-0.1.0/lib/structured_log.rb:58:in `block in section'
221
+ C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/structured_log-0.1.0/lib/structured_log.rb:116:in `put_element'
222
+ C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/structured_log-0.1.0/lib/structured_log.rb:57:in `section'
223
+ C:/Users/Burdette/Documents/GitHub/structured_log/readme_files/scripts/rescue.rb:4:in `block in <main>'
224
+ C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/structured_log-0.1.0/lib/structured_log.rb:38:in `open'
225
+ C:/Users/Burdette/Documents/GitHub/structured_log/readme_files/scripts/rescue.rb:3:in `<main>'
226
+ ]]>
227
+ </backtrace>
228
+ </rescued_exception>
229
+ </section>
230
+ <section name='Another section'>
231
+ <comment>
232
+ This comment will be reached and logged, because of rescue above.
233
+ </comment>
234
+ </section>
235
+ </log>
236
+ ```
237
+ <!-- <<<<<< END INCLUDED FILE (xml): SOURCE readme_files/logs/rescue.xml -->
238
+
239
+ ### Potpourri
240
+ <img src="images/potpourri.png" height="70" alt="Potpourri">
241
+
242
+ Pass any mixture of arguments to method <code>section</code>.
243
+
244
+ The section name must be first; after that, anything goes.
245
+
246
+ <!-- >>>>>> BEGIN INCLUDED FILE (ruby): SOURCE readme_files/scripts/potpourri.rb -->
247
+ ```potpourri.rb```:
248
+ ```ruby
249
+ require 'structured_log'
250
+
251
+ attributes = {:a => 0, :b => 1}
252
+ more_attributes = {:c => 2, :d => 3}
253
+ text = 'This section has a potpourri.'
254
+ float = 3.14159
255
+ boolean = false
256
+ fixnum = 1066
257
+ time = Time.new
258
+ exception = RuntimeError.new('Oops!')
259
+
260
+ StructuredLog.open('potpourri.xml') do |log|
261
+ log.section('All together now', 'Order does not matter except in aggregating text and attributes.') do
262
+ args = [attributes, :rescue, text, float, :duration, more_attributes, boolean, :timestamp, fixnum, time, exception]
263
+ log.section('Potpourri', *args) do
264
+ end
265
+ log.section('Reverse potpourri', *args.reverse) do
266
+ end
267
+ end
268
+ end
269
+ ```
270
+ <!-- <<<<<< END INCLUDED FILE (ruby): SOURCE readme_files/scripts/potpourri.rb -->
271
+
272
+ <!-- >>>>>> BEGIN INCLUDED FILE (xml): SOURCE readme_files/logs/potpourri.xml -->
273
+ ```potpourri.xml```:
274
+ ```xml
275
+ <log>
276
+ <section name='All together now'>
277
+ Order does not matter except in aggregating text and attributes.
278
+ <section name='Potpourri' a='0' b='1' c='2' d='3' timestamp='2018-06-09-Sat-11.38.11.279' duration_seconds='0.000'>
279
+ This section has a potpourri.3.14159false10662018-06-09 11:38:11
280
+ -0500#&lt;RuntimeError: Oops!&gt;
281
+ </section>
282
+ <section name='Reverse potpourri' timestamp='2018-06-09-Sat-11.38.11.279' c='2' d='3' a='0' b='1' duration_seconds='0.000'>
283
+ #&lt;RuntimeError: Oops!&gt;2018-06-09 11:38:11 -05001066false3.14159This
284
+ section has a potpourri.
285
+ </section>
286
+ </section>
287
+ </log>
288
+ ```
289
+ <!-- <<<<<< END INCLUDED FILE (xml): SOURCE readme_files/logs/potpourri.xml -->
290
+
291
+ ## Data
292
+
293
+ ### Hash-LIke Objects
294
+ <img src="images/hash.png" height="30" alt="Hash">
295
+
296
+ Use method <code>put_each_pair</code>, or its alias <code>put_hash</code>, to log an object that <code>respond_to?(:each_pair)</code>.
297
+
298
+ <!-- >>>>>> BEGIN INCLUDED FILE (ruby): SOURCE readme_files/scripts/hash.rb -->
299
+ ```hash.rb```:
300
+ ```ruby
301
+ require 'structured_log'
302
+
303
+ hash = {
304
+ :a => 'z',
305
+ :aa => 'zz',
306
+ :aaa => 'zzz',
307
+ :aaaa => 'zzzz',
308
+ }
309
+ StructuredLog.open('hash.xml') do |log|
310
+ log.put_hash('my_hash', hash)
311
+ end
312
+ ```
313
+ <!-- <<<<<< END INCLUDED FILE (ruby): SOURCE readme_files/scripts/hash.rb -->
314
+
315
+ <!-- >>>>>> BEGIN INCLUDED FILE (xml): SOURCE readme_files/logs/hash.xml -->
316
+ ```hash.xml```:
317
+ ```xml
318
+ <log>
319
+ <each_pair name='my_hash' class='Hash'>
320
+ <![CDATA[
321
+ a => z
322
+ aa => zz
323
+ aaa => zzz
324
+ aaaa => zzzz
325
+ ]]>
326
+ </each_pair>
327
+ </log>
328
+ ```
329
+ <!-- <<<<<< END INCLUDED FILE (xml): SOURCE readme_files/logs/hash.xml -->
330
+
331
+ ### Array-Like Objects
332
+ <img src="images/array.jpg" height="30" alt="Array">
333
+
334
+ Use method <code>put_each_with_index</code>, or its aliases <code>put_array</code> and <code>put_set</code>, to log an object that <code>respond_to?(:each_with_index)</code>.
335
+
336
+ <!-- >>>>>> BEGIN INCLUDED FILE (ruby): SOURCE readme_files/scripts/array.rb -->
337
+ ```array.rb```:
338
+ ```ruby
339
+ require 'structured_log'
340
+
341
+ array = %w/foo bar baz bat/
342
+ StructuredLog.open('array.xml') do |log|
343
+ log.put_array('my_array', array)
344
+ end
345
+ ```
346
+ <!-- <<<<<< END INCLUDED FILE (ruby): SOURCE readme_files/scripts/array.rb -->
347
+
348
+ <!-- >>>>>> BEGIN INCLUDED FILE (xml): SOURCE readme_files/logs/array.xml -->
349
+ ```array.xml```:
350
+ ```xml
351
+ <log>
352
+ <each_with_index name='my_array' class='Array'>
353
+ <![CDATA[
354
+ 0 foo
355
+ 1 bar
356
+ 2 baz
357
+ 3 bat
358
+ ]]>
359
+ </each_with_index>
360
+ </log>
361
+ ```
362
+ <!-- <<<<<< END INCLUDED FILE (xml): SOURCE readme_files/logs/array.xml -->
363
+
364
+ ### Other Objects
365
+ <img src="images/data.png" height="70" alt="Data">
366
+
367
+ Use method <code>put_data</code> to log any object.
368
+
369
+ <!-- >>>>>> BEGIN INCLUDED FILE (ruby): SOURCE readme_files/scripts/data.rb -->
370
+ ```data.rb```:
371
+ ```ruby
372
+ require 'structured_log'
373
+
374
+ data = {
375
+ :float => 3.14,
376
+ :fixnum => 1066,
377
+ :false => false,
378
+ :time => Time.new,
379
+ :exception => RuntimeError.new('Oops!'),
380
+ :nil => nil,
381
+ }
382
+ StructuredLog.open('data.xml') do |log|
383
+ data.each_pair do |type, datum|
384
+ name = "my_#{type}"
385
+ log.put_data(name, datum)
386
+ end
387
+ end
388
+ ```
389
+ <!-- <<<<<< END INCLUDED FILE (ruby): SOURCE readme_files/scripts/data.rb -->
390
+
391
+ <!-- >>>>>> BEGIN INCLUDED FILE (xml): SOURCE readme_files/logs/data.xml -->
392
+ ```data.xml```:
393
+ ```xml
394
+ <log>
395
+ <data name='my_float' class='Float'>
396
+ <![CDATA[3.14]]>
397
+ </data>
398
+ <data name='my_fixnum' class='Fixnum'>
399
+ <![CDATA[1066]]>
400
+ </data>
401
+ <data name='my_false' class='FalseClass'>
402
+ <![CDATA[false]]>
403
+ </data>
404
+ <data name='my_time' class='Time'>
405
+ <![CDATA[2018-06-09 11:38:10 -0500]]>
406
+ </data>
407
+ <data name='my_exception' class='RuntimeError'>
408
+ <![CDATA[#<RuntimeError: Oops!>]]>
409
+ </data>
410
+ <data name='my_nil' class='NilClass'>
411
+ <![CDATA[nil]]>
412
+ </data>
413
+ </log>
414
+ ```
415
+ <!-- <<<<<< END INCLUDED FILE (xml): SOURCE readme_files/logs/data.xml -->
416
+
417
+ ### Formatted Text
418
+
419
+ Use method <code>put_cdata</code> to log a string (possibly multi-line) as CDATA.
420
+
421
+ <!-- >>>>>> BEGIN INCLUDED FILE (ruby): SOURCE readme_files/scripts/cdata.rb -->
422
+ ```cdata.rb```:
423
+ ```ruby
424
+ require 'structured_log'
425
+
426
+ text = <<EOT
427
+
428
+ Method put_cdata puts the data verbatim:
429
+
430
+ * Nothing is added or subtracted.
431
+ * Not even whitespace.
432
+
433
+ So you can use the method to log a formatted string.
434
+
435
+ (You'll need to add your own leading and trailing newlines, if desired.)
436
+
437
+ EOT
438
+ StructuredLog.open('cdata.xml') do |log|
439
+ log.put_cdata(text)
440
+ end
441
+ ```
442
+ <!-- <<<<<< END INCLUDED FILE (ruby): SOURCE readme_files/scripts/cdata.rb -->
443
+
444
+ <!-- >>>>>> BEGIN INCLUDED FILE (xml): SOURCE readme_files/logs/cdata.xml -->
445
+ ```cdata.xml```:
446
+ ```xml
447
+ <log>
448
+ <![CDATA[
449
+ Method put_cdata puts the data verbatim:
450
+
451
+ * Nothing is added or subtracted.
452
+ * Not even whitespace.
453
+
454
+ So you can use the method to log a formatted string.
455
+
456
+ (You'll need to add your own leading and trailing newlines, if desired.)
457
+ ]]>
458
+ </log>
459
+ ```
460
+ <!-- <<<<<< END INCLUDED FILE (xml): SOURCE readme_files/logs/cdata.xml -->
461
+
462
+ ### Comment
463
+ <img src="images/comment.png" height="70" alt="Comment">
464
+
465
+ Use method <code>comment</code> to log a comment.
466
+
467
+ <!-- >>>>>> BEGIN INCLUDED FILE (ruby): SOURCE readme_files/scripts/comment.rb -->
468
+ ```comment.rb```:
469
+ ```ruby
470
+ require 'structured_log'
471
+
472
+ StructuredLog.open('comment.xml') do |log|
473
+ log.comment('My comment can be any text.')
474
+ end
475
+ ```
476
+ <!-- <<<<<< END INCLUDED FILE (ruby): SOURCE readme_files/scripts/comment.rb -->
477
+
478
+ <!-- >>>>>> BEGIN INCLUDED FILE (xml): SOURCE readme_files/logs/comment.xml -->
479
+ ```comment.xml```:
480
+ ```xml
481
+ <log>
482
+ <comment>
483
+ My comment can be any text.
484
+ </comment>
485
+ </log>
486
+ ```
487
+ <!-- <<<<<< END INCLUDED FILE (xml): SOURCE readme_files/logs/comment.xml -->
488
+
489
+ ## Custom Logging
490
+ <img src="images/custom.png" width="70" alt="Custom">
491
+
492
+ At the heart of class <code>StructuredLog</code> is method <code>put_element</code>. It logs an element, possibly with children, attributes, and text. Several methods call it, and you can too.
493
+
494
+ Basically, it's just like method <code>section</code>, except that you choose the element name (instead of the fixed name <code>section</code>).
495
+
496
+ Otherwise, it handles a block and all the same arguments as <code>section</code>.
497
+
498
+ ### Section
499
+
500
+ Create a custom section by calling method <code>put_element</code> with a block. The custom section will have children if you call logging methods within the block.
501
+
502
+ <!-- >>>>>> BEGIN INCLUDED FILE (ruby): SOURCE readme_files/scripts/custom_section.rb -->
503
+ ```custom_section.rb```:
504
+ ```ruby
505
+ require 'structured_log'
506
+
507
+ StructuredLog.open('custom_section.xml') do |log|
508
+ log.section('With blocks') do
509
+ log.put_element('section_with_children') do
510
+ log.put_element('child', :rank => 'Older')
511
+ log.put_element('child', :rank => 'Younger')
512
+ end
513
+ log.put_element('section_with_duration', :duration, 'Block contains timed code to be timed.') do
514
+ sleep 1
515
+ end
516
+ log.put_element('section_with_rescue', :rescue, 'Block contains code to be rescued if necessary.') do
517
+ end
518
+ end
519
+ end
520
+ ```
521
+ <!-- <<<<<< END INCLUDED FILE (ruby): SOURCE readme_files/scripts/custom_section.rb -->
522
+
523
+ <!-- >>>>>> BEGIN INCLUDED FILE (xml): SOURCE readme_files/logs/custom_section.xml -->
524
+ ```custom_section.xml```:
525
+ ```xml
526
+ <log>
527
+ <section name='With blocks'>
528
+ <section_with_children>
529
+ <child rank='Older'/>
530
+ <child rank='Younger'/>
531
+ </section_with_children>
532
+ <section_with_duration duration_seconds='1.016'>
533
+ Block contains timed code to be timed.
534
+ </section_with_duration>
535
+ <section_with_rescue>
536
+ Block contains code to be rescued if necessary.
537
+ </section_with_rescue>
538
+ </section>
539
+ </log>
540
+ ```
541
+ <!-- <<<<<< END INCLUDED FILE (xml): SOURCE readme_files/logs/custom_section.xml -->
542
+
543
+ ### Entry
544
+
545
+ Create a custom entry by calling method <code>put_element</code> without a block. The custom entry will not have children.
546
+
547
+ <!-- >>>>>> BEGIN INCLUDED FILE (ruby): SOURCE readme_files/scripts/custom_entry.rb -->
548
+ ```custom_entry.rb```:
549
+ ```ruby
550
+ require 'structured_log'
551
+
552
+ StructuredLog.open('custom_entry.xml') do |log|
553
+ log.section('Without blocks') do
554
+ log.put_element('element_with_text', 'No child elements, just this text.')
555
+ log.put_element('element_with_attributes', {:a => 0, :b => 1})
556
+ log.put_element('element_with_timestamp', :timestamp)
557
+ log.put_element('element_with_data', 3.14159)
558
+ end
559
+ end
560
+ ```
561
+ <!-- <<<<<< END INCLUDED FILE (ruby): SOURCE readme_files/scripts/custom_entry.rb -->
562
+
563
+ <!-- >>>>>> BEGIN INCLUDED FILE (xml): SOURCE readme_files/logs/custom_entry.xml -->
564
+ ```custom_entry.xml```:
565
+ ```xml
566
+ <log>
567
+ <section name='Without blocks'>
568
+ <element_with_text>
569
+ No child elements, just this text.
570
+ </element_with_text>
571
+ <element_with_attributes a='0' b='1'/>
572
+ <element_with_timestamp timestamp='2018-06-09-Sat-11.38.09.357'/>
573
+ <element_with_data>
574
+ 3.14159
575
+ </element_with_data>
576
+ </section>
577
+ </log>
578
+ ```
579
+ <!-- <<<<<< END INCLUDED FILE (xml): SOURCE readme_files/logs/custom_entry.xml -->
580
+
581
+ ## Uncaught Exception
582
+ <img src="images/exception.png" width="70" alt="Exception">
583
+
584
+ Finally, what about an uncaught exception, one not rescued by <code>:rescue</code>?
585
+
586
+ When an exception is raised in a section that does not have <code>:rescue</code>, the logger rescues and logs it anyway, just as if there were an invisible "outermost section" with <code>:rescue</code> (which, in fact, there is).
587
+
588
+ Just as for a rescued exception, the log includes the exception's class, message, and backtrace.
589
+
590
+ <!-- >>>>>> BEGIN INCLUDED FILE (ruby): SOURCE readme_files/scripts/exception.rb -->
591
+ ```exception.rb```:
592
+ ```ruby
593
+ require 'structured_log'
594
+
595
+ StructuredLog.open('exception.xml') do |log|
596
+ fail('Oops!')
597
+ end
598
+ ```
599
+ <!-- <<<<<< END INCLUDED FILE (ruby): SOURCE readme_files/scripts/exception.rb -->
600
+
601
+ <!-- >>>>>> BEGIN INCLUDED FILE (xml): SOURCE readme_files/logs/exception.xml -->
602
+ ```exception.xml```:
603
+ ```xml
604
+ <log>
605
+ <uncaught_exception timestamp='2018-06-09-Sat-11.38.10.967' class='RuntimeError'>
606
+ <message>
607
+ Oops!
608
+ </message>
609
+ <backtrace>
610
+ <![CDATA[
611
+ C:/Users/Burdette/Documents/GitHub/structured_log/readme_files/scripts/exception.rb:4:in `block in <main>'
612
+ C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/structured_log-0.1.0/lib/structured_log.rb:38:in `open'
613
+ C:/Users/Burdette/Documents/GitHub/structured_log/readme_files/scripts/exception.rb:3:in `<main>'
614
+ ]]>
615
+ </backtrace>
616
+ </uncaught_exception>
617
+ </log>
618
+ ```
619
+ <!-- <<<<<< END INCLUDED FILE (xml): SOURCE readme_files/logs/exception.xml -->
620
+ <!-- <<<<<< END GENERATED FILE (include): SOURCE readme_files/README.template.md -->