loggun 0.1.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8815d61a25fc71e36d6648e2e65fd7d6d73ebffd56e0b4631c95e589a5b9ced4
4
+ data.tar.gz: a6d4aea0a8971708fc3eb78eb42c50d99afb6fcbfec1e4dad5753c6552865566
5
+ SHA512:
6
+ metadata.gz: aa4ee9f1f9d4a2fe3dae09083a9554c30d28d3821a5de8230db81efeef204e36e075c79335d4e7b587ead7a20da17cdf81b695acd6f00d27c751109ea2753902
7
+ data.tar.gz: 52768e02e7021307b455d5c5b7e2a55cda78152e61d387a2eff6cb1878060ea9b885d09b09c6b5f3c586eb9d33a0e8b6724e2827769f98d8c241f69920d16034
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+ *.gem
13
+ /Gemfile.lock
14
+ *.log
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.3.8
7
+ - 2.4.6
8
+ - 2.5.5
9
+ - 2.6.5
10
+ before_install: gem install bundler
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in loggun.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 Aleksandr Noskov
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,145 @@
1
+ # Loggun
2
+
3
+ [![Sponsored by FunBox](https://funbox.ru/badges/sponsored_by_funbox_compact.svg)](https://funbox.ru)
4
+
5
+ [![Gem Version](https://badge.fury.io/rb/loggun.svg)](https://badge.fury.io/rb/loggun)
6
+ [![Build Status](https://travis-ci.org/funbox/loggun.svg?branch=master)](https://travis-ci.org/funbox/loggun)
7
+
8
+ ## Описание
9
+ Loggun - это гем, позволяющий привести все логи приложения к единому формату
10
+
11
+ ## Установка
12
+
13
+ Чтобы установить гем, добавьте в ваш Gemfile:
14
+
15
+ ```ruby
16
+ gem 'loggun'
17
+ ```
18
+
19
+ И выполните команду :
20
+
21
+ $ bundle
22
+
23
+ ## Использование
24
+
25
+ ### Конфигурация
26
+ Для успешной конфигурации гема необходимо подгружать файл при инициализации вашего приложения.
27
+
28
+ `config/initializers/loggun.rb`
29
+
30
+ ```ruby
31
+ Loggun::Config.configure do |config|
32
+ config.precision = :milliseconds
33
+ config.pattern = '%{time} - %{pid} %{severity} %{type} %{tags_text} %{message}'
34
+ config.parent_transaction_to_message = false
35
+
36
+ config.modifiers.rails = true
37
+ config.modifiers.sidekiq = false
38
+ config.modifiers.clockwork = false
39
+ config.modifiers.incoming_http = false
40
+ config.modifiers.outgoing_http = false
41
+ end
42
+ ```
43
+ Все настройки являются опциональными.
44
+ #### Настройки
45
+ `precision` - точность отметок времени. По умолчанию - `milliseconds`. Может принимать одно из следующих значений: `sec`, `seconds`, `ms`, `millis`, `milliseconds`, `us`, `micros`, `microseconds`, `ns`, `nanos`, `nanoseconds`
46
+
47
+ `pattern` - шаблон для формата вывода данных в лог. Доступные ключи: `time`, `pid`, `severity`, `type`, `tags_text`, `message`, `parent_transaction`
48
+
49
+ `parent_transaction_to_message` - признак необходимости добавлять значение `parent_transaction` в тело логируемого сообщения.
50
+ Вне зависимости от данной настройки можно использовать ключ `parent_transaction` в шаблоне `pattern`.
51
+
52
+ `modifiers` - модификаторы для переопределения формата логирования указанного компонента. См. далее.
53
+
54
+ #### Модификаторы
55
+ Каждый модифкатор может быть активирован двумя равнозначными способами:
56
+ ```ruby
57
+ config.modifiers.rails = true
58
+ ```
59
+ или
60
+ ```ruby
61
+ config.modifiers.rails.enable = true
62
+ ```
63
+
64
+ `rails` - модифицирует форматирование логгера Rails.
65
+
66
+ `sidekiq` - модифицирует форматирование логгера Sidekiq.
67
+
68
+ `clockwork` - модифицирует форматирование логгера Clockwork.
69
+
70
+ `outgoing_http` - добавляет логирование исходящих http запросов.
71
+ На данный момент поддерживаются только запросы посредством гема `HTTP`.
72
+
73
+ `incoming_http` - добавляет логирование входящих http запросов для контроллеров Rails.
74
+ Данный модификатор может иметь дополнительные настройки, которые устанавливаются следующим образом
75
+ (приведены значения по умолчанию):
76
+
77
+ ```ruby
78
+ Loggun::Config.configure do |config|
79
+ #...
80
+ config.modifiers.incoming_http.enable = true
81
+ config.modifiers.incoming_http.controllers = ['ApplicationController']
82
+ config.modifiers.incoming_http.success_condition = -> { response.code == '200' }
83
+ config.modifiers.incoming_http.error_info = -> { nil }
84
+ #...
85
+ end
86
+ ```
87
+
88
+ `controllers` - массив имён базовых контроллеров, для которых необходимо добавить указанное логирование.
89
+
90
+ `success_condition` - лямбда, определяющая, содержит ли успех ответ экшена. Например `-> { JSON.parse(response.body)['result'] == 'ok' }`
91
+
92
+ `error_info` - лямбда, позволяющая добавить в лог информацию об ошибке, содержащейся в неуспешном ответе экшена.
93
+ Например `-> { JSON.parse(response.body)['error_code'] }`
94
+
95
+ Помимо указанных модификаторов существует возможность добавить собственный.
96
+ Необходимо уснаследовать его от `Loggun::Modifiers::Base` и указать в методе `apply` все необходимые действия.
97
+ ```ruby
98
+ require 'sinatra/custom_logger'
99
+
100
+ class NewModifier < Loggun::Modifiers::Base
101
+ def apply
102
+ Loggun::Config.setup_formatter(Sinatra::CustomLogger)
103
+ end
104
+ end
105
+ ```
106
+ Затем необходимо добавить его при конфигурации гема.
107
+
108
+ ```ruby
109
+ Loggun::Config.configure do |config|
110
+ #...
111
+ config.add_mofifier NewModifier
112
+ #...
113
+ end
114
+ ```
115
+
116
+ ### Хелперы
117
+ Подключение хелперов в класс позволяет использовать методы логирования `log_info` и `log_error`,
118
+ а также генерировать идентификатор транзации для каждого метода класса.
119
+
120
+ Например:
121
+ ```ruby
122
+ class SomeClass
123
+ include Loggun::Helpers
124
+
125
+ log_options entity_action: :method_name, as_transaction: true
126
+
127
+ def some_action
128
+ log_info 'type_for_action', 'Information'
129
+ bar
130
+ end
131
+
132
+ def bar
133
+ log_info 'type_for_bar', 'Bar information'
134
+ end
135
+ end
136
+ ```
137
+ Даёт подобный вывод в лог:
138
+ ```
139
+ 2020-03-04T16:58:38.207+05:00 - 28476 INFO type_for_action.some_class.some_action#msg_id_1583323118203 - {"value":["Information"]}
140
+ 2020-03-04T16:58:38.208+05:00 - 28476 INFO type_for_bar.some_class.bar#msg_id_1583323118207 - {"value":["Bar information"],"parent_transaction":"class.geo_location__actual_location.fetch_input#msg_id_1583323118203"}
141
+ ```
142
+
143
+ ## License
144
+
145
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
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 'loggun'
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,91 @@
1
+ require 'singleton'
2
+
3
+ module Loggun
4
+ ## Class for configurations
5
+ class Config
6
+ include Singleton
7
+
8
+ DEFAULTS = {
9
+ pattern: '%{time} - %{pid} %{severity} %{type} %{tags_text}%{agent} %{message}',
10
+ parent_transaction_to_message: true,
11
+ precision: :milliseconds,
12
+ incoming_http: {
13
+ controllers: %w[ApplicationController],
14
+ success_condition: -> { response.code == '200' },
15
+ error_info: -> { nil }
16
+ }
17
+ }.freeze
18
+ DEFAULT_MODIFIERS = %i[rails sidekiq clockwork incoming_http outgoing_http].freeze
19
+
20
+ attr_accessor(
21
+ :formatter,
22
+ :pattern,
23
+ :parent_transaction_to_message,
24
+ :precision,
25
+ :modifiers,
26
+ :custom_modifiers
27
+ )
28
+
29
+ def initialize
30
+ @formatter = Loggun::Formatter.new
31
+ @precision = DEFAULTS[:precision]
32
+ @pattern = DEFAULTS[:pattern]
33
+ @parent_transaction_to_message = DEFAULTS[:parent_transaction_to_message]
34
+ @modifiers = Loggun::OrderedOptions.new
35
+ @custom_modifiers = []
36
+ set_default_modifiers
37
+ end
38
+
39
+ class << self
40
+ def configure(&block)
41
+ block.call(instance)
42
+ use_modifiers
43
+ instance
44
+ end
45
+
46
+ def use_modifiers
47
+ DEFAULT_MODIFIERS.each do |modifier|
48
+ next unless instance.modifiers.public_send(modifier)&.enable
49
+
50
+ require_relative "modifiers/#{modifier}"
51
+ klass = Loggun::Modifiers.const_get(modifier.to_s.camelize)
52
+ klass.use
53
+ end
54
+
55
+ instance.custom_modifiers.each(&:use)
56
+ end
57
+
58
+ def setup_formatter(app)
59
+ Loggun.application = app
60
+ Loggun.application.logger.formatter = instance.formatter
61
+ end
62
+ end
63
+
64
+ def set_default_modifiers
65
+ DEFAULT_MODIFIERS.each do |modifier|
66
+ modifiers[modifier] = Loggun::OrderedOptions.new
67
+ modifiers[modifier].enable = false
68
+ next unless DEFAULTS[modifier].is_a?(Hash)
69
+
70
+ modifiers[modifier].merge!(DEFAULTS[modifier])
71
+ end
72
+ end
73
+
74
+ def add_modifier(modifier)
75
+ return unless modifier.respond_to? :use
76
+
77
+ custom_modifiers << modifier
78
+ end
79
+
80
+ def timestamp_precision
81
+ case precision
82
+ when :sec, :seconds then 0
83
+ when :millis, :milliseconds, :ms then 3
84
+ when :micros, :microseconds, :us then 6
85
+ when :nanos, :nanoseconds, :ns then 9
86
+ else
87
+ 3 # milliseconds
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,80 @@
1
+ require 'date'
2
+
3
+ module Loggun
4
+ class Formatter
5
+ DEFAULT_VALUE = '-'.freeze
6
+
7
+ def call(severity, time, _program_name, message)
8
+ data = Hash.new(DEFAULT_VALUE)
9
+ data[:time] = time.iso8601(config.timestamp_precision)
10
+ data[:pid] = Process.pid
11
+
12
+ if message.is_a?(Hash)
13
+ if config.parent_transaction_to_message && parent_transaction
14
+ message[:parent_transaction] = parent_transaction
15
+ end
16
+ message = JSON.generate(message)
17
+ end
18
+
19
+ data[:message] = message.to_s.tr("\r\n", ' ').strip
20
+ data[:severity] = severity&.present? ? severity.to_s : 'INFO'
21
+ data[:tags_text] = tags_text
22
+ data[:type] = Loggun.type || DEFAULT_VALUE.dup
23
+ data[:transaction_id] = Loggun.transaction_id
24
+ data[:parent_transaction] = parent_transaction if parent_transaction
25
+
26
+ if data[:transaction_id]&.to_i != Process.pid && data[:type] != DEFAULT_VALUE
27
+ data[:type] = "#{data[:type]}##{data[:transaction_id]}"
28
+ end
29
+
30
+ format(config.pattern + "\n", data)
31
+ end
32
+
33
+ def tagged(*tags)
34
+ new_tags = push_tags(*tags)
35
+ yield self
36
+ ensure
37
+ pop_tags(new_tags.size)
38
+ end
39
+
40
+ def push_tags(*tags)
41
+ tags.flatten.reject(&:blank?).tap do |new_tags|
42
+ current_tags.concat new_tags
43
+ end
44
+ end
45
+
46
+ def pop_tags(size = 1)
47
+ current_tags.pop size
48
+ end
49
+
50
+ def clear_tags!
51
+ current_tags.clear
52
+ end
53
+
54
+ def current_tags
55
+ thread_key = @thread_key ||= "loggun_tagged_logging_tags:#{object_id}"
56
+ Thread.current[thread_key] ||= []
57
+ end
58
+
59
+ def tags_text
60
+ tags = current_tags
61
+ if tags.one?
62
+ "[#{tags[0]}] "
63
+ elsif tags.any?
64
+ tags.collect { |tag| "[#{tag}] " }.join
65
+ end
66
+ end
67
+
68
+ private
69
+
70
+ def parent_transaction
71
+ return unless Loggun.parent_type && Loggun.parent_transaction_id
72
+
73
+ "#{Loggun.parent_type}##{Loggun.parent_transaction_id}"
74
+ end
75
+
76
+ def config
77
+ Loggun::Config.instance
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,178 @@
1
+ require 'securerandom'
2
+
3
+ module Loggun
4
+ module Helpers
5
+ SKIPPED_METHODS = %i[
6
+ initialize loggun logger modified_methods
7
+ ].freeze
8
+ DEFAULT_TYPE = 'class'.freeze
9
+
10
+ def self.included(klass)
11
+ klass.extend(ClassMethods)
12
+ klass.init
13
+ end
14
+
15
+ module ClassMethods
16
+ attr_accessor(
17
+ :with_log_transaction_id,
18
+ :log_transaction_generator,
19
+ :log_entity_name,
20
+ :log_entity_action,
21
+ :modified_methods,
22
+ :generate_transaction_except
23
+ )
24
+
25
+ def log_options(**options)
26
+ @log_entity_name = options[:entity_name]
27
+ @log_entity_action = options[:entity_action]
28
+ @with_log_transaction_id = options[:as_transaction]
29
+ @log_transaction_generator = options[:transaction_generator]
30
+ @generate_transaction_except = options[:generate_transaction_except]&.map(&:to_sym)
31
+ end
32
+
33
+ def init
34
+ @modified_methods = []
35
+ end
36
+
37
+ def method_added(method_name)
38
+ super
39
+ @modified_methods ||= []
40
+ return if SKIPPED_METHODS.include?(method_name) ||
41
+ modified_methods.include?(method_name)
42
+
43
+ modified_methods << method_name
44
+ method = instance_method(method_name)
45
+ undef_method(method_name)
46
+
47
+ define_method(method_name) do |*args, &block|
48
+ if self.class.generate_transaction_except&.include?(method_name.to_sym)
49
+ method.bind(self).call(*args, &block)
50
+ else
51
+ type = log_type(nil, method_name)
52
+ in_transaction(type) do
53
+ method.bind(self).call(*args, &block)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ %i[unknown fatal error warn info debug].each do |method|
61
+ define_method("log_#{method}") do |*args, **attrs, &block|
62
+ type = args.shift
63
+ next application.logger.send(method, type, &block) if args.empty? &&
64
+ attrs.empty?
65
+
66
+ method_name = caller_locations.first.label.split(' ').last
67
+ type = log_type(type, method_name)
68
+
69
+ if %i[fatal error].include?(method)
70
+ methods = args.first.methods
71
+ next unless methods.include?(:message) && methods.include?(:backtrace)
72
+
73
+ error = args.shift
74
+ attrs[:error] = { class: error.class, msg: error.message }
75
+ if attrs[:hidden]
76
+ attrs[:hidden][:error] = { backtrace: error.backtrace }
77
+ else
78
+ attrs[:hidden] = { error: { backtrace: error.backtrace } }
79
+ end
80
+ end
81
+ attrs[:value] = args if args.present?
82
+
83
+ with_type(type) do
84
+ application.logger.send(method, **attrs, &block)
85
+ end
86
+ end
87
+ end
88
+
89
+ %w[type transaction_id].each do |method_name|
90
+ %I[#{method_name} parent_#{method_name}].each do |method|
91
+ define_method(method) { Thread.current["loggun_#{method}".to_sym] }
92
+
93
+ define_method("#{method}=") do |value|
94
+ value = normalize(value) if method == :type
95
+
96
+ Thread.current["loggun_#{method}".to_sym] = value
97
+ end
98
+ end
99
+ end
100
+
101
+ def in_transaction(current_type = nil, current_transaction_id = nil)
102
+ current_transaction_id ||= generate_log_transaction_id
103
+ previous_transaction_id = self.parent_transaction_id
104
+ previous_type = self.parent_type
105
+
106
+ self.parent_transaction_id = self.transaction_id
107
+ self.parent_type = self.type
108
+
109
+ self.transaction_id = current_transaction_id
110
+ self.type = current_type if current_type
111
+ yield
112
+ ensure
113
+ self.transaction_id = self.parent_transaction_id
114
+ self.type = self.parent_type
115
+
116
+ self.parent_transaction_id = previous_transaction_id
117
+ self.parent_type = previous_type
118
+ end
119
+
120
+ def with_type(current_type)
121
+ previous_type = self.type
122
+ self.type = current_type
123
+ yield
124
+ ensure
125
+ self.type = previous_type
126
+ end
127
+
128
+ def log_type(type, method_name)
129
+ type ||= DEFAULT_TYPE.dup
130
+ type_as_arr = type.split('.')
131
+ klass = self.class
132
+ log_entity_name = klass.log_entity_name if klass.respond_to?(:log_entity_name)
133
+ log_entity_name ||= underscore(klass.name)
134
+ type << ".#{log_entity_name}" if type_as_arr.size == 1
135
+
136
+ return type unless klass.respond_to?(:log_entity_action)
137
+
138
+ if klass.log_entity_action && type_as_arr.size < 3
139
+ if klass.log_entity_action == :method_name && method_name
140
+ type << ".#{method_name}"
141
+ end
142
+ end
143
+
144
+ type
145
+ end
146
+
147
+ private
148
+
149
+ def normalize(type)
150
+ return unless type
151
+
152
+ type = type.to_s.strip.tr(" \t\r\n", ' ').squeeze(' ')
153
+ type.empty? ? nil : type
154
+ end
155
+
156
+ def underscore(word)
157
+ word.gsub!(/::/, '__')
158
+ word.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
159
+ word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
160
+ word.tr!('-', '_')
161
+ word.downcase!
162
+ word
163
+ end
164
+
165
+ def generate_log_transaction_id
166
+ return unless self.class.with_log_transaction_id
167
+ if self.class.log_transaction_generator
168
+ return self.class.log_transaction_generator.call
169
+ end
170
+
171
+ "#{SecureRandom.uuid[0..7]}_#{DateTime.now.strftime('%Q')}"
172
+ end
173
+
174
+ def application
175
+ Loggun.application
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,19 @@
1
+ require 'singleton'
2
+
3
+ module Loggun
4
+ module Modifiers
5
+ class Base
6
+ include Singleton
7
+
8
+ class << self
9
+ def use
10
+ instance.apply
11
+ end
12
+ end
13
+
14
+ def apply
15
+ raise NotImplementedError, 'You must implement #apply in your modifier.'
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ require 'clockwork/manager'
2
+
3
+ Clockwork::Manager.class_eval do
4
+ def configure_loggun
5
+ yield(config)
6
+ setup_formatter
7
+ if config[:sleep_timeout] < 1
8
+ config[:logger].warn 'sleep_timeout must be >= 1 second'
9
+ end
10
+ end
11
+
12
+ private
13
+
14
+ def setup_formatter
15
+ return unless Loggun::Config.instance.modifiers.clockwork
16
+
17
+ Loggun::Config.setup_formatter(self)
18
+ end
19
+ end
20
+
@@ -0,0 +1,9 @@
1
+ require 'clockwork'
2
+
3
+ module Clockwork
4
+ module Methods
5
+ def configure(&block)
6
+ Clockwork.manager.configure_loggun(&block)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined?(::Clockwork)
4
+ require_relative 'clockwork/manager'
5
+ require_relative 'clockwork/methods'
6
+ end
7
+
8
+ module Loggun
9
+ module Modifiers
10
+ class Clockwork < Loggun::Modifiers::Base
11
+ def apply
12
+ return unless defined?(::Clockwork)
13
+
14
+ ::Clockwork.configure {}
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,36 @@
1
+ module LogHttpActions
2
+
3
+ private
4
+
5
+ def log_http_actions
6
+ log_action :start
7
+ yield
8
+ log_action :response
9
+ end
10
+
11
+ def log_action(action = :start)
12
+ api = request.path[/\w+/]
13
+ api_version = request.path[/v./]
14
+ type = "http_request.#{api}.#{action}"
15
+ data = { path: clean_pathname, api_version: api_version }
16
+ return Loggun.info type, data if action == :start
17
+
18
+ success = instance_exec(&modifier_config.success_condition)
19
+ data[:success] = success
20
+ unless success
21
+ error = instance_exec(&modifier_config.error_info)
22
+ data[:error] = error if error
23
+ end
24
+ Loggun.info type, data
25
+ end
26
+
27
+ def clean_pathname
28
+ filtered_params = params.to_unsafe_h
29
+ filtered_params.delete('action')
30
+ request.path.gsub(/(#{filtered_params.values.join('|')})/, '').gsub(/\/api\/v./, '')
31
+ end
32
+
33
+ def modifier_config
34
+ Loggun::Config.instance.modifiers.incoming_http
35
+ end
36
+ end
@@ -0,0 +1,19 @@
1
+ require_relative 'incoming_http/log_http_actions'
2
+
3
+ module Loggun
4
+ module Modifiers
5
+ class IncomingHttp < Loggun::Modifiers::Base
6
+ def apply
7
+ return unless defined?(ActionPack)
8
+
9
+ controllers = Loggun::Config.instance.modifiers.incoming_http.controllers
10
+ controllers.each do |controller|
11
+ controller.constantize.class_eval do
12
+ include LogHttpActions
13
+ around_action :log_http_actions
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+ class BlockLogger
2
+ class << self
3
+ %i[unknown fatal error warn info debug].each do |method|
4
+ define_method(method) do |&block|
5
+ type = 'http_requests.outgoing.'
6
+ type += caller[0][/request/] ? 'start' : 'response'
7
+ Loggun.send("log_#{method}".to_sym, type, args, &block)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,18 @@
1
+ require 'http' if defined?(HTTP)
2
+ require_relative 'outgoing_http/block_logger'
3
+
4
+ module Loggun
5
+ module Modifiers
6
+ class OutgoingHttp < Loggun::Modifiers::Base
7
+ def apply
8
+ return unless defined?(HTTP)
9
+
10
+ ::HTTP.default_options = {
11
+ features: {
12
+ logging: { logger: BlockLogger }
13
+ }
14
+ }
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,13 @@
1
+ require 'rails/railtie'
2
+
3
+ module Loggun
4
+ module Modifiers
5
+ class Rails
6
+ class Railtie < ::Rails::Railtie
7
+ config.after_initialize do |_app|
8
+ Loggun::Config.setup_formatter(::Rails)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ module Loggun
2
+ module Modifiers
3
+ class Rails < Loggun::Modifiers::Base
4
+ def apply
5
+ require_relative 'rails/railtie' if defined?(::Rails)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ require 'sidekiq' if defined?(::Sidekiq)
2
+
3
+ module Loggun
4
+ module Modifiers
5
+ class Sidekiq < Loggun::Modifiers::Base
6
+ def apply
7
+ return unless defined?(::Sidekiq)
8
+
9
+ ::Sidekiq.configure_server do |config|
10
+ Loggun::Config.setup_formatter(config)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,4 @@
1
+ module Loggun
2
+ module Modifiers
3
+ end
4
+ end
@@ -0,0 +1,38 @@
1
+ module Loggun
2
+ class OrderedOptions < Hash
3
+ alias _get [] # preserve the original #[] method
4
+ protected :_get # make it protected
5
+
6
+ def []=(key, value)
7
+ if self[key.to_sym].is_a?(Loggun::OrderedOptions) &&
8
+ [true, false].include?(value)
9
+ return self[key.to_sym][:enable] = value
10
+ end
11
+
12
+ super(key.to_sym, value)
13
+ end
14
+
15
+ def [](key)
16
+ super(key.to_sym)
17
+ end
18
+
19
+ def method_missing(name, *args)
20
+ name_string = +name.to_s
21
+ if name_string.chomp!('=')
22
+ self[name_string] = args.first
23
+ else
24
+ bangs = name_string.chomp!('!')
25
+
26
+ if bangs
27
+ self[name_string].presence || raise(KeyError, ":#{name_string} is blank")
28
+ else
29
+ self[name_string]
30
+ end
31
+ end
32
+ end
33
+
34
+ def respond_to_missing?(_name, _include_private)
35
+ true
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,3 @@
1
+ module Loggun
2
+ VERSION = '0.1.0'.freeze
3
+ end
data/lib/loggun.rb ADDED
@@ -0,0 +1,21 @@
1
+ require 'loggun/version'
2
+ require 'loggun/ordered_options'
3
+ require 'loggun/formatter'
4
+ require 'loggun/config'
5
+ require 'loggun/modifiers'
6
+ require 'loggun/modifiers/base'
7
+ require 'loggun/helpers'
8
+
9
+ module Loggun
10
+ class Error < StandardError; end
11
+
12
+ class << self
13
+ include Loggun::Helpers
14
+
15
+ attr_accessor :application
16
+
17
+ %i[unknown fatal error warn info debug].each do |method|
18
+ alias_method method, "log_#{method}"
19
+ end
20
+ end
21
+ end
data/loggun.gemspec ADDED
@@ -0,0 +1,39 @@
1
+ lib = File.expand_path('lib', __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'loggun/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'loggun'
7
+ spec.version = Loggun::VERSION
8
+ spec.authors = ['Aleksandr Noskov', 'Sergey Nesterov']
9
+ spec.email = ['nemestny@politeh.ru', 'qnesterr@gmail.com']
10
+
11
+ spec.summary = 'Loggun'
12
+ spec.description = 'Gem for formatting ruby application logs'
13
+ spec.homepage = 'https://github.com/funbox/loggun'
14
+ spec.license = 'MIT'
15
+
16
+ # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
17
+
18
+ spec.metadata['homepage_uri'] = spec.homepage
19
+ spec.metadata['source_code_uri'] = spec.homepage
20
+ # spec.metadata['changelog_uri'] = "TODO: Put your gem's CHANGELOG.md URL here."
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
25
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
+ end
27
+ spec.bindir = 'exe'
28
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
29
+ spec.require_paths = ['lib']
30
+
31
+ spec.add_development_dependency 'bundler', '~> 2.0'
32
+ spec.add_development_dependency 'clockwork', '~> 2.0'
33
+ spec.add_development_dependency 'http', '~> 4.0'
34
+ spec.add_development_dependency 'rails', '~> 6.0'
35
+ spec.add_development_dependency 'rake', '~> 10.0'
36
+ spec.add_development_dependency 'rspec', '~> 3.0'
37
+ spec.add_development_dependency 'rspec-rails', '~> 3.0'
38
+ spec.add_development_dependency 'sidekiq', '~> 6.0'
39
+ end
metadata ADDED
@@ -0,0 +1,187 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: loggun
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Aleksandr Noskov
8
+ - Sergey Nesterov
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2020-03-29 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '2.0'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '2.0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: clockwork
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '2.0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '2.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: http
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '4.0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '4.0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: rails
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '6.0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '6.0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: rake
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '10.0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '10.0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: rspec
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: '3.0'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '3.0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: rspec-rails
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: '3.0'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - "~>"
110
+ - !ruby/object:Gem::Version
111
+ version: '3.0'
112
+ - !ruby/object:Gem::Dependency
113
+ name: sidekiq
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - "~>"
117
+ - !ruby/object:Gem::Version
118
+ version: '6.0'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - "~>"
124
+ - !ruby/object:Gem::Version
125
+ version: '6.0'
126
+ description: Gem for formatting ruby application logs
127
+ email:
128
+ - nemestny@politeh.ru
129
+ - qnesterr@gmail.com
130
+ executables: []
131
+ extensions: []
132
+ extra_rdoc_files: []
133
+ files:
134
+ - ".gitignore"
135
+ - ".rspec"
136
+ - ".travis.yml"
137
+ - Gemfile
138
+ - LICENSE.txt
139
+ - README.md
140
+ - Rakefile
141
+ - bin/console
142
+ - bin/setup
143
+ - lib/loggun.rb
144
+ - lib/loggun/config.rb
145
+ - lib/loggun/formatter.rb
146
+ - lib/loggun/helpers.rb
147
+ - lib/loggun/modifiers.rb
148
+ - lib/loggun/modifiers/base.rb
149
+ - lib/loggun/modifiers/clockwork.rb
150
+ - lib/loggun/modifiers/clockwork/manager.rb
151
+ - lib/loggun/modifiers/clockwork/methods.rb
152
+ - lib/loggun/modifiers/incoming_http.rb
153
+ - lib/loggun/modifiers/incoming_http/log_http_actions.rb
154
+ - lib/loggun/modifiers/outgoing_http.rb
155
+ - lib/loggun/modifiers/outgoing_http/block_logger.rb
156
+ - lib/loggun/modifiers/rails.rb
157
+ - lib/loggun/modifiers/rails/railtie.rb
158
+ - lib/loggun/modifiers/sidekiq.rb
159
+ - lib/loggun/ordered_options.rb
160
+ - lib/loggun/version.rb
161
+ - loggun.gemspec
162
+ homepage: https://github.com/funbox/loggun
163
+ licenses:
164
+ - MIT
165
+ metadata:
166
+ homepage_uri: https://github.com/funbox/loggun
167
+ source_code_uri: https://github.com/funbox/loggun
168
+ post_install_message:
169
+ rdoc_options: []
170
+ require_paths:
171
+ - lib
172
+ required_ruby_version: !ruby/object:Gem::Requirement
173
+ requirements:
174
+ - - ">="
175
+ - !ruby/object:Gem::Version
176
+ version: '0'
177
+ required_rubygems_version: !ruby/object:Gem::Requirement
178
+ requirements:
179
+ - - ">="
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ requirements: []
183
+ rubygems_version: 3.0.6
184
+ signing_key:
185
+ specification_version: 4
186
+ summary: Loggun
187
+ test_files: []