├── lib ├── influxdb-logger │ ├── version.rb │ └── logger.rb └── influxdb-logger.rb ├── Gemfile ├── Rakefile ├── gemfiles ├── rails_4.2.gemfile ├── rails_5.0.gemfile ├── rails_5.1.gemfile ├── rails_4.0.gemfile ├── rails_4.1.gemfile └── rails_5.2.gemfile ├── .gitignore ├── spec ├── spec_helper.rb └── logger_spec.rb ├── Appraisals ├── .travis.yml ├── influxdb-logger.gemspec ├── LICENSE.txt ├── CHANGELOG.md └── README.md /lib/influxdb-logger/version.rb: -------------------------------------------------------------------------------- 1 | module InfluxdbLogger 2 | VERSION = "2.0.0" 3 | end 4 | -------------------------------------------------------------------------------- /lib/influxdb-logger.rb: -------------------------------------------------------------------------------- 1 | require "influxdb-logger/version" 2 | require "influxdb-logger/logger" 3 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'appraisal' 4 | 5 | # Specify your gem's dependencies in influxdb-logger.gemspec 6 | gemspec 7 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rspec/core/rake_task" 3 | 4 | RSpec::Core::RakeTask.new(:spec) 5 | 6 | task :default => :spec 7 | -------------------------------------------------------------------------------- /gemfiles/rails_4.2.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "appraisal" 6 | gem "railties", "4.2.8" 7 | gem "activesupport", "4.2.8" 8 | gem 'influxdb', "0.5.3" 9 | 10 | gemspec path: "../" 11 | -------------------------------------------------------------------------------- /gemfiles/rails_5.0.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "appraisal" 6 | gem "railties", "5.0.2" 7 | gem "activesupport", "5.0.2" 8 | gem 'influxdb', "0.5.3" 9 | 10 | gemspec path: "../" 11 | -------------------------------------------------------------------------------- /gemfiles/rails_5.1.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "appraisal" 6 | gem "railties", "5.1.0" 7 | gem "activesupport", "5.1.0" 8 | gem 'influxdb', "0.5.3" 9 | 10 | gemspec path: "../" 11 | -------------------------------------------------------------------------------- /gemfiles/rails_4.0.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "appraisal" 6 | gem "railties", "4.0.13" 7 | gem "activesupport", "4.0.13" 8 | gem 'influxdb', "0.5.3" 9 | 10 | gemspec path: "../" 11 | -------------------------------------------------------------------------------- /gemfiles/rails_4.1.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "appraisal" 6 | gem "railties", "4.1.16" 7 | gem "activesupport", "4.1.16" 8 | gem 'influxdb', "0.5.3" 9 | 10 | gemspec path: "../" 11 | -------------------------------------------------------------------------------- /gemfiles/rails_5.2.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "appraisal" 6 | gem "railties", "5.2.0" 7 | gem "activesupport", "5.2.0" 8 | gem 'influxdb', "0.5.3" 9 | 10 | 11 | gemspec path: "../" 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | *~ 4 | .bundle 5 | .config 6 | .yardoc 7 | /vendor 8 | Gemfile.lock 9 | gemfiles/*.lock 10 | InstalledFiles 11 | _yardoc 12 | coverage 13 | doc/ 14 | lib/bundler/man 15 | pkg 16 | rdoc 17 | spec/reports 18 | test/tmp 19 | test/version_tmp 20 | tmp 21 | .ruby-version 22 | .idea 23 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 2 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 3 | 4 | require 'active_support' 5 | require 'active_support/deprecation' 6 | require 'active_support/core_ext/module' 7 | require 'active_support/logger' 8 | require 'active_support/tagged_logging' 9 | require 'yaml' 10 | require 'influxdb-logger/logger' 11 | -------------------------------------------------------------------------------- /Appraisals: -------------------------------------------------------------------------------- 1 | appraise 'rails-4.0' do 2 | gem 'railties', '4.0.13' 3 | gem 'activesupport', '4.0.13' 4 | end 5 | 6 | appraise 'rails-4.1' do 7 | gem 'railties', '4.1.16' 8 | gem 'activesupport', '4.1.16' 9 | end 10 | 11 | appraise 'rails-4.2' do 12 | gem 'railties', '4.2.8' 13 | gem 'activesupport', '4.2.8' 14 | end 15 | 16 | appraise 'rails-5.0' do 17 | gem 'railties', '5.0.2' 18 | gem 'activesupport', '5.0.2' 19 | end 20 | 21 | appraise 'rails-5.1' do 22 | gem 'railties', '5.1.0' 23 | gem 'activesupport', '5.1.0' 24 | end 25 | 26 | appraise 'rails-5.2' do 27 | gem 'railties', '5.2.0' 28 | gem 'activesupport', '5.2.0' 29 | end 30 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.1.10 4 | - 2.2.10 5 | - 2.3.7 6 | - 2.4.4 7 | - 2.5.1 8 | gemfile: 9 | - gemfiles/rails_4.0.gemfile 10 | - gemfiles/rails_4.1.gemfile 11 | - gemfiles/rails_4.2.gemfile 12 | - gemfiles/rails_5.0.gemfile 13 | - gemfiles/rails_5.1.gemfile 14 | - gemfiles/rails_5.2.gemfile 15 | matrix: 16 | exclude: 17 | - rvm: 2.1.10 18 | gemfile: gemfiles/rails_5.0.gemfile 19 | - rvm: 2.1.10 20 | gemfile: gemfiles/rails_5.1.gemfile 21 | - rvm: 2.1.10 22 | gemfile: gemfiles/rails_5.2.gemfile 23 | - rvm: 2.4.4 24 | gemfile: gemfiles/rails_4.0.gemfile 25 | - rvm: 2.4.4 26 | gemfile: gemfiles/rails_4.1.gemfile 27 | - rvm: 2.5.1 28 | gemfile: gemfiles/rails_4.0.gemfile 29 | - rvm: 2.5.1 30 | gemfile: gemfiles/rails_4.1.gemfile 31 | -------------------------------------------------------------------------------- /influxdb-logger.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'influxdb-logger/version' 5 | 6 | Gem::Specification.new do |gem| 7 | gem.name = "influxdb-logger" 8 | gem.version = InfluxdbLogger::VERSION 9 | gem.authors = ["Paiyou"] 10 | gem.email = ["info@paiyou.co"] 11 | gem.description = %q{Influxdb logger} 12 | gem.summary = %q{Influxdb logger} 13 | gem.homepage = "https://github.com/paiyous-network/influxdb-logger" 14 | gem.license = "MIT" 15 | 16 | gem.files = `git ls-files`.split($/) 17 | gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } 18 | gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) 19 | gem.require_paths = ["lib"] 20 | 21 | gem.add_development_dependency "rspec", '~> 3.5.0' 22 | gem.add_runtime_dependency "railties", ">= 4.2.0", "< 6.0" 23 | gem.add_runtime_dependency "activesupport", ">= 5.0" 24 | gem.add_runtime_dependency "influxdb", "~> 0.5.3" 25 | end 26 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Yoshinori Tahara 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.4.0 / April 29 2017 2 | 3 | * Rails 5.1 4 | 5 | ## 0.3.1 / August 18 2016 6 | 7 | * Replace dependency from rails to railties and activesupport. 8 | 9 | ## 0.3.0 / July 12 2016 10 | 11 | * Rails 5 12 | 13 | ## 0.2.0 / Mar 20 2016 14 | 15 | * Add severity_key parameter. It is The key of severity(DEBUG, INFO, WARN, ERROR). 16 | 17 | ## 0.1.10 / Dec 23 2015 18 | 19 | * flush immediately. 20 | 21 | ## 0.1.9 / Dec 16 2015 22 | 23 | * Added settings: parameter to InfluxdbLogger::Logger.new. 24 | 25 | ## 0.1.8 / Nov 14 2015 26 | 27 | * Output Object#inspect if message is not String and not Exception. 28 | 29 | ## 0.1.7 / July 30 2015 30 | 31 | * Be able to log exceptions #15. 32 | 33 | ## 0.1.6 / March 20 2015 34 | 35 | * Fix incompatible character encodings #13. 36 | 37 | ## 0.1.5 / July 19 2014 38 | 39 | * Fix keynames of EVN['INFLUXDB_URL'] 40 | 41 | ## 0.1.4 / July 18 2014 42 | 43 | * Enable to use EVN['INFLUXDB_URL'] 44 | 45 | ## 0.1.3 / April 11 2014 46 | 47 | * Rails 4.1.0 48 | 49 | ## 0.1.2 / September 30 2013 50 | 51 | * Add 'gem.license = "MIT"' to gemspec. 52 | 53 | ## 0.1.1 / September 26 2013 54 | 55 | * Add log_tags feature. 56 | 57 | ## 0.1.0 / September 16 2013 58 | 59 | * Rails 4.0.0 60 | 61 | ## 0.0.4 / January 19 2013 62 | 63 | * Add messages_type parameter to fluent-logger.yml to specifying 64 | output messages type 'string' or 'array'. Thanks to davidrenne. 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # influxdb-logger (Rails) 2 | 3 | **Logger** for **Influxdb** in **Rails** 4 | 5 | ## Supported versions 6 | 7 | * Rails 4 and 5 8 | 9 | ## Installation 10 | 11 | Add this line to your application's Gemfile: 12 | 13 | gem 'influxdb-logger', '2.0.0' 14 | 15 | And then execute: 16 | 17 | $ bundle 18 | 19 | Or install it yourself as: 20 | 21 | $ gem install influxdb-logger 22 | 23 | ## Basic Usage 24 | 25 | In `config/environments/production.rb`(`test.rb`, `development.rb`) 26 | 27 | ```ruby 28 | config.logger = InfluxdbLogger::Logger.new(influxdb_tags: ... tags: ... settings: ... batch_size: ..., interval: ..., async: ...) 29 | 30 | ``` 31 | 32 | By default, influxdb-logger will log 33 | `duration, db, format, location, message, message_type, method, params, path, severity, status, view` as [fields](https://docs.influxdata.com/influxdb/v1.7/concepts/key_concepts/#field-key) into specified 34 | [series](https://docs.influxdata.com/influxdb/v1.7/concepts/key_concepts/#series). 35 | 36 | Which means, your `influxdb-logger` is good to go with configuration only about how to talk to influxdb: 37 | ```ruby 38 | config.logger = InfluxdbLogger::Logger.new(settings: { 39 | database: ENV['INFLUXDB_DB_NAME'], 40 | series: ENV['INFLUXDB_SERIES'], 41 | username: ENV['INFLUXDB_USER'], 42 | password: ENV['INFLUXDB_USER_PASSWORD'] 43 | }) 44 | ``` 45 | 46 | ## Advanced Usage 47 | 48 | * `influxdb_tags`[Array]: This argument specifies [tag-set](https://docs.influxdata.com/influxdb/v1.7/concepts/key_concepts/#tag-set) of series. 49 | If we need to constantly checkout influxdb logs about specific `controller` or `action`, the best way is to tag both fields to speed up any query on them utilizing `influxdb_tags`: 50 | ```ruby 51 | config.logger = InfluxdbLogger::Logger.new(infludb_tags: [:controller, :action], settings: ...) 52 | ``` 53 | 54 | * `tags`[Hash]: If extra fields are required to be sent to influxdb, `tags` could be utilized, e.g., ip info of agents: 55 | ```ruby 56 | config.logger = InfluxdbLogger::Logger.new(tags: { 57 | remote_ip: -> request { request.remote_ip } 58 | }, settings: ...) 59 | ``` 60 | Passed `tags` can be a `Hash` consisting values of any basic ruby type or a `lambda`. 61 | 62 | * `settings`: Which defines how our `influxdb-logger` connects to influxdb database. Detailed doc about it is here: [influxdb-ruby](https://github.com/influxdata/influxdb-ruby). 63 | ```ruby 64 | InfluxdbLogger::Logger.new(settings: { 65 | host: 'influxdb', 66 | retry: 3, 67 | time_precision: 'ms', 68 | database: ENV['INFLUXDB_DB_NAME'], 69 | series: ENV['INFLUXDB_SERIES'], 70 | username: ENV['INFLUXDB_USER'], 71 | password: ENV['INFLUXDB_USER_PASSWORD'] 72 | }) 73 | ``` 74 | 75 | * `batch_size`, `interval`: Since logging is a high frequncy job for any application with large user base in production environment. These two parameters 76 | give a chance for the logger to batch logging actions. 77 | 78 | For example, you can tell the logger to log when size of logging actions hits `1000` or that the last logging action is `1000`ms later than the first one in the queue by: 79 | 80 | ```ruby 81 | InfluxdbLogger::Logger.new(batch_size: 1000, interval: 1000, settings: ...) 82 | ``` 83 | 84 | * `async`: Determines whether the logger write asynchronously to influxdb, default to `false`. Read code [here](https://github.com/influxdata/influxdb-ruby/blob/master/lib/influxdb/writer/async.rb#L48) to know how it works. 85 | ```ruby 86 | InfluxdbLogger::Logger.new(async: false, settings: ...) 87 | ``` 88 | 89 | ## License 90 | 91 | MIT 92 | 93 | ## Contributing 94 | 95 | 1. Fork it 96 | 2. Create your feature branch (`git checkout -b my-new-feature`) 97 | 3. Commit your changes (`git commit -am 'Add some feature'`) 98 | 4. Push to the branch (`git push origin my-new-feature`) 99 | 5. Create new Pull Request 100 | -------------------------------------------------------------------------------- /lib/influxdb-logger/logger.rb: -------------------------------------------------------------------------------- 1 | require 'influxdb' 2 | require 'active_support/core_ext' 3 | require 'uri' 4 | require 'cgi' 5 | 6 | 7 | class Time 8 | def to_precision(precision) 9 | case precision 10 | when 'ns' 11 | self.to_ns 12 | when 'u' 13 | (self.to_r * 1000000).to_i 14 | when 'ms' 15 | self.to_ms 16 | when 's' 17 | self.to_i 18 | when 'm' 19 | self.to_i / 60 20 | when 'h' 21 | self.to_i / 3600 22 | else 23 | self.to_ns 24 | end 25 | end 26 | 27 | def to_ns 28 | (self.to_r * 1000000000).to_i 29 | end 30 | 31 | def to_ms 32 | (self.to_r * 1000).to_i 33 | end 34 | end 35 | 36 | def log_to_file(message) # for test 37 | open("#{Rails.root}/log/rails-influxdb-logger-test.log", 'a') { |f| 38 | f.puts message.inspect 39 | } 40 | end 41 | 42 | module InfluxdbLogger 43 | module Logger 44 | # Severity label for logging. (max 5 char) 45 | SEV_LABEL = %w(DEBUG INFO WARN ERROR FATAL ANY) 46 | 47 | def self.new(influxdb_tags: [], tags: {}, settings: {}, batch_size: 1000, interval: 1000, async: true) 48 | log_tags = tags.values 49 | Rails.application.config.log_tags = log_tags 50 | if Rails.application.config.respond_to?(:action_cable) 51 | Rails.application.config.action_cable.log_tags = log_tags.map do |x| 52 | case 53 | when x.respond_to?(:call) 54 | x 55 | when x.is_a?(Symbol) 56 | -> (request) { request.send(x) } 57 | else 58 | -> (request) { x } 59 | end 60 | end 61 | end 62 | 63 | if ENV["INFLUXDB_URL"] 64 | settings = self.parse_url(ENV["INFLUXDB_URL"]).merge(settings) 65 | end 66 | 67 | settings[:batch_size] ||= batch_size 68 | settings[:interval] ||= interval 69 | settings[:async] = async 70 | 71 | level = SEV_LABEL.index(Rails.application.config.log_level.to_s.upcase) 72 | inner_logger = InfluxdbLogger::InnerLogger.new(settings, level, tags, influxdb_tags) 73 | logger = ActiveSupport::TaggedLogging.new(inner_logger) 74 | logger.extend self 75 | end 76 | 77 | def self.parse_url(influxdb_url) 78 | uri = URI.parse influxdb_url 79 | params = CGI.parse uri.query 80 | { 81 | database: uri.path[1..-1], 82 | host: uri.host, 83 | port: uri.port, 84 | messages_type: params['messages_type'].try(:first), 85 | severity_key: params['severity_key'].try(:first), 86 | username: params['username'].try(:first), 87 | password: params['password'].try(:first), 88 | series: params['series'].try(:first), 89 | time_precision: params['time_precision'].try(:first), 90 | retry: params['retry'].try(:first).to_i 91 | } 92 | end 93 | 94 | def tagged(*tags) 95 | @tags = tags.flatten 96 | yield self 97 | ensure 98 | flush 99 | end 100 | end 101 | 102 | class InnerLogger < ActiveSupport::Logger 103 | def initialize(options, level, initialized_tags, influxdb_tags) 104 | self.level = level 105 | @messages_type = (options[:messages_type] || :array).to_sym 106 | @tag = options[:tag] 107 | @severity_key = (options[:severity_key] || :severity).to_sym 108 | @batch_size = options[:batch_size] 109 | @interval = options[:interval] 110 | @series = options[:series] 111 | @retention = options[:retention] 112 | @global_tags = {} 113 | @last_flush_time = Time.now.to_ms 114 | @value_filter = options[:value_filter] || {} 115 | @time_precision = options[:time_precision] || 'ns' 116 | 117 | @influxdb_logger = InfluxDB::Client.new( 118 | **options.slice(:host, :port, :database, :retry, :username, :password, :async), 119 | time_precision: @time_precision, 120 | discard_write_errors: true 121 | ) 122 | 123 | @influxdb_tags = influxdb_tags 124 | @severity = 0 125 | @messages = [] 126 | @initialized_tags = initialized_tags 127 | after_initialize if respond_to? :after_initialize 128 | end 129 | 130 | def [](key) 131 | @global_tags[key] 132 | end 133 | 134 | def []=(key, value) 135 | @global_tags[key] = value 136 | end 137 | 138 | def add(severity, message = nil, progname = nil, &block) 139 | return true if severity < level 140 | message = (block_given? ? block.call : progname) if message.blank? 141 | return true if message.blank? 142 | add_message(severity, message) 143 | true 144 | end 145 | 146 | def utf8_encoded(message) 147 | if message.encoding == Encoding::UTF_8 148 | message 149 | else 150 | message.dup.force_encoding(Encoding::UTF_8) 151 | end 152 | end 153 | 154 | def add_message(severity, message) 155 | @severity = severity if @severity < severity 156 | 157 | values = 158 | case message 159 | when ::String 160 | { 161 | message_type: 'String', 162 | message: utf8_encoded(message) 163 | } 164 | when ::Hash 165 | message.slice!(*@value_filter[:only]) if @value_filter[:only].present? 166 | message.except!(*@value_filter[:except]) if @value_filter[:except].present? 167 | message.merge({ 168 | message_type: 'Hash' 169 | }) 170 | when ::Exception 171 | { 172 | message_type: 'Exception', 173 | message: message.message, 174 | class: message.class, 175 | backtrace: message.backtrace 176 | } 177 | else 178 | { 179 | message_type: 'Others', 180 | message: message.inspect 181 | } 182 | end 183 | 184 | if @tags 185 | @initialized_tags.keys.zip(@tags).each do |k, v| 186 | values[k] = v 187 | end 188 | end 189 | message = { 190 | series: @series, 191 | timestamp: Time.now.to_precision(@time_precision), 192 | tags: values.slice(*@influxdb_tags).merge(@global_tags), 193 | values: values.except(*@influxdb_tags).merge({ 194 | severity: format_severity(@severity) 195 | }).transform_values {|value| 196 | case value 197 | when ::Numeric, ::String 198 | value 199 | when ::Hash 200 | value.to_json 201 | when ::Symbol 202 | value.to_s 203 | else 204 | value.inspect 205 | end 206 | } 207 | } 208 | 209 | @messages << message 210 | flush if @messages.size >= @batch_size || (Time.now.to_ms - @last_flush_time) > @interval 211 | end 212 | 213 | def flush 214 | return if @messages.empty? 215 | @influxdb_logger.write_points(@messages, @time_precision, @retention) 216 | @severity = 0 217 | @messages.clear 218 | @last_flush_time = Time.now.to_ms 219 | @tags = nil 220 | end 221 | 222 | def close 223 | end 224 | 225 | def level 226 | @level 227 | end 228 | 229 | def level=(l) 230 | @level = l 231 | end 232 | 233 | def format_severity(severity) 234 | InfluxdbLogger::Logger::SEV_LABEL[severity] || 'ANY' 235 | end 236 | end 237 | end 238 | -------------------------------------------------------------------------------- /spec/logger_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'tempfile' 3 | 4 | class Time 5 | def to_ms # for convenience of comparing timestamps 6 | (self.to_f * 1000.0).to_i 7 | end 8 | end 9 | 10 | module InfluxdbLogger 11 | module Logger 12 | attr_accessor :settings 13 | end 14 | end 15 | 16 | describe InfluxdbLogger::Logger do 17 | before do 18 | stub_const('Rails', Class.new) unless defined?(Rails) 19 | allow(Rails).to receive(:env).and_return('test') 20 | allow(Rails).to receive_message_chain(:application, :config, :log_level).and_return(:debug) 21 | allow(Rails).to receive_message_chain(:application, :config, :log_tags=) 22 | 23 | class MyLogger 24 | attr_accessor :log 25 | def initialize() 26 | @log = [] 27 | end 28 | 29 | def post(tag, map) 30 | end 31 | 32 | def clear 33 | @log.clear 34 | end 35 | 36 | def close 37 | end 38 | 39 | def write_point(point) 40 | @log << point 41 | end 42 | 43 | def write_points(points, time_precision, rentention = nil) 44 | @log ||= [] 45 | @log.concat(points) 46 | end 47 | end 48 | @my_logger = MyLogger.new 49 | allow(InfluxDB::Client).to receive(:new).and_return(@my_logger) 50 | end 51 | 52 | let(:series) { 'Request' } 53 | 54 | let(:influxdb_tags) { 55 | [:uuid, :foo] 56 | } 57 | 58 | let(:tags) { 59 | { 60 | uuid: :uuid, 61 | foo: ->(request) { 'foo_value' } 62 | } 63 | } 64 | 65 | let(:settings) { 66 | { 67 | host: 'influxdb', 68 | database: 'paiyou', 69 | series: series, 70 | retry: 3, 71 | username: 'user', 72 | password: 'password', 73 | time_precision: 'ms' 74 | } 75 | } 76 | 77 | let(:logger) { 78 | InfluxdbLogger::Logger.new(influxdb_tags: influxdb_tags, tags: tags, settings: settings) 79 | } 80 | 81 | let(:request) { 82 | double('request', uuid: 'uuid_value') 83 | } 84 | 85 | describe 'logging' do 86 | 87 | describe 'basic' do 88 | it 'info', now: true do 89 | # see Rails::Rack::compute_tags 90 | tag_values = tags.values.collect do |tag| 91 | case tag 92 | when Proc 93 | tag.call(request) 94 | when Symbol 95 | request.send(tag) 96 | else 97 | tag 98 | end 99 | end 100 | logger[:abc] = 'xyz' 101 | logger.tagged(tag_values) { logger.info('hello') } 102 | expect(@my_logger.log).to eq( 103 | [{ 104 | series: series, 105 | timestamp: Time.now.to_ms, 106 | tags: { 107 | abc: 'xyz', 108 | uuid: 'uuid_value', 109 | foo: 'foo_value' 110 | }, 111 | values: { 112 | message_type: 'String', 113 | message: 'hello', 114 | severity: 'INFO' 115 | } 116 | }]) 117 | @my_logger.clear 118 | logger.tagged(tag_values) { logger.info('world'); logger.info('bye') } 119 | expect(@my_logger.log).to eq( 120 | [{ 121 | series: series, 122 | timestamp: Time.now.to_ms, 123 | tags: { 124 | abc: 'xyz', 125 | uuid: 'uuid_value', 126 | foo: 'foo_value' 127 | }, 128 | values: { 129 | message_type: 'String', 130 | message: 'world', 131 | severity: 'INFO' 132 | } 133 | }, { 134 | series: series, 135 | timestamp: Time.now.to_ms, 136 | tags: { 137 | abc: 'xyz', 138 | uuid: 'uuid_value', 139 | foo: 'foo_value' 140 | }, 141 | values: { 142 | message_type: 'String', 143 | message: 'bye', 144 | severity: 'INFO' 145 | } 146 | }]) 147 | end 148 | end 149 | 150 | describe 'frozen ascii-8bit string' do 151 | before do 152 | logger.instance_variable_set(:@messages_type, :string) 153 | end 154 | 155 | after do 156 | logger.instance_variable_set(:@messages_type, :array) 157 | end 158 | 159 | it 'join messages' do 160 | ascii = "\xe8\x8a\xb1".force_encoding('ascii-8bit').freeze 161 | logger.tagged([request]) { 162 | logger.info(ascii) 163 | logger.info('咲く') 164 | } 165 | expect(@my_logger.log[0][:values][:message]).to eq("花") 166 | expect(@my_logger.log[1][:values][:message]).to eq("咲く") 167 | expect(ascii.encoding).to eq(Encoding::ASCII_8BIT) 168 | end 169 | end 170 | 171 | describe 'Exception' do 172 | it 'output message, class, backtrace' do 173 | begin 174 | 3 / 0 175 | rescue => e 176 | logger.tagged([request]) { 177 | logger.error(e) 178 | } 179 | expect(@my_logger.log[0][:values][:message]).to eq("divided by 0") 180 | end 181 | end 182 | end 183 | 184 | describe 'Object' do 185 | it 'output inspect' do 186 | x = Object.new 187 | logger.tagged([request]) { 188 | logger.info(x) 189 | } 190 | expect(@my_logger.log[0][:values][:message]).to eq(x.inspect) 191 | end 192 | end 193 | end 194 | 195 | describe "settings with ENV['INDUX_DB_URL']" do 196 | let(:influxdb_url) { 'http://influxdb:8086/paiyou?messages_type=string&severity_key=level&username=user&password=pass&time_precision=ms&series=Log&retry=3' } 197 | 198 | it "settings are properly set" do 199 | stub_const("ENV", {'INFLUXDB_URL' => influxdb_url }) 200 | allow(InfluxdbLogger::InnerLogger).to receive(:new) do |settings| 201 | expect(settings).to eq({ 202 | database: "paiyou", 203 | host: "influxdb", 204 | port: 8086, 205 | messages_type: "string", 206 | severity_key: "level", 207 | username: "user", 208 | password: "pass", 209 | series: "Log", 210 | time_precision: "ms", 211 | retry: 3, 212 | batch_size: 1000, 213 | interval: 1000, 214 | async: true 215 | }) 216 | ActiveSupport::Logger.new(STDOUT) 217 | end 218 | InfluxdbLogger::Logger.new(settings: {}) 219 | end 220 | end 221 | 222 | describe 'batch size' do 223 | it 'inner logger write_points after every two messages if batch_size is set to 2' do 224 | logger = InfluxdbLogger::Logger.new(settings: settings, batch_size: 2) 225 | logger.info('message 1') 226 | expect(@my_logger.log.size).to eq 0 227 | logger.info('message 2') 228 | expect(@my_logger.log.size).to eq 2 229 | logger.info('message 3') 230 | expect(@my_logger.log.size).to eq 2 231 | logger.info('message 4') 232 | expect(@my_logger.log.size).to eq 4 233 | end 234 | end 235 | end 236 | --------------------------------------------------------------------------------