smarter_logging 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e793102aedb16d70480b3b012d9a220973f7ae4d
4
+ data.tar.gz: 11d8c0d59dc0797adfb334a54f21615bd127eb14
5
+ SHA512:
6
+ metadata.gz: 16d8b4cb389c2b97fd84fffb14ba0dc70b2daca77510b76eae75aa9eb2d3a71ddcbf5e81982ce50ac48531a100c477ea12453185ced00ab887a4b5711926d3fd
7
+ data.tar.gz: f2e0972a840b18c442270632f4601b798a8752bd2308cebd74bba1fcea767f0ae83dd7a5936437b502abfb109ad326c302ea78905d8d8a42dd7d7180994ef372
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
13
+ *~
14
+ *.bak
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.3
5
+ before_install: gem install bundler -v 1.14.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in smarter_logging.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Tilo Sloboda
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,126 @@
1
+ # SmarterLogging
2
+
3
+ SmarterLogging helps you log mission critical data in key=value and single-line format, so it can be easily analyzed.
4
+ The format lends itself to being easily importable by SumoLogic and Splunk.
5
+
6
+ The main idea is to have dedicated log files for `activities` which happen during normal operations, as well as `anomalies` which happen when something unexpected happens.
7
+
8
+ Each log line starts with a UTC timestamp in [ISO8601 format](https://en.wikipedia.org/wiki/ISO_8601), and contains space-separated key=value pairs. If a value contains spaces, it is quoted: `key="value"`.
9
+ The advantage of ISO6801 timestamps is that they are both standardized / easy to parse, as well as human readable.
10
+
11
+ time=2017-01-30T21:21:04.013Z activity=some_unique_name key1=value1 key2="value 2"
12
+
13
+ The only place where this gem is opinionated is that UTC time is used for logging.
14
+
15
+ **Tip:** If you are currently not using UTC time in your application, you should seriously consider doing that - it makes it much easier analyzing logs and errors when dealing with clients which can be anywhere in the world.
16
+
17
+ Key goals:
18
+
19
+ * get average call durations
20
+ * get activity and anomaly logs
21
+ * help forensics on any calls / trace incidents
22
+ * quickly identify top anomalies
23
+ * get timings for blocks of code
24
+
25
+ ## Installation
26
+
27
+ Add this line to your application's Gemfile:
28
+
29
+
30
+ gem 'smarter_logging'
31
+
32
+
33
+ And then execute:
34
+
35
+ $ bundle
36
+
37
+ Or install it yourself as:
38
+
39
+ $ gem install smarter_logging
40
+
41
+ ## Usage
42
+
43
+ When included in a Rails project, two default loggers are defined:
44
+
45
+ SmarterLogging.anomaly_logger
46
+ SmarterLogging.activity_logger
47
+
48
+ Each of them provides a `log()` method.
49
+
50
+ As a shortcut, you can `include SmarterLogging` and do this:
51
+
52
+ SmarterLogging.log_anomaly(unique_key, params)
53
+ SmarterLogging.log_activity(unique_key, params)
54
+
55
+ You can either use the above two functioncalls throughout your code, or include the module `SmarterLogging` and use the methods directly throughout your code.
56
+
57
+ ## Examples
58
+
59
+ **Tip:** make sure that you use a globally unique naming convention for your anomaly and activity names -- this way you can pin-point what went wrong, and where it went wrong.
60
+
61
+ ### Logging Anomalies
62
+ 
Anomaly logs are meant to be used for errors, exceptions and caught unexpected behavior.
63
+
64
+ include SmarterLogging
65
+ 
 # you can use a single-line log statement, with a hash of all key=value pairs
66
+ log_anomaly( :invalid_parameter , {key1: 'value 1', key2: 'value 2'}
67
+
68
+ # or you can use a block - this will add a key `duration` to the log line.
69
+
70
+
71
+ if parameters_valid?(params)
72
+
73
+ # do something useful
74
+
75
+ log_activity( :user_parameters, params )
76
+
77
+ else
78
+ log_anomaly( :user_invalid_parameters ) do |logdata|
79
+ log_data.merge( invalid_parameter_hash )
80
+
81
+ # some other code
82
+
83
+ end
84
+ end
85
+
86
+
87
+
88
+ ### Logging Activities
89
+
90
+ Anomaly logs are meant to be used for reporting on expected behavior.
91
+
92
+ include SmarterLogging
93
+
94
+ # you can use a single-line log statement, with a hash of all key=value pairs
95
+ log_activity( :user_signed_up, {user_id: current_user.id} )
96
+ => time=2017-01-30T23:15:20.689Z env=development activity=user_signed_up user_id=123
97
+
98
+ # or you can use a block - this will add a key `duration` to the log line.
99
+ log_activity( :user_updated ) do |logdata|
100
+ begin
101
+
102
+ # update some values in user record here
103
+
104
+ logdata[:result] = :sucess
105
+ 
 rescue => e
106
+ log_data[:error_message] = e.message
107
+
108
+ logdata[:result] = :failure
109
+ end
110
+ end
111
+
112
+ ## Development
113
+
114
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
115
+
116
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
117
+
118
+ ## Contributing
119
+
120
+ Bug reports and pull requests are welcome on GitHub at https://github.com/Tilo Sloboda/smarter_logging. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
121
+
122
+
123
+ ## License
124
+
125
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
126
+
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "smarter_logging"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,13 @@
1
+ module SmarterLogging
2
+
3
+ class ActivityLogger < BaseLogger
4
+ attr_accessor :anomaly_logger # to keep a reference to the anomaly logger
5
+
6
+ def log(unique_identifier, data, &block)
7
+ data = {:activity => unique_identifier}.merge( data )
8
+ _log_wrapper(data, &block)
9
+ end
10
+
11
+ end
12
+
13
+ end
@@ -0,0 +1,17 @@
1
+ module SmarterLogging
2
+
3
+ class AnomalyLogger < BaseLogger
4
+
5
+ def log(unique_identifier, data, &block)
6
+ data = {:anomaly => unique_identifier}.merge( data )
7
+ _log_wrapper(data, &block)
8
+ end
9
+
10
+ def _log(data)
11
+ # If activity logging broke, make sure we log it as an anomaly:
12
+ data[:anomaly] = data.delete(:activity) if data[:activity]
13
+ super
14
+ end
15
+ end
16
+
17
+ end
@@ -0,0 +1,89 @@
1
+ require 'fileutils'
2
+
3
+ module SmarterLogging
4
+ class BaseLogger
5
+
6
+ def initialize(log_filename)
7
+ if defined?(Rails)
8
+ log_filename = "#{Rails.root}/log/#{log_filename}"
9
+ else
10
+ FilUtils.mkdir './log'
11
+ log_filename = "./log/#{log_filename}"
12
+ end
13
+ @logger = Logger.new(log_filename)
14
+ @logger.formatter = nil if @logger.respond_to?(:formatter)
15
+ end
16
+
17
+ # this wrapper around _log() handles blocks and errors which could handle in a block
18
+ # if there is an exception during evaluation of the block, we log it as an anomaly and re-raise the exception
19
+ def _log_wrapper(data={}, &block)
20
+ had_anomaly = false
21
+ if block_given?
22
+ begin
23
+ start = Time.now.utc # to measure duration of the block
24
+ result = yield(data)
25
+ data[:duration] = ((Time.now.utc - start) * 1000).to_f.round(3) # in ms
26
+ result
27
+
28
+ rescue => e
29
+ data[:exception] = e
30
+ data[:duration] = ((Time.now.utc - start) * 1000).to_f.round(3) # in ms
31
+
32
+ # we should log this to the anomaly logger also if one is defined
33
+ if @anomaly_logger
34
+ @anomaly_logger._log( data )
35
+ had_anomaly = true
36
+ else
37
+ data[:success] = false
38
+ end
39
+ raise # re-raise
40
+ ensure
41
+ # log it, even if there is no anomaly logger
42
+ _log( data ) unless had_anomaly # if it was logged as an anomaly, don't log again
43
+ end
44
+
45
+ else # without a block, just call the lowest-level logging
46
+ _log( data )
47
+ end
48
+ end
49
+
50
+ # lowest-level logging method:
51
+ def _log(data)
52
+ if exception = data.delete(:exception)
53
+ data[:error] = [exception.class, exception.message].join[' : ']
54
+ data[:backtrace] = exception.backtrace[0..5].join(' | ')
55
+ end
56
+
57
+ extra_data = {
58
+ :time => Time.now.utc.strftime("%Y-%m-%dT%H:%M:%S.%LZ"),
59
+ :env => Rails.env,
60
+ }
61
+ SmarterLogging::ControllerHelper::KEYS.each do |key|
62
+ extra_data[key] = Thread.current[key] if Thread.current[key]
63
+ end
64
+
65
+ data = extra_data.merge( data )
66
+ # using low-level logging:
67
+ @logger << reformat( data ) + "\n"
68
+ end
69
+
70
+ private
71
+ # reformat incoming values, and make sure they are not listed in the Rails Filter Parameters
72
+ def reformat(data)
73
+ filtered_keys = Rails.application.config.filter_parameters.map{|x| x.to_s}
74
+ result = []
75
+ data.each do |k,v|
76
+ if filtered_keys.include?(k.to_s)
77
+ v = '[FILTERED]'
78
+ elsif v =~ /\s/ # quote values with spaces
79
+ v = "\"#{v}\""
80
+ end
81
+
82
+ result << "#{k}=#{v}"
83
+ end
84
+ result.join(' ')
85
+ end
86
+
87
+ end
88
+
89
+ end
@@ -0,0 +1,21 @@
1
+ module SmarterLogging
2
+
3
+ module ControllerHelper
4
+ KEYS = [:rqid, :ip, :user_agent]
5
+
6
+ def self.included(base)
7
+ base.class_eval do
8
+ before_filter do
9
+ # Get some basic info from the current thread, and add it to the log parameters:
10
+ # The RequestID RQID can be passed-in on subsequent API-calls,
11
+ # so if several APIs call each other, they can be traced with the initial call's RQID
12
+ Thread.current[:rqid] = request.headers['X_RQID'] || ::SmarterLogging::Extensions::String.random(24)
13
+ Thread.current[:ip] = request.remote_ip || request.ip
14
+ Thread.current[:user_agent] = request.user_agent
15
+ end
16
+ end
17
+ end
18
+
19
+ end
20
+
21
+ end
@@ -0,0 +1,36 @@
1
+ module SmarterLogging
2
+ module Extensions
3
+
4
+ module String # adding functionality without monkey patching String
5
+
6
+ # Create a random String of given length, using given character set
7
+ #
8
+ # Character set is an Array which can contain Ranges, Arrays, Characters
9
+ #
10
+ # Examples
11
+ #
12
+ # String.random
13
+ # String.random(10, ['0'..'9','A'..'F'] )
14
+ # => "3EBF48AD3D"
15
+ #
16
+ # BASE64_CHAR_SET = ["A".."Z", "a".."z", "0".."9", '_', '-']
17
+ # String.random(10, BASE64_CHAR_SET)
18
+ # => "xM_1t3qcNn"
19
+ #
20
+ # SPECIAL_CHARS = ["!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "-", "_", "=", "+", "|", "/", "?", ".", ",", ";", ":", "~", "`", "[", "]", "{", "}", "<", ">"]
21
+ # BASE91_CHAR_SET = ["A".."Z", "a".."z", "0".."9", SPECIAL_CHARS]
22
+ # String.random(10, BASE91_CHAR_SET)
23
+ # => "S(Z]z,J{v;"
24
+ #
25
+ # CREDIT: Tilo Sloboda
26
+ #
27
+ # SEE: https://gist.github.com/tilo/3ee8d94871d30416feba
28
+
29
+ def self.random(len=32, character_set = ["A".."Z", "a".."z", "0".."9"])
30
+ chars = character_set.map{|x| x.is_a?(Range) ? x.to_a : x }.flatten
31
+ Array.new(len){ chars.sample }.join
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,27 @@
1
+ module SmarterLogging
2
+ mattr_accessor :anomaly_logger
3
+ mattr_accessor :activity_logger
4
+
5
+ class Railtie < Rails::Railtie
6
+ initializer 'smarter_logging.configure_rails_initialization' do |app|
7
+ app.config.anomaly_logger = SmarterLogging.anomaly_logger = SmarterLogging::AnomalyLogger.new('anomaly.log')
8
+ # make sure the activity logger has a reference to the anomaly logger, in case errors need to be logged:
9
+ act_logger = SmarterLogging::ActivityLogger.new('activity.log')
10
+ act_logger.anomaly_logger = SmarterLogging.anomaly_logger
11
+
12
+ app.config.activity_logger = SmarterLogging.activity_logger = act_logger
13
+ end
14
+ end
15
+
16
+ # after including SmarterLogging, you can simply use these convenience methods:
17
+
18
+ def self.log_activity(unique_identifier, data, &block)
19
+ activity_logger.log(unique_identifier, data, &block)
20
+ end
21
+
22
+ def self.log_anomaly(unique_identifier, data, &block)
23
+ anomaly_logger.log(unique_identifier, data, &block)
24
+ end
25
+
26
+ end
27
+
@@ -0,0 +1,3 @@
1
+ module SmarterLogging
2
+ VERSION = "1.0.4"
3
+ end
@@ -0,0 +1,15 @@
1
+ require 'smarter_logging/version'
2
+
3
+ require 'smarter_logging/extensions/string'
4
+
5
+ require 'smarter_logging/base_logger'
6
+ require 'smarter_logging/anomaly_logger'
7
+ require 'smarter_logging/activity_logger'
8
+ require 'smarter_logging/controller_helper'
9
+
10
+ require 'smarter_logging/railtie' if defined?(Rails)
11
+
12
+
13
+ module SmarterLogging
14
+ # Your code goes here...
15
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'smarter_logging/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "smarter_logging"
8
+ spec.version = SmarterLogging::VERSION
9
+ spec.authors = ["Tilo Sloboda"]
10
+ spec.email = ["tilo.sloboda@gmail.com"]
11
+
12
+ spec.summary = %q{Ruby Gem for smarter logging of key=value data}
13
+ spec.description = %q{Ruby Gem for smarter logging of key=value data in single-line format}
14
+ spec.homepage = "https://github.com/tilo/smarter_logging"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = "exe"
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.14"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "rspec", "~> 3.0"
27
+ end
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: smarter_logging
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.4
5
+ platform: ruby
6
+ authors:
7
+ - Tilo Sloboda
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-02-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.14'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.14'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ description: Ruby Gem for smarter logging of key=value data in single-line format
56
+ email:
57
+ - tilo.sloboda@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".rspec"
64
+ - ".travis.yml"
65
+ - Gemfile
66
+ - LICENSE.txt
67
+ - README.md
68
+ - Rakefile
69
+ - bin/console
70
+ - bin/setup
71
+ - lib/smarter_logging.rb
72
+ - lib/smarter_logging/activity_logger.rb
73
+ - lib/smarter_logging/anomaly_logger.rb
74
+ - lib/smarter_logging/base_logger.rb
75
+ - lib/smarter_logging/controller_helper.rb
76
+ - lib/smarter_logging/extensions/string.rb
77
+ - lib/smarter_logging/railtie.rb
78
+ - lib/smarter_logging/version.rb
79
+ - smarter_logging.gemspec
80
+ homepage: https://github.com/tilo/smarter_logging
81
+ licenses:
82
+ - MIT
83
+ metadata: {}
84
+ post_install_message:
85
+ rdoc_options: []
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ requirements: []
99
+ rubyforge_project:
100
+ rubygems_version: 2.5.2
101
+ signing_key:
102
+ specification_version: 4
103
+ summary: Ruby Gem for smarter logging of key=value data
104
+ test_files: []