logsly 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -3
  3. data/lib/logsly/colors.rb +2 -2
  4. data/lib/logsly/logging182/appender.rb +290 -0
  5. data/lib/logsly/logging182/appenders/buffering.rb +398 -0
  6. data/lib/logsly/logging182/appenders/console.rb +81 -0
  7. data/lib/logsly/logging182/appenders/email.rb +178 -0
  8. data/lib/logsly/logging182/appenders/file.rb +85 -0
  9. data/lib/logsly/logging182/appenders/growl.rb +200 -0
  10. data/lib/logsly/logging182/appenders/io.rb +84 -0
  11. data/lib/logsly/logging182/appenders/rolling_file.rb +338 -0
  12. data/lib/logsly/logging182/appenders/string_io.rb +92 -0
  13. data/lib/logsly/logging182/appenders/syslog.rb +215 -0
  14. data/lib/logsly/logging182/appenders.rb +64 -0
  15. data/lib/logsly/logging182/color_scheme.rb +248 -0
  16. data/lib/logsly/logging182/config/configurator.rb +187 -0
  17. data/lib/logsly/logging182/config/yaml_configurator.rb +190 -0
  18. data/lib/logsly/logging182/diagnostic_context.rb +332 -0
  19. data/lib/logsly/logging182/layout.rb +132 -0
  20. data/lib/logsly/logging182/layouts/basic.rb +38 -0
  21. data/lib/logsly/logging182/layouts/parseable.rb +256 -0
  22. data/lib/logsly/logging182/layouts/pattern.rb +568 -0
  23. data/lib/logsly/logging182/layouts.rb +9 -0
  24. data/lib/logsly/logging182/log_event.rb +44 -0
  25. data/lib/logsly/logging182/logger.rb +509 -0
  26. data/lib/logsly/logging182/proxy.rb +59 -0
  27. data/lib/logsly/logging182/rails_compat.rb +36 -0
  28. data/lib/logsly/logging182/repository.rb +231 -0
  29. data/lib/logsly/logging182/root_logger.rb +60 -0
  30. data/lib/logsly/logging182/stats.rb +277 -0
  31. data/lib/logsly/logging182/utils.rb +231 -0
  32. data/lib/logsly/logging182.rb +559 -0
  33. data/lib/logsly/outputs.rb +5 -5
  34. data/lib/logsly/version.rb +1 -1
  35. data/lib/logsly.rb +6 -6
  36. data/logsly.gemspec +4 -2
  37. data/test/unit/colors_tests.rb +3 -3
  38. data/test/unit/logsly_tests.rb +14 -14
  39. data/test/unit/outputs_tests.rb +34 -24
  40. metadata +45 -6
@@ -0,0 +1,132 @@
1
+
2
+ module Logsly::Logging182
3
+
4
+ # The +Layout+ class provides methods for formatting log events into a
5
+ # string representation. Layouts are used by Appenders to format log
6
+ # events before writing them to the logging destination.
7
+ #
8
+ # All other Layouts inherit from this class which provides stub methods.
9
+ # Each subclass should provide a +format+ method. A layout can be used by
10
+ # more than one +Appender+ so all the methods need to be thread safe.
11
+ #
12
+ class Layout
13
+
14
+ # call-seq:
15
+ # Layout.new( :format_as => :string )
16
+ #
17
+ # Creates a new layout that will format objects as strings using the
18
+ # given <tt>:format_as</tt> style. This can be one of <tt>:string</tt>,
19
+ # <tt>:inspect</tt>, or <tt>:yaml</tt>. These formatting commands map to
20
+ # the following object methods:
21
+ #
22
+ # * :string => to_s
23
+ # * :inspect => inspect
24
+ # * :yaml => to_yaml
25
+ # * :json => MultiJson.encode(obj)
26
+ #
27
+ # If the format is not specified then the global object format is used
28
+ # (see Logsly::Logging182#format_as). If the global object format is not specified
29
+ # then <tt>:string</tt> is used.
30
+ #
31
+ def initialize( opts = {} )
32
+ ::Logsly::Logging182.init unless ::Logsly::Logging182.initialized?
33
+
34
+ default = ::Logsly::Logging182.const_defined?('OBJ_FORMAT') ?
35
+ ::Logsly::Logging182::OBJ_FORMAT : nil
36
+
37
+ f = opts.getopt(:format_as, default)
38
+ f = f.intern if f.instance_of? String
39
+
40
+ @obj_format = case f
41
+ when :inspect, :yaml, :json; f
42
+ else :string end
43
+
44
+ b = opts.getopt(:backtrace, ::Logsly::Logging182.backtrace)
45
+ @backtrace = case b
46
+ when :on, 'on', true; true
47
+ when :off, 'off', false; false
48
+ else
49
+ raise ArgumentError, "backtrace must be true or false"
50
+ end
51
+ end
52
+
53
+ # call-seq:
54
+ # format( event )
55
+ #
56
+ # Returns a string representation of the given logging _event_. It is
57
+ # up to subclasses to implement this method.
58
+ #
59
+ def format( event ) nil end
60
+
61
+ # call-seq:
62
+ # header
63
+ #
64
+ # Returns a header string to be used at the beginning of a logging
65
+ # appender.
66
+ #
67
+ def header( ) '' end
68
+
69
+ # call-seq:
70
+ # footer
71
+ #
72
+ # Returns a footer string to be used at the end of a logging appender.
73
+ #
74
+ def footer( ) '' end
75
+
76
+ # call-seq:
77
+ # format_obj( obj )
78
+ #
79
+ # Return a string representation of the given object. Depending upon
80
+ # the configuration of the logger system the format will be an +inspect+
81
+ # based representation or a +yaml+ based representation.
82
+ #
83
+ def format_obj( obj )
84
+ case obj
85
+ when String; obj
86
+ when Exception
87
+ str = "<#{obj.class.name}> #{obj.message}"
88
+ if @backtrace && !obj.backtrace.nil?
89
+ str << "\n\t" << obj.backtrace.join("\n\t")
90
+ end
91
+ str
92
+ when nil; "<#{obj.class.name}> nil"
93
+ else
94
+ str = "<#{obj.class.name}> "
95
+ str << case @obj_format
96
+ when :inspect; obj.inspect
97
+ when :yaml; try_yaml(obj)
98
+ when :json; try_json(obj)
99
+ else obj.to_s end
100
+ str
101
+ end
102
+ end
103
+
104
+ # Attempt to format the _obj_ using yaml, but fall back to inspect style
105
+ # formatting if yaml fails.
106
+ #
107
+ # obj - The Object to format.
108
+ #
109
+ # Returns a String representation of the object.
110
+ #
111
+ def try_yaml( obj )
112
+ "\n#{obj.to_yaml}"
113
+ rescue TypeError
114
+ obj.inspect
115
+ end
116
+
117
+ # Attempt to format the given object as a JSON string, but fall back to
118
+ # inspect formatting if JSON encoding fails.
119
+ #
120
+ # obj - The Object to format.
121
+ #
122
+ # Returns a String representation of the object.
123
+ #
124
+ def try_json( obj )
125
+ MultiJson.encode(obj)
126
+ rescue StandardError
127
+ obj.inspect
128
+ end
129
+
130
+ end # class Layout
131
+ end # module Logsly::Logging182
132
+
@@ -0,0 +1,38 @@
1
+
2
+ module Logsly::Logging182::Layouts
3
+
4
+ # Accessor / Factory for the Basic layout.
5
+ #
6
+ def self.basic( *args )
7
+ return ::Logsly::Logging182::Layouts::Basic if args.empty?
8
+ ::Logsly::Logging182::Layouts::Basic.new(*args)
9
+ end
10
+
11
+ # The +Basic+ layout class provides methods for simple formatting of log
12
+ # events. The resulting string follows the format below.
13
+ #
14
+ # LEVEL LoggerName : log message
15
+ #
16
+ # _LEVEL_ is the log level of the event. _LoggerName_ is the name of the
17
+ # logger that generated the event. <em>log message</em> is the message
18
+ # or object that was passed to the logger. If multiple message or objects
19
+ # were passed to the logger then each will be printed on its own line with
20
+ # the format show above.
21
+ #
22
+ class Basic < ::Logsly::Logging182::Layout
23
+
24
+ # call-seq:
25
+ # format( event )
26
+ #
27
+ # Returns a string representation of the given logging _event_. See the
28
+ # class documentation for details about the formatting used.
29
+ #
30
+ def format( event )
31
+ obj = format_obj(event.data)
32
+ sprintf("%*s %s : %s\n", ::Logsly::Logging182::MAX_LEVEL_LENGTH,
33
+ ::Logsly::Logging182::LNAMES[event.level], event.logger, obj)
34
+ end
35
+
36
+ end # Basic
37
+ end # Logsly::Logging182::Layouts
38
+
@@ -0,0 +1,256 @@
1
+
2
+ module Logsly::Logging182::Layouts
3
+
4
+ # Accessor for the Parseable layout.
5
+ #
6
+ def self.parseable
7
+ ::Logsly::Logging182::Layouts::Parseable
8
+ end
9
+
10
+ # Factory for the Parseable layout using JSON formatting.
11
+ #
12
+ def self.json( *args )
13
+ ::Logsly::Logging182::Layouts::Parseable.json(*args)
14
+ end
15
+
16
+ # Factory for the Parseable layout using YAML formatting.
17
+ #
18
+ def self.yaml( *args )
19
+ ::Logsly::Logging182::Layouts::Parseable.yaml(*args)
20
+ end
21
+
22
+ # This layout will produce parseable log output in either JSON or YAML
23
+ # format. This makes it much easier for machines to parse log files and
24
+ # perform analysis on those logs.
25
+ #
26
+ # The information about the log event can be configured when the layout is
27
+ # created. Any or all of the following labels can be set as the _items_ to
28
+ # log:
29
+ #
30
+ # 'logger' Used to output the name of the logger that generated the
31
+ # log event.
32
+ # 'timestamp' Used to output the timestamp of the log event.
33
+ # 'level' Used to output the level of the log event.
34
+ # 'message' Used to output the application supplied message
35
+ # associated with the log event.
36
+ # 'file' Used to output the file name where the logging request
37
+ # was issued.
38
+ # 'line' Used to output the line number where the logging request
39
+ # was issued.
40
+ # 'method' Used to output the method name where the logging request
41
+ # was issued.
42
+ # 'pid' Used to output the process ID of the currently running
43
+ # program.
44
+ # 'millis' Used to output the number of milliseconds elapsed from
45
+ # the construction of the Layout until creation of the log
46
+ # event.
47
+ # 'thread_id' Used to output the object ID of the thread that generated
48
+ # the log event.
49
+ # 'thread' Used to output the name of the thread that generated the
50
+ # log event. Name can be specified using Thread.current[:name]
51
+ # notation. Output empty string if name not specified. This
52
+ # option helps to create more human readable output for
53
+ # multithread application logs.
54
+ #
55
+ # These items are supplied to the layout as an array of strings. The items
56
+ # 'file', 'line', and 'method' will only work if the Logger generating the
57
+ # events is configured to generate tracing information. If this is not the
58
+ # case these fields will always be empty.
59
+ #
60
+ # When configured to output log events in YAML format, each log message
61
+ # will be formatted as a hash in it's own YAML document. The hash keys are
62
+ # the name of the item, and the value is what you would expect it to be.
63
+ # Therefore, for the default set of times log message would appear as
64
+ # follows:
65
+ #
66
+ # ---
67
+ # timestamp: 2009-04-17T16:15:42
68
+ # level: INFO
69
+ # logger: Foo::Bar
70
+ # message: this is a log message
71
+ # ---
72
+ # timestamp: 2009-04-17T16:15:43
73
+ # level: ERROR
74
+ # logger: Foo
75
+ # message: <RuntimeError> Oooops!!
76
+ #
77
+ # The output order of the fields is not guaranteed to be the same as the
78
+ # order specified in the _items_ list. This is because Ruby hashes are not
79
+ # ordered by default (unless you're running this in Ruby 1.9).
80
+ #
81
+ # When configured to output log events in JSON format, each log message
82
+ # will be formatted as an object (in the JSON sense of the word) on it's
83
+ # own line in the log output. Therefore, to parse the output you must read
84
+ # it line by line and parse the individual objects. Taking the same
85
+ # example above the JSON output would be:
86
+ #
87
+ # {"timestamp":"2009-04-17T16:15:42","level":"INFO","logger":"Foo::Bar","message":"this is a log message"}
88
+ # {"timestamp":"2009-04-17T16:15:43","level":"ERROR","logger":"Foo","message":"<RuntimeError> Oooops!!"}
89
+ #
90
+ # The output order of the fields is guaranteed to be the same as the order
91
+ # specified in the _items_ list.
92
+ #
93
+ class Parseable < ::Logsly::Logging182::Layout
94
+
95
+ # :stopdoc:
96
+ # Arguments to sprintf keyed to directive letters
97
+ DIRECTIVE_TABLE = {
98
+ 'logger' => 'event.logger'.freeze,
99
+ 'timestamp' => 'iso8601_format(event.time)'.freeze,
100
+ 'level' => '::Logsly::Logging182::LNAMES[event.level]'.freeze,
101
+ 'message' => 'format_obj(event.data)'.freeze,
102
+ 'file' => 'event.file'.freeze,
103
+ 'line' => 'event.line'.freeze,
104
+ 'method' => 'event.method'.freeze,
105
+ 'pid' => 'Process.pid'.freeze,
106
+ 'millis' => 'Integer((event.time-@created_at)*1000)'.freeze,
107
+ 'thread_id' => 'Thread.current.object_id'.freeze,
108
+ 'thread' => 'Thread.current[:name]'.freeze,
109
+ 'mdc' => 'Logsly::Logging182::MappedDiagnosticContext.context'.freeze,
110
+ 'ndc' => 'Logsly::Logging182::NestedDiagnosticContext.context'.freeze
111
+ }
112
+
113
+ # call-seq:
114
+ # Pattern.create_yaml_format_methods( layout )
115
+ #
116
+ # This method will create the +format+ method in the given Parseable
117
+ # _layout_ based on the configured items for the layout instance.
118
+ #
119
+ def self.create_yaml_format_method( layout )
120
+ code = "undef :format if method_defined? :format\n"
121
+ code << "def format( event )\nstr = {\n"
122
+
123
+ code << layout.items.map {|name|
124
+ "'#{name}' => #{Parseable::DIRECTIVE_TABLE[name]}"
125
+ }.join(",\n")
126
+ code << "\n}.to_yaml\nreturn str\nend\n"
127
+
128
+ (class << layout; self end).class_eval(code, __FILE__, __LINE__)
129
+ end
130
+
131
+ # call-seq:
132
+ # Pattern.create_json_format_methods( layout )
133
+ #
134
+ # This method will create the +format+ method in the given Parseable
135
+ # _layout_ based on the configured items for the layout instance.
136
+ #
137
+ def self.create_json_format_method( layout )
138
+ code = "undef :format if method_defined? :format\n"
139
+ code << "def format( event )\nh = {\n"
140
+
141
+ code << layout.items.map {|name|
142
+ "'#{name}' => #{Parseable::DIRECTIVE_TABLE[name]}"
143
+ }.join(",\n")
144
+ code << "\n}\nMultiJson.encode(h) << \"\\n\"\nend\n"
145
+
146
+ (class << layout; self end).class_eval(code, __FILE__, __LINE__)
147
+ end
148
+ # :startdoc:
149
+
150
+ # call-seq:
151
+ # Parseable.json( opts )
152
+ #
153
+ # Create a new Parseable layout that outputs log events using JSON style
154
+ # formatting. See the initializer documentation for available options.
155
+ #
156
+ def self.json( opts = {} )
157
+ opts[:style] = 'json'
158
+ new(opts)
159
+ end
160
+
161
+ # call-seq:
162
+ # Parseable.yaml( opts )
163
+ #
164
+ # Create a new Parseable layout that outputs log events using YAML style
165
+ # formatting. See the initializer documentation for available options.
166
+ #
167
+ def self.yaml( opts = {} )
168
+ opts[:style] = 'yaml'
169
+ new(opts)
170
+ end
171
+
172
+ # call-seq:
173
+ # Parseable.new( opts )
174
+ #
175
+ # Creates a new Parseable layout using the following options:
176
+ #
177
+ # :style => :json or :yaml
178
+ # :items => %w[timestamp level logger message]
179
+ #
180
+ def initialize( opts = {} )
181
+ super
182
+ @created_at = Time.now
183
+ @style = opts.getopt(:style, 'json').to_s.intern
184
+ self.items = opts.getopt(:items, %w[timestamp level logger message])
185
+ end
186
+
187
+ attr_reader :items
188
+
189
+ # call-seq:
190
+ # layout.items = %w[timestamp level logger message]
191
+ #
192
+ # Set the log event items that will be formatted by this layout. These
193
+ # items, and only these items, will appear in the log output.
194
+ #
195
+ def items=( ary )
196
+ @items = Array(ary).map {|name| name.to_s.downcase}
197
+ valid = DIRECTIVE_TABLE.keys
198
+ @items.each do |name|
199
+ raise ArgumentError, "unknown item - #{name.inspect}" unless valid.include? name
200
+ end
201
+ create_format_method
202
+ end
203
+
204
+ # Public: Take a given object and convert it into a format suitable for
205
+ # inclusion as a log message. The conversion allows the object to be more
206
+ # easily expressed in YAML or JSON form.
207
+ #
208
+ # If the object is an Exception, then this method will return a Hash
209
+ # containing the exception class name, message, and backtrace (if any).
210
+ #
211
+ # obj - The Object to format
212
+ #
213
+ # Returns the formatted Object.
214
+ #
215
+ def format_obj( obj )
216
+ case obj
217
+ when Exception
218
+ h = { :class => obj.class.name,
219
+ :message => obj.message }
220
+ h[:backtrace] = obj.backtrace if @backtrace && !obj.backtrace.nil?
221
+ h
222
+ when Time
223
+ iso8601_format(obj)
224
+ else
225
+ obj
226
+ end
227
+ end
228
+
229
+ private
230
+
231
+ # Call the appropriate class level create format method based on the
232
+ # style of this parseable layout.
233
+ #
234
+ def create_format_method
235
+ case @style
236
+ when :json; Parseable.create_json_format_method(self)
237
+ when :yaml; Parseable.create_yaml_format_method(self)
238
+ else raise ArgumentError, "unknown format style '#@style'" end
239
+ end
240
+
241
+ # Convert the given time _value_ into an ISO8601 formatted time string.
242
+ #
243
+ def iso8601_format( value )
244
+ str = value.strftime('%Y-%m-%dT%H:%M:%S')
245
+ str << ('.%06d' % value.usec)
246
+
247
+ offset = value.gmt_offset.abs
248
+ return str << 'Z' if offset == 0
249
+
250
+ offset = sprintf('%02d:%02d', offset / 3600, offset % 3600 / 60)
251
+ return str << (value.gmt_offset < 0 ? '-' : '+') << offset
252
+ end
253
+
254
+ end # Parseable
255
+ end # Logsly::Logging182::Layouts
256
+