logsly 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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
+