loggun 0.1.0 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8815d61a25fc71e36d6648e2e65fd7d6d73ebffd56e0b4631c95e589a5b9ced4
4
- data.tar.gz: a6d4aea0a8971708fc3eb78eb42c50d99afb6fcbfec1e4dad5753c6552865566
3
+ metadata.gz: 3a78f3c782857269e283aa2f00ef6f5a2002e57c1e0d2fe5d43284a54d5aa106
4
+ data.tar.gz: fb9d8dad64af86d1f43c510dc34c1fd3acb9a515441315973ab0a5b72d493099
5
5
  SHA512:
6
- metadata.gz: aa4ee9f1f9d4a2fe3dae09083a9554c30d28d3821a5de8230db81efeef204e36e075c79335d4e7b587ead7a20da17cdf81b695acd6f00d27c751109ea2753902
7
- data.tar.gz: 52768e02e7021307b455d5c5b7e2a55cda78152e61d387a2eff6cb1878060ea9b885d09b09c6b5f3c586eb9d33a0e8b6724e2827769f98d8c241f69920d16034
6
+ metadata.gz: e0d93bbbe8ed26b5c5decc4d88c16876d4db185d5ae415b07fb47a23848149183e56fb0be6128f8c321410d205d19778228888f9cdb7ce57414ba29c380f22db
7
+ data.tar.gz: '08d78b02426b5b3da9a31b90bf3715c9728de0eaeb40ed9d5b55ccfeab76c3e241146fcdaa9f052b11ee233a40fdb07e68be5f33ff4a104467cbd08b959fe07a'
data/README.md CHANGED
@@ -21,6 +21,23 @@ gem 'loggun'
21
21
  $ bundle
22
22
 
23
23
  ## Использование
24
+ Вы можете использовать Loggun как обертку для вашего Logger. Для этого необходимо передать
25
+ ему инстанс вашего логгера и настроить его formatter:
26
+ ```ruby
27
+ Loggun.logger = Rails.logger
28
+ Loggun.logger.formatter = Loggun::Formatter.new
29
+ ```
30
+
31
+ Теперь вы можете использовать Loggun для логгирования в стандартизированном формате:
32
+ ```ruby
33
+ Loggun.info('http_request.api.request', user_id: current_user.id)
34
+ #=> 2020-04-11T22:35:04.225+03:00 - 170715 INFO http_request.api.request - {"user_id": 5465}
35
+ ...
36
+ Loggun.info('http_request.api.response', user_id: current_user.id, success: true)
37
+ #=> 2020-04-11T22:35:04.225+03:00 - 170715 INFO http_request.api.response - {"user_id": 5465, "success": true}
38
+ ```
39
+
40
+ Подробнее об конфигурации и использовании Loggun ниже.
24
41
 
25
42
  ### Конфигурация
26
43
  Для успешной конфигурации гема необходимо подгружать файл при инициализации вашего приложения.
@@ -32,6 +49,7 @@ Loggun::Config.configure do |config|
32
49
  config.precision = :milliseconds
33
50
  config.pattern = '%{time} - %{pid} %{severity} %{type} %{tags_text} %{message}'
34
51
  config.parent_transaction_to_message = false
52
+ config.message_format = :json
35
53
 
36
54
  config.modifiers.rails = true
37
55
  config.modifiers.sidekiq = false
@@ -42,14 +60,15 @@ end
42
60
  ```
43
61
  Все настройки являются опциональными.
44
62
  #### Настройки
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` - модификаторы для переопределения формата логирования указанного компонента. См. далее.
63
+ - `precision` - точность отметок времени. По умолчанию - `milliseconds`. Может принимать одно из следующих значений: `sec`, `seconds`, `ms`, `millis`, `milliseconds`, `us`, `micros`, `microseconds`, `ns`, `nanos`, `nanoseconds`
64
+ - `pattern` - текстовый шаблон для формата вывода данных в лог.
65
+ Доступные ключи: `time`, `pid`, `severity`, `type`, `tags_text`, `message`, `parent_transaction`
66
+ - `parent_transaction_to_message` - признак необходимости добавлять значение `parent_transaction` в тело логируемого сообщения.
67
+ Вне зависимости от данной настройки можно использовать ключ `parent_transaction` в шаблоне `pattern`.
68
+ - `message_format` - формат переменной message в шаблоне pattern. Доступны два формата:
69
+ - `:json` - `message` логгируется как json строка
70
+ - `:key_value` - `message` логгируется в формате `key1=value1 key2=value2`
71
+ - `modifiers` - модификаторы для переопределения формата логирования указанного компонента. См. далее.
53
72
 
54
73
  #### Модификаторы
55
74
  Каждый модифкатор может быть активирован двумя равнозначными способами:
@@ -61,16 +80,43 @@ config.modifiers.rails = true
61
80
  config.modifiers.rails.enable = true
62
81
  ```
63
82
 
64
- `rails` - модифицирует форматирование логгера Rails.
83
+ ##### Rails модификатор
84
+ `config.modifier.rails` - модифицирует форматирование логгера Rails.
65
85
 
66
- `sidekiq` - модифицирует форматирование логгера Sidekiq.
86
+ ##### Active Record модификатор
87
+ `config.modifier.active_record` - добавляет (именно добавляет, а не модифицирует) нового подписчика на SQL события.
88
+ SQL начинает дополнительно логгироваться в Loggun формате, severity - info. Например:
89
+ ```text
90
+ 2020-04-12T20:08:52.913+03:00 - 487257 INFO storage.sql.query - {"sql":"SELECT 1","name":null,"duration":0.837}
91
+ ```
92
+ Пример настроек:
93
+ ```ruby
94
+ Loggun::Config.configure do |config|
95
+ #...
96
+ config.modifiers.active_record.enable = true
97
+ config.modifiers.active_record.log_subscriber_class_name = 'MyApp::MyLogSubscriber'
98
+ config.modifiers.active_record.payload_keys = %i[sql duration]
99
+ #...
100
+ end
101
+ ```
102
+ - `log_subscriber_class_name` - имя класса, реализующего логирование sql события.
103
+ Необходим метод `#sql`. По-умолчанию `::Loggun::Modifiers::ActiveRecord::LoggunLogSubscriber`
104
+
105
+ - `payload_keys` - необходимые ключи в полезной нарзуке. Используется в дефолтном классе. Доступные
106
+ ключи: ```%i[sql name duration source]```.
67
107
 
68
- `clockwork` - модифицирует форматирование логгера Clockwork.
108
+ ##### Sidekiq модификатор
109
+ `config.modifiers.sidekiq` - модифицирует форматирование логгера Sidekiq.
69
110
 
70
- `outgoing_http` - добавляет логирование исходящих http запросов.
111
+ ##### Clockwork модификатор
112
+ `config.modifiers.clockwork` - модифицирует форматирование логгера Clockwork.
113
+
114
+ ##### Модификатор исходящих HTTP запросово
115
+ `config.modifiers.outgoing_http` - добавляет логирование исходящих http запросов.
71
116
  На данный момент поддерживаются только запросы посредством гема `HTTP`.
72
117
 
73
- `incoming_http` - добавляет логирование входящих http запросов для контроллеров Rails.
118
+ ##### Модификатор входящих запросов в Rails
119
+ `config.modifiers.incoming_http` - добавляет логирование входящих http запросов для контроллеров Rails.
74
120
  Данный модификатор может иметь дополнительные настройки, которые устанавливаются следующим образом
75
121
  (приведены значения по умолчанию):
76
122
 
@@ -84,14 +130,24 @@ Loggun::Config.configure do |config|
84
130
  #...
85
131
  end
86
132
  ```
133
+ - `controllers` - массив имён базовых контроллеров, для которых необходимо добавить указанное логирование.
87
134
 
88
- `controllers` - массив имён базовых контроллеров, для которых необходимо добавить указанное логирование.
89
-
90
- `success_condition` - лямбда, определяющая, содержит ли успех ответ экшена. Например `-> { JSON.parse(response.body)['result'] == 'ok' }`
135
+ - `success_condition` - лямбда, определяющая, содержит ли успех ответ экшена. Например `-> { JSON.parse(response.body)['result'] == 'ok' }`
91
136
 
92
- `error_info` - лямбда, позволяющая добавить в лог информацию об ошибке, содержащейся в неуспешном ответе экшена.
137
+ - `error_info` - лямбда, позволяющая добавить в лог информацию об ошибке, содержащейся в неуспешном ответе экшена.
93
138
  Например `-> { JSON.parse(response.body)['error_code'] }`
94
139
 
140
+ **Для Rails 6 и выше данный модификатор может работать некорректно.**
141
+ В этом случае можно указать в требуемом базовом контроллере строку:
142
+ ```ruby
143
+ include Loggun::HttpHelpers
144
+ ```
145
+ Это делает настройки `enable` и `controllers` модификатора безсполезными,
146
+ однако позволяет гарантированно логировать входящие http запросы.
147
+
148
+ Настройки `success_condition` и `error_info` продолжают использоваться и могут быть установлены требуемым образом.
149
+
150
+ ##### Персональные модификаторы
95
151
  Помимо указанных модификаторов существует возможность добавить собственный.
96
152
  Необходимо уснаследовать его от `Loggun::Modifiers::Base` и указать в методе `apply` все необходимые действия.
97
153
  ```ruby
@@ -122,24 +178,35 @@ end
122
178
  class SomeClass
123
179
  include Loggun::Helpers
124
180
 
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
181
+ log_options entity_action: :method_name, as_transaction: true, only: %i[download_data]
131
182
 
132
- def bar
133
- log_info 'type_for_bar', 'Bar information'
183
+ def download_data
184
+ log_info 'http_request', 'Information'
185
+ # ... make http request here
186
+ log_info 'http_response', success: true
134
187
  end
135
188
  end
136
189
  ```
137
- Даёт подобный вывод в лог:
190
+ При вызове `#download_data` мы получим следующий вывод в лог:
138
191
  ```
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"}
192
+ 2020-03-04T16:58:38.207+05:00 - 28476 INFO http_request.some_class.download_data#ffg5431_1583323118203 - {"message":["Information"]}
193
+ 2020-03-04T16:58:38.208+05:00 - 28476 INFO http_response.some_class.download_data#ffg5431_1583323118203 - {"success": true}
141
194
  ```
142
195
 
196
+ **Важно**, что с хелпером log_options необходимо использовать только методы вида `log_<severity>`.
197
+ Методы модуля `Loggun` не будут работать.
198
+
199
+ Список всех опций хелпера log_options:
200
+
201
+ - `entity_name` - имя сущности метода, string
202
+ - `entity_action` - действие сущности метода, string
203
+ - `as_transaction` - добавлять уникальный ID транзакции для метода, boolean
204
+ - `transaction_generator` - собственный генератор ID транзакции, lambda
205
+ - `log_all_methods` - признак необходимости применения хелпера ко всем методам, boolean
206
+ - `only` - список методов, для которых необходимо применить хелпер (работает только если `log_all_methods` - false), Array{Symbol}
207
+ - `except` - список методов, которые надо исключить для хелпера, Array{Symbol}
208
+ - `log_transaction_except` - список методов, логирование которых не нужно обогащать ID транзакции, Array{Symbol}
209
+
143
210
  ## License
144
211
 
145
212
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -5,6 +5,7 @@ require 'loggun/config'
5
5
  require 'loggun/modifiers'
6
6
  require 'loggun/modifiers/base'
7
7
  require 'loggun/helpers'
8
+ require 'logger'
8
9
 
9
10
  module Loggun
10
11
  class Error < StandardError; end
@@ -12,10 +13,22 @@ module Loggun
12
13
  class << self
13
14
  include Loggun::Helpers
14
15
 
15
- attr_accessor :application
16
+ attr_writer :logger
16
17
 
17
18
  %i[unknown fatal error warn info debug].each do |method|
18
19
  alias_method method, "log_#{method}"
19
20
  end
21
+
22
+ def logger
23
+ @logger ||= default_logger
24
+ end
25
+
26
+ private
27
+
28
+ def default_logger
29
+ logger = Logger.new(STDOUT)
30
+ logger.formatter = Config.instance.formatter
31
+ logger
32
+ end
20
33
  end
21
34
  end
@@ -6,21 +6,28 @@ module Loggun
6
6
  include Singleton
7
7
 
8
8
  DEFAULTS = {
9
- pattern: '%{time} - %{pid} %{severity} %{type} %{tags_text}%{agent} %{message}',
9
+ pattern: '%{time} - %{pid} %{severity} %{type} %{tags_text} %{message}',
10
10
  parent_transaction_to_message: true,
11
+ message_format: :json,
11
12
  precision: :milliseconds,
12
13
  incoming_http: {
13
14
  controllers: %w[ApplicationController],
14
15
  success_condition: -> { response.code == '200' },
15
16
  error_info: -> { nil }
17
+ },
18
+ active_record: {
19
+ log_subscriber_class_name: '::Loggun::Modifiers::ActiveRecord::LoggunLogSubscriber',
20
+ payload_keys: %i[sql name duration source]
16
21
  }
17
22
  }.freeze
18
- DEFAULT_MODIFIERS = %i[rails sidekiq clockwork incoming_http outgoing_http].freeze
23
+ DEFAULT_MODIFIERS = %i[rails active_record sidekiq clockwork outgoing_http].freeze
24
+ MESSAGE_FORMATS = %i[json key_value].freeze
19
25
 
20
26
  attr_accessor(
21
27
  :formatter,
22
28
  :pattern,
23
29
  :parent_transaction_to_message,
30
+ :message_format,
24
31
  :precision,
25
32
  :modifiers,
26
33
  :custom_modifiers
@@ -31,6 +38,7 @@ module Loggun
31
38
  @precision = DEFAULTS[:precision]
32
39
  @pattern = DEFAULTS[:pattern]
33
40
  @parent_transaction_to_message = DEFAULTS[:parent_transaction_to_message]
41
+ @message_format = DEFAULTS[:message_format]
34
42
  @modifiers = Loggun::OrderedOptions.new
35
43
  @custom_modifiers = []
36
44
  set_default_modifiers
@@ -40,6 +48,7 @@ module Loggun
40
48
  def configure(&block)
41
49
  block.call(instance)
42
50
  use_modifiers
51
+ check_config
43
52
  instance
44
53
  end
45
54
 
@@ -55,9 +64,15 @@ module Loggun
55
64
  instance.custom_modifiers.each(&:use)
56
65
  end
57
66
 
58
- def setup_formatter(app)
59
- Loggun.application = app
60
- Loggun.application.logger.formatter = instance.formatter
67
+ def check_config
68
+ return if MESSAGE_FORMATS.include? instance.message_format
69
+
70
+ raise FailureConfiguration, 'Unknown value for message_format'
71
+ end
72
+
73
+ def setup_formatter(app, formatter = nil)
74
+ Loggun.logger = app.logger
75
+ Loggun.logger.formatter = formatter || instance.formatter
61
76
  end
62
77
  end
63
78
 
@@ -87,5 +102,7 @@ module Loggun
87
102
  3 # milliseconds
88
103
  end
89
104
  end
105
+
106
+ class FailureConfiguration < StandardError; end
90
107
  end
91
108
  end
@@ -1,10 +1,11 @@
1
- require 'date'
1
+ require 'time'
2
+ require 'json'
2
3
 
3
4
  module Loggun
4
5
  class Formatter
5
6
  DEFAULT_VALUE = '-'.freeze
6
7
 
7
- def call(severity, time, _program_name, message)
8
+ def call(severity, time, _program_name, message, loggun_type: nil)
8
9
  data = Hash.new(DEFAULT_VALUE)
9
10
  data[:time] = time.iso8601(config.timestamp_precision)
10
11
  data[:pid] = Process.pid
@@ -13,17 +14,18 @@ module Loggun
13
14
  if config.parent_transaction_to_message && parent_transaction
14
15
  message[:parent_transaction] = parent_transaction
15
16
  end
16
- message = JSON.generate(message)
17
+ message = format_message(message)
17
18
  end
18
19
 
19
20
  data[:message] = message.to_s.tr("\r\n", ' ').strip
20
- data[:severity] = severity&.present? ? severity.to_s : 'INFO'
21
+ data[:severity] = severity&.to_s || 'INFO'
21
22
  data[:tags_text] = tags_text
22
- data[:type] = Loggun.type || DEFAULT_VALUE.dup
23
+ data[:type] = loggun_type || Loggun.type || DEFAULT_VALUE.dup
23
24
  data[:transaction_id] = Loggun.transaction_id
24
25
  data[:parent_transaction] = parent_transaction if parent_transaction
25
26
 
26
- if data[:transaction_id]&.to_i != Process.pid && data[:type] != DEFAULT_VALUE
27
+ if data[:transaction_id] && data[:type] != DEFAULT_VALUE &&
28
+ data[:transaction_id].to_i != Process.pid
27
29
  data[:type] = "#{data[:type]}##{data[:transaction_id]}"
28
30
  end
29
31
 
@@ -76,5 +78,16 @@ module Loggun
76
78
  def config
77
79
  Loggun::Config.instance
78
80
  end
81
+
82
+ def format_message(message)
83
+ if config.message_format == :json
84
+ JSON.generate(message)
85
+ elsif config.message_format == :key_value
86
+ message.map { |key, value| "#{key}=#{value}" }.join(' ')
87
+ else
88
+ warn('Unknown value for message_format')
89
+ JSON.generate(message)
90
+ end
91
+ end
79
92
  end
80
93
  end
@@ -3,53 +3,65 @@ require 'securerandom'
3
3
  module Loggun
4
4
  module Helpers
5
5
  SKIPPED_METHODS = %i[
6
- initialize loggun logger modified_methods
6
+ initialize loggun logger log_modified_methods loggun_init in_log_transaction with_log_type
7
7
  ].freeze
8
8
  DEFAULT_TYPE = 'class'.freeze
9
9
 
10
10
  def self.included(klass)
11
+ klass.extend(InitMethods)
12
+ klass.loggun_init
11
13
  klass.extend(ClassMethods)
12
- klass.init
13
14
  end
14
15
 
15
- module ClassMethods
16
+ module InitMethods
16
17
  attr_accessor(
17
18
  :with_log_transaction_id,
18
19
  :log_transaction_generator,
19
20
  :log_entity_name,
20
21
  :log_entity_action,
21
- :modified_methods,
22
- :generate_transaction_except
22
+ :log_modified_methods,
23
+ :log_skip_methods,
24
+ :log_only_methods,
25
+ :log_all_methods,
26
+ :log_transaction_except
23
27
  )
24
28
 
29
+ def loggun_init
30
+ @log_modified_methods = []
31
+ @log_all_methods = false
32
+ end
33
+ end
34
+
35
+ module ClassMethods
25
36
  def log_options(**options)
26
37
  @log_entity_name = options[:entity_name]
27
38
  @log_entity_action = options[:entity_action]
28
39
  @with_log_transaction_id = options[:as_transaction]
29
40
  @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 = []
41
+ @log_transaction_except = options[:log_transaction_except]&.map(&:to_sym)
42
+ @log_skip_methods = options[:except]&.map(&:to_sym)
43
+ @log_only_methods = options[:only]&.map(&:to_sym)
44
+ @log_all_methods = options[:log_all_methods]
35
45
  end
36
46
 
37
47
  def method_added(method_name)
38
48
  super
39
- @modified_methods ||= []
40
- return if SKIPPED_METHODS.include?(method_name) ||
41
- modified_methods.include?(method_name)
49
+ @log_modified_methods ||= []
42
50
 
43
- modified_methods << method_name
51
+ return if !log_all_methods && !log_only_methods&.include?(method_name)
52
+ return if log_skip_methods&.include?(method_name)
53
+ return if SKIPPED_METHODS.include?(method_name) || log_modified_methods.include?(method_name)
54
+
55
+ log_modified_methods << method_name
44
56
  method = instance_method(method_name)
45
57
  undef_method(method_name)
46
58
 
47
59
  define_method(method_name) do |*args, &block|
48
- if self.class.generate_transaction_except&.include?(method_name.to_sym)
60
+ if self.class.log_transaction_except&.include?(method_name.to_sym)
49
61
  method.bind(self).call(*args, &block)
50
62
  else
51
63
  type = log_type(nil, method_name)
52
- in_transaction(type) do
64
+ in_log_transaction(type) do
53
65
  method.bind(self).call(*args, &block)
54
66
  end
55
67
  end
@@ -57,19 +69,15 @@ module Loggun
57
69
  end
58
70
  end
59
71
 
60
- %i[unknown fatal error warn info debug].each do |method|
61
- define_method("log_#{method}") do |*args, **attrs, &block|
72
+ %i[unknown fatal error warn info debug].each do |method_name|
73
+ define_method("log_#{method_name}") do |*args, **attrs, &block|
62
74
  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)
75
+ next logger.send(method_name, type, &block) if args.empty? && attrs.empty?
68
76
 
69
- if %i[fatal error].include?(method)
70
- methods = args.first.methods
71
- next unless methods.include?(:message) && methods.include?(:backtrace)
77
+ caller_method_name = caller_locations.first.label.split(' ').last
78
+ type = log_type(type, caller_method_name)
72
79
 
80
+ if %i[fatal error].include?(method_name) && %i[backtrace message].all? { |m| args.first.respond_to?(m) }
73
81
  error = args.shift
74
82
  attrs[:error] = { class: error.class, msg: error.message }
75
83
  if attrs[:hidden]
@@ -78,10 +86,10 @@ module Loggun
78
86
  attrs[:hidden] = { error: { backtrace: error.backtrace } }
79
87
  end
80
88
  end
81
- attrs[:value] = args if args.present?
89
+ attrs[:message] = args unless args.empty?
82
90
 
83
- with_type(type) do
84
- application.logger.send(method, **attrs, &block)
91
+ with_log_type(type) do
92
+ logger.send(method_name, **attrs, &block)
85
93
  end
86
94
  end
87
95
  end
@@ -98,7 +106,7 @@ module Loggun
98
106
  end
99
107
  end
100
108
 
101
- def in_transaction(current_type = nil, current_transaction_id = nil)
109
+ def in_log_transaction(current_type = nil, current_transaction_id = nil)
102
110
  current_transaction_id ||= generate_log_transaction_id
103
111
  previous_transaction_id = self.parent_transaction_id
104
112
  previous_type = self.parent_type
@@ -117,7 +125,7 @@ module Loggun
117
125
  self.parent_type = previous_type
118
126
  end
119
127
 
120
- def with_type(current_type)
128
+ def with_log_type(current_type)
121
129
  previous_type = self.type
122
130
  self.type = current_type
123
131
  yield
@@ -126,22 +134,30 @@ module Loggun
126
134
  end
127
135
 
128
136
  def log_type(type, method_name)
137
+ klass = self.class
129
138
  type ||= DEFAULT_TYPE.dup
130
139
  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
140
+
141
+ if type_as_arr.size == 1
142
+ log_entity_name = klass.respond_to?(:log_entity_name) ? klass.log_entity_name : underscore(klass.name.dup)
143
+ type_as_arr << log_entity_name
144
+ end
135
145
 
136
146
  return type unless klass.respond_to?(:log_entity_action)
137
147
 
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
148
+ if klass.log_entity_action && klass.log_entity_action == :method_name && type_as_arr.size < 3 && method_name
149
+ type_as_arr << method_name
142
150
  end
143
151
 
144
- type
152
+ type_as_arr.join('.')
153
+ end
154
+
155
+ def generate_log_transaction_id
156
+ if self.class.log_transaction_generator
157
+ return self.class.log_transaction_generator.call(self)
158
+ end
159
+
160
+ "#{SecureRandom.uuid[0..7]}_#{DateTime.now.strftime('%Q')}"
145
161
  end
146
162
 
147
163
  private
@@ -162,17 +178,8 @@ module Loggun
162
178
  word
163
179
  end
164
180
 
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
181
+ def logger
182
+ Loggun.logger
176
183
  end
177
184
  end
178
185
  end
@@ -0,0 +1,22 @@
1
+ module Loggun
2
+ module Modifiers
3
+ class ActiveRecord < Loggun::Modifiers::Base
4
+ def apply
5
+ return unless defined?(::ActiveRecord) && defined?(::ActiveRecord::LogSubscriber)
6
+
7
+ subscriber_class_name = config.modifiers.active_record.log_subscriber_class_name
8
+ if subscriber_class_name == ::Loggun::Config::DEFAULTS[:active_record][:log_subscriber_class_name]
9
+ require 'loggun/modifiers/active_record/loggun_log_subscriber'
10
+ end
11
+ klass = Object.const_get(subscriber_class_name)
12
+
13
+ return klass.attach_to :active_record if klass.respond_to?(:attach_to)
14
+
15
+ Loggun.warn(
16
+ "Loggun: passed active_record.log_subscriber_class_name `#{subscriber_class_name}`" \
17
+ "must respond to #attached_to method"
18
+ )
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ module Loggun
2
+ module Modifiers
3
+ class ActiveRecord
4
+ class LoggunLogSubscriber < ::ActiveRecord::LogSubscriber
5
+ def sql(event)
6
+ payload = event.payload
7
+ return if IGNORE_PAYLOAD_NAMES.include?(payload[:name]) || payload[:cached]
8
+
9
+ available_keys = ::Loggun::Config.instance.modifiers.active_record.payload_keys&.map { |k| k.downcase.to_sym }
10
+ data = { sql: payload[:sql], name: payload[:name], duration: event.duration.round(4) }
11
+ source = respond_to?(:extract_query_source_location) ? extract_query_source_location(caller) : nil
12
+ data.merge!(source: source.gsub(/(?<=\.rb)(.*)$/, '')) if source
13
+ if available_keys&.any?
14
+ data.each { |k, _| data.delete(k) unless available_keys.include?(k) }
15
+ end
16
+
17
+ Loggun.info 'storage.sql.query', data
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -14,6 +14,10 @@ module Loggun
14
14
  def apply
15
15
  raise NotImplementedError, 'You must implement #apply in your modifier.'
16
16
  end
17
+
18
+ def config
19
+ Loggun::Config.instance
20
+ end
17
21
  end
18
22
  end
19
23
  end
@@ -3,11 +3,21 @@ require 'sidekiq' if defined?(::Sidekiq)
3
3
  module Loggun
4
4
  module Modifiers
5
5
  class Sidekiq < Loggun::Modifiers::Base
6
+ MIN_SIDEKIQ_V = '4.0.0'.freeze
7
+
6
8
  def apply
7
- return unless defined?(::Sidekiq)
9
+ return unless defined?(::Sidekiq) && ::Sidekiq::VERSION >= MIN_SIDEKIQ_V
8
10
 
9
11
  ::Sidekiq.configure_server do |config|
10
- Loggun::Config.setup_formatter(config)
12
+ Loggun::Config.setup_formatter(config, LoggunFormatter.new)
13
+ end
14
+ end
15
+
16
+ if defined?(::Sidekiq)
17
+ if ::Sidekiq::VERSION >= '6.0.0'
18
+ require 'loggun/modifiers/sidekiq/sidekiq6'
19
+ else
20
+ require 'loggun/modifiers/sidekiq/sidekiq4'
11
21
  end
12
22
  end
13
23
  end
@@ -0,0 +1,39 @@
1
+ module Loggun
2
+ module Modifiers
3
+ class Sidekiq
4
+ class LoggunFormatter < ::Sidekiq::Logging::Pretty
5
+ def call(severity, time, _program_name, message)
6
+ message, loggun_type = prepared_message(message)
7
+ Loggun::Formatter.new.call(severity, time.utc, nil, message, loggun_type: loggun_type)
8
+ end
9
+
10
+ def prepared_message(message)
11
+ if %w[start].include?(message) || message[/^(done|fail):\s(.*)\ssec$/]
12
+ message, elapsed = split_msg_and_time(message)
13
+ loggun_type = "sidekiq.job.#{message}"
14
+ message = { tid: "#{Thread.current.object_id.to_s(36)}", context: context.strip }
15
+ message[:elapsed] = elapsed if elapsed
16
+ else
17
+ loggun_type = 'app.sidekiq.control'
18
+ message = { tid: Thread.current.object_id.to_s(36), message: message }
19
+ message.merge!(context: context) if context
20
+ end
21
+
22
+ [message, loggun_type]
23
+ end
24
+
25
+ def split_msg_and_time(message)
26
+ unless message[/^done:\s(.*)\ssec$/] || message[/^fail:\s(.*)\ssec$/]
27
+ return [message, nil]
28
+ end
29
+
30
+ msg_type = message[/^done:\s(.*)\ssec$/] ? 'done' : 'fail'
31
+
32
+ msg = message[/#{msg_type}:\s(.*)\ssec/] ? msg_type : message
33
+ elapsed = message.gsub(/#{msg_type}:\s/, '').gsub('sec', '').strip
34
+ [msg, elapsed]
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,25 @@
1
+ module Loggun
2
+ module Modifiers
3
+ class Sidekiq
4
+ class LoggunFormatter < ::Sidekiq::Logger::Formatters::Base
5
+ def call(severity, time, _program_name, message)
6
+ message, loggun_type = prepared_message(message)
7
+ Loggun::Formatter.new.call(severity, time.utc, nil, message, loggun_type: loggun_type)
8
+ end
9
+
10
+ def prepared_message(message)
11
+ if %w[start done fail].include?(message)
12
+ loggun_type = "sidekiq.job.#{message}"
13
+ message = "#{::Sidekiq.dump_json(ctx)}"
14
+ else
15
+ loggun_type = 'app.sidekiq.control'
16
+ message = { tid: tid, message: message }
17
+ message.merge!(context: format_context) if format_context
18
+ end
19
+
20
+ [message, loggun_type]
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,3 +1,3 @@
1
1
  module Loggun
2
- VERSION = '0.1.0'.freeze
2
+ VERSION = '0.4.0'.freeze
3
3
  end
@@ -32,7 +32,7 @@ Gem::Specification.new do |spec|
32
32
  spec.add_development_dependency 'clockwork', '~> 2.0'
33
33
  spec.add_development_dependency 'http', '~> 4.0'
34
34
  spec.add_development_dependency 'rails', '~> 6.0'
35
- spec.add_development_dependency 'rake', '~> 10.0'
35
+ spec.add_development_dependency 'rake', '~> 13.0'
36
36
  spec.add_development_dependency 'rspec', '~> 3.0'
37
37
  spec.add_development_dependency 'rspec-rails', '~> 3.0'
38
38
  spec.add_development_dependency 'sidekiq', '~> 6.0'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: loggun
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aleksandr Noskov
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2020-03-29 00:00:00.000000000 Z
12
+ date: 2020-06-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -73,14 +73,14 @@ dependencies:
73
73
  requirements:
74
74
  - - "~>"
75
75
  - !ruby/object:Gem::Version
76
- version: '10.0'
76
+ version: '13.0'
77
77
  type: :development
78
78
  prerelease: false
79
79
  version_requirements: !ruby/object:Gem::Requirement
80
80
  requirements:
81
81
  - - "~>"
82
82
  - !ruby/object:Gem::Version
83
- version: '10.0'
83
+ version: '13.0'
84
84
  - !ruby/object:Gem::Dependency
85
85
  name: rspec
86
86
  requirement: !ruby/object:Gem::Requirement
@@ -145,17 +145,19 @@ files:
145
145
  - lib/loggun/formatter.rb
146
146
  - lib/loggun/helpers.rb
147
147
  - lib/loggun/modifiers.rb
148
+ - lib/loggun/modifiers/active_record.rb
149
+ - lib/loggun/modifiers/active_record/loggun_log_subscriber.rb
148
150
  - lib/loggun/modifiers/base.rb
149
151
  - lib/loggun/modifiers/clockwork.rb
150
152
  - lib/loggun/modifiers/clockwork/manager.rb
151
153
  - lib/loggun/modifiers/clockwork/methods.rb
152
- - lib/loggun/modifiers/incoming_http.rb
153
- - lib/loggun/modifiers/incoming_http/log_http_actions.rb
154
154
  - lib/loggun/modifiers/outgoing_http.rb
155
155
  - lib/loggun/modifiers/outgoing_http/block_logger.rb
156
156
  - lib/loggun/modifiers/rails.rb
157
157
  - lib/loggun/modifiers/rails/railtie.rb
158
158
  - lib/loggun/modifiers/sidekiq.rb
159
+ - lib/loggun/modifiers/sidekiq/sidekiq4.rb
160
+ - lib/loggun/modifiers/sidekiq/sidekiq6.rb
159
161
  - lib/loggun/ordered_options.rb
160
162
  - lib/loggun/version.rb
161
163
  - loggun.gemspec
@@ -1,19 +0,0 @@
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
@@ -1,36 +0,0 @@
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