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.
Files changed (50) hide show
  1. checksums.yaml +15 -0
  2. data/LICENSE +7136 -0
  3. data/README.md +78 -0
  4. data/Rakefile +16 -0
  5. data/bin/steno-prettify +99 -0
  6. data/lib/steno.rb +133 -0
  7. data/lib/steno/codec.rb +2 -0
  8. data/lib/steno/codec/base.rb +34 -0
  9. data/lib/steno/codec/json.rb +36 -0
  10. data/lib/steno/config.rb +97 -0
  11. data/lib/steno/context.rb +59 -0
  12. data/lib/steno/core_ext.rb +11 -0
  13. data/lib/steno/errors.rb +3 -0
  14. data/lib/steno/http_handler.rb +41 -0
  15. data/lib/steno/json_prettifier.rb +131 -0
  16. data/lib/steno/log_level.rb +24 -0
  17. data/lib/steno/logger.rb +174 -0
  18. data/lib/steno/record.rb +41 -0
  19. data/lib/steno/sink.rb +6 -0
  20. data/lib/steno/sink/base.rb +38 -0
  21. data/lib/steno/sink/counter.rb +44 -0
  22. data/lib/steno/sink/eventlog.rb +46 -0
  23. data/lib/steno/sink/fluentd.rb +31 -0
  24. data/lib/steno/sink/io.rb +72 -0
  25. data/lib/steno/sink/syslog.rb +59 -0
  26. data/lib/steno/tagged_logger.rb +59 -0
  27. data/lib/steno/version.rb +3 -0
  28. data/spec/spec_helper.rb +6 -0
  29. data/spec/support/barrier.rb +22 -0
  30. data/spec/support/null_sink.rb +17 -0
  31. data/spec/support/shared_context_specs.rb +7 -0
  32. data/spec/unit/config_spec.rb +221 -0
  33. data/spec/unit/context_spec.rb +62 -0
  34. data/spec/unit/core_ext_spec.rb +38 -0
  35. data/spec/unit/http_handler_spec.rb +73 -0
  36. data/spec/unit/json_codec_spec.rb +48 -0
  37. data/spec/unit/json_prettifier_spec.rb +84 -0
  38. data/spec/unit/log_level_spec.rb +19 -0
  39. data/spec/unit/logger_spec.rb +101 -0
  40. data/spec/unit/record_spec.rb +30 -0
  41. data/spec/unit/sink/counter_spec.rb +27 -0
  42. data/spec/unit/sink/eventlog_spec.rb +41 -0
  43. data/spec/unit/sink/fluentd_spec.rb +46 -0
  44. data/spec/unit/sink/io_spec.rb +111 -0
  45. data/spec/unit/sink/syslog_spec.rb +74 -0
  46. data/spec/unit/steno_spec.rb +86 -0
  47. data/spec/unit/tagged_logger_spec.rb +33 -0
  48. data/steno-1.2.1.gem +0 -0
  49. data/steno.gemspec +41 -0
  50. 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