loggun 0.1.1 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3a8a4d4015105e9d55b653cab3305ab2b1ddeeda0e1429bd5e2449c5841771fe
4
- data.tar.gz: e6a84389338458849f87dfb2ffcdea13403537553fa547b170c4debb5901f433
3
+ metadata.gz: cfd41231567df2c15cca3c6ba500fc8228dcf03ac808b2adb452385be59d4217
4
+ data.tar.gz: 86c26424200b7c443a4160770d7b3f0d22490db5e0ac3c41fffdd2dadd795d9e
5
5
  SHA512:
6
- metadata.gz: f10eb810cb421dde460b1c97c777496447d22197dfdbf35bf6eae341a1b9b2a6270044135887a062882c0f8bf4ccc21efbfe3231598ee7f060ca5be220385a6f
7
- data.tar.gz: 3362704777d4d6814ba58e1a72aee945d5f1f8105d03ff20930e3ecd02d559d5878f90681a77aefd14231aef67dbb79b41cd1883a5cbc6e54a8fa3817c1a49c9
6
+ metadata.gz: '058933cfb04111fe6f0f106e97efde068932b575bfc2be6bdfee0f419db586b07e0a11025eba01b65d12f71a9ef8121dc88e4c53dc39037cd95d5ee6d1ccd90b'
7
+ data.tar.gz: ebd06ae4867bb4b8cc03fa0ef1815ca73f58af294fab484f3f1207c01d3b4c305cfaa442a6b82771524a909fdb88cb68a8421c760ba8099731957a158c910858
@@ -3,8 +3,6 @@ sudo: false
3
3
  language: ruby
4
4
  cache: bundler
5
5
  rvm:
6
- - 2.3.8
7
- - 2.4.6
8
6
  - 2.5.5
9
7
  - 2.6.5
10
8
  before_install: gem install bundler
@@ -0,0 +1,36 @@
1
+ # Changelog
2
+
3
+ ## 0.4.1 - 2020-07-28
4
+ * fixes
5
+ * fill type with class name for empty `log_entity_name`
6
+
7
+ ## 0.4.0 - 2020-06-02
8
+ * fixes
9
+ * fix key_value join character to space
10
+ * fix dummy dependency alerts
11
+ * features
12
+ * add new const MESSAGE_FORMATS and test for checking the value of message_format
13
+ * add check message_format
14
+ * boolean flag replace with a whitelist and create method for generating a message
15
+ * added ability to log messages format k=v
16
+
17
+ ## 0.3.1 - 2020-04-21
18
+ * fixes
19
+ * fix error "modify frozen String" for class name in helpers
20
+
21
+ ## 0.3.0 - 2020-04-17
22
+ * fixes
23
+ * make public #generate_log_transaction_id in helper
24
+ * removed incoming http modifier
25
+
26
+ ## 0.2.0 - 2020-04-13
27
+ * fixes
28
+ * fix Readme with active_record modifier
29
+ * fix except option for helper #log_options
30
+ * refactor helpers #log_type
31
+ * fix helper define log_methods caller
32
+ * improve stability of helpers
33
+ * features
34
+ * remove legacy agent from default log pattern
35
+ * new active record modifier
36
+ * improve sidekiq modifier for difference versions
data/README.md CHANGED
@@ -1,37 +1,70 @@
1
1
  # Loggun
2
2
 
3
- [![Sponsored by FunBox](https://funbox.ru/badges/sponsored_by_funbox_compact.svg)](https://funbox.ru)
4
-
5
3
  [![Gem Version](https://badge.fury.io/rb/loggun.svg)](https://badge.fury.io/rb/loggun)
6
4
  [![Build Status](https://travis-ci.org/funbox/loggun.svg?branch=master)](https://travis-ci.org/funbox/loggun)
7
5
 
8
- ## Описание
9
- Loggun - это гем, позволяющий привести все логи приложения к единому формату
6
+ Приводит логи приложения к единому формату.
7
+
8
+ ## Содержание
9
+
10
+ - [Установка](#установка)
11
+ - [Использование](#использование)
12
+ - [Конфигурация](#конфигурация)
13
+ - [Настройки](#настройки)
14
+ - [Модификаторы](#модификаторы)
15
+ - [Rails модификатор](#rails-модификатор)
16
+ - [Active Record модификатор](#active-record-модификатор)
17
+ - [Sidekiq модификатор](#sidekiq-модификатор)
18
+ - [Clockwork модификатор](#clockwork-модификатор)
19
+ - [Модификатор исходящих HTTP-запросов](#модификатор-исходящих-http-запросов)
20
+ - [Модификатор входящих запросов в Rails](#модификатор-входящих-запросов-в-rails)
21
+ - [Пользовательские модификаторы](#пользовательские-модификаторы)
22
+ - [Хелперы](#хелперы)
10
23
 
11
24
  ## Установка
12
25
 
13
- Чтобы установить гем, добавьте в ваш Gemfile:
26
+ Чтобы установить гем, добавьте его в Gemfile:
14
27
 
15
28
  ```ruby
16
29
  gem 'loggun'
17
30
  ```
18
31
 
19
- И выполните команду :
32
+ И выполните команду:
20
33
 
21
- $ bundle
34
+ ```bash
35
+ $ bundle
36
+ ```
22
37
 
23
38
  ## Использование
24
39
 
25
- ### Конфигурация
26
- Для успешной конфигурации гема необходимо подгружать файл при инициализации вашего приложения.
40
+ Loggun можно использовать как обертку для вашего `logger`. Для этого необходимо передать
41
+ ему инстанс логгера и настроить его `formatter`:
42
+
43
+ ```ruby
44
+ Loggun.logger = Rails.logger
45
+ Loggun.logger.formatter = Loggun::Formatter.new
46
+ ```
47
+
48
+ Теперь можно использовать Loggun для логирования в стандартизированном формате:
49
+
50
+ ```ruby
51
+ Loggun.info('http_request.api.request', user_id: current_user.id)
52
+ #=> 2020-04-11T22:35:04.225+03:00 - 170715 INFO http_request.api.request - {"user_id": 5465}
53
+ ...
54
+ Loggun.info('http_request.api.response', user_id: current_user.id, success: true)
55
+ #=> 2020-04-11T22:35:04.225+03:00 - 170715 INFO http_request.api.response - {"user_id": 5465, "success": true}
56
+ ```
57
+
58
+ ## Конфигурация
27
59
 
28
- `config/initializers/loggun.rb`
60
+ Конфигурацию гема необходимо производить при инициализации приложения. Например, так:
29
61
 
30
62
  ```ruby
31
63
  Loggun::Config.configure do |config|
32
64
  config.precision = :milliseconds
33
65
  config.pattern = '%{time} - %{pid} %{severity} %{type} %{tags_text} %{message}'
34
66
  config.parent_transaction_to_message = false
67
+ config.message_format = :json
35
68
 
36
69
  config.modifiers.rails = true
37
70
  config.modifiers.sidekiq = false
@@ -40,39 +73,130 @@ Loggun::Config.configure do |config|
40
73
  config.modifiers.outgoing_http = false
41
74
  end
42
75
  ```
43
- Все настройки являются опциональными.
44
- #### Настройки
45
- `precision` - точность отметок времени. По умолчанию - `milliseconds`. Может принимать одно из следующих значений: `sec`, `seconds`, `ms`, `millis`, `milliseconds`, `us`, `micros`, `microseconds`, `ns`, `nanos`, `nanoseconds`
46
76
 
47
- `pattern` - шаблон для формата вывода данных в лог. Доступные ключи: `time`, `pid`, `severity`, `type`, `tags_text`, `message`, `parent_transaction`
77
+ ### Настройки
78
+
79
+ Все настройки опциональны.
80
+
81
+ - `precision` — точность отметок времени.
82
+
83
+ Может принимать одно из следующих значений:
84
+ `sec`, `seconds`, `ms`, `millis`, `milliseconds`, `us`, `micros`, `microseconds`, `ns`, `nanos`, `nanoseconds`.
85
+
86
+ По умолчанию `milliseconds`.
87
+
88
+ - `pattern` — текстовый шаблон для формата вывода данных в лог.
89
+
90
+ Доступные ключи внутри шаблона: `time`, `pid`, `severity`, `type`, `tags_text`, `message`, `parent_transaction`.
91
+
92
+ - `parent_transaction_to_message` — если `true`, то значение `parent_transaction` будет добавлено в тело логируемого сообщения.
93
+
94
+ Ключ `parent_transaction` в шаблоне `pattern` можно использовать вне зависимости от значения этой настройки.
95
+
96
+ - `message_format` — формат переменной `message` в шаблоне `pattern`.
48
97
 
49
- `parent_transaction_to_message` - признак необходимости добавлять значение `parent_transaction` в тело логируемого сообщения.
50
- Вне зависимости от данной настройки можно использовать ключ `parent_transaction` в шаблоне `pattern`.
98
+ Доступные значения:
99
+
100
+ - `:json` — `message` логируется как JSON-строка;
101
+ - `:key_value` — `message` логируется в формате `key1=value1 key2=value2`.
102
+
103
+ - `modifiers` — модификаторы для переопределения формата логирования указанного компонента. См. «[Модификаторы](#модификаторы)».
51
104
 
52
- `modifiers` - модификаторы для переопределения формата логирования указанного компонента. См. далее.
105
+ ### Модификаторы
53
106
 
54
- #### Модификаторы
55
107
  Каждый модифкатор может быть активирован двумя равнозначными способами:
108
+
56
109
  ```ruby
57
110
  config.modifiers.rails = true
58
111
  ```
112
+
59
113
  или
114
+
60
115
  ```ruby
61
116
  config.modifiers.rails.enable = true
62
117
  ```
63
118
 
64
- `rails` - модифицирует форматирование логгера Rails.
119
+ качестве примера активируется Rails модификатор, но может быть любой другой.)
120
+
121
+ #### Rails модификатор
122
+
123
+ `config.modifier.rails`
124
+
125
+ Модифицирует форматирование логгера Rails.
126
+
127
+ #### Active Record модификатор
128
+
129
+ `config.modifier.active_record`
130
+
131
+ Добавляет (именно добавляет, а не модифицирует) нового подписчика на SQL-события.
132
+
133
+ SQL начинает дополнительно логироваться в Loggun формате, `severity` — `info`. Например:
134
+
135
+ ```text
136
+ 2020-04-12T20:08:52.913+03:00 - 487257 INFO storage.sql.query - {"sql":"SELECT 1","name":null,"duration":0.837}
137
+ ```
138
+
139
+ Дополнительные настройки:
140
+
141
+ - `log_subscriber_class_name` — имя класса, реализующего логирование SQL-события.
142
+
143
+ Необходим метод `#sql`. По умолчанию: `::Loggun::Modifiers::ActiveRecord::LoggunLogSubscriber`.
144
+
145
+ - `payload_keys` — необходимые ключи в полезной нарзуке. Используется в классе по умолчанию.
146
+
147
+ Доступные ключи: `%i[sql name duration source]`.
148
+
149
+ Пример:
150
+
151
+ ```ruby
152
+ Loggun::Config.configure do |config|
153
+ #...
154
+ config.modifiers.active_record.enable = true
155
+ config.modifiers.active_record.log_subscriber_class_name = 'MyApp::MyLogSubscriber'
156
+ config.modifiers.active_record.payload_keys = %i[sql duration]
157
+ #...
158
+ end
159
+ ```
160
+
161
+ #### Sidekiq модификатор
162
+
163
+ `config.modifiers.sidekiq`
164
+
165
+ Модифицирует форматирование логгера Sidekiq.
166
+
167
+ #### Clockwork модификатор
168
+
169
+ `config.modifiers.clockwork`
170
+
171
+ Модифицирует форматирование логгера Clockwork.
172
+
173
+ #### Модификатор исходящих HTTP-запросов
174
+
175
+ `config.modifiers.outgoing_http`
176
+
177
+ Добавляет логирование исходящих HTTP-запросов.
178
+ На данный момент поддерживаются только запросы, выполненные с помощью гема `HTTP`.
179
+
180
+ #### Модификатор входящих запросов в Rails
181
+
182
+ `config.modifiers.incoming_http`
183
+
184
+ Добавляет логирование входящих HTTP-запросов для контроллеров Rails.
185
+
186
+ Может иметь дополнительные настройки:
65
187
 
66
- `sidekiq` - модифицирует форматирование логгера Sidekiq.
188
+ - `controllers` массив имён базовых контроллеров, для которых необходимо добавить указанное логирование.
67
189
 
68
- `clockwork` - модифицирует форматирование логгера Clockwork.
190
+ - `success_condition` лямбда, определяющая, содержит ли успех ответ экшена.
69
191
 
70
- `outgoing_http` - добавляет логирование исходящих http запросов.
71
- На данный момент поддерживаются только запросы посредством гема `HTTP`.
192
+ Например: `-> { JSON.parse(response.body)['result'] == 'ok' }`
72
193
 
73
- `incoming_http` - добавляет логирование входящих http запросов для контроллеров Rails.
74
- Данный модификатор может иметь дополнительные настройки, которые устанавливаются следующим образом
75
- (приведены значения по умолчанию):
194
+ - `error_info` лямбда, позволяющая добавить в лог информацию об ошибке, содержащейся в неуспешном ответе экшена.
195
+
196
+ Например: `-> { JSON.parse(response.body)['error_code'] }`
197
+
198
+
199
+ Пример (приведены значения по умолчанию):
76
200
 
77
201
  ```ruby
78
202
  Loggun::Config.configure do |config|
@@ -85,25 +209,24 @@ Loggun::Config.configure do |config|
85
209
  end
86
210
  ```
87
211
 
88
- `controllers` - массив имён базовых контроллеров, для которых необходимо добавить указанное логирование.
212
+ **Для Rails 6 и выше данный модификатор может работать некорректно.**
89
213
 
90
- `success_condition` - лямбда, определяющая, содержит ли успех ответ экшена. Например `-> { JSON.parse(response.body)['result'] == 'ok' }`
214
+ В этом случае можно добавить в требуемый базовый контроллер строку:
91
215
 
92
- `error_info` - лямбда, позволяющая добавить в лог информацию об ошибке, содержащейся в неуспешном ответе экшена.
93
- Например `-> { JSON.parse(response.body)['error_code'] }`
94
-
95
- Для Rails 6 и выше данный модификатор может работать некорректно.
96
- В этом случае можно указать в требуемом базовом контроллере строку:
97
216
  ```ruby
98
217
  include Loggun::HttpHelpers
99
218
  ```
219
+
100
220
  Это делает настройки `enable` и `controllers` модификатора безсполезными,
101
- однако позволяет гарантированно логировать входящие http запросы.
221
+ однако позволяет гарантированно логировать входящие HTTP-запросы.
102
222
 
103
- Настройки `success_condition` и `error_info` продолжают использоваться и могу быть установлены требуемым образом.
223
+ Настройки `success_condition` и `error_info` продолжают использоваться и могут быть установлены требуемым образом.
224
+
225
+ #### Пользовательские модификаторы
104
226
 
105
227
  Помимо указанных модификаторов существует возможность добавить собственный.
106
- Необходимо уснаследовать его от `Loggun::Modifiers::Base` и указать в методе `apply` все необходимые действия.
228
+ Необходимо уснаследовать его от `Loggun::Modifiers::Base` и указать в методе `apply` все необходимые действия:
229
+
107
230
  ```ruby
108
231
  require 'sinatra/custom_logger'
109
232
 
@@ -113,7 +236,9 @@ class NewModifier < Loggun::Modifiers::Base
113
236
  end
114
237
  end
115
238
  ```
116
- Затем необходимо добавить его при конфигурации гема.
239
+
240
+ Затем необходимо добавить его при конфигурации гема:
241
+
117
242
 
118
243
  ```ruby
119
244
  Loggun::Config.configure do |config|
@@ -124,32 +249,45 @@ end
124
249
  ```
125
250
 
126
251
  ### Хелперы
127
- Подключение хелперов в класс позволяет использовать методы логирования `log_info` и `log_error`,
252
+
253
+ Подключение хэлперов в класс позволяет использовать методы логирования `log_info` и `log_error`,
128
254
  а также генерировать идентификатор транзации для каждого метода класса.
129
255
 
130
256
  Например:
257
+
131
258
  ```ruby
132
259
  class SomeClass
133
260
  include Loggun::Helpers
134
261
 
135
- log_options entity_action: :method_name, as_transaction: true
136
-
137
- def some_action
138
- log_info 'type_for_action', 'Information'
139
- bar
140
- end
262
+ log_options entity_action: :method_name, as_transaction: true, only: %i[download_data]
141
263
 
142
- def bar
143
- log_info 'type_for_bar', 'Bar information'
264
+ def download_data
265
+ log_info 'http_request', 'Information'
266
+ # ... make http request here
267
+ log_info 'http_response', success: true
144
268
  end
145
269
  end
146
270
  ```
147
- Даёт подобный вывод в лог:
271
+
272
+ При вызове `#download_data` будет следующий вывод в лог:
273
+
148
274
  ```
149
- 2020-03-04T16:58:38.207+05:00 - 28476 INFO type_for_action.some_class.some_action#msg_id_1583323118203 - {"value":["Information"]}
150
- 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"}
275
+ 2020-03-04T16:58:38.207+05:00 - 28476 INFO http_request.some_class.download_data#ffg5431_1583323118203 - {"message":["Information"]}
276
+ 2020-03-04T16:58:38.208+05:00 - 28476 INFO http_response.some_class.download_data#ffg5431_1583323118203 - {"success": true}
151
277
  ```
152
278
 
153
- ## License
279
+ **Важно**, что с хэлпером `log_options` необходимо использовать только методы вида `log_<severity>`.
280
+ Методы модуля `Loggun` не будут работать.
281
+
282
+ Список всех опций хэлпера `log_options`:
283
+
284
+ - `entity_name` — имя сущности метода, `string`;
285
+ - `entity_action` — действие сущности метода, `string`;
286
+ - `as_transaction` — добавлять ли уникальный ID транзакции для метода, `boolean`;
287
+ - `transaction_generator` — собственный генератор ID транзакции, `lambda`;
288
+ - `log_all_methods` — применять ли хэлпер ко всем методам, `boolean`;
289
+ - `only` — список методов, для которых необходимо применить хэлпер (работает только когда `log_all_methods = false`), `Array{Symbol}`;
290
+ - `except` — список методов, которые надо исключить для хэлпера, `Array{Symbol}`;
291
+ - `log_transaction_except` — список методов, логирование которых не нужно обогащать ID транзакции, `Array{Symbol}`.
154
292
 
155
- The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
293
+ [![Sponsored by FunBox](https://funbox.ru/badges/sponsored_by_funbox_centered.svg)](https://funbox.ru)
@@ -5,7 +5,6 @@ require 'loggun/config'
5
5
  require 'loggun/modifiers'
6
6
  require 'loggun/modifiers/base'
7
7
  require 'loggun/helpers'
8
- require 'loggun/http_helpers'
9
8
  require 'logger'
10
9
 
11
10
  module Loggun
@@ -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)
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)
59
74
  Loggun.logger = app.logger
60
- Loggun.logger.formatter = instance.formatter
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
@@ -5,7 +5,7 @@ module Loggun
5
5
  class Formatter
6
6
  DEFAULT_VALUE = '-'.freeze
7
7
 
8
- def call(severity, time, _program_name, message)
8
+ def call(severity, time, _program_name, message, loggun_type: nil)
9
9
  data = Hash.new(DEFAULT_VALUE)
10
10
  data[:time] = time.iso8601(config.timestamp_precision)
11
11
  data[:pid] = Process.pid
@@ -14,13 +14,13 @@ module Loggun
14
14
  if config.parent_transaction_to_message && parent_transaction
15
15
  message[:parent_transaction] = parent_transaction
16
16
  end
17
- message = JSON.generate(message)
17
+ message = format_message(message)
18
18
  end
19
19
 
20
20
  data[:message] = message.to_s.tr("\r\n", ' ').strip
21
21
  data[:severity] = severity&.to_s || 'INFO'
22
22
  data[:tags_text] = tags_text
23
- data[:type] = Loggun.type || DEFAULT_VALUE.dup
23
+ data[:type] = loggun_type || Loggun.type || DEFAULT_VALUE.dup
24
24
  data[:transaction_id] = Loggun.transaction_id
25
25
  data[:parent_transaction] = parent_transaction if parent_transaction
26
26
 
@@ -78,5 +78,16 @@ module Loggun
78
78
  def config
79
79
  Loggun::Config.instance
80
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
81
92
  end
82
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 ||= []
50
+
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)
42
54
 
43
- modified_methods << method_name
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 logger.send(method, type, &block) if args.empty? &&
64
- attrs.empty?
75
+ next logger.send(method_name, type, &block) if args.empty? && attrs.empty?
65
76
 
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)
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 unless args.empty?
89
+ attrs[:message] = args unless args.empty?
82
90
 
83
- with_type(type) do
84
- 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,32 @@ 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.log_entity_name if klass.respond_to?(:log_entity_name)
143
+
144
+ log_entity_name ||= underscore(klass.name.dup)
145
+ type_as_arr << log_entity_name
146
+ end
135
147
 
136
148
  return type unless klass.respond_to?(:log_entity_action)
137
149
 
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
150
+ if klass.log_entity_action && klass.log_entity_action == :method_name && type_as_arr.size < 3 && method_name
151
+ type_as_arr << method_name
142
152
  end
143
153
 
144
- type
154
+ type_as_arr.join('.')
155
+ end
156
+
157
+ def generate_log_transaction_id
158
+ if self.class.log_transaction_generator
159
+ return self.class.log_transaction_generator.call(self)
160
+ end
161
+
162
+ "#{SecureRandom.uuid[0..7]}_#{DateTime.now.strftime('%Q')}"
145
163
  end
146
164
 
147
165
  private
@@ -162,15 +180,6 @@ module Loggun
162
180
  word
163
181
  end
164
182
 
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
183
  def logger
175
184
  Loggun.logger
176
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.1'.freeze
2
+ VERSION = '0.4.1'.freeze
3
3
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: loggun
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aleksandr Noskov
8
8
  - Sergey Nesterov
9
- autorequire:
9
+ autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2020-04-06 00:00:00.000000000 Z
12
+ date: 2020-07-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -134,6 +134,7 @@ files:
134
134
  - ".gitignore"
135
135
  - ".rspec"
136
136
  - ".travis.yml"
137
+ - CHANGELOG.md
137
138
  - Gemfile
138
139
  - LICENSE.txt
139
140
  - README.md
@@ -144,18 +145,20 @@ files:
144
145
  - lib/loggun/config.rb
145
146
  - lib/loggun/formatter.rb
146
147
  - lib/loggun/helpers.rb
147
- - lib/loggun/http_helpers.rb
148
148
  - lib/loggun/modifiers.rb
149
+ - lib/loggun/modifiers/active_record.rb
150
+ - lib/loggun/modifiers/active_record/loggun_log_subscriber.rb
149
151
  - lib/loggun/modifiers/base.rb
150
152
  - lib/loggun/modifiers/clockwork.rb
151
153
  - lib/loggun/modifiers/clockwork/manager.rb
152
154
  - lib/loggun/modifiers/clockwork/methods.rb
153
- - lib/loggun/modifiers/incoming_http.rb
154
155
  - lib/loggun/modifiers/outgoing_http.rb
155
156
  - lib/loggun/modifiers/outgoing_http/block_logger.rb
156
157
  - lib/loggun/modifiers/rails.rb
157
158
  - lib/loggun/modifiers/rails/railtie.rb
158
159
  - lib/loggun/modifiers/sidekiq.rb
160
+ - lib/loggun/modifiers/sidekiq/sidekiq4.rb
161
+ - lib/loggun/modifiers/sidekiq/sidekiq6.rb
159
162
  - lib/loggun/ordered_options.rb
160
163
  - lib/loggun/version.rb
161
164
  - loggun.gemspec
@@ -165,7 +168,7 @@ licenses:
165
168
  metadata:
166
169
  homepage_uri: https://github.com/funbox/loggun
167
170
  source_code_uri: https://github.com/funbox/loggun
168
- post_install_message:
171
+ post_install_message:
169
172
  rdoc_options: []
170
173
  require_paths:
171
174
  - lib
@@ -181,7 +184,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
181
184
  version: '0'
182
185
  requirements: []
183
186
  rubygems_version: 3.0.6
184
- signing_key:
187
+ signing_key:
185
188
  specification_version: 4
186
189
  summary: Loggun
187
190
  test_files: []
@@ -1,48 +0,0 @@
1
- module Loggun
2
- module HttpHelpers
3
- def self.included(klass)
4
- klass.class_eval do
5
- around_action :log_http_actions
6
- end
7
- end
8
-
9
- private
10
-
11
- def log_http_actions
12
- log_action :start
13
- yield
14
- log_action :response
15
- rescue StandardError => e
16
- log_action :response, fail: e
17
- raise e
18
- end
19
-
20
- def log_action(action = :start, fail: nil)
21
- api = request.path[/\w+/]
22
- api_version = request.path[/v./]
23
- type = "http_request.#{api}.#{action}"
24
- data = { path: clean_pathname }
25
- data[:api_version] = api_version if api_version
26
-
27
- return Loggun.info type, data if action == :start
28
-
29
- success = fail.nil? && instance_exec(&modifier_config.success_condition)
30
- data[:success] = success
31
- unless success
32
- error = instance_exec(&modifier_config.error_info)
33
- data[:error] = error if error
34
- end
35
- Loggun.info type, data
36
- end
37
-
38
- def clean_pathname
39
- filtered_params = params.to_unsafe_h
40
- filtered_params.delete('action')
41
- request.path.gsub(/(#{filtered_params.values.join('|')})/, '').gsub(/\/api\/v./, '')
42
- end
43
-
44
- def modifier_config
45
- Loggun::Config.instance.modifiers.incoming_http
46
- end
47
- end
48
- end
@@ -1,16 +0,0 @@
1
- module Loggun
2
- module Modifiers
3
- class IncomingHttp < Loggun::Modifiers::Base
4
- def apply
5
- return unless defined?(ActionPack)
6
-
7
- controllers = Loggun::Config.instance.modifiers.incoming_http.controllers
8
- controllers.each do |controller|
9
- controller.constantize.class_eval do
10
- include Loggun::HttpHelpers
11
- end
12
- end
13
- end
14
- end
15
- end
16
- end