# Structured Log
Class StructuredLog
offers structured (as opposed to flat) logging. Nested sections (blocks) in Ruby code become nested XML elements in the log.
This sectioning allows you to group actions in your program, and that grouping carries over into the log.
Optionally, each section may include:
section
element by passing a string argument.
```text.rb```:
```ruby
require 'structured_log'
text = 'This section has text.'
StructuredLog.open('text.xml') do |log|
log.section('with_text', text) do
# Any code can be here.
end
end
```
```text.xml```:
```xml
section
element by passing a hash argument.
```attributes.rb```:
```ruby
require 'structured_log'
attributes = {:a => 0, :b => 1}
StructuredLog.open('attributes.xml') do |log|
log.section('with_attributes', attributes) do
log.comment('This section has attributes a and b.')
end
end
```
```attributes.xml```:
```xml
section
element by passing a special symbol argument.
```time.rb```:
```ruby
require 'structured_log'
StructuredLog.open('time.xml') do |log|
log.section('Section with timestamp', :timestamp) do
log.comment('This section has a timestamp.')
end
log.section('Section with duration', :duration) do
log.comment('This section has a duration.')
sleep 1
end
log.section('Section with both', :duration, :timestamp) do
log.comment('This section has both.')
sleep 1
end
end
```
```time.xml```:
```xml
section
element by passing a special symbol argument.
For the rescued exception, the class, message, and backtrace are logged.
```rescue.rb```:
```ruby
require 'structured_log'
StructuredLog.open('rescue.xml') do |log|
log.section('Section with rescue', :rescue) do
log.comment('This section will terminate because of the failure.')
fail 'This exception will be rescued and logged.'
log.comment('This comment will not be reached.')
end
log.section('Another section') do
log.comment('This comment will be reached and logged, because of rescue above.')
end
end
```
```rescue.xml```:
```xml
section
.
The section name must be first; after that, anything goes.
```potpourri.rb```:
```ruby
require 'structured_log'
attributes = {:a => 0, :b => 1}
more_attributes = {:c => 2, :d => 3}
text = 'This section has a potpourri.'
float = 3.14159
boolean = false
fixnum = 1066
time = Time.new
exception = RuntimeError.new('Oops!')
StructuredLog.open('potpourri.xml') do |log|
log.section('All together now', 'Order does not matter except in aggregating text and attributes.') do
args = [attributes, :rescue, text, float, :duration, more_attributes, boolean, :timestamp, fixnum, time, exception]
log.section('Potpourri', *args) do
end
log.section('Reverse potpourri', *args.reverse) do
end
end
end
```
```potpourri.xml```:
```xml
put_each_pair
, or its alias put_hash
, to log an object that respond_to?(:each_pair)
.
```hash.rb```:
```ruby
require 'structured_log'
hash = {
:a => 'z',
:aa => 'zz',
:aaa => 'zzz',
:aaaa => 'zzzz',
}
StructuredLog.open('hash.xml') do |log|
log.put_hash('my_hash', hash)
end
```
```hash.xml```:
```xml
put_each_with_index
, or its aliases put_array
and put_set
, to log an object that respond_to?(:each_with_index)
.
```array.rb```:
```ruby
require 'structured_log'
array = %w/foo bar baz bat/
StructuredLog.open('array.xml') do |log|
log.put_array('my_array', array)
end
```
```array.xml```:
```xml
put_data
to log any object.
```data.rb```:
```ruby
require 'structured_log'
data = {
:float => 3.14,
:fixnum => 1066,
:false => false,
:time => Time.new,
:exception => RuntimeError.new('Oops!'),
:nil => nil,
}
StructuredLog.open('data.xml') do |log|
data.each_pair do |type, datum|
name = "my_#{type}"
log.put_data(name, datum)
end
end
```
```data.xml```:
```xml
put_cdata
to log a string (possibly multi-line) as CDATA.
```cdata.rb```:
```ruby
require 'structured_log'
text = <comment
to log a comment.
```comment.rb```:
```ruby
require 'structured_log'
StructuredLog.open('comment.xml') do |log|
log.comment('My comment can be any text.')
end
```
```comment.xml```:
```xml
StructuredLog
is method put_element
. It logs an element, possibly with children, attributes, and text. Several methods call it, and you can too.
Basically, it's just like method section
, except that you choose the element name (instead of the fixed name section
).
Otherwise, it handles a block and all the same arguments as section
.
### Section
Create a custom section by calling method put_element
with a block. The custom section will have children if you call logging methods within the block.
```custom_section.rb```:
```ruby
require 'structured_log'
StructuredLog.open('custom_section.xml') do |log|
log.section('With blocks') do
log.put_element('section_with_children') do
log.put_element('child', :rank => 'Older')
log.put_element('child', :rank => 'Younger')
end
log.put_element('section_with_duration', :duration, 'Block contains timed code to be timed.') do
sleep 1
end
log.put_element('section_with_rescue', :rescue, 'Block contains code to be rescued if necessary.') do
end
end
end
```
```custom_section.xml```:
```xml
put_element
without a block. The custom entry will not have children.
```custom_entry.rb```:
```ruby
require 'structured_log'
StructuredLog.open('custom_entry.xml') do |log|
log.section('Without blocks') do
log.put_element('element_with_text', 'No child elements, just this text.')
log.put_element('element_with_attributes', {:a => 0, :b => 1})
log.put_element('element_with_timestamp', :timestamp)
log.put_element('element_with_data', 3.14159)
end
end
```
```custom_entry.xml```:
```xml
:rescue
?
When an exception is raised in a section that does not have :rescue
, the logger rescues and logs it anyway, just as if there were an invisible "outermost section" with :rescue
(which, in fact, there is).
Just as for a rescued exception, the log includes the exception's class, message, and backtrace.
```exception.rb```:
```ruby
require 'structured_log'
StructuredLog.open('exception.xml') do |log|
fail('Oops!')
end
```
```exception.xml```:
```xml