steno-capi 1.3.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +7136 -0
- data/README.md +78 -0
- data/Rakefile +15 -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 +49 -0
- data/lib/steno/config.rb +101 -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/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 +62 -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 +229 -0
- data/spec/unit/context_spec.rb +62 -0
- data/spec/unit/core_ext_spec.rb +38 -0
- data/spec/unit/json_codec_spec.rb +68 -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 +75 -0
- data/spec/unit/steno_spec.rb +86 -0
- data/spec/unit/tagged_logger_spec.rb +35 -0
- data/steno-capi.gemspec +39 -0
- metadata +179 -0
data/lib/steno/sink.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require "rbconfig"
|
2
|
+
require "thread"
|
3
|
+
|
4
|
+
module Steno
|
5
|
+
module Sink
|
6
|
+
WINDOWS = (RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
# Sinks represent the final destination for log records. They abstract storage
|
11
|
+
# mediums (like files) and transport layers (like sockets).
|
12
|
+
class Steno::Sink::Base
|
13
|
+
|
14
|
+
attr_accessor :codec
|
15
|
+
|
16
|
+
# @param [Steno::Codec::Base] formatter Transforms log records to their
|
17
|
+
# raw, string-based representation that will be written to the underlying
|
18
|
+
# sink.
|
19
|
+
def initialize(codec = nil)
|
20
|
+
@codec = codec
|
21
|
+
end
|
22
|
+
|
23
|
+
# Adds a record to be flushed at a later time.
|
24
|
+
#
|
25
|
+
# @param [Hash] record
|
26
|
+
#
|
27
|
+
# @return [nil]
|
28
|
+
def add_record(record)
|
29
|
+
raise NotImplementedError
|
30
|
+
end
|
31
|
+
|
32
|
+
# Flushes any buffered records.
|
33
|
+
#
|
34
|
+
# @return [nil]
|
35
|
+
def flush
|
36
|
+
raise NotImplementedError
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "steno/sink/base"
|
2
|
+
|
3
|
+
module Steno
|
4
|
+
module Sink
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
class Steno::Sink::Counter < Steno::Sink::Base
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
# Map of String -> numeric count
|
12
|
+
@counts = {}
|
13
|
+
@mutex = Mutex.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_record(record)
|
17
|
+
level = record.log_level.to_s
|
18
|
+
|
19
|
+
@mutex.synchronize do
|
20
|
+
unless @counts[level]
|
21
|
+
@counts[level] = 0
|
22
|
+
end
|
23
|
+
@counts[level] += 1
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def flush
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_json
|
31
|
+
hash = {}
|
32
|
+
@mutex.synchronize do
|
33
|
+
Steno::Logger::LEVELS.keys.each do |level_name|
|
34
|
+
hash[level_name] = @counts.fetch(level_name.to_s, 0)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
Yajl::Encoder.encode(hash)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Provide a map of string level -> count. This is thread-safe, the return value is a copy.
|
41
|
+
def counts
|
42
|
+
@mutex.synchronize { @counts.dup }
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
if Steno::Sink::WINDOWS
|
2
|
+
require "steno/sink/base"
|
3
|
+
|
4
|
+
require "singleton"
|
5
|
+
require "thread"
|
6
|
+
require 'win32/eventlog'
|
7
|
+
|
8
|
+
class Steno::Sink::Eventlog < Steno::Sink::Base
|
9
|
+
include Singleton
|
10
|
+
|
11
|
+
LOG_LEVEL_MAP = {
|
12
|
+
:fatal => Win32::EventLog::ERROR_TYPE,
|
13
|
+
:error => Win32::EventLog::ERROR_TYPE,
|
14
|
+
:warn => Win32::EventLog::WARN_TYPE,
|
15
|
+
:info => Win32::EventLog::INFO_TYPE,
|
16
|
+
:debug => Win32::EventLog::INFO_TYPE,
|
17
|
+
:debug1 => Win32::EventLog::INFO_TYPE,
|
18
|
+
:debug2 => Win32::EventLog::INFO_TYPE,
|
19
|
+
}
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
super
|
23
|
+
@eventlog = nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def open()
|
27
|
+
@eventlog = Win32::EventLog::open('Application')
|
28
|
+
end
|
29
|
+
|
30
|
+
def add_record(record)
|
31
|
+
msg = @codec.encode_record(record)
|
32
|
+
pri = LOG_LEVEL_MAP[record.log_level]
|
33
|
+
|
34
|
+
@eventlog.report_event(
|
35
|
+
:source => 'CloudFoundry',
|
36
|
+
:event_type => pri,
|
37
|
+
:data => msg
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
def flush
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'fluent-logger'
|
2
|
+
#
|
3
|
+
# Steno sink implementation for Fluentd
|
4
|
+
#
|
5
|
+
# See fluentd at http://fluentd.org/
|
6
|
+
# and fluent-logger at https://github.com/fluent/fluent-logger-ruby
|
7
|
+
#
|
8
|
+
class Steno::Sink::Fluentd < Steno::Sink::Base
|
9
|
+
|
10
|
+
# @param [Hash] opts Key :tag_prefix tag prefix of fluent logs (default: steno)
|
11
|
+
# Key :host fluentd host (default: 127.0.0.1)
|
12
|
+
# Key :port fluentd port (deafult: 24224)
|
13
|
+
# Key :buffer_limit buffer limit of fluent-logger
|
14
|
+
def initialize(opts = {})
|
15
|
+
super
|
16
|
+
|
17
|
+
@fluentd = Fluent::Logger::FluentLogger.new(opts[:tag_prefix] || "steno",
|
18
|
+
:host => opts[:host] || "127.0.0.1",
|
19
|
+
:port => opts[:port] || 24224,
|
20
|
+
:buffer_limit => opts[:buffer_limit] || Fluent::Logger::FluentLogger::BUFFER_LIMIT)
|
21
|
+
@io_lock = Mutex.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def add_record(record)
|
25
|
+
@fluentd.post(record.source, record)
|
26
|
+
end
|
27
|
+
|
28
|
+
def flush
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require "steno/sink/base"
|
2
|
+
|
3
|
+
module Steno
|
4
|
+
module Sink
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
class Steno::Sink::IO < Steno::Sink::Base
|
9
|
+
class << self
|
10
|
+
# Returns a new sink configured to append to the file at path.
|
11
|
+
#
|
12
|
+
# @param [String] path
|
13
|
+
# @param [Hash] If the key :autoflush is set to true, encoded records
|
14
|
+
# will not be buffered by Ruby. The key :max_retries
|
15
|
+
# is forwarded to Steno::Sink::IO object during creation.
|
16
|
+
# @return [Steno::Sink::IO]
|
17
|
+
def for_file(path, opts = {})
|
18
|
+
autoflush = true
|
19
|
+
if opts.include?(:autoflush)
|
20
|
+
autoflush = opts[:autoflush]
|
21
|
+
end
|
22
|
+
|
23
|
+
io = File.open(path, "a+")
|
24
|
+
|
25
|
+
io.sync = autoflush
|
26
|
+
|
27
|
+
new(io, :max_retries => opts[:max_retries])
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
attr_reader :max_retries
|
32
|
+
|
33
|
+
# @param [IO] io The IO object that will be written to
|
34
|
+
# @param [Hash] opts Key :codec is used to specify a codec inheriting from
|
35
|
+
# Steno::Codec::Base.
|
36
|
+
# Key :max_retries takes an integer value which specifies
|
37
|
+
# the number of times the write operation can be retried
|
38
|
+
# when IOError is raised while writing a record.
|
39
|
+
def initialize(io, opts = {})
|
40
|
+
super(opts[:codec])
|
41
|
+
|
42
|
+
@max_retries = opts[:max_retries] || -1
|
43
|
+
@io_lock = Mutex.new
|
44
|
+
@io = io
|
45
|
+
end
|
46
|
+
|
47
|
+
def add_record(record)
|
48
|
+
bytes = @codec.encode_record(record)
|
49
|
+
|
50
|
+
@io_lock.synchronize do
|
51
|
+
retries = 0
|
52
|
+
begin
|
53
|
+
@io.write(bytes)
|
54
|
+
rescue IOError => e
|
55
|
+
if retries < @max_retries
|
56
|
+
retries += 1
|
57
|
+
retry
|
58
|
+
else
|
59
|
+
raise e
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
nil
|
65
|
+
end
|
66
|
+
|
67
|
+
def flush
|
68
|
+
@io_lock.synchronize { @io.flush }
|
69
|
+
|
70
|
+
nil
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
unless Steno::Sink::WINDOWS
|
2
|
+
require "steno/sink/base"
|
3
|
+
|
4
|
+
require "singleton"
|
5
|
+
require "thread"
|
6
|
+
require "syslog/logger"
|
7
|
+
|
8
|
+
class Steno::Sink::Syslog < Steno::Sink::Base
|
9
|
+
include Singleton
|
10
|
+
|
11
|
+
MAX_MESSAGE_SIZE = 1024 * 3
|
12
|
+
TRUNCATE_POSTFIX = "..."
|
13
|
+
|
14
|
+
LOG_LEVEL_MAP = {
|
15
|
+
:fatal => Syslog::LOG_CRIT,
|
16
|
+
:error => Syslog::LOG_ERR,
|
17
|
+
:warn => Syslog::LOG_WARNING,
|
18
|
+
:info => Syslog::LOG_INFO,
|
19
|
+
:debug => Syslog::LOG_DEBUG,
|
20
|
+
:debug1 => Syslog::LOG_DEBUG,
|
21
|
+
:debug2 => Syslog::LOG_DEBUG,
|
22
|
+
}
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
super
|
26
|
+
|
27
|
+
@syslog = nil
|
28
|
+
@syslog_lock = Mutex.new
|
29
|
+
end
|
30
|
+
|
31
|
+
def open(identity)
|
32
|
+
@identity = identity
|
33
|
+
|
34
|
+
Syslog::Logger.new(@identity)
|
35
|
+
@syslog = Syslog::Logger.syslog
|
36
|
+
end
|
37
|
+
|
38
|
+
def add_record(record)
|
39
|
+
record = truncate_record(record)
|
40
|
+
msg = @codec.encode_record(record)
|
41
|
+
pri = LOG_LEVEL_MAP[record.log_level]
|
42
|
+
@syslog_lock.synchronize { @syslog.log(pri, "%s", msg) }
|
43
|
+
end
|
44
|
+
|
45
|
+
def flush
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def truncate_record(record)
|
52
|
+
return record if record.message.size <= MAX_MESSAGE_SIZE
|
53
|
+
|
54
|
+
truncated = record.message.slice(0...(MAX_MESSAGE_SIZE - TRUNCATE_POSTFIX.size))
|
55
|
+
truncated << TRUNCATE_POSTFIX
|
56
|
+
Steno::Record.new(record.source, record.log_level,
|
57
|
+
truncated,
|
58
|
+
[record.file, record.lineno, record.method],
|
59
|
+
record.data)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require "steno/logger"
|
2
|
+
|
3
|
+
module Steno
|
4
|
+
end
|
5
|
+
|
6
|
+
# Provides a proxy that allows persistent user data
|
7
|
+
class Steno::TaggedLogger
|
8
|
+
|
9
|
+
attr_reader :proxied_logger
|
10
|
+
attr_accessor :user_data
|
11
|
+
|
12
|
+
class << self
|
13
|
+
# The following helpers are used to create a new scope for binding the log
|
14
|
+
# level.
|
15
|
+
|
16
|
+
def define_log_method(name)
|
17
|
+
define_method(name) { |*args, &blk| log(name, *args, &blk) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def define_logf_method(name)
|
21
|
+
define_method(name.to_s + "f") { |fmt, *args| log(name, fmt % args) }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
Steno::Logger::LEVELS.each do |name, _|
|
26
|
+
# Define #debug, for example
|
27
|
+
define_log_method(name)
|
28
|
+
|
29
|
+
# Define #debugf, for example
|
30
|
+
define_logf_method(name)
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(proxied_logger, user_data = {})
|
34
|
+
@proxied_logger = proxied_logger
|
35
|
+
@user_data = user_data
|
36
|
+
end
|
37
|
+
|
38
|
+
def method_missing(method, *args, &blk)
|
39
|
+
@proxied_logger.send(method, *args, &blk)
|
40
|
+
end
|
41
|
+
|
42
|
+
# @see Steno::Logger#log
|
43
|
+
def log(level_name, message = nil, user_data = nil, &blk)
|
44
|
+
ud = @user_data.merge(user_data || {})
|
45
|
+
|
46
|
+
@proxied_logger.log(level_name, message, ud, &blk)
|
47
|
+
end
|
48
|
+
|
49
|
+
# @see Steno::Logger#log_exception
|
50
|
+
def log_exception(ex, user_data = {})
|
51
|
+
ud = @user_data.merge(user_data || {})
|
52
|
+
|
53
|
+
@proxied_logger.log_exception(ex, ud)
|
54
|
+
end
|
55
|
+
|
56
|
+
def tag(new_user_data = {})
|
57
|
+
Steno::TaggedLogger.new(proxied_logger, user_data.merge(new_user_data))
|
58
|
+
end
|
59
|
+
end
|
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,229 @@
|
|
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
|
+
expect(@mock_sink_file).to receive(:codec=)
|
15
|
+
expect(Steno::Sink::IO).to receive(:for_file).with(@log_path,
|
16
|
+
:max_retries => 5)
|
17
|
+
.and_return(@mock_sink_file)
|
18
|
+
|
19
|
+
@mock_sink_eventlog = double("sink")
|
20
|
+
expect(@mock_sink_eventlog).to receive(:codec=)
|
21
|
+
expect(@mock_sink_eventlog).to receive(:open).with("test")
|
22
|
+
expect(Steno::Sink::Eventlog).to 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
|
+
expect(@config.default_log_level).to eq(:debug2)
|
30
|
+
expect(@config.context.class).to eq(Steno::Context::Null)
|
31
|
+
expect(@config.codec.class).to eq(Steno::Codec::Json)
|
32
|
+
|
33
|
+
expect(@config.sinks.size).to eq(2)
|
34
|
+
expect(@config.sinks).to match_array([@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
|
+
allow(@mock_sink_file).to receive(:codec=)
|
65
|
+
expect(Steno::Sink::IO).to receive(:for_file).with(@log_path,
|
66
|
+
:max_retries => 5)
|
67
|
+
.and_return(@mock_sink_file)
|
68
|
+
|
69
|
+
@mock_sink_syslog = double("sink")
|
70
|
+
expect(@mock_sink_syslog).to receive(:codec=)
|
71
|
+
expect(@mock_sink_syslog).to receive(:open).with("test")
|
72
|
+
expect(Steno::Sink::Syslog).to 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
|
+
expect(@config.default_log_level).to eq(:debug2)
|
80
|
+
expect(@config.context.class).to eq(Steno::Context::Null)
|
81
|
+
expect(@config.codec.class).to eq(Steno::Codec::Json)
|
82
|
+
|
83
|
+
expect(@config.sinks.size).to eq(2)
|
84
|
+
expect(@config.sinks).to match_array([@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
|
+
expect(config.sinks.size).to eq(1)
|
127
|
+
expect(config.sinks[0].class).to eq(Steno::Sink::IO)
|
128
|
+
|
129
|
+
expect(config.default_log_level).to eq(:info)
|
130
|
+
|
131
|
+
expect(config.context.class).to eq(Steno::Context::Null)
|
132
|
+
|
133
|
+
expect(config.codec.class).to eq(Steno::Codec::Json)
|
134
|
+
expect(config.codec.iso8601_timestamps?).to eq(false)
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should configure json codec with readable dates if iso8601_timestamps is true" do
|
138
|
+
write_config(@config_path, {"iso8601_timestamps" => "true"})
|
139
|
+
config = Steno::Config.from_file(@config_path)
|
140
|
+
expect(config.codec.class).to eq(Steno::Codec::Json)
|
141
|
+
expect(config.codec.iso8601_timestamps?).to eq(true)
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should set the default_log_level if a key with the same name is supplied" do
|
145
|
+
write_config(@config_path, {"level" => "debug2"})
|
146
|
+
expect(Steno::Config.from_file(@config_path).default_log_level).to eq(:debug2)
|
147
|
+
|
148
|
+
write_config(@config_path, {"default_log_level" => "debug2"})
|
149
|
+
expect(Steno::Config.from_file(@config_path).default_log_level).to eq(:debug2)
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should read the 'level' key if both default_log_level and level are spscified" do
|
153
|
+
write_config(@config_path, {"level" => "debug2",
|
154
|
+
"default_log_level" => "warn"})
|
155
|
+
expect(Steno::Config.from_file(@config_path).default_log_level).to eq(:debug2)
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should add a file sink if the 'file' key is specified" do
|
159
|
+
write_config(@config_path, {"file" => @log_path, "max_retries" => 2})
|
160
|
+
mock_sink = double("sink")
|
161
|
+
expect(mock_sink).to receive(:codec=)
|
162
|
+
|
163
|
+
expect(Steno::Sink::IO).to receive(:for_file).
|
164
|
+
with(@log_path, :max_retries => 2).and_return(mock_sink)
|
165
|
+
config = Steno::Config.from_file(@config_path)
|
166
|
+
expect(config.sinks.size).to eq(1)
|
167
|
+
expect(config.sinks[0]).to eq(mock_sink)
|
168
|
+
end
|
169
|
+
|
170
|
+
if Steno::Sink::WINDOWS
|
171
|
+
it "should add a event sink if the 'eventlog' key is specified" do
|
172
|
+
write_config(@config_path, {"eventlog" => "test"})
|
173
|
+
mock_sink = double("sink")
|
174
|
+
expect(mock_sink).to receive(:open).with("test")
|
175
|
+
expect(mock_sink).to receive(:codec=)
|
176
|
+
|
177
|
+
expect(Steno::Sink::Eventlog).to receive(:instance).twice().and_return(mock_sink)
|
178
|
+
|
179
|
+
config = Steno::Config.from_file(@config_path)
|
180
|
+
expect(config.sinks.size).to eq(1)
|
181
|
+
expect(config.sinks[0]).to eq(mock_sink)
|
182
|
+
end
|
183
|
+
else
|
184
|
+
it "should add a syslog sink if the 'syslog' key is specified" do
|
185
|
+
write_config(@config_path, {"syslog" => "test"})
|
186
|
+
mock_sink = double("sink")
|
187
|
+
expect(mock_sink).to receive(:open).with("test")
|
188
|
+
expect(mock_sink).to receive(:codec=)
|
189
|
+
|
190
|
+
expect(Steno::Sink::Syslog).to receive(:instance).twice().and_return(mock_sink)
|
191
|
+
|
192
|
+
config = Steno::Config.from_file(@config_path)
|
193
|
+
expect(config.sinks.size).to eq(1)
|
194
|
+
expect(config.sinks[0]).to eq(mock_sink)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
|
199
|
+
|
200
|
+
it "should add an io sink to stdout if no sinks are explicitly specified in the config file" do
|
201
|
+
write_config(@config_path, {})
|
202
|
+
mock_sink = double("sink")
|
203
|
+
expect(mock_sink).to receive(:codec=)
|
204
|
+
|
205
|
+
expect(Steno::Sink::IO).to receive(:new).with(STDOUT).and_return(mock_sink)
|
206
|
+
|
207
|
+
config = Steno::Config.from_file(@config_path)
|
208
|
+
expect(config.sinks.size).to eq(1)
|
209
|
+
expect(config.sinks[0]).to eq(mock_sink)
|
210
|
+
end
|
211
|
+
|
212
|
+
it "should merge supplied overrides with the file based config" do
|
213
|
+
write_config(@config_path, {"default_log_level" => "debug"})
|
214
|
+
|
215
|
+
context = Steno::Context::ThreadLocal.new
|
216
|
+
config = Steno::Config.from_file(@config_path,
|
217
|
+
:default_log_level => "warn",
|
218
|
+
:context => context)
|
219
|
+
expect(config.context).to eq(context)
|
220
|
+
expect(config.default_log_level).to eq(:warn)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def write_config(path, config)
|
225
|
+
File.open(path, "w+") do |f|
|
226
|
+
f.write(YAML.dump({"logging" => config}))
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|