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,17 @@
1
+ class NullSink
2
+ attr_accessor :records
3
+
4
+ def initialize
5
+ @records = []
6
+ end
7
+
8
+ def add_record(record)
9
+ @records << record
10
+
11
+ nil
12
+ end
13
+
14
+ def flush
15
+ nil
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ shared_context :steno_context do
2
+ it "should support clearing context local data" do
3
+ context.data["test"] = "value"
4
+ context.clear
5
+ context.data["test"].should be_nil
6
+ end
7
+ end
@@ -0,0 +1,221 @@
1
+ require "fileutils"
2
+ require "yaml"
3
+
4
+ require "spec_helper"
5
+
6
+ describe Steno::Config do
7
+
8
+ if Steno::Sink::WINDOWS
9
+ describe ".from_hash" do
10
+ before :each do
11
+ @log_path = "some_file"
12
+
13
+ @mock_sink_file = double("sink")
14
+ @mock_sink_file.stub(:codec=)
15
+ Steno::Sink::IO.should_receive(:for_file).with(@log_path,
16
+ :max_retries => 5)
17
+ .and_return(@mock_sink_file)
18
+
19
+ @mock_sink_eventlog = double("sink")
20
+ @mock_sink_eventlog.stub(:codec=)
21
+ @mock_sink_eventlog.should_receive(:open).with("test")
22
+ Steno::Sink::Eventlog.should_receive(:instance).twice()
23
+ .and_return(@mock_sink_eventlog)
24
+ end
25
+
26
+ after :each do
27
+ @config = Steno::Config.from_hash(@hash)
28
+
29
+ @config.default_log_level.should == :debug2
30
+ @config.context.should.class == Steno::Context::Null
31
+ @config.codec.should.class == Steno::Codec::Json
32
+
33
+ @config.sinks.size.should == 2
34
+ @config.sinks.should =~ [@mock_sink_file, @mock_sink_eventlog]
35
+ end
36
+
37
+ it "should work for symbolized keys" do
38
+ @hash = {
39
+ :file => @log_path,
40
+ :level => "debug2",
41
+ :default_log_level => "warn",
42
+ :eventlog => "test",
43
+ :max_retries => 5,
44
+ }
45
+ end
46
+
47
+ it "should work for non-symbolized keys" do
48
+ @hash = {
49
+ "file" => @log_path,
50
+ "level" => "debug2",
51
+ "default_log_level" => "warn",
52
+ "eventlog" => "test",
53
+ "max_retries" => 5,
54
+ }
55
+ end
56
+
57
+ end
58
+ else
59
+ describe ".from_hash" do
60
+ before :each do
61
+ @log_path = "some_file"
62
+
63
+ @mock_sink_file = double("sink")
64
+ @mock_sink_file.stub(:codec=)
65
+ Steno::Sink::IO.should_receive(:for_file).with(@log_path,
66
+ :max_retries => 5)
67
+ .and_return(@mock_sink_file)
68
+
69
+ @mock_sink_syslog = double("sink")
70
+ @mock_sink_syslog.stub(:codec=)
71
+ @mock_sink_syslog.should_receive(:open).with("test")
72
+ Steno::Sink::Syslog.should_receive(:instance).twice()
73
+ .and_return(@mock_sink_syslog)
74
+ end
75
+
76
+ after :each do
77
+ @config = Steno::Config.from_hash(@hash)
78
+
79
+ @config.default_log_level.should == :debug2
80
+ @config.context.should.class == Steno::Context::Null
81
+ @config.codec.should.class == Steno::Codec::Json
82
+
83
+ @config.sinks.size.should == 2
84
+ @config.sinks.should =~ [@mock_sink_file, @mock_sink_syslog]
85
+ end
86
+
87
+ it "should work for symbolized keys" do
88
+ @hash = {
89
+ :file => @log_path,
90
+ :level => "debug2",
91
+ :default_log_level => "warn",
92
+ :syslog => "test",
93
+ :max_retries => 5,
94
+ }
95
+ end
96
+
97
+ it "should work for non-symbolized keys" do
98
+ @hash = {
99
+ "file" => @log_path,
100
+ "level" => "debug2",
101
+ "default_log_level" => "warn",
102
+ "syslog" => "test",
103
+ "max_retries" => 5,
104
+ }
105
+ end
106
+
107
+ end
108
+ end
109
+
110
+ describe ".from_file" do
111
+ before :each do
112
+ @tmpdir = Dir.mktmpdir
113
+ @config_path = File.join(@tmpdir, "config.yml")
114
+ @log_path = File.join(@tmpdir, "test.log")
115
+ end
116
+
117
+ after :each do
118
+ FileUtils.rm_rf(@tmpdir)
119
+ end
120
+
121
+ it "should return Steno::Config instance with sane defaults" do
122
+ write_config(@config_path, {})
123
+
124
+ config = Steno::Config.from_file(@config_path)
125
+
126
+ config.sinks.size.should == 1
127
+ config.sinks[0].class.should == Steno::Sink::IO
128
+
129
+ config.default_log_level.should == :info
130
+
131
+ config.context.should.class == Steno::Context::Null
132
+
133
+ config.codec.should.class == Steno::Codec::Json
134
+ end
135
+
136
+ it "should set the default_log_level if a key with the same name is supplied" do
137
+ write_config(@config_path, {"level" => "debug2"})
138
+ Steno::Config.from_file(@config_path).default_log_level.should == :debug2
139
+
140
+ write_config(@config_path, {"default_log_level" => "debug2"})
141
+ Steno::Config.from_file(@config_path).default_log_level.should == :debug2
142
+ end
143
+
144
+ it "should read the 'level' key if both default_log_level and level are spscified" do
145
+ write_config(@config_path, {"level" => "debug2",
146
+ "default_log_level" => "warn"})
147
+ Steno::Config.from_file(@config_path).default_log_level.should == :debug2
148
+ end
149
+
150
+ it "should add a file sink if the 'file' key is specified" do
151
+ write_config(@config_path, {"file" => @log_path, "max_retries" => 2})
152
+ mock_sink = double("sink")
153
+ mock_sink.stub(:codec=)
154
+
155
+ Steno::Sink::IO.should_receive(:for_file).
156
+ with(@log_path, :max_retries => 2).and_return(mock_sink)
157
+ config = Steno::Config.from_file(@config_path)
158
+ config.sinks.size.should == 1
159
+ config.sinks[0].should == mock_sink
160
+ end
161
+
162
+ if Steno::Sink::WINDOWS
163
+ it "should add a event sink if the 'eventlog' key is specified" do
164
+ write_config(@config_path, {"eventlog" => "test"})
165
+ mock_sink = double("sink")
166
+ mock_sink.should_receive(:open).with("test")
167
+ mock_sink.stub(:codec=)
168
+
169
+ Steno::Sink::Eventlog.should_receive(:instance).twice().and_return(mock_sink)
170
+
171
+ config = Steno::Config.from_file(@config_path)
172
+ config.sinks.size.should == 1
173
+ config.sinks[0].should == mock_sink
174
+ end
175
+ else
176
+ it "should add a syslog sink if the 'syslog' key is specified" do
177
+ write_config(@config_path, {"syslog" => "test"})
178
+ mock_sink = double("sink")
179
+ mock_sink.should_receive(:open).with("test")
180
+ mock_sink.stub(:codec=)
181
+
182
+ Steno::Sink::Syslog.should_receive(:instance).twice().and_return(mock_sink)
183
+
184
+ config = Steno::Config.from_file(@config_path)
185
+ config.sinks.size.should == 1
186
+ config.sinks[0].should == mock_sink
187
+ end
188
+ end
189
+
190
+
191
+
192
+ it "should add an io sink to stdout if no sinks are explicitly specified in the config file" do
193
+ write_config(@config_path, {})
194
+ mock_sink = double("sink")
195
+ mock_sink.stub(:codec=)
196
+
197
+ Steno::Sink::IO.should_receive(:new).with(STDOUT).and_return(mock_sink)
198
+
199
+ config = Steno::Config.from_file(@config_path)
200
+ config.sinks.size.should == 1
201
+ config.sinks[0].should == mock_sink
202
+ end
203
+
204
+ it "should merge supplied overrides with the file based config" do
205
+ write_config(@config_path, {"default_log_level" => "debug"})
206
+
207
+ context = Steno::Context::ThreadLocal.new
208
+ config = Steno::Config.from_file(@config_path,
209
+ :default_log_level => "warn",
210
+ :context => context)
211
+ config.context.should == context
212
+ config.default_log_level.should == :warn
213
+ end
214
+ end
215
+
216
+ def write_config(path, config)
217
+ File.open(path, "w+") do |f|
218
+ f.write(YAML.dump({"logging" => config}))
219
+ end
220
+ end
221
+ 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,38 @@
1
+ require "spec_helper"
2
+
3
+ require "steno/core_ext"
4
+
5
+ module Foo
6
+ class Bar
7
+ end
8
+ end
9
+
10
+ describe Module do
11
+ describe "#logger" do
12
+ it "should request a logger named after itself" do
13
+ x = Foo.logger
14
+ x.should be_a(Steno::Logger)
15
+ x.name.should include("Foo")
16
+ end
17
+ end
18
+ end
19
+
20
+ describe Class do
21
+ describe "#logger" do
22
+ it "should request a logger named after itself" do
23
+ x = Foo::Bar.logger
24
+ x.should be_a(Steno::Logger)
25
+ x.name.should include("Foo::Bar")
26
+ end
27
+ end
28
+ end
29
+
30
+ describe Object do
31
+ describe "#logger" do
32
+ it "should request a logger named after its class" do
33
+ x = Foo::Bar.new.logger
34
+ x.should be_a(Steno::Logger)
35
+ x.name.should include("Foo::Bar")
36
+ end
37
+ end
38
+ 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,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] || :debug,
44
+ opts[:message] || "test message",
45
+ nil,
46
+ opts[:data] || {})
47
+ end
48
+ end