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
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
|