steno 1.2.2-x86-mingw32
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/lib/steno/record.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require "digest/md5"
|
2
|
+
require "thread"
|
3
|
+
|
4
|
+
module Steno
|
5
|
+
end
|
6
|
+
|
7
|
+
class Steno::Record
|
8
|
+
|
9
|
+
attr_reader :timestamp
|
10
|
+
attr_reader :message
|
11
|
+
attr_reader :log_level
|
12
|
+
attr_reader :source
|
13
|
+
attr_reader :data
|
14
|
+
attr_reader :thread_id
|
15
|
+
attr_reader :fiber_id
|
16
|
+
attr_reader :process_id
|
17
|
+
attr_reader :file
|
18
|
+
attr_reader :lineno
|
19
|
+
attr_reader :method
|
20
|
+
|
21
|
+
# @param [String] source Identifies message source.
|
22
|
+
# @param [Symbol] log_level
|
23
|
+
# @param [String] message
|
24
|
+
# @param [Array] loc Location where the record was generated.
|
25
|
+
# Format is [<filename>, <lineno>, <method>].
|
26
|
+
# @param [Hash] data User-supplied data
|
27
|
+
def initialize(source, log_level, message, loc = [], data = {})
|
28
|
+
raise "Log level must be a Symbol" unless log_level.is_a? Symbol
|
29
|
+
|
30
|
+
@timestamp = Time.now
|
31
|
+
@source = source
|
32
|
+
@log_level = log_level
|
33
|
+
@message = message.to_s
|
34
|
+
@data = {}.merge(data)
|
35
|
+
@thread_id = Thread.current.object_id
|
36
|
+
@fiber_id = Fiber.current.object_id
|
37
|
+
@process_id = Process.pid
|
38
|
+
|
39
|
+
@file, @lineno, @method = loc
|
40
|
+
end
|
41
|
+
end
|
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,
|
13
|
+
:error => Win32::EventLog::ERROR,
|
14
|
+
:warn => Win32::EventLog::WARN,
|
15
|
+
:info => Win32::EventLog::INFO,
|
16
|
+
:debug => Win32::EventLog::INFO,
|
17
|
+
:debug1 => Win32::EventLog::INFO,
|
18
|
+
:debug2 => Win32::EventLog::INFO,
|
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,59 @@
|
|
1
|
+
unless Steno::Sink::WINDOWS
|
2
|
+
require "steno/sink/base"
|
3
|
+
|
4
|
+
require "singleton"
|
5
|
+
require "thread"
|
6
|
+
require "syslog"
|
7
|
+
|
8
|
+
class Steno::Sink::Syslog < Steno::Sink::Base
|
9
|
+
include Singleton
|
10
|
+
|
11
|
+
MAX_MESSAGE_SIZE = 1024 * 3
|
12
|
+
|
13
|
+
LOG_LEVEL_MAP = {
|
14
|
+
:fatal => Syslog::LOG_CRIT,
|
15
|
+
:error => Syslog::LOG_ERR,
|
16
|
+
:warn => Syslog::LOG_WARNING,
|
17
|
+
:info => Syslog::LOG_INFO,
|
18
|
+
:debug => Syslog::LOG_DEBUG,
|
19
|
+
:debug1 => Syslog::LOG_DEBUG,
|
20
|
+
:debug2 => Syslog::LOG_DEBUG,
|
21
|
+
}
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
super
|
25
|
+
|
26
|
+
@syslog = nil
|
27
|
+
@syslog_lock = Mutex.new
|
28
|
+
end
|
29
|
+
|
30
|
+
def open(identity)
|
31
|
+
@identity = identity
|
32
|
+
@syslog = Syslog.open(@identity, Syslog::LOG_PID, Syslog::LOG_USER)
|
33
|
+
end
|
34
|
+
|
35
|
+
def add_record(record)
|
36
|
+
record = truncate_record(record)
|
37
|
+
msg = @codec.encode_record(record)
|
38
|
+
pri = LOG_LEVEL_MAP[record.log_level]
|
39
|
+
@syslog_lock.synchronize { @syslog.log(pri, "%s", msg) }
|
40
|
+
end
|
41
|
+
|
42
|
+
def flush
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def truncate_record(record)
|
49
|
+
return record if record.message.size <= MAX_MESSAGE_SIZE
|
50
|
+
|
51
|
+
truncated = record.message.slice(0..(MAX_MESSAGE_SIZE - 1))
|
52
|
+
truncated << "..."
|
53
|
+
Steno::Record.new(record.source, record.log_level,
|
54
|
+
truncated,
|
55
|
+
[record.file, record.lineno, record.method],
|
56
|
+
record.data)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
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
|