steno 1.2.2-x86-mingw32
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.
- checksums.yaml +15 -0
- data/LICENSE +7136 -0
- data/README.md +78 -0
- data/Rakefile +16 -0
- data/bin/steno-prettify +99 -0
- data/lib/steno.rb +133 -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 +97 -0
- data/lib/steno/context.rb +59 -0
- data/lib/steno/core_ext.rb +11 -0
- data/lib/steno/errors.rb +3 -0
- data/lib/steno/http_handler.rb +41 -0
- data/lib/steno/json_prettifier.rb +131 -0
- data/lib/steno/log_level.rb +24 -0
- data/lib/steno/logger.rb +174 -0
- data/lib/steno/record.rb +41 -0
- data/lib/steno/sink.rb +6 -0
- data/lib/steno/sink/base.rb +38 -0
- data/lib/steno/sink/counter.rb +44 -0
- data/lib/steno/sink/eventlog.rb +46 -0
- data/lib/steno/sink/fluentd.rb +31 -0
- data/lib/steno/sink/io.rb +72 -0
- data/lib/steno/sink/syslog.rb +59 -0
- data/lib/steno/tagged_logger.rb +59 -0
- data/lib/steno/version.rb +3 -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/config_spec.rb +221 -0
- data/spec/unit/context_spec.rb +62 -0
- data/spec/unit/core_ext_spec.rb +38 -0
- data/spec/unit/http_handler_spec.rb +73 -0
- data/spec/unit/json_codec_spec.rb +48 -0
- data/spec/unit/json_prettifier_spec.rb +84 -0
- data/spec/unit/log_level_spec.rb +19 -0
- data/spec/unit/logger_spec.rb +101 -0
- data/spec/unit/record_spec.rb +30 -0
- data/spec/unit/sink/counter_spec.rb +27 -0
- data/spec/unit/sink/eventlog_spec.rb +41 -0
- data/spec/unit/sink/fluentd_spec.rb +46 -0
- data/spec/unit/sink/io_spec.rb +111 -0
- data/spec/unit/sink/syslog_spec.rb +74 -0
- data/spec/unit/steno_spec.rb +86 -0
- data/spec/unit/tagged_logger_spec.rb +33 -0
- data/steno-1.2.1.gem +0 -0
- data/steno.gemspec +41 -0
- metadata +224 -0
@@ -0,0 +1,84 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
require "steno/json_prettifier"
|
4
|
+
|
5
|
+
describe Steno::JsonPrettifier do
|
6
|
+
let(:prettifier) { Steno::JsonPrettifier.new }
|
7
|
+
let(:codec) { Steno::Codec::Json.new }
|
8
|
+
|
9
|
+
describe "#prettify_line" do
|
10
|
+
it "should return a properly formatted string" do
|
11
|
+
record = Steno::Record.new("test", :info, "message",
|
12
|
+
["filename", "line", "method"], "test" => "data")
|
13
|
+
encoded = codec.encode_record(record)
|
14
|
+
prettified = prettifier.prettify_line(encoded)
|
15
|
+
|
16
|
+
exp_regex = ['\d{4}-\d{2}-\d{2}', # YYYY-MM-DD
|
17
|
+
'\d{2}:\d{2}:\d{2}\.\d{6}', # HH:MM:SS.uS
|
18
|
+
'test', # Source
|
19
|
+
'pid=\d+', # Process id
|
20
|
+
'tid=\w{4}', # Thread shortid
|
21
|
+
'fid=\w{4}', # Fiber shortid
|
22
|
+
'filename\/method:line', # Location
|
23
|
+
'test=data', # User supplied data
|
24
|
+
'INFO', # Level
|
25
|
+
'--',
|
26
|
+
'message', # Log message
|
27
|
+
].join("\s+") + "\n"
|
28
|
+
prettified.should match(exp_regex)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should always use the largest src len to determine src column width" do
|
32
|
+
test_srcs = [
|
33
|
+
'a' * (Steno::JsonPrettifier::MIN_COL_WIDTH - 3),
|
34
|
+
'a' * (Steno::JsonPrettifier::MIN_COL_WIDTH - 1),
|
35
|
+
'a' * (Steno::JsonPrettifier::MIN_COL_WIDTH),
|
36
|
+
'a' * (Steno::JsonPrettifier::MIN_COL_WIDTH + 1),
|
37
|
+
'a' * (Steno::JsonPrettifier::MIN_COL_WIDTH - 3),
|
38
|
+
'a' * (Steno::JsonPrettifier::MIN_COL_WIDTH + 3),
|
39
|
+
'a' * (Steno::JsonPrettifier::MIN_COL_WIDTH - 2),
|
40
|
+
'a' * (Steno::JsonPrettifier::MIN_COL_WIDTH + 2)
|
41
|
+
]
|
42
|
+
|
43
|
+
regex = ['\d{4}-\d{2}-\d{2}', # YYYY-MM-DD
|
44
|
+
'\d{2}:\d{2}:\d{2}\.\d{6}', # HH:MM:SS.uS
|
45
|
+
'([a-zA-Z0-9\ ]+)', # Source (to be captured)
|
46
|
+
'pid=\d+', # Process id
|
47
|
+
'.+' # Everything else
|
48
|
+
].join("\s") + "\n"
|
49
|
+
|
50
|
+
max_src_len = Steno::JsonPrettifier::MIN_COL_WIDTH
|
51
|
+
test_srcs.each do |src|
|
52
|
+
record = Steno::Record.new(src,
|
53
|
+
:info,
|
54
|
+
"message",
|
55
|
+
["filename", "line", "method"],
|
56
|
+
"test" => "data")
|
57
|
+
|
58
|
+
encoded = codec.encode_record(record)
|
59
|
+
prettified = prettifier.prettify_line(encoded)
|
60
|
+
src_col = prettified.match(regex)[1]
|
61
|
+
|
62
|
+
max_src_len = [max_src_len, src.length].max
|
63
|
+
src_col.length.should == max_src_len
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should raise a parse error when the json-encoded string is not a hash" do
|
68
|
+
expect {
|
69
|
+
prettifier.prettify_line("[1,2,3]")
|
70
|
+
}.to raise_error(Steno::JsonPrettifier::ParseError)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should raise a parse error when the json-encoded string is malformed" do
|
74
|
+
expect {
|
75
|
+
prettifier.prettify_line("blah")
|
76
|
+
}.to raise_error(Steno::JsonPrettifier::ParseError)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should work with a nil data field" do
|
80
|
+
line = prettifier.prettify_line(%@{"data":null}@)
|
81
|
+
line.should include(" - ")
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,19 @@
|
|
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
|
+
|
19
|
+
end
|
@@ -0,0 +1,101 @@
|
|
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 = double("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 = double("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
|
+
|
76
|
+
it "creates a record with the proper level" do
|
77
|
+
sink = double("sink")
|
78
|
+
Steno::Record.should_receive(:new).with("test", :warn, "message", anything, anything).and_call_original
|
79
|
+
sink.stub(:add_record)
|
80
|
+
|
81
|
+
my_logger = Steno::Logger.new("test", [sink])
|
82
|
+
|
83
|
+
my_logger.warn("message")
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe "#logf" do
|
88
|
+
it "should format messages according to the supplied format string" do
|
89
|
+
logger.should_receive(:log).with(:debug, "test 1 2.20")
|
90
|
+
logger.debugf("test %d %0.2f", 1, 2.2)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "#tag" do
|
95
|
+
it "should return a tagged logger" do
|
96
|
+
tagged_logger = logger.tag("foo" => "bar")
|
97
|
+
tagged_logger.should_not be_nil
|
98
|
+
tagged_logger.user_data.should == { "foo" => "bar" }
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Steno::Record do
|
4
|
+
let(:message) { Array("test message") }
|
5
|
+
let(:record) { Steno::Record.new("test", :info, message) }
|
6
|
+
|
7
|
+
it "should set the process id" do
|
8
|
+
record.process_id.should == Process.pid
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should set the thread id" do
|
12
|
+
record.thread_id.should == Thread.current.object_id
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should set the fiber id(if available)", :needs_fibers => true do
|
16
|
+
record.fiber_id.should == Fiber.current.object_id
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should set the source" do
|
20
|
+
record.source.should == "test"
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should stringify the message" do
|
24
|
+
record.message.should be_a(String)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should use a UTC timestamp" do
|
28
|
+
record.timestamp.to_f.should be_within(0.1).of(Time.now.utc.to_f)
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Steno::Sink::Counter do
|
4
|
+
let(:level) do
|
5
|
+
Steno::Logger.lookup_level(:info)
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:record) do
|
9
|
+
Steno::Record.new("source", level.name, "message")
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "add_record" do
|
13
|
+
it "counts added records" do
|
14
|
+
expect(subject.counts).to be_empty
|
15
|
+
subject.add_record(record)
|
16
|
+
expect(subject.counts.size).to eq 1
|
17
|
+
expect(subject.counts['info']).to eq 1
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "to_json" do
|
22
|
+
it "produces a valid json representation" do
|
23
|
+
subject.add_record(record)
|
24
|
+
expect(subject.to_json).to match '"info":1'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
if Steno::Sink::WINDOWS
|
3
|
+
describe Steno::Sink::Eventlog do
|
4
|
+
let(:level) do
|
5
|
+
Steno::Logger.lookup_level(:info)
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:record) do
|
9
|
+
Steno::Record.new("source", level.name, "message")
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#add_record" do
|
13
|
+
|
14
|
+
it "should append an encoded record with the correct priority" do
|
15
|
+
eventlog = double("Win32::EventLog")
|
16
|
+
Win32::EventLog.should_receive(:open) \
|
17
|
+
.with('Application') \
|
18
|
+
.and_return(eventlog)
|
19
|
+
|
20
|
+
sink = Steno::Sink::Eventlog.instance
|
21
|
+
sink.open
|
22
|
+
|
23
|
+
codec = double("codec")
|
24
|
+
codec.should_receive(:encode_record).with(record).and_return(record.message)
|
25
|
+
sink.codec = codec
|
26
|
+
|
27
|
+
eventlog.should_receive(:report_event).with(:source => "CloudFoundry",
|
28
|
+
:event_type => Win32::EventLog::INFO,
|
29
|
+
:data => record.message)
|
30
|
+
|
31
|
+
sink.add_record(record)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#flush" do
|
36
|
+
it "should do nothing" do
|
37
|
+
Steno::Sink::Eventlog.instance.flush
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Steno::Sink::IO do
|
4
|
+
let(:level) do
|
5
|
+
Steno::Logger.lookup_level(:info)
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:record) do
|
9
|
+
Steno::Record.new("source", level.name, "message")
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#initialize" do
|
13
|
+
it "should initialize FluentLogger with the default option" do
|
14
|
+
Fluent::Logger::FluentLogger.should_receive(:new).with("steno", {
|
15
|
+
:host => "127.0.0.1",
|
16
|
+
:port => 24224,
|
17
|
+
:buffer_limit => Fluent::Logger::FluentLogger::BUFFER_LIMIT,
|
18
|
+
}).and_return()
|
19
|
+
sink = Steno::Sink::Fluentd.new()
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should initialize FliuentLogger with override options" do
|
23
|
+
Fluent::Logger::FluentLogger.should_receive(:new).with("vcap", {
|
24
|
+
:host => "localhost",
|
25
|
+
:port => 8080,
|
26
|
+
:buffer_limit => 1024,
|
27
|
+
}).and_return()
|
28
|
+
sink = Steno::Sink::Fluentd.new({
|
29
|
+
:tag_prefix => "vcap",
|
30
|
+
:host => "localhost",
|
31
|
+
:port => 8080,
|
32
|
+
:buffer_limit => 1024
|
33
|
+
})
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "#add_record" do
|
38
|
+
it "should post an record with the correct tag" do
|
39
|
+
fluentd = double("fluentd")
|
40
|
+
Fluent::Logger::FluentLogger.should_receive(:new).and_return(fluentd)
|
41
|
+
fluentd.should_receive(:post).with("source", record)
|
42
|
+
sink = Steno::Sink::Fluentd.new()
|
43
|
+
sink.add_record(record)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Steno::Sink::IO do
|
4
|
+
let(:level) do
|
5
|
+
Steno::Logger.lookup_level(:info)
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:record) do
|
9
|
+
Steno::Record.new("source", level.name, "message")
|
10
|
+
end
|
11
|
+
|
12
|
+
describe ".for_file" do
|
13
|
+
it "should return a new sink configured to append to the file at path with autosync set to true by default" do
|
14
|
+
mock_handle = double("file handle")
|
15
|
+
|
16
|
+
File.should_receive(:open).with("path", "a+").and_return(mock_handle)
|
17
|
+
mock_handle.should_receive(:sync=).with(true)
|
18
|
+
|
19
|
+
mock_sink = double("sink")
|
20
|
+
Steno::Sink::IO.should_receive(:new).with(mock_handle,
|
21
|
+
:max_retries => 10).
|
22
|
+
and_return(mock_sink)
|
23
|
+
|
24
|
+
returned = Steno::Sink::IO.for_file("path",
|
25
|
+
:max_retries => 10)
|
26
|
+
returned.should == mock_sink
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should return a new sink configured to append to the file at path with specified options" do
|
30
|
+
mock_handle = double("file handle")
|
31
|
+
|
32
|
+
File.should_receive(:open).with("path", "a+").and_return(mock_handle)
|
33
|
+
mock_handle.should_receive(:sync=).with(false)
|
34
|
+
|
35
|
+
mock_sink = double("sink")
|
36
|
+
Steno::Sink::IO.should_receive(:new).with(mock_handle,
|
37
|
+
:max_retries => 10).
|
38
|
+
and_return(mock_sink)
|
39
|
+
|
40
|
+
returned = Steno::Sink::IO.for_file("path",
|
41
|
+
:autoflush => false,
|
42
|
+
:max_retries => 10)
|
43
|
+
returned.should == mock_sink
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#add_record" do
|
48
|
+
it "should encode the record and write it to the underlying io object" do
|
49
|
+
codec = double("codec")
|
50
|
+
codec.should_receive(:encode_record).with(record).and_return(record.message)
|
51
|
+
|
52
|
+
io = double("io")
|
53
|
+
io.should_receive(:write).with(record.message)
|
54
|
+
|
55
|
+
Steno::Sink::IO.new(io, :codec => codec).add_record(record)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should by default not retry on IOError" do
|
59
|
+
codec = double("codec")
|
60
|
+
codec.should_receive(:encode_record).with(record).and_return(record.message)
|
61
|
+
|
62
|
+
io = double("io")
|
63
|
+
|
64
|
+
io.should_receive(:write).with(record.message).ordered.and_raise(IOError)
|
65
|
+
|
66
|
+
expect do
|
67
|
+
Steno::Sink::IO.new(io, :codec => codec).add_record(record)
|
68
|
+
end.to raise_error(IOError)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should retry not more than specified number of times on IOError" do
|
72
|
+
codec = double("codec")
|
73
|
+
codec.should_receive(:encode_record).with(record).and_return(record.message)
|
74
|
+
|
75
|
+
io = double("io")
|
76
|
+
|
77
|
+
io.should_receive(:write).exactly(3).times.with(record.message).
|
78
|
+
and_raise(IOError)
|
79
|
+
|
80
|
+
expect do
|
81
|
+
Steno::Sink::IO.new(io, :codec => codec, :max_retries => 2).
|
82
|
+
add_record(record)
|
83
|
+
end.to raise_error(IOError)
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should retry on IOError and succeed" do
|
87
|
+
codec = double("codec")
|
88
|
+
codec.should_receive(:encode_record).with(record).and_return(record.message)
|
89
|
+
|
90
|
+
io = double("io")
|
91
|
+
io.should_receive(:write).with(record.message).once.
|
92
|
+
and_raise(IOError)
|
93
|
+
io.should_receive(:write).with(record.message).once.ordered.
|
94
|
+
and_return(record.message)
|
95
|
+
|
96
|
+
expect do
|
97
|
+
Steno::Sink::IO.new(io, :codec => codec, :max_retries => 1).
|
98
|
+
add_record(record)
|
99
|
+
end.to_not raise_error(IOError)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe "#flush" do
|
104
|
+
it "should call flush on the underlying io object" do
|
105
|
+
io = double("io")
|
106
|
+
io.should_receive(:flush)
|
107
|
+
|
108
|
+
Steno::Sink::IO.new(io).flush
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|