u-log 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b29ab5eaabe74647d865740ccd810a406a31e103
4
+ data.tar.gz: 2ebf738981d7231f0035b34fc0354694eb04069e
5
+ SHA512:
6
+ metadata.gz: fdf58a1d3f8161dcbd6354d39eeff60ea1e59c2cc2f47b3861e2b84e1084fc8b486cf5049ebc1632fee8f343babe14edf5f47031483b6c57d31b4bf20a694227
7
+ data.tar.gz: f940536cc3aa21e48940ee588827189e1741df56ab4479479f990a88ea357fb8e93a79a8d9837694f2defb49c12c4290fec2ae27cd1346600392dd83213989c2
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "https://rubygems.org"
2
+ gemspec
@@ -0,0 +1,20 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ u-log (0.0.1)
5
+ lines
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ benchmark-ips (1.2.0)
11
+ lines (0.2.0)
12
+ minitest (5.3.3)
13
+
14
+ PLATFORMS
15
+ ruby
16
+
17
+ DEPENDENCIES
18
+ benchmark-ips
19
+ minitest
20
+ u-log!
@@ -0,0 +1,100 @@
1
+ U::Log - a different take on logging
2
+ ====================================
3
+
4
+ [![Build
5
+ Status](https://travis-ci.org/zimbatm/u-log.svg?branch=master)](https://travis-ci.org/zimbatm/u-log)
6
+
7
+ An oppinionated logging library.
8
+
9
+ * Log everything in development AND production.
10
+ * Logs should be easy to read, grep and parse.
11
+ * Logging something should never fail.
12
+ * Let the system handle the storage. Write to syslog or STDERR.
13
+ * No log levels necessary. Just log whatever you want.
14
+
15
+ STATUS: ALPHA
16
+ =============
17
+
18
+ Doc is still scarce so it's quite hard to get started. I think reading the
19
+ lib/u-log.rb should give a good idea of the capabilities.
20
+
21
+ It would be nice to expose a method that resolves a context into a hash. It's
22
+ useful to share the context with other tools like an error reporter. Btw,
23
+ Sentry/Raven is great.
24
+
25
+ Quick intro
26
+ -----------
27
+
28
+ ```ruby
29
+ require 'u-log'
30
+
31
+ # Setups the outputs. IO and Syslog are supported.
32
+ l = U::Log.new($stderr, Lines,
33
+ at: ->{ Time.now.utc },
34
+ pid: ->{ Process.pid },
35
+ )
36
+
37
+ # First example
38
+ l.log(foo: 'bar') # logs: at=2013-07-14T14:19:28Z foo=bar
39
+
40
+ # If not a hash, the argument is transformed. A second argument is accepted as
41
+ # a hash
42
+ l.log("Hey", count: 3) # logs: at=2013-07-14T14:19:28Z msg=Hey count=3
43
+
44
+ # You can also keep a context
45
+ class MyClass < ActiveRecord::Base
46
+ attr_reader :lines
47
+ def initialize(logger)
48
+ @logger = logger.context(my_class_id: self.id)
49
+ end
50
+
51
+ def do_something
52
+ logger.log("Something happened")
53
+ # logs: at=2013-07-14T14:19:28Z msg='Something happeend' my_class_id: 2324
54
+ end
55
+ end
56
+ ```
57
+
58
+ Features
59
+ --------
60
+
61
+ * Simple to use
62
+ * Thread safe (if the IO#write is)
63
+ * Designed to not raise exceptions (unless it's an IO issue)
64
+ * A backward-compatible Logger is provided in case you want to retrofit
65
+ * require "u-log/active_record" for sane ActiveRecord logs
66
+ * "u-log/rack_logger" is a logging middleware for Rack
67
+
68
+ Known issues
69
+ ------------
70
+
71
+ Syslog seems to truncate lines longer than 2056 chars and Lines makes if very
72
+ easy to put too much data.
73
+
74
+ Lines logging speed is reasonable but it could be faster. It writes at around
75
+ 5000 lines per second to Syslog on my machine.
76
+
77
+ Inspired by
78
+ -----------
79
+
80
+ * Scrolls : https://github.com/asenchi/scrolls
81
+ * Lograge : https://github.com/roidrage/lograge
82
+
83
+
84
+
85
+
86
+
87
+ * Very simple (aka. fast)
88
+ * No log level
89
+
90
+
91
+
92
+ TODO
93
+ ----
94
+
95
+ Error reporting
96
+
97
+ Create message context
98
+
99
+
100
+ Because the log context is hierarchical,
@@ -0,0 +1,26 @@
1
+ require 'benchmark/ips'
2
+ require 'stringio'
3
+
4
+ data = 100.times.map{ 'sadfljkasfjlk' }
5
+
6
+ Benchmark.ips do |x|
7
+ x.report "join" do |n|
8
+ n.times do
9
+ data.join
10
+ end
11
+ end
12
+ x.report "StringIO" do |n|
13
+ n.times do
14
+ s = StringIO.new('wb')
15
+ data.each do |y|
16
+ s.write y
17
+ end
18
+ s.string
19
+ end
20
+ end
21
+ x.report "inject+" do |n|
22
+ n.times do
23
+ data.inject(&:+)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,113 @@
1
+ # U::Log is an opinionated logging library.
2
+ #
3
+ # The main take is that logs are structured.
4
+ # Too much time is spend formatting logs, they should look nice by default.
5
+ # That's why U::Log uses the lines format by default.
6
+ #
7
+ # Log everything in development AND production.
8
+ # Logs should be easy to read, grep and parse.
9
+ # Logging something should never fail.
10
+ # Let the system handle the storage. Write to syslog or STDERR.
11
+ # No log levels necessary. Just log whatever you want.
12
+ #
13
+ # Example:
14
+ #
15
+ # U.log("Oops !", foo: {}, g: [])
16
+ # #outputs:
17
+ # # at=2013-03-07T09:21:39Z pid=3242 app=some-process msg="Oops !" foo={} g=[]
18
+ #
19
+ # Usage:
20
+ #
21
+ #
22
+ # U.log(foo: 3, msg: "This")
23
+ #
24
+ # ctx = U.log_context(encoding_id: Log.id)
25
+ # ctx.log({})
26
+ #
27
+ module U; module Log
28
+ # A very simple logger and log context
29
+ #
30
+ class Logger
31
+ NL = "\n".freeze
32
+
33
+ # +out+ is the log destination. Has a #<< method that takes a string.
34
+ # +format+ is what transforms the data into a string using the #dump method
35
+ # +data+ is context data for the logger. Responds to #to_h.
36
+ def initialize(out, format, data)
37
+ @out = out
38
+ @format = format
39
+ @data = data.to_h
40
+ end
41
+
42
+ # Outputs the given arguments merged with the context.
43
+ def log(*args)
44
+ @out << with_data(args_to_hash args).to_s + NL
45
+ end
46
+
47
+ # Creates a derivative context so that `context(a: 1).context(b: 2)`
48
+ # is equivalent to `contect(a: 1, b: 2)`
49
+ def context(data = {})
50
+ return self unless data.to_h.any?
51
+ with_data @data.merge(data.to_h)
52
+ end
53
+
54
+ alias merge context
55
+
56
+ def to_s
57
+ @format.dump(evaluate_procs @data)
58
+ end
59
+
60
+ def to_h; @data; end
61
+
62
+ # Returns a ::Logger-compatible object.
63
+ #
64
+ # Make sure to require 'u-log/compat' before invoking this method.
65
+ #
66
+ def compat; Compat.new(self) end
67
+
68
+ protected
69
+
70
+ def with_data(data)
71
+ self.class.new @out, @format, data
72
+ end
73
+
74
+ def evaluate_procs(obj)
75
+ obj.each_with_object({}) do |(k,v), merged|
76
+ merged[k] = v.respond_to?(:call) ? (v.call rescue $!) : v
77
+ end
78
+ end
79
+
80
+ def args_to_hash(args)
81
+ return {} if args.empty?
82
+ data = @data.dup
83
+ # Allow the first argument to be a message
84
+ if !args.first.respond_to? :to_h
85
+ data.merge!(msg: args.shift)
86
+ end
87
+ args.inject(data) do |h, obj|
88
+ h.merge! obj.to_h
89
+ end
90
+ data
91
+ end
92
+ end
93
+ end end
94
+
95
+ require 'lines'
96
+
97
+ module U
98
+ class << self
99
+ # Default logger that outputs to stderr with the Logfmt format
100
+ attr_accessor :logger
101
+
102
+ # shortcut for U.logger.log
103
+ def log(*args); logger.log(*args); end
104
+ # shotcut for U.logger.context
105
+ def log_context(data={}); logger.context(data); end
106
+ end
107
+
108
+ # Default global
109
+ self.logger = Log::Logger.new($stderr, Lines,
110
+ at: ->{ Time.now.utc },
111
+ pid: ->{ Process.pid },
112
+ )
113
+ end
@@ -0,0 +1,68 @@
1
+ require 'active_record'
2
+ require 'active_record/log_subscriber'
3
+ require 'u-log'
4
+ require 'u-log/compat'
5
+
6
+ module U; module Log
7
+ class ActiveRecordSubscriber < ActiveSupport::LogSubscriber
8
+ def render_bind(column, value)
9
+ if column
10
+ if column.binary?
11
+ value = "<#{value.bytesize} bytes of binary data>"
12
+ end
13
+
14
+ [column.name, value]
15
+ else
16
+ [nil, value]
17
+ end
18
+ end
19
+
20
+ def sql(event)
21
+ payload = event.payload
22
+
23
+ return if payload[:name] == 'SCHEMA' || payload[:name] == 'EXPLAIN'
24
+
25
+ args = {}
26
+
27
+ args[:name] = payload[:name] if payload[:name]
28
+ args[:sql] = payload[:sql].squeeze(' ')
29
+
30
+ if payload[:binds] && payload[:binds].any?
31
+ args[:binds] = payload[:binds].inject({}) do |hash,(col, v)|
32
+ k, v = render_bind(col, v)
33
+ hash[k] = v
34
+ hash
35
+ end
36
+ end
37
+
38
+ args[:elapsed] = [event.duration.round(1), 'ms']
39
+
40
+ logger.ulogger.log(args)
41
+ end
42
+
43
+ def identity(event)
44
+ logger.ulogger.log(name: event.payload[:name], line: event.payload[:line])
45
+ end
46
+ end
47
+ end end
48
+
49
+ # Replace the base logger with our own compatible logger
50
+ ActiveRecord::Base.logger = U.logger.compat
51
+
52
+ # Subscribe u-log to the AR events
53
+ U::Log::ActiveRecordSubscriber.attach_to :active_record
54
+
55
+ # Remove the default ActiveRecord::LogSubscriber to avoid double outputs
56
+ ActiveSupport::LogSubscriber.log_subscribers.each do |subscriber|
57
+ if subscriber.is_a?(ActiveRecord::LogSubscriber)
58
+ component = :active_record
59
+ events = subscriber.public_methods(false).reject{ |method| method.to_s == 'call' }
60
+ events.each do |event|
61
+ ActiveSupport::Notifications.notifier.listeners_for("#{event}.#{component}").each do |listener|
62
+ if listener.instance_variable_get('@delegate') == subscriber
63
+ ActiveSupport::Notifications.unsubscribe listener
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,61 @@
1
+ module U; module Log
2
+ # Backward-compatible with the stdlib Logger
3
+ # http://ruby-doc.org/stdlib-2.0/libdoc/logger/rdoc/Logger.html
4
+ class Compat
5
+ LEVELS = {
6
+ 0 => :debug,
7
+ 1 => :info,
8
+ 2 => :warn,
9
+ 3 => :error,
10
+ 4 => :fatal,
11
+ 5 => :unknown,
12
+ }
13
+
14
+ attr_reader :ulogger
15
+
16
+ def initialize(ulogger)
17
+ @ulogger = ulogger
18
+ end
19
+
20
+ def log(severity, message = nil, progname = nil, &block)
21
+ pri = LEVELS[severity] || severity
22
+ if block_given?
23
+ progname = message
24
+ message = yield rescue $!
25
+ end
26
+
27
+ data = { pri: pri }
28
+ data[:app] = progname if progname
29
+ data[:msg] = message if message
30
+
31
+ @logger.log(data)
32
+ end
33
+
34
+ LEVELS.values.each do |level|
35
+ eval "def #{level}(msg=nil, &block); log(:#{level}, msg, &block); end"
36
+ end
37
+
38
+ alias << info
39
+ alias unknown info
40
+
41
+ def noop(*); true end
42
+ %w[add
43
+ clone
44
+ datetime_format
45
+ datetime_format=
46
+ debug?
47
+ info?
48
+ error?
49
+ fatal?
50
+ warn?
51
+ level
52
+ level=
53
+ progname
54
+ progname=
55
+ sev_threshold
56
+ sev_threshold=
57
+ ].each do |op|
58
+ alias_method(op, :noop)
59
+ end
60
+ end
61
+ end end
@@ -0,0 +1,39 @@
1
+ require 'rack/commonlogger'
2
+
3
+ module U; module Log
4
+ class RackLogger < Rack::CommonLogger
5
+ # In development mode the common logger is always inserted
6
+ def self.silence_common_logger!
7
+ Rack::CommonLogger.module_eval('def call(env); @app.call(env); end')
8
+ self
9
+ end
10
+
11
+ def initialize(app, logger)
12
+ @app = app
13
+ @logger = logger
14
+ end
15
+
16
+ def call(env)
17
+ began_at = Time.now
18
+ status, header, body = @app.call(env)
19
+ header = Rack::Utils::HeaderHash.new(header)
20
+ body = Rack::BodyProxy.new(body) { log(env, status, header, began_at) }
21
+ [status, header, body]
22
+ end
23
+
24
+ protected
25
+
26
+ def log(env, status, header, began_at)
27
+ @logger.log(
28
+ remote_addr: env['HTTP_X_FORWARDED_FOR'] || env['REMOTE_ADDR'],
29
+ remote_user: env['REMOTE_USER'] || '',
30
+ method: env['REQUEST_METHOD'],
31
+ path: env['PATH_INFO'],
32
+ query: env['QUERY_STRING'],
33
+ status: status.to_s[0..3],
34
+ length: extract_content_length(header),
35
+ elapsed: [Time.now - began_at, 's'],
36
+ )
37
+ end
38
+ end
39
+ end end
@@ -0,0 +1,3 @@
1
+ module U; module Log
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,23 @@
1
+ $:.unshift File.expand_path('../../lib', __FILE__)
2
+
3
+ require 'minitest/autorun'
4
+ require 'stringio'
5
+
6
+ require 'u-log'
7
+
8
+ class U_LoggerTest < MiniTest::Spec
9
+ include U::Log
10
+ NL = "\n"
11
+
12
+ def setup
13
+ @out = StringIO.new
14
+ @ctx = Logger.new(@out, Lines, {})
15
+ end
16
+
17
+ describe 'context' do
18
+ it 'does stuff' do
19
+ @ctx.log('hello')
20
+ assert_equal 'msg=hello' + NL, @out.string
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ # coding: utf-8
2
+ Gem::Specification.new do |spec|
3
+ spec.name = "u-log"
4
+ spec.version = "0.0.1"
5
+ spec.authors = ["zimbatm"]
6
+ spec.email = ["zimbatm@zimbatm.com"]
7
+ spec.summary = %q{a different take on logging}
8
+ spec.description = %q{U::Logger is a very simple logging library made for humans}
9
+ spec.homepage = 'https://github.com/zimbatm/u-logger'
10
+ spec.license = "MIT"
11
+
12
+ spec.files = `git ls-files`.split($/)
13
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
14
+
15
+ spec.add_runtime_dependency "lines"
16
+
17
+ spec.add_development_dependency "benchmark-ips"
18
+ spec.add_development_dependency "minitest"
19
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: u-log
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - zimbatm
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-06-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: lines
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: benchmark-ips
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: U::Logger is a very simple logging library made for humans
56
+ email:
57
+ - zimbatm@zimbatm.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - Gemfile
63
+ - Gemfile.lock
64
+ - README.md
65
+ - bench_concat.rb
66
+ - lib/u-log.rb
67
+ - lib/u-log/active_record.rb
68
+ - lib/u-log/compat.rb
69
+ - lib/u-log/rack_logger.rb
70
+ - lib/u-log/version.rb
71
+ - test/u-log_test.rb
72
+ - u-log.gemspec
73
+ homepage: https://github.com/zimbatm/u-logger
74
+ licenses:
75
+ - MIT
76
+ metadata: {}
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ requirements: []
92
+ rubyforge_project:
93
+ rubygems_version: 2.2.2
94
+ signing_key:
95
+ specification_version: 4
96
+ summary: a different take on logging
97
+ test_files:
98
+ - test/u-log_test.rb