steno 1.2.2-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
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,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,6 @@
1
+ require "steno/sink/base"
2
+ require "steno/sink/io"
3
+ require "steno/sink/syslog"
4
+ require "steno/sink/fluentd"
5
+ require "steno/sink/counter"
6
+ require "steno/sink/eventlog"
@@ -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
@@ -0,0 +1,3 @@
1
+ module Steno
2
+ VERSION = "1.2.2"
3
+ end
@@ -0,0 +1,6 @@
1
+ require "rack/test"
2
+ require "rspec"
3
+
4
+ require "steno"
5
+
6
+ Dir["./spec/support/**/*.rb"].each { |file| require file }
@@ -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