logg 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- data/Guardfile +21 -0
- data/MIT-LICENSE +20 -0
- data/README.md +96 -0
- data/Rakefile +25 -0
- data/lib/logg.rb +4 -0
- data/lib/logg/core.rb +220 -0
- data/lib/logg/render.rb +2 -0
- data/lib/logg/version.rb +6 -0
- metadata +63 -0
data/Guardfile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at http://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard 'cucumber' do
|
5
|
+
watch('^features/(.*).feature')
|
6
|
+
watch('^features/support') { 'features' }
|
7
|
+
watch('^features/step_definitions') { 'features' }
|
8
|
+
end
|
9
|
+
|
10
|
+
guard 'rspec', :version => 2 do
|
11
|
+
watch('^spec/(.*)_spec.rb')
|
12
|
+
watch('^lib/(.*)\.rb') { |m| "spec/lib/#{m[1]}_spec.rb" }
|
13
|
+
watch('^spec/spec_helper.rb') { "spec" }
|
14
|
+
|
15
|
+
# Rails example
|
16
|
+
watch('^app/(.*)\.rb') { |m| "spec/#{m[1]}_spec.rb" }
|
17
|
+
# watch('^lib/(.*)\.rb') { |m| "spec/lib/#{m[1]}_spec.rb" }
|
18
|
+
watch('^config/routes.rb') { "spec/routing" }
|
19
|
+
watch('^app/controllers/application_controller.rb') { "spec/controllers" }
|
20
|
+
watch('^spec/factories.rb') { "spec/models" }
|
21
|
+
end
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2010 YOURNAME
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
# Logg
|
2
|
+
|
3
|
+
A simple message dispatcher (aka. logger) for your Ruby applications.
|
4
|
+
|
5
|
+
## Install
|
6
|
+
|
7
|
+
``` bash
|
8
|
+
$ gem install logg
|
9
|
+
```
|
10
|
+
|
11
|
+
## Synopsis
|
12
|
+
|
13
|
+
Logg is a library providing generic logging features. At the core of Logg is a module, `Logg::Machine`, which you may include (mixin) in a class, or extend within another module. This will inject the Logg helpers, so one can write something like this:
|
14
|
+
|
15
|
+
``` ruby
|
16
|
+
class Foo
|
17
|
+
include Logg::Machine
|
18
|
+
end
|
19
|
+
|
20
|
+
Foo.log.debug "test!" # => Fri Dec 31 16:00:09 +0100 2010 | [debug] test!
|
21
|
+
Foo.new.log.debug "test…" # => Fri Dec 31 16:00:09 +0100 2010 | [debug] test…
|
22
|
+
Foo.new.log.error "failed" # => Fri Dec 31 16:00:09 +0100 2010 | [error] failed
|
23
|
+
```
|
24
|
+
|
25
|
+
This illustrates the basic use case, with the default message format being: `time | [namespace] message` where namespace is the method called on the logger.
|
26
|
+
|
27
|
+
Many other use cases are available under the `examples/` directory, based on the Cucumber `features/`. This README explains some of them.
|
28
|
+
|
29
|
+
## Custom loggers
|
30
|
+
|
31
|
+
Usually, logging engines provide you with a bunch of "log levels", such as FATAL, ERROR, WARNING, NOTICE. Logg does not enforce such a convention and rather let you define your own, if required, but does not enforce you to do so. More generally, one may create custom loggers using `Logg::Dispatcher#as`:
|
32
|
+
|
33
|
+
``` ruby
|
34
|
+
class Foo
|
35
|
+
include Logg::Machine
|
36
|
+
|
37
|
+
# let's define a custom logger
|
38
|
+
log.as(:failure) do |data|
|
39
|
+
# play with data and render/do something, somewhere: output a String,
|
40
|
+
# send an email, anything you want.
|
41
|
+
end
|
42
|
+
|
43
|
+
# then use it!
|
44
|
+
log.failure my_data
|
45
|
+
end
|
46
|
+
```
|
47
|
+
|
48
|
+
`as` expects a mandatory block, which may take any number of arguments, of any kind. Within the block, it is expected you will "log" somehow, but actually you are free to perform anything. You may output a simple string on $stdout, call an external API through HTTP, send an email, or even render a template (see below): that's just legacy ruby code in here! All in all, Logg is just a mega-method-definition-machine, aimed at logging—but feel free to use it the way you like (dispatching events, for instance).
|
49
|
+
|
50
|
+
Soon to come: the possibility to change `#log` for any other valid method name.
|
51
|
+
|
52
|
+
Note: if you would like to define a custom logger under the name `#as`, the helper method is also available under the `#_as` alias.
|
53
|
+
|
54
|
+
## Message formatting, templates
|
55
|
+
|
56
|
+
Logging is all about building meaningful messages. You may also want to log to the tty, a file and send an email on top of that, and each one of those output channels would benefit from using a different data representation. One should thus be provided with efficient tools to define how a message is rendered in particular context. Logg makes use of Tilt to help you format your data. Tilt is a wrapper around several template engines (you may know about ERB or haml, but there are many others). Just tell Logg which format you want to use and go ahead! The dispatching logic is of your responsability.
|
57
|
+
|
58
|
+
For more details, see `examples/` and read/run the Cucumber `features/` (command: `cucumber features`).
|
59
|
+
|
60
|
+
``` ruby
|
61
|
+
class Foo
|
62
|
+
include Logg::Machine
|
63
|
+
|
64
|
+
# let's use vanilla ruby code for the first example: output the
|
65
|
+
# message to $stdout using #puts
|
66
|
+
log.as(:foo) do
|
67
|
+
puts "something really important happened"
|
68
|
+
end
|
69
|
+
|
70
|
+
# you may also define a template within the block, render it and
|
71
|
+
# use the result.
|
72
|
+
# (Keep in mind that this kind of template with heavy code is considered bad practice ;))
|
73
|
+
log.as(:foo) do |data|
|
74
|
+
tpl = "Data: <%= self.map { |k,v| puts k.to_s + v.to_s } %>"
|
75
|
+
puts render_inline(tpl, :as => :erb, :data => data)
|
76
|
+
end
|
77
|
+
log.foo({:foo => :bar}) # => "Data: foobar"
|
78
|
+
|
79
|
+
# now we want to render an external HAML template, providing its path with
|
80
|
+
# or withouth the .haml extension (if not provided, the :as option is mandatory)
|
81
|
+
log.as(:http_response) do |resp|
|
82
|
+
output = render('tpl/foo', :as => :haml, :data => resp)
|
83
|
+
# do something with output
|
84
|
+
end
|
85
|
+
end
|
86
|
+
```
|
87
|
+
|
88
|
+
If you want to render to several logging endpoints, and send a mail on top of that, just do it within the block!
|
89
|
+
|
90
|
+
## Dispatching helpers
|
91
|
+
|
92
|
+
TODO: provide helpers for message dispatching/logging, levels managment and the like.
|
93
|
+
|
94
|
+
## About the logger implementation
|
95
|
+
|
96
|
+
When a class mixins the `Logg::Machine` module, a `Logg::Dispatcher` instance is created and associated (if possible, see below) to the receiving class, through method injection. The custom loggers blocks are runned in the context of a `Logg::Dispatcher::Render` class, so be aware you must inject in the closure any data you would require. This is by design so as to keep the logger's logic separated from the application burden, enforcing explicit control over the data payloads. If this is just too much a burden for you, you may avoid mixin `Logg::Machine` and just make use of the `Render` core implementation.
|
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'rubygems'
|
3
|
+
begin
|
4
|
+
require 'bundler/setup'
|
5
|
+
rescue LoadError
|
6
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'rake'
|
10
|
+
require 'rake/rdoctask'
|
11
|
+
|
12
|
+
require 'rspec/core'
|
13
|
+
require 'rspec/core/rake_task'
|
14
|
+
|
15
|
+
RSpec::Core::RakeTask.new(:spec)
|
16
|
+
|
17
|
+
task :default => :spec
|
18
|
+
|
19
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
20
|
+
rdoc.rdoc_dir = 'rdoc'
|
21
|
+
rdoc.title = 'Logg'
|
22
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
23
|
+
rdoc.rdoc_files.include('README.rdoc')
|
24
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
25
|
+
end
|
data/lib/logg.rb
ADDED
data/lib/logg/core.rb
ADDED
@@ -0,0 +1,220 @@
|
|
1
|
+
module Logg
|
2
|
+
# Set to true to puts output when using logger#debug default's method.
|
3
|
+
ALWAYS_PUTS = true
|
4
|
+
|
5
|
+
# A Dispatcher is a logger core implementation, providing logger's definition
|
6
|
+
# and output methods. It's not intented to be used directly but through the
|
7
|
+
# Er mixin, within a class.
|
8
|
+
#
|
9
|
+
class Dispatcher
|
10
|
+
class Render
|
11
|
+
# Render a template. Just a mere proxy for Tilt::Template#render method,
|
12
|
+
# the first argument being the filepath or file, and the latter,
|
13
|
+
# the usual arguments for Tilt's #render.
|
14
|
+
#
|
15
|
+
# @param [String, #path, #realpath] path filepath or an object behaving
|
16
|
+
# like a legacy File
|
17
|
+
# @param [Object] obj context object the template will be rendered within
|
18
|
+
# @param [Hash] args rendering context
|
19
|
+
# @option [Symbol] :as syntax engine
|
20
|
+
# @option [Object] :data template's rendering contextual object
|
21
|
+
# @option [Hash] :locals template's locals
|
22
|
+
# @return [String] the interpolated template
|
23
|
+
#
|
24
|
+
def render(path, *args)
|
25
|
+
args = args.first
|
26
|
+
path = detect_path(path)
|
27
|
+
tpl = fetch_template(args, path)
|
28
|
+
tpl.render(args[:data], args[:locals])
|
29
|
+
end
|
30
|
+
|
31
|
+
def render_inline(content, *args)
|
32
|
+
args = args.first
|
33
|
+
syntax = detect_syntax(args)
|
34
|
+
res = Object
|
35
|
+
|
36
|
+
Better::Tempfile.open(['dummylogg', ".#{syntax}"]) do |f|
|
37
|
+
f.write(content)
|
38
|
+
f.rewind
|
39
|
+
res = Tilt.new(f.path).render(args[:data], args[:locals])
|
40
|
+
end
|
41
|
+
res
|
42
|
+
end
|
43
|
+
|
44
|
+
def detect_path(path)
|
45
|
+
if path.respond_to?(:path)
|
46
|
+
path.path
|
47
|
+
elsif path.respond_to?(:realpath)
|
48
|
+
path.to_s
|
49
|
+
elsif path.respond_to?(:to_s)
|
50
|
+
path.to_s
|
51
|
+
else
|
52
|
+
raise ArgumentError, 'Missing file or a filepath.'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def fetch_template(args, path)
|
57
|
+
if args[:as]
|
58
|
+
begin
|
59
|
+
test_path = Pathname.new(path)
|
60
|
+
raise ArgumentError, "Invalid filepath #{path}" unless test_path.file?
|
61
|
+
rescue
|
62
|
+
test_path = Pathname.new(path + ".#{args[:as].to_s.downcase}")
|
63
|
+
raise ArgumentError, "Invalid filepath #{path}" unless test_path.file?
|
64
|
+
path = test_path.to_s
|
65
|
+
end
|
66
|
+
Tilt.const_get("#{args[:as].to_s.downcase.capitalize}Template").new(path)
|
67
|
+
else
|
68
|
+
Tilt.new(path)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def detect_syntax(options)
|
73
|
+
unless options.has_key?(:as)
|
74
|
+
raise ArgumentError, 'Missing template syntax specified as the :as option.'
|
75
|
+
end
|
76
|
+
options[:as].to_s
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
attr_reader :message, :namespace
|
81
|
+
|
82
|
+
# The Dispatcher default behavior relies on #method_missing. It sets both the
|
83
|
+
# message and a namespace, then auto-sends the order to output.
|
84
|
+
#
|
85
|
+
def method_missing(meth, *args, &block)
|
86
|
+
@namespace = meth.to_s
|
87
|
+
@message = (args.first.to_s == 'debug') ? nil : args.first.to_s
|
88
|
+
self.send :output!
|
89
|
+
end
|
90
|
+
|
91
|
+
def eigenclass
|
92
|
+
class << self; self; end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Define a custom logger, using a template. The template may be defined
|
96
|
+
# within the block as a (multi-line) string, or one may reference a
|
97
|
+
# file.
|
98
|
+
# # do whatever you want with data or anything else, for instance,
|
99
|
+
# send mails, tweet, then…
|
100
|
+
#
|
101
|
+
# Inline templates (defined within the block) make use of #render_inline
|
102
|
+
# (indentation broken for the sake of example readibility):
|
103
|
+
#
|
104
|
+
# logger.as(:custom) do |response|
|
105
|
+
# tpl = <<-TPL
|
106
|
+
# %h2 Query log report
|
107
|
+
# %span
|
108
|
+
# Statu:
|
109
|
+
# = data.status
|
110
|
+
# %span
|
111
|
+
# Response:
|
112
|
+
# = data.body
|
113
|
+
# %br/
|
114
|
+
# TPL
|
115
|
+
# puts render_inline(tpl, :as => :haml, :data => response)
|
116
|
+
# end
|
117
|
+
#
|
118
|
+
# With an external template, one should use the #render helper to, well,
|
119
|
+
# render the template file. The extension will be used to infer the proper
|
120
|
+
# rendering engine. If not provided or when a custom extension is used, one
|
121
|
+
# may declare the template syntax.
|
122
|
+
#
|
123
|
+
# logger.as(:custom) do |data|
|
124
|
+
# # do whatever you want with data or anything else, then…
|
125
|
+
# out = render('my/template.erb', :data => data)
|
126
|
+
# # one may then use out to send mails, log to file, tweet…
|
127
|
+
# end
|
128
|
+
#
|
129
|
+
# logger.as(:custom) do |data|
|
130
|
+
# render('my/template', :as => :erb, :data => data)
|
131
|
+
# end
|
132
|
+
#
|
133
|
+
# See #render and #render_inline for more details.
|
134
|
+
#
|
135
|
+
# TODO: memoize the Render instance somehow? Or find another trick to
|
136
|
+
# execute the block.
|
137
|
+
#
|
138
|
+
def as(method, &block)
|
139
|
+
raise ArgumentError, 'Missing mandatory block' unless block_given?
|
140
|
+
|
141
|
+
method = method.to_sym
|
142
|
+
|
143
|
+
# Define the guard at class-level, if not already defined.
|
144
|
+
if !eigenclass.respond_to?(method)
|
145
|
+
eigenclass.send(:define_method, method) do |*args|
|
146
|
+
Render.new.instance_exec(*args, &block)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Define the guard at instance-level by overriding #initialize, if not
|
151
|
+
# already defined.
|
152
|
+
eigenclass.send(:define_method, :new) do
|
153
|
+
o = super
|
154
|
+
if !o.respond_to?(method)
|
155
|
+
o.send(:define_method, method) do |*args|
|
156
|
+
Render.new.instance_exec(*args, &block)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
o
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
private
|
164
|
+
|
165
|
+
# Default logging behavior. Outputs to $stdout using #puts and return
|
166
|
+
# the message.
|
167
|
+
#
|
168
|
+
def output!
|
169
|
+
output = "#{Time.now} | "
|
170
|
+
output += "[#{@namespace.gsub('_', ' ')}] " unless @namespace.nil?
|
171
|
+
output += @message
|
172
|
+
puts output if defined?(Logg::ALWAYS_PUTS) && Logg::ALWAYS_PUTS
|
173
|
+
return output
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# The Er module, when mixed-in a class, instantiates a Dispatcher and performs some
|
178
|
+
# simple meta-programming on this receiver to add support for the logger. It thus
|
179
|
+
# enable the receiver to use the logger's default implementation and/or define
|
180
|
+
# custom loggers.
|
181
|
+
#
|
182
|
+
module Machine
|
183
|
+
LOGGER = Logg::Dispatcher.new
|
184
|
+
#NAME = (defined?(::Logg::LOG_METHOD) && ::Logg::LOG_METHOD) || :log
|
185
|
+
NAME = :log
|
186
|
+
|
187
|
+
def self.included(base)
|
188
|
+
if !base.respond_to?(NAME)
|
189
|
+
base.instance_eval do
|
190
|
+
# Memoized logger for the receiver's class.
|
191
|
+
#
|
192
|
+
# TODO: add support for defining the logger under a different name than #log,
|
193
|
+
# this means either defining a constant before mixin, or delaying the metaprog.
|
194
|
+
class << self; self; end.instance_eval do
|
195
|
+
define_method(NAME) do
|
196
|
+
@@_logg_er ||= LOGGER
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
else
|
201
|
+
raise RuntimeError, "Cannot mixin Logg::Er as #{base}#logger, method's already defined."
|
202
|
+
end
|
203
|
+
|
204
|
+
# Memoized logger for the receiver.
|
205
|
+
base.extend RedefInit
|
206
|
+
end
|
207
|
+
|
208
|
+
module RedefInit
|
209
|
+
def new *args, &block
|
210
|
+
o = super
|
211
|
+
if !o.respond_to?(NAME)
|
212
|
+
class << o; self; end.send(:define_method, NAME) do
|
213
|
+
@_logg_er ||= LOGGER
|
214
|
+
end
|
215
|
+
end
|
216
|
+
o
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
data/lib/logg/render.rb
ADDED
data/lib/logg/version.rb
ADDED
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: logg
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.1.4
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jean-Denis Vauguet <jdvauguet@af83.com>
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-07-02 00:00:00 +02:00
|
14
|
+
default_executable:
|
15
|
+
dependencies: []
|
16
|
+
|
17
|
+
description: A simple logger for your ruby applications.
|
18
|
+
email: jdvauguet@af83.com
|
19
|
+
executables: []
|
20
|
+
|
21
|
+
extensions: []
|
22
|
+
|
23
|
+
extra_rdoc_files: []
|
24
|
+
|
25
|
+
files:
|
26
|
+
- lib/logg/render.rb
|
27
|
+
- lib/logg/version.rb
|
28
|
+
- lib/logg/core.rb
|
29
|
+
- lib/logg.rb
|
30
|
+
- MIT-LICENSE
|
31
|
+
- Rakefile
|
32
|
+
- Guardfile
|
33
|
+
- README.md
|
34
|
+
has_rdoc: true
|
35
|
+
homepage: http://www.github.com/af83/logg
|
36
|
+
licenses: []
|
37
|
+
|
38
|
+
post_install_message:
|
39
|
+
rdoc_options: []
|
40
|
+
|
41
|
+
require_paths:
|
42
|
+
- lib
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: "0"
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: "0"
|
55
|
+
requirements: []
|
56
|
+
|
57
|
+
rubyforge_project:
|
58
|
+
rubygems_version: 1.6.1
|
59
|
+
signing_key:
|
60
|
+
specification_version: 3
|
61
|
+
summary: A simple logger.
|
62
|
+
test_files: []
|
63
|
+
|