tracer_client 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.idea/.name +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +63 -0
- data/Rakefile +1 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/tracer_client.rb +5 -0
- data/lib/tracer_client/changes.rb +177 -0
- data/lib/tracer_client/client.rb +122 -0
- data/lib/tracer_client/errors/api_error.rb +2 -0
- data/lib/tracer_client/errors/api_error_alert.rb +2 -0
- data/lib/tracer_client/errors/api_error_log.rb +2 -0
- data/lib/tracer_client/errors/api_request_failure.rb +15 -0
- data/lib/tracer_client/errors/api_request_failure_alert.rb +2 -0
- data/lib/tracer_client/errors/api_request_failure_log.rb +2 -0
- data/lib/tracer_client/errors/application_error.rb +28 -0
- data/lib/tracer_client/errors/logic_error.rb +2 -0
- data/lib/tracer_client/errors/logic_error_alert.rb +2 -0
- data/lib/tracer_client/errors/logic_error_log.rb +2 -0
- data/lib/tracer_client/log.rb +153 -0
- data/lib/tracer_client/server.rb +36 -0
- data/lib/tracer_client/version.rb +3 -0
- data/tracer_client.gemspec +28 -0
- metadata +139 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 824a991b553db0aefd11d0f2d83dafffd9435b28
|
4
|
+
data.tar.gz: 64248db71c319412b85f8be5e32187c7fe40530e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2475d3fa342a85d8564869043e85df849fe4cf9f35e8ab96e5b08813293a30e11e6da473ea866eb623c93df8cdadc25d7806ce265a42a8075cc3193c6d25ed26
|
7
|
+
data.tar.gz: a718a7a74831aee0f0d6aa13fa1d5d7851f1be9cf91cf91ab09c15a45c5a64e7696c56810fdecd4c06587e3756c7c85b4c116a6ba84f87f5e18e201121fa397d
|
data/.gitignore
ADDED
data/.idea/.name
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
tracer_client
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Serge Galanin
|
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,63 @@
|
|
1
|
+
# TracerClient
|
2
|
+
|
3
|
+
Library for logging errors and ActiveRecord object changes to Tracer.
|
4
|
+
|
5
|
+
Tracer is a web application for accumulating, showing and analyzing of a vital application information.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'tracer_client'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install tracer_client
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
Methods for debugging:
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
Log.debug('Debug message') # log simple message
|
29
|
+
Log.debug({a: 2, b: 5}) # log hash values
|
30
|
+
Log.debug(any_object) # log any object
|
31
|
+
```
|
32
|
+
|
33
|
+
Log some info with severity level. Each method requires message text and log tags and accepts optional data.
|
34
|
+
```ruby
|
35
|
+
Log.info('Order created', 'order create', id: id)
|
36
|
+
Log.warn('Order shipping too long!', 'order shipping', id: id)
|
37
|
+
Log.error('Order was not paid', 'order payment', id: id)
|
38
|
+
```
|
39
|
+
|
40
|
+
Log catched exception
|
41
|
+
```ruby
|
42
|
+
begin
|
43
|
+
raise 'Some exception'
|
44
|
+
rescue => e
|
45
|
+
Log.exception(e, 'Unable to send request', 'request', request_data: data)
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
## Development
|
50
|
+
|
51
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
52
|
+
|
53
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
54
|
+
|
55
|
+
## Contributing
|
56
|
+
|
57
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/galanin/tracer_client.
|
58
|
+
|
59
|
+
|
60
|
+
## License
|
61
|
+
|
62
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
63
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "tracer_client"
|
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
|
data/bin/setup
ADDED
@@ -0,0 +1,177 @@
|
|
1
|
+
module Tracer
|
2
|
+
module Changes
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.send :extend, ClassMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
|
10
|
+
def log_record_changes(options = {})
|
11
|
+
send :include, InstanceMethods
|
12
|
+
|
13
|
+
class_attribute :changes_logging_options
|
14
|
+
self.changes_logging_options = options.dup
|
15
|
+
|
16
|
+
%i(ignore skip only).each do |k|
|
17
|
+
changes_logging_options[k] =
|
18
|
+
[changes_logging_options[k]].flatten.compact.map { |attr| attr.is_a?(Hash) ? attr.stringify_keys : attr.to_s }
|
19
|
+
end
|
20
|
+
|
21
|
+
options_on = Array(options[:on]) # so that a single symbol can be passed in without wrapping it in an `Array`
|
22
|
+
|
23
|
+
after_create :log_create, :if => :log_changes? if options_on.empty? || options_on.include?(:create)
|
24
|
+
|
25
|
+
if options_on.empty? || options_on.include?(:update)
|
26
|
+
before_update :log_update, :if => :log_changes?
|
27
|
+
end
|
28
|
+
|
29
|
+
after_destroy :log_destroy, :if => :log_changes? if options_on.empty? || options_on.include?(:destroy)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
module InstanceMethods
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
|
40
|
+
def log_create
|
41
|
+
Tracer::Client.log_changes(
|
42
|
+
item_id: id,
|
43
|
+
item_type: self.class.base_class.name,
|
44
|
+
event: 'create',
|
45
|
+
changes: changes_for_tracing,
|
46
|
+
)
|
47
|
+
rescue Exception => e
|
48
|
+
Log.exception_with_alert(e, 'Ошибка регистрации создания', 'log_changes create',
|
49
|
+
item_id: id,
|
50
|
+
item_type: self.class.base_class.name)
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
def log_update
|
55
|
+
if changed_notably?
|
56
|
+
Tracer::Client.log_changes(
|
57
|
+
item_id: id,
|
58
|
+
item_type: self.class.base_class.name,
|
59
|
+
event: 'update',
|
60
|
+
object: object_attrs_for_tracing(item_before_change),
|
61
|
+
changes: changes_for_tracing,
|
62
|
+
)
|
63
|
+
end
|
64
|
+
rescue Exception => e
|
65
|
+
Log.exception_with_alert(e, 'Ошибка регистрации изменения', 'log_changes update',
|
66
|
+
item_id: id,
|
67
|
+
item_type: self.class.base_class.name)
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
def log_destroy
|
72
|
+
if persisted?
|
73
|
+
Tracer::Client.log_changes(
|
74
|
+
item_id: id,
|
75
|
+
item_type: self.class.base_class.name,
|
76
|
+
event: 'destroy',
|
77
|
+
object: object_attrs_for_tracing(item_before_change),
|
78
|
+
)
|
79
|
+
end
|
80
|
+
rescue Exception => e
|
81
|
+
Log.exception_with_alert(e, 'Ошибка регистрации удаления', 'log_changes destroy',
|
82
|
+
item_id: id,
|
83
|
+
item_type: self.class.base_class.name)
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
def object_attrs_for_tracing(object)
|
88
|
+
object_attrs = object.attributes.except(*changes_logging_options[:skip]).with_indifferent_access
|
89
|
+
|
90
|
+
unwrap_serialized_attributes(object_attrs) do |object_attrs, values, attr|
|
91
|
+
object_attrs[attr] = values[attr] if values.key?(attr)
|
92
|
+
end
|
93
|
+
|
94
|
+
object_attrs.as_json
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
def changes_for_tracing
|
99
|
+
changed = self.changes.delete_if do |key, value|
|
100
|
+
!notably_changed.include?(key)
|
101
|
+
end
|
102
|
+
|
103
|
+
unwrap_serialized_attributes(changed) do |changed, (a, b), attr|
|
104
|
+
if (a.key?(attr) || b.key?(attr)) && a[attr] != b[attr]
|
105
|
+
changed[attr] = [a[attr], b[attr]]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
changed.as_json
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
# attrs должны быть либо с ключами-символами, либо HashWithIndifferentAccess
|
114
|
+
def unwrap_serialized_attributes(attrs)
|
115
|
+
stored_attrs = self.class.stored_attributes
|
116
|
+
serialized = attrs.extract!(*stored_attrs.keys)
|
117
|
+
|
118
|
+
serialized.each do |store_attr, value|
|
119
|
+
stored_attrs[store_attr.to_sym].each do |attr|
|
120
|
+
yield(attrs, value, attr)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
def item_before_change
|
127
|
+
previous = self.dup
|
128
|
+
# `dup` clears timestamps so we add them back.
|
129
|
+
all_timestamp_attributes.each do |column|
|
130
|
+
previous[column] = send(column) if self.class.column_names.include?(column.to_s) and not send(column).nil?
|
131
|
+
end
|
132
|
+
previous.tap do |prev|
|
133
|
+
prev.id = id # `dup` clears the `id` so we add that back
|
134
|
+
changed_attributes.select { |k,v| self.class.column_names.include?(k) }.each { |attr, before| prev[attr] = before }
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
def changed_notably?
|
140
|
+
notably_changed.any?
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
def notably_changed
|
145
|
+
only = self.changes_logging_options[:only].dup
|
146
|
+
# remove Hash arguments and then evaluate whether the attributes (the keys of the hash) should also get pushed into the collection
|
147
|
+
only.delete_if do |obj|
|
148
|
+
obj.is_a?(Hash) && obj.each { |attr, condition| only << attr if condition.respond_to?(:call) && condition.call(self) }
|
149
|
+
end
|
150
|
+
only.empty? ? changed_and_not_ignored : (changed_and_not_ignored & only)
|
151
|
+
end
|
152
|
+
|
153
|
+
def changed_and_not_ignored
|
154
|
+
ignore = self.changes_logging_options[:ignore].dup
|
155
|
+
# remove Hash arguments and then evaluate whether the attributes (the keys of the hash) should also get pushed into the collection
|
156
|
+
ignore.delete_if do |obj|
|
157
|
+
obj.is_a?(Hash) && obj.each { |attr, condition| ignore << attr if condition.respond_to?(:call) && condition.call(self) }
|
158
|
+
end
|
159
|
+
skip = self.changes_logging_options[:skip]
|
160
|
+
changed - ignore - skip
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
|
166
|
+
def log_changes?
|
167
|
+
if_condition = self.changes_logging_options[:if]
|
168
|
+
unless_condition = self.changes_logging_options[:unless]
|
169
|
+
(if_condition.blank? || if_condition.call(self)) && !unless_condition.try(:call, self)
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
ActiveSupport.on_load(:active_record) do
|
176
|
+
include Tracer::Changes
|
177
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
module Tracer
|
2
|
+
module Client
|
3
|
+
|
4
|
+
class << self
|
5
|
+
|
6
|
+
def start_request(current_user, request, params, session)
|
7
|
+
Thread.current[:tracer_current_user] = current_user
|
8
|
+
Thread.current[:tracer_request] = request
|
9
|
+
Thread.current[:tracer_params] = get_params(params)
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
def end_request
|
14
|
+
Thread.current[:tracer_current_user] = nil
|
15
|
+
Thread.current[:tracer_request] = nil
|
16
|
+
Thread.current[:tracer_params] = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def log(notice)
|
21
|
+
LogJob.new.async.perform(notice.merge(request_log_data))
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def log_changes(changes)
|
26
|
+
LogChangesJob.new.async.perform(changes.merge(request_changes_data))
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
|
33
|
+
def get_params(params)
|
34
|
+
@parameter_filter ||= ActionDispatch::Http::ParameterFilter.new Rails.application.config.filter_parameters
|
35
|
+
@parameter_filter.filter(params.except(:utf8, :authenticity_token, :_method)).symbolize_keys
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def request_data
|
40
|
+
data = {context: {}}
|
41
|
+
|
42
|
+
request = Thread.current[:tracer_request]
|
43
|
+
if request
|
44
|
+
data[:context].merge!(
|
45
|
+
url: request.original_url,
|
46
|
+
referer: request.headers['Referer'],
|
47
|
+
user_agent: request.headers['User-Agent'],
|
48
|
+
ip: request.remote_ip,
|
49
|
+
method: request.request_method,
|
50
|
+
request_id: request.uuid,
|
51
|
+
headers: {
|
52
|
+
accept: request.headers['Accept'],
|
53
|
+
}
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
cookies = Thread.current[:cookies]
|
58
|
+
if cookies && cookies[:administrator_id]
|
59
|
+
data[:context][:cookies] = {
|
60
|
+
administrator_id: cookies.signed[:administrator_id]
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
current_user = Thread.current[:tracer_current_user]
|
65
|
+
if current_user
|
66
|
+
data[:context].merge!(
|
67
|
+
userType: current_user[:type],
|
68
|
+
userId: current_user[:id],
|
69
|
+
)
|
70
|
+
end
|
71
|
+
|
72
|
+
params = Thread.current[:tracer_params]
|
73
|
+
if params
|
74
|
+
data[:context].merge!(
|
75
|
+
component: params[:controller],
|
76
|
+
action: params[:action],
|
77
|
+
)
|
78
|
+
data[:params] = params.except(:controller, :action)
|
79
|
+
end
|
80
|
+
|
81
|
+
data[:context].merge!(
|
82
|
+
environment: Rails.env,
|
83
|
+
rootDirectory: Rails.root,
|
84
|
+
)
|
85
|
+
|
86
|
+
data
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
def request_log_data
|
91
|
+
request_data
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
def request_changes_data
|
96
|
+
request_data
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
|
105
|
+
class LogJob
|
106
|
+
include SuckerPunch::Job
|
107
|
+
|
108
|
+
def perform(notice)
|
109
|
+
Tracer::Server.log(notice)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class LogChangesJob
|
114
|
+
include SuckerPunch::Job
|
115
|
+
|
116
|
+
def perform(changes)
|
117
|
+
Tracer::Server.log_changes(changes)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class ApiRequestFailure < ApiError
|
2
|
+
|
3
|
+
def initialize(subject, tags, response, data = {})
|
4
|
+
super(subject, tags, data.merge({
|
5
|
+
api_response: {
|
6
|
+
status: response.code + ' ' + response.message,
|
7
|
+
type: response.content_type,
|
8
|
+
length: response.content_length,
|
9
|
+
body: response.body.force_encoding('UTF-8'),
|
10
|
+
|
11
|
+
},
|
12
|
+
}))
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class ApplicationError < StandardError
|
2
|
+
|
3
|
+
attr_reader :tags, :data
|
4
|
+
|
5
|
+
|
6
|
+
def initialize(subject, tags = '', data = {})
|
7
|
+
super(subject)
|
8
|
+
@tags = tags
|
9
|
+
@data = data
|
10
|
+
|
11
|
+
puts "#{'-'*10}\n#{@data}\n#{'-'*10}" if Rails.env.development?
|
12
|
+
|
13
|
+
Log.on_raise(self, caller(2))
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
# для логирования при бросании и в дефолтном обработчике
|
18
|
+
def with_log?
|
19
|
+
self.class.name.end_with?('Log', 'Alert')
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
# для логирования при бросании и в дефолтном обработчике
|
24
|
+
def with_alert?
|
25
|
+
self.class.name.end_with?('Alert')
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
require 'config'
|
2
|
+
|
3
|
+
module Log
|
4
|
+
|
5
|
+
# error - ошибки
|
6
|
+
# crit - исключения
|
7
|
+
# emerg - неперехваченные исключения
|
8
|
+
SEVERITIES = %i(debug info notice warn error crit alert emerg)
|
9
|
+
FACILITIES = %i(request lib auth user product order line_item delivery odkl email direct_mail page exception)
|
10
|
+
|
11
|
+
ROBOT_UA_FRAGMENTS = /AhrefsBot|bingbot|DotBot|Googlebot|Mail.RU_Bot|MJ12bot|msnbot|SputnikBot|updown_tester|Web-Monitoring|WebMasterAid|YaDirectFetcher|YandexBot/
|
12
|
+
|
13
|
+
|
14
|
+
def self.start_request(current_user, request, params, session, do_log_request)
|
15
|
+
Tracer::Client.start_request(current_user, request, params, session)
|
16
|
+
|
17
|
+
info('HTTP запрос', 'http_request') if do_log_request
|
18
|
+
|
19
|
+
Thread.current[:request_tags] = %w(robot) if request.headers['User-Agent'] =~ ROBOT_UA_FRAGMENTS
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.end_request
|
23
|
+
Thread.current[:request_tags] = nil
|
24
|
+
Tracer::Client.end_request
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
# Можно вызывать в сокращённой форме:
|
29
|
+
# Log.debug('Сообщение')
|
30
|
+
# Log.debug({a: 2, b: 5})
|
31
|
+
# Log.debug(any_object)
|
32
|
+
def self.debug(subject = '', tags = '', data = {})
|
33
|
+
if Hash === subject
|
34
|
+
# передан только хэш с данными
|
35
|
+
message(:debug, subject.inspect[0...40], '', subject, caller)
|
36
|
+
elsif String === subject
|
37
|
+
# передан только subject
|
38
|
+
message(:debug, subject, tags, data, caller)
|
39
|
+
else
|
40
|
+
# переданы некие другие данные
|
41
|
+
message(:debug, subject.inspect[0...40], '', {debug: subject}, caller)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
%i(info warn error).each do |base_method|
|
47
|
+
instance_eval <<-CODE, __FILE__, __LINE__ + 1
|
48
|
+
def #{base_method}(subject, tags = '', data = {})
|
49
|
+
message(:#{base_method}, subject, tags, data, caller)
|
50
|
+
end
|
51
|
+
|
52
|
+
def #{base_method}_with_alert(subject, tags = '', data = {})
|
53
|
+
message(:#{base_method}, subject, tags, data.merge(with_alert: true), caller)
|
54
|
+
end
|
55
|
+
CODE
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
def self.exception(exception, subject, tags = '', data = {})
|
60
|
+
exception_message(exception, :crit, subject, tags, data, exception.backtrace)
|
61
|
+
exception
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
def self.exception_with_alert(exception, subject, tags = '', data = {})
|
66
|
+
exception(exception, subject, tags, data.merge(with_alert: true))
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
def self.on_raise(exception, backtrace)
|
71
|
+
if exception.with_log?
|
72
|
+
exception_message(exception, :warn, '', 'raise', {with_alert: exception.with_alert?}, backtrace)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
def self.unhandled(exception)
|
78
|
+
if exception.with_log? && !exception.logged?
|
79
|
+
exception_message(exception, :emerg, '', 'unhandled', {with_alert: exception.with_alert?}, exception.backtrace)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
|
87
|
+
def self.message(severity, subject, tags, data, backtrace)
|
88
|
+
return unless log?(severity)
|
89
|
+
|
90
|
+
tags = (Thread.current[:request_tags] || []) + tags.split(' ')
|
91
|
+
|
92
|
+
data = {data: data} unless Hash === data
|
93
|
+
|
94
|
+
return if data.key?(:exception) && data[:exception][:class] == 'ActiveRecord::RecordNotFound' && tags == %w(robot)
|
95
|
+
|
96
|
+
error_hash = {
|
97
|
+
severity: severity,
|
98
|
+
tags: tags,
|
99
|
+
message: subject,
|
100
|
+
backtrace: get_backtrace(backtrace).first(10)
|
101
|
+
}
|
102
|
+
|
103
|
+
if data.key?(:exception)
|
104
|
+
error_hash[:exception_message] = data[:exception][:message].strip
|
105
|
+
error_hash[:type] = data[:exception][:class]
|
106
|
+
end
|
107
|
+
|
108
|
+
Tracer::Client.log(
|
109
|
+
errors: [error_hash],
|
110
|
+
data: data.except(:exception, :with_alert),
|
111
|
+
with_alert: data[:with_alert],
|
112
|
+
)
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
def self.exception_message(exception, severity, subject, tags, data, backtrace)
|
117
|
+
tags = (tags + ' ' + exception.tags).strip if exception.respond_to?(:tags)
|
118
|
+
data.merge!(exception.data.except(:with_alert)) if exception.respond_to?(:data)
|
119
|
+
data.merge!(exception: {
|
120
|
+
class: exception.class.name,
|
121
|
+
message: exception.message
|
122
|
+
})
|
123
|
+
|
124
|
+
message(severity, subject, tags, data, backtrace)
|
125
|
+
exception.mark_logged
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
def self.log?(severity)
|
130
|
+
Settings.log.severity_level.nil? || enabled_severity?(severity, Settings.log.severity_level)
|
131
|
+
end
|
132
|
+
|
133
|
+
def self.enabled_severity?(message_severity, configured_severity)
|
134
|
+
message_index = SEVERITIES.index(message_severity)
|
135
|
+
configured_index = SEVERITIES.index(configured_severity.to_sym)
|
136
|
+
message_index.nil? || configured_index.nil? || message_index >= configured_index
|
137
|
+
end
|
138
|
+
|
139
|
+
def self.get_backtrace(backtrace)
|
140
|
+
locations = Rails.backtrace_cleaner.clean(backtrace || [])
|
141
|
+
|
142
|
+
locations.map do |location|
|
143
|
+
if location =~ /\A(.*):(\d+):\s*(?:in `(.*)')\z/
|
144
|
+
{
|
145
|
+
file: $~[1],
|
146
|
+
line: $~[2],
|
147
|
+
function: $~[3],
|
148
|
+
}
|
149
|
+
end
|
150
|
+
end.compact
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'config'
|
2
|
+
|
3
|
+
module Tracer
|
4
|
+
module Server
|
5
|
+
|
6
|
+
def self.log(notice_hash)
|
7
|
+
send("http://#{Settings.tracer.host}:#{Settings.tracer.port}/api/v3/projects/#{Settings.tracer.project}/notices?api_key=#{Settings.tracer.api_key}", notice_hash)
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
def self.log_changes(changes_hash)
|
12
|
+
Rails.logger.debug "Log change: #{changes_hash.inspect}"
|
13
|
+
send("http://#{Settings.tracer.host}:#{Settings.tracer.port}/api/v3/projects/#{Settings.tracer.project}/changes?api_key=#{Settings.tracer.api_key}", changes_hash)
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
|
20
|
+
def self.send(url, data)
|
21
|
+
uri = URI.parse(url)
|
22
|
+
|
23
|
+
Net::HTTP.start(uri.host, uri.port, read_timeout: 20) do |http|
|
24
|
+
http.request_post(uri.path + '?' + uri.query, JSON.dump(data), 'Content-Type' => 'text/xml; charset=utf-8') do |response|
|
25
|
+
return true if Net::HTTPNoContent === response
|
26
|
+
Rails.logger.error "Tracer request url: #{url}"
|
27
|
+
Rails.logger.error "Tracer response #{response.inspect}: #{response.body.inspect}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
rescue Exception => e
|
31
|
+
Rails.logger.error "Tracer request url: #{url}"
|
32
|
+
Rails.logger.error "Tracer exception #{e.inspect}: #{e.message}"
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'tracer_client/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'tracer_client'
|
8
|
+
spec.version = TracerClient::VERSION
|
9
|
+
spec.authors = ['Serge Galanin']
|
10
|
+
spec.email = ['s.galanin@gmail.com']
|
11
|
+
|
12
|
+
spec.summary = %q{Tracer API}
|
13
|
+
spec.description = %q{Log errors and objects changes to Tracer.}
|
14
|
+
spec.homepage = "https://github.com/galanin/tracer_client"
|
15
|
+
spec.license = 'MIT'
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = 'exe'
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ['lib']
|
21
|
+
|
22
|
+
spec.add_dependency 'config', '~> 1.0'
|
23
|
+
spec.add_dependency 'rails', '~> 4.2'
|
24
|
+
spec.add_dependency 'sucker_punch', '~> 1.5'
|
25
|
+
|
26
|
+
spec.add_development_dependency 'bundler', '~> 1.10'
|
27
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tracer_client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Serge Galanin
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-08-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: config
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rails
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '4.2'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '4.2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: sucker_punch
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.5'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.5'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.10'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.10'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '10.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '10.0'
|
83
|
+
description: Log errors and objects changes to Tracer.
|
84
|
+
email:
|
85
|
+
- s.galanin@gmail.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- ".gitignore"
|
91
|
+
- ".idea/.name"
|
92
|
+
- Gemfile
|
93
|
+
- LICENSE.txt
|
94
|
+
- README.md
|
95
|
+
- Rakefile
|
96
|
+
- bin/console
|
97
|
+
- bin/setup
|
98
|
+
- lib/tracer_client.rb
|
99
|
+
- lib/tracer_client/changes.rb
|
100
|
+
- lib/tracer_client/client.rb
|
101
|
+
- lib/tracer_client/errors/api_error.rb
|
102
|
+
- lib/tracer_client/errors/api_error_alert.rb
|
103
|
+
- lib/tracer_client/errors/api_error_log.rb
|
104
|
+
- lib/tracer_client/errors/api_request_failure.rb
|
105
|
+
- lib/tracer_client/errors/api_request_failure_alert.rb
|
106
|
+
- lib/tracer_client/errors/api_request_failure_log.rb
|
107
|
+
- lib/tracer_client/errors/application_error.rb
|
108
|
+
- lib/tracer_client/errors/logic_error.rb
|
109
|
+
- lib/tracer_client/errors/logic_error_alert.rb
|
110
|
+
- lib/tracer_client/errors/logic_error_log.rb
|
111
|
+
- lib/tracer_client/log.rb
|
112
|
+
- lib/tracer_client/server.rb
|
113
|
+
- lib/tracer_client/version.rb
|
114
|
+
- tracer_client.gemspec
|
115
|
+
homepage: https://github.com/galanin/tracer_client
|
116
|
+
licenses:
|
117
|
+
- MIT
|
118
|
+
metadata: {}
|
119
|
+
post_install_message:
|
120
|
+
rdoc_options: []
|
121
|
+
require_paths:
|
122
|
+
- lib
|
123
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
124
|
+
requirements:
|
125
|
+
- - ">="
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
version: '0'
|
128
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: '0'
|
133
|
+
requirements: []
|
134
|
+
rubyforge_project:
|
135
|
+
rubygems_version: 2.4.8
|
136
|
+
signing_key:
|
137
|
+
specification_version: 4
|
138
|
+
summary: Tracer API
|
139
|
+
test_files: []
|