steno 0.0.3
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.
- data/.gitignore +2 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +29 -0
- data/Rakefile +14 -0
- data/lib/steno.rb +134 -0
- data/lib/steno/codec.rb +2 -0
- data/lib/steno/codec/base.rb +34 -0
- data/lib/steno/codec/json.rb +36 -0
- data/lib/steno/config.rb +56 -0
- data/lib/steno/context.rb +59 -0
- data/lib/steno/core_ext.rb +5 -0
- data/lib/steno/errors.rb +3 -0
- data/lib/steno/http_handler.rb +42 -0
- data/lib/steno/log_level.rb +24 -0
- data/lib/steno/logger.rb +177 -0
- data/lib/steno/record.rb +39 -0
- data/lib/steno/sink.rb +3 -0
- data/lib/steno/sink/base.rb +36 -0
- data/lib/steno/sink/io.rb +48 -0
- data/lib/steno/sink/syslog.rb +38 -0
- data/lib/steno/tagged_logger.rb +59 -0
- data/lib/steno/version.rb +3 -0
- data/spec/core_ext_spec.rb +15 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/support/barrier.rb +22 -0
- data/spec/support/null_sink.rb +17 -0
- data/spec/support/shared_context_specs.rb +7 -0
- data/spec/unit/context_spec.rb +62 -0
- data/spec/unit/http_handler_spec.rb +73 -0
- data/spec/unit/io_sink_spec.rb +26 -0
- data/spec/unit/json_codec_spec.rb +48 -0
- data/spec/unit/log_level_spec.rb +18 -0
- data/spec/unit/logger_spec.rb +91 -0
- data/spec/unit/record_spec.rb +21 -0
- data/spec/unit/steno_spec.rb +86 -0
- data/spec/unit/syslog_sink_spec.rb +27 -0
- data/spec/unit/tagged_logger_spec.rb +33 -0
- data/steno.gemspec +26 -0
- metadata +195 -0
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require "thread"
|
2
|
+
|
3
|
+
class Barrier
|
4
|
+
def initialize
|
5
|
+
@lock = Mutex.new
|
6
|
+
@cvar = ConditionVariable.new
|
7
|
+
@done = false
|
8
|
+
end
|
9
|
+
|
10
|
+
def release
|
11
|
+
@lock.synchronize do
|
12
|
+
@done = true
|
13
|
+
@cvar.broadcast
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def wait
|
18
|
+
@lock.synchronize do
|
19
|
+
@cvar.wait(@lock) if !@done
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Steno::Context::Null do
|
4
|
+
include_context :steno_context
|
5
|
+
|
6
|
+
let(:context) { Steno::Context::Null.new }
|
7
|
+
|
8
|
+
it "should store no data" do
|
9
|
+
context.data.should == {}
|
10
|
+
context.data["foo"] = "bar"
|
11
|
+
context.data.should == {}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe Steno::Context::ThreadLocal do
|
16
|
+
include_context :steno_context
|
17
|
+
|
18
|
+
let (:context) { Steno::Context::ThreadLocal.new }
|
19
|
+
|
20
|
+
it "should store data local to threads" do
|
21
|
+
b1 = Barrier.new
|
22
|
+
b2 = Barrier.new
|
23
|
+
|
24
|
+
t1 = Thread.new do
|
25
|
+
context.data["thread"] = "t1"
|
26
|
+
b1.release
|
27
|
+
b2.wait
|
28
|
+
context.data["thread"].should == "t1"
|
29
|
+
end
|
30
|
+
|
31
|
+
t2 = Thread.new do
|
32
|
+
b1.wait
|
33
|
+
context.data["thread"].should be_nil
|
34
|
+
context.data["thread"] = "t2"
|
35
|
+
b2.release
|
36
|
+
end
|
37
|
+
|
38
|
+
t1.join
|
39
|
+
t2.join
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe Steno::Context::FiberLocal do
|
44
|
+
include_context :steno_context
|
45
|
+
|
46
|
+
let(:context) { Steno::Context::FiberLocal.new }
|
47
|
+
|
48
|
+
it "should store data local to fibers" do
|
49
|
+
f2 = Fiber.new do
|
50
|
+
context.data["fiber"].should be_nil
|
51
|
+
context.data["fiber"] = "f2"
|
52
|
+
end
|
53
|
+
|
54
|
+
f1 = Fiber.new do
|
55
|
+
context.data["fiber"] = "f1"
|
56
|
+
f2.resume
|
57
|
+
context.data["fiber"].should == "f1"
|
58
|
+
end
|
59
|
+
|
60
|
+
f1.resume
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
require "steno/http_handler"
|
4
|
+
|
5
|
+
describe Steno::HttpHandler do
|
6
|
+
include Rack::Test::Methods
|
7
|
+
|
8
|
+
let(:config) { Steno::Config.new }
|
9
|
+
|
10
|
+
before :each do
|
11
|
+
Steno.init(config)
|
12
|
+
end
|
13
|
+
|
14
|
+
def app
|
15
|
+
Steno::HttpHandler
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "GET /loggers/levels" do
|
19
|
+
it "returns a hash of logger name to level" do
|
20
|
+
get "/loggers/levels"
|
21
|
+
json_body.should == {}
|
22
|
+
|
23
|
+
foo = Steno.logger("foo")
|
24
|
+
foo.level = :debug
|
25
|
+
|
26
|
+
bar = Steno.logger("bar")
|
27
|
+
bar.level = :info
|
28
|
+
|
29
|
+
get "/loggers/levels"
|
30
|
+
json_body.should == { "foo" => "debug", "bar" => "info" }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "PUT /loggers/levels" do
|
35
|
+
it "returns an error on missing parameters" do
|
36
|
+
put "/loggers/levels"
|
37
|
+
last_response.status.should == 400
|
38
|
+
json_body["error"].should match(/Missing query parameters/)
|
39
|
+
|
40
|
+
put "/loggers/levels", :regexp => "hi"
|
41
|
+
last_response.status.should == 400
|
42
|
+
json_body["error"].should match(/Missing query parameters/)
|
43
|
+
|
44
|
+
put "/loggers/levels", :level => "debug"
|
45
|
+
last_response.status.should == 400
|
46
|
+
json_body["error"].should match(/Missing query parameters/)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "returns an error on invalid log levels" do
|
50
|
+
put "/loggers/levels", :regexp => "hi", :level => "foobar"
|
51
|
+
last_response.status.should == 400
|
52
|
+
json_body["error"].should match(/Unknown level/)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "updates log levels for loggers whose name matches the regexp" do
|
56
|
+
foo = Steno.logger("foo")
|
57
|
+
foo.level = :debug
|
58
|
+
|
59
|
+
bar = Steno.logger("bar")
|
60
|
+
bar.level = :warn
|
61
|
+
|
62
|
+
put "/loggers/levels", :regexp => "f", :level => "error"
|
63
|
+
last_response.status.should == 200
|
64
|
+
|
65
|
+
foo.level.should == :error
|
66
|
+
bar.level.should == :warn
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def json_body
|
71
|
+
Yajl::Parser.parse(last_response.body)
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Steno::Sink::IO do
|
4
|
+
let(:record) { { :data => "test" } }
|
5
|
+
|
6
|
+
describe "#add_record" do
|
7
|
+
it "should encode the record and write it to the underlying io object" do
|
8
|
+
codec = mock("codec")
|
9
|
+
codec.should_receive(:encode_record).with(record).and_return(record[:data])
|
10
|
+
|
11
|
+
io = mock("io")
|
12
|
+
io.should_receive(:write).with(record[:data])
|
13
|
+
|
14
|
+
Steno::Sink::IO.new(io, codec).add_record(record)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#flush" do
|
19
|
+
it "should call flush on the underlying io object" do
|
20
|
+
io = mock("io")
|
21
|
+
io.should_receive(:flush)
|
22
|
+
|
23
|
+
Steno::Sink::IO.new(io).flush
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Steno::Codec::Json do
|
4
|
+
let(:codec) { Steno::Codec::Json.new }
|
5
|
+
let(:record) { make_record(:data => { "user" => "data" }) }
|
6
|
+
|
7
|
+
describe "#encode_record" do
|
8
|
+
it "should encode records as json hashes" do
|
9
|
+
parsed = Yajl::Parser.parse(codec.encode_record(record))
|
10
|
+
parsed.class.should == Hash
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should encode the timestamp as a float" do
|
14
|
+
parsed = Yajl::Parser.parse(codec.encode_record(record))
|
15
|
+
parsed["timestamp"].class.should == Float
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should escape newlines" do
|
19
|
+
rec = make_record(:message => "newline\ntest")
|
20
|
+
codec.encode_record(rec).should match(/newline\\ntest/)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should escape carriage returns" do
|
24
|
+
rec = make_record(:message => "newline\rtest")
|
25
|
+
codec.encode_record(rec).should match(/newline\\rtest/)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should allow messages with valid encodings to pass through untouched" do
|
29
|
+
msg = "HI\u2600"
|
30
|
+
rec = make_record(:message => msg)
|
31
|
+
codec.encode_record(rec).should match(/#{msg}/)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should treat messages with invalid encodings as binary data" do
|
35
|
+
msg = "HI\u2026".force_encoding("US-ASCII")
|
36
|
+
rec = make_record(:message => msg)
|
37
|
+
codec.encode_record(rec).should match(/HI\\\\xe2\\\\x80\\\\xa6/)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def make_record(opts = {})
|
42
|
+
Steno::Record.new(opts[:source] || "my_source",
|
43
|
+
opts[:level] || Steno::LogLevel.new("debug", 0),
|
44
|
+
opts[:message] || "test message",
|
45
|
+
nil,
|
46
|
+
opts[:data] || {})
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Steno::LogLevel do
|
4
|
+
let(:info_level) { Steno::LogLevel.new(:info, 2) }
|
5
|
+
let(:debug_level) { Steno::LogLevel.new(:debug, 1) }
|
6
|
+
|
7
|
+
it "should be comparable" do
|
8
|
+
(info_level > debug_level).should be_true
|
9
|
+
(debug_level > info_level).should be_false
|
10
|
+
(info_level == info_level).should be_true
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "#to_s" do
|
14
|
+
it "should return the name of the level" do
|
15
|
+
info_level.to_s.should == "info"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Steno::Logger do
|
4
|
+
let(:logger) { Steno::Logger.new("test", []) }
|
5
|
+
|
6
|
+
it "should provide #level, #levelf, and #level? methods for each log level" do
|
7
|
+
Steno::Logger::LEVELS.each do |name, _|
|
8
|
+
[name, name.to_s + "f", name.to_s + "?"].each do |meth|
|
9
|
+
logger.respond_to?(meth).should be_true
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#level_active?" do
|
15
|
+
it "should return a boolean indicating if the level is enabled" do
|
16
|
+
logger.level_active?(:error).should be_true
|
17
|
+
logger.level_active?(:info).should be_true
|
18
|
+
logger.level_active?(:debug).should be_false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#<level>?" do
|
23
|
+
it "should return a boolean indiciating if <level> is enabled" do
|
24
|
+
logger.error?.should be_true
|
25
|
+
logger.info?.should be_true
|
26
|
+
logger.debug?.should be_false
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#level" do
|
31
|
+
it "should return the name of the currently active level" do
|
32
|
+
logger.level.should == :info
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#level=" do
|
37
|
+
it "should allow the level to be changed" do
|
38
|
+
logger.level = :warn
|
39
|
+
logger.level.should == :warn
|
40
|
+
logger.level_active?(:info).should be_false
|
41
|
+
logger.level_active?(:warn).should be_true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#log" do
|
46
|
+
it "should not forward any messages for levels that are inactive" do
|
47
|
+
sink = mock("sink")
|
48
|
+
sink.should_not_receive(:add_record)
|
49
|
+
|
50
|
+
my_logger = Steno::Logger.new("test", [sink])
|
51
|
+
|
52
|
+
my_logger.debug("test")
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should forward messages for levels that are active" do
|
56
|
+
sink = mock("sink")
|
57
|
+
sink.should_receive(:add_record).with(any_args())
|
58
|
+
|
59
|
+
my_logger = Steno::Logger.new("test", [sink])
|
60
|
+
|
61
|
+
my_logger.warn("test")
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should not invoke a supplied block if the level is inactive" do
|
65
|
+
invoked = false
|
66
|
+
logger.debug { invoked = true }
|
67
|
+
invoked.should be_false
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should invoke a supplied block if the level is active" do
|
71
|
+
invoked = false
|
72
|
+
logger.warn { invoked = true }
|
73
|
+
invoked.should be_true
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "#logf" do
|
78
|
+
it "should format messages according to the supplied format string" do
|
79
|
+
logger.should_receive(:log).with(:debug, "test 1 2.20")
|
80
|
+
logger.debugf("test %d %0.2f", 1, 2.2)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "#tag" do
|
85
|
+
it "should return a tagged logger" do
|
86
|
+
tagged_logger = logger.tag("foo" => "bar")
|
87
|
+
tagged_logger.should_not be_nil
|
88
|
+
tagged_logger.user_data.should == { "foo" => "bar" }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Steno::Record do
|
4
|
+
let(:record) { Steno::Record.new("test", :info, "test message") }
|
5
|
+
|
6
|
+
it "should set the process id" do
|
7
|
+
record.process_id.should == Process.pid
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should set the thread id" do
|
11
|
+
record.thread_id.should == Thread.current.object_id
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should set the fiber id(if available)", :needs_fibers => true do
|
15
|
+
record.fiber_id.should == Fiber.current.object_id
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should set the source" do
|
19
|
+
record.source.should == "test"
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Steno do
|
4
|
+
let(:config) { Steno::Config.new }
|
5
|
+
|
6
|
+
before :each do
|
7
|
+
Steno.init(config)
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "#logger" do
|
11
|
+
it "should return a new Steno::Logger instance" do
|
12
|
+
logger = Steno.logger("test")
|
13
|
+
logger.should_not be_nil
|
14
|
+
logger.name.should == "test"
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should memoize loggers by name" do
|
18
|
+
logger1 = Steno.logger("test")
|
19
|
+
logger2 = Steno.logger("test")
|
20
|
+
|
21
|
+
logger1.object_id.should == logger2.object_id
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#set_logger_regexp" do
|
26
|
+
it "should modify the levels of existing loggers that match the regex" do
|
27
|
+
logger = Steno.logger("test")
|
28
|
+
|
29
|
+
logger.level.should == :info
|
30
|
+
|
31
|
+
Steno.set_logger_regexp(/te/, :debug)
|
32
|
+
|
33
|
+
logger.level.should == :debug
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should modify the levels of new loggers after a regexp has been set" do
|
37
|
+
Steno.set_logger_regexp(/te/, :debug)
|
38
|
+
|
39
|
+
Steno.logger("te").level.should == :debug
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should reset the levels of previously matching loggers when changed" do
|
43
|
+
Steno.set_logger_regexp(/foo/, :debug)
|
44
|
+
|
45
|
+
logger = Steno.logger("foo")
|
46
|
+
logger.level.should == :debug
|
47
|
+
|
48
|
+
Steno.set_logger_regexp(/bar/, :debug)
|
49
|
+
|
50
|
+
logger.level.should == :info
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "#clear_logger_regexp" do
|
55
|
+
it "should reset any loggers matching the existing regexp" do
|
56
|
+
Steno.set_logger_regexp(/te/, :debug)
|
57
|
+
|
58
|
+
logger = Steno.logger("test")
|
59
|
+
logger.level.should == :debug
|
60
|
+
|
61
|
+
Steno.clear_logger_regexp
|
62
|
+
|
63
|
+
logger.level.should == :info
|
64
|
+
Steno.logger_regexp.should be_nil
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "#logger_level_snapshot" do
|
69
|
+
it "should return a hash mapping logger name to level" do
|
70
|
+
loggers = []
|
71
|
+
|
72
|
+
expected = {
|
73
|
+
"foo" => :debug,
|
74
|
+
"bar" => :warn,
|
75
|
+
}
|
76
|
+
|
77
|
+
expected.each do |name, level|
|
78
|
+
# Prevent GC
|
79
|
+
loggers << Steno.logger(name)
|
80
|
+
loggers.last.level = level
|
81
|
+
end
|
82
|
+
|
83
|
+
Steno.logger_level_snapshot.should == expected
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|