loggun 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +3 -0
- data/.travis.yml +10 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +145 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/loggun/config.rb +91 -0
- data/lib/loggun/formatter.rb +80 -0
- data/lib/loggun/helpers.rb +178 -0
- data/lib/loggun/modifiers/base.rb +19 -0
- data/lib/loggun/modifiers/clockwork/manager.rb +20 -0
- data/lib/loggun/modifiers/clockwork/methods.rb +9 -0
- data/lib/loggun/modifiers/clockwork.rb +18 -0
- data/lib/loggun/modifiers/incoming_http/log_http_actions.rb +36 -0
- data/lib/loggun/modifiers/incoming_http.rb +19 -0
- data/lib/loggun/modifiers/outgoing_http/block_logger.rb +11 -0
- data/lib/loggun/modifiers/outgoing_http.rb +18 -0
- data/lib/loggun/modifiers/rails/railtie.rb +13 -0
- data/lib/loggun/modifiers/rails.rb +9 -0
- data/lib/loggun/modifiers/sidekiq.rb +15 -0
- data/lib/loggun/modifiers.rb +4 -0
- data/lib/loggun/ordered_options.rb +38 -0
- data/lib/loggun/version.rb +3 -0
- data/lib/loggun.rb +21 -0
- data/loggun.gemspec +39 -0
- metadata +187 -0
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
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
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
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,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,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,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,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
|
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: []
|