stackify-api-ruby 1.0.15 → 1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cd19881ee793a2b23b9137218fd184427d9a9502efd1b81ba2064f23626d1228
4
- data.tar.gz: 9c3757c006c8f013afb097cf8a0cb7e016f1b1314375dfacfe3bd2d87403a9c8
3
+ metadata.gz: 000465ac09f10d108d71cf5b9441315f01aa42d81b6ff90ce31c053724688536
4
+ data.tar.gz: 6b151709a6b4e297b3e1655b1de68297a43d65af1866abf034d64f16df04b438
5
5
  SHA512:
6
- metadata.gz: bee61b9d0b9b18a3ad4db32dd6b7680b3e6c7db50979cdf1a8f9f222c97465c295474f05c757c734fa26b2082a4e5cbdf541083173c295d0bb48071bc64f04db
7
- data.tar.gz: 87ac37dd7b3ec51fc91f79eadad7316c587e25ee2e55844a974437cd4ad731c9ece070178ff51366f6132696cc93bd21c2cdb5eaa8c92df46d8b586ef265415d
6
+ metadata.gz: e42e021203b70b6d43535ea43163b42af23ba4528ef0e7a2a377223541ca3122d0773034e66ad0b6e58b77d9b43ea355456efbcad97b9d2432cf134ab3fcf6c3
7
+ data.tar.gz: ff51578f76cea1ceed8d89e78570237e2ef0b67266a1f9f92c0f866edd727925ae0a5c0c98c3af662643980a062120e3baef8ffb3cad08cce3310cf91c9015d0
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- stackify-api-ruby (1.0.15)
4
+ stackify-api-ruby (1.1.0)
5
5
  faraday (~> 0.8)
6
6
 
7
7
  GEM
@@ -0,0 +1,102 @@
1
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
2
+ # source: stackify-agent.proto
3
+
4
+ require 'google/protobuf'
5
+
6
+ Google::Protobuf::DescriptorPool.generated_pool.build do
7
+ add_message "stackify.LogGroup" do
8
+ optional :environment, :string, 1
9
+ optional :server_name, :string, 2
10
+ optional :application_name, :string, 3
11
+ optional :application_location, :string, 4
12
+ optional :logger, :string, 5
13
+ optional :platform, :string, 6
14
+ repeated :logs, :message, 7, "stackify.LogGroup.Log"
15
+ optional :container, :message, 8, "stackify.LogGroup.Container"
16
+ optional :kubernetes, :message, 9, "stackify.LogGroup.Kubernetes"
17
+ end
18
+ add_message "stackify.LogGroup.Container" do
19
+ optional :image_id, :string, 1
20
+ optional :image_repository, :string, 2
21
+ optional :image_tag, :string, 3
22
+ optional :container_id, :string, 4
23
+ optional :container_name, :string, 5
24
+ end
25
+ add_message "stackify.LogGroup.Kubernetes" do
26
+ optional :pod_name, :string, 1
27
+ optional :pod_namespace, :string, 2
28
+ optional :cluster_name, :string, 3
29
+ end
30
+ add_message "stackify.LogGroup.Log" do
31
+ optional :message, :string, 1
32
+ optional :data, :string, 2
33
+ optional :thread_name, :string, 3
34
+ optional :date_millis, :int64, 4
35
+ optional :level, :string, 5
36
+ optional :transaction_id, :string, 6
37
+ optional :source_method, :string, 7
38
+ optional :source_line, :int32, 8
39
+ optional :id, :string, 9
40
+ repeated :tags, :string, 10
41
+ optional :error, :message, 11, "stackify.LogGroup.Log.Error"
42
+ end
43
+ add_message "stackify.LogGroup.Log.Error" do
44
+ optional :environment_detail, :message, 1, "stackify.LogGroup.Log.Error.EnvironmentDetail"
45
+ optional :date_millis, :int64, 2
46
+ optional :error_item, :message, 3, "stackify.LogGroup.Log.Error.ErrorItem"
47
+ optional :web_request_detail, :message, 4, "stackify.LogGroup.Log.Error.WebRequestDetail"
48
+ map :server_variables, :string, :string, 5
49
+ optional :customer_name, :string, 6
50
+ optional :username, :string, 7
51
+ end
52
+ add_message "stackify.LogGroup.Log.Error.EnvironmentDetail" do
53
+ optional :device_name, :string, 1
54
+ optional :application_name, :string, 2
55
+ optional :application_location, :string, 3
56
+ optional :configured_application_name, :string, 4
57
+ optional :configured_environment_name, :string, 5
58
+ end
59
+ add_message "stackify.LogGroup.Log.Error.ErrorItem" do
60
+ optional :message, :string, 1
61
+ optional :error_type, :string, 2
62
+ optional :error_type_code, :string, 3
63
+ map :data, :string, :string, 4
64
+ optional :source_method, :string, 5
65
+ repeated :stacktrace, :message, 6, "stackify.LogGroup.Log.Error.ErrorItem.TraceFrame"
66
+ optional :inner_error, :message, 7, "stackify.LogGroup.Log.Error.ErrorItem"
67
+ end
68
+ add_message "stackify.LogGroup.Log.Error.ErrorItem.TraceFrame" do
69
+ optional :code_filename, :string, 1
70
+ optional :line_number, :int32, 2
71
+ optional :method, :string, 3
72
+ end
73
+ add_message "stackify.LogGroup.Log.Error.WebRequestDetail" do
74
+ optional :user_ip_address, :string, 1
75
+ optional :http_method, :string, 2
76
+ optional :request_protocol, :string, 3
77
+ optional :request_url, :string, 4
78
+ optional :request_url_root, :string, 5
79
+ optional :referral_url, :string, 6
80
+ map :headers, :string, :string, 7
81
+ map :cookies, :string, :string, 8
82
+ map :querystring, :string, :string, 9
83
+ map :post_data, :string, :string, 10
84
+ map :session_data, :string, :string, 11
85
+ optional :post_data_raw, :string, 12
86
+ optional :mvc_action, :string, 13
87
+ optional :mvc_controller, :string, 14
88
+ optional :mvc_area, :string, 15
89
+ end
90
+ end
91
+
92
+ module Stackify
93
+ LogGroup = Google::Protobuf::DescriptorPool.generated_pool.lookup("stackify.LogGroup").msgclass
94
+ LogGroup::Container = Google::Protobuf::DescriptorPool.generated_pool.lookup("stackify.LogGroup.Container").msgclass
95
+ LogGroup::Kubernetes = Google::Protobuf::DescriptorPool.generated_pool.lookup("stackify.LogGroup.Kubernetes").msgclass
96
+ LogGroup::Log = Google::Protobuf::DescriptorPool.generated_pool.lookup("stackify.LogGroup.Log").msgclass
97
+ LogGroup::Log::Error = Google::Protobuf::DescriptorPool.generated_pool.lookup("stackify.LogGroup.Log.Error").msgclass
98
+ LogGroup::Log::Error::EnvironmentDetail = Google::Protobuf::DescriptorPool.generated_pool.lookup("stackify.LogGroup.Log.Error.EnvironmentDetail").msgclass
99
+ LogGroup::Log::Error::ErrorItem = Google::Protobuf::DescriptorPool.generated_pool.lookup("stackify.LogGroup.Log.Error.ErrorItem").msgclass
100
+ LogGroup::Log::Error::ErrorItem::TraceFrame = Google::Protobuf::DescriptorPool.generated_pool.lookup("stackify.LogGroup.Log.Error.ErrorItem.TraceFrame").msgclass
101
+ LogGroup::Log::Error::WebRequestDetail = Google::Protobuf::DescriptorPool.generated_pool.lookup("stackify.LogGroup.Log.Error.WebRequestDetail").msgclass
102
+ end
@@ -2,14 +2,19 @@ require 'stackify/version'
2
2
  require 'stackify/utils/methods'
3
3
  require 'core_ext/core_ext' unless defined? Rails
4
4
 
5
+ require 'google/protobuf'
6
+ require 'proto/stackify-agent.rb'
7
+
5
8
  module Stackify
6
9
 
7
10
  INTERNAL_LOG_PREFIX = '[Stackify]'.freeze
8
11
  STATUSES = { working: 'working', terminating: 'terminating', terminated: 'terminated'}
9
12
  MODES = { logging: :logging, metrics: :metrics, both: :both }
13
+ TRANSPORT = [DEFAULT = 'default', UNIX_SOCKET = 'agent_socket']
10
14
 
11
15
  autoload :Backtrace, 'stackify/utils/backtrace'
12
16
  autoload :MsgObject, 'stackify/utils/msg_object'
17
+ autoload :ProtobufLogObject, 'stackify/utils/protobuf_log_object'
13
18
  autoload :Configuration, 'stackify/utils/configuration'
14
19
  autoload :HttpClient, 'stackify/http_client'
15
20
  autoload :Authorizable, 'stackify/authorization/authorizable'
@@ -24,7 +29,10 @@ module Stackify
24
29
  autoload :AddMsgWorker, 'stackify/workers/add_msg_worker'
25
30
  autoload :MsgsQueue, 'stackify/msgs_queue'
26
31
  autoload :LoggerClient, 'stackify/logger_client'
32
+ autoload :UnixSocketClient, 'stackify/unix_socket_client'
33
+ autoload :TransportSelector, 'stackify/transport_selector'
27
34
  autoload :LogsSender, 'stackify/logs_sender'
35
+ autoload :UnixSocketSender, 'stackify/unix_socket_sender'
28
36
  autoload :LoggerProxy, 'stackify/logger_proxy'
29
37
  autoload :StackifiedError, 'stackify/error'
30
38
  autoload :StringException, 'stackify/error'
@@ -44,6 +52,7 @@ module Stackify
44
52
  def setup
45
53
  @workers = []
46
54
  yield(configuration) if block_given?
55
+ configuration.validate_transport_type
47
56
  if configuration.is_valid?
48
57
  @status = STATUSES[:working]
49
58
  else
@@ -53,7 +62,6 @@ module Stackify
53
62
  end
54
63
  raise msg
55
64
  end
56
-
57
65
  end
58
66
 
59
67
  def msgs_queue
@@ -64,8 +72,16 @@ module Stackify
64
72
  @logger_client ||= Stackify::LoggerClient.new
65
73
  end
66
74
 
67
- def logs_sender
68
- @logs_sender ||= Stackify::LogsSender.new
75
+ def unix_socket_client
76
+ @unix_socket_client ||= Stackify::UnixSocketClient.new
77
+ end
78
+
79
+ def get_transport
80
+ @logger_client.get_transport
81
+ end
82
+
83
+ def send_unix_socket
84
+ @unix_socket ||= Stackify::UnixSocketSender.new
69
85
  end
70
86
 
71
87
  def logger
@@ -105,24 +121,43 @@ module Stackify
105
121
 
106
122
  def run
107
123
  Stackify::Utils.is_api_enabled
124
+ Stackify.internal_log :debug, "Stackify.run = #{Stackify.configuration.transport}"
108
125
  if Stackify.configuration.api_enabled
109
126
  if Stackify.is_valid?
110
- at_exit { make_remained_job }
111
- t1 = Thread.new { Stackify.authorize }
112
- case Stackify.configuration.mode
113
- when MODES[:both]
114
- t2 = start_logging
115
- t3 = start_metrics
116
- when MODES[:logging]
117
- t2 = start_logging
118
- when MODES[:metrics]
119
- t3 = start_metrics
127
+ # check transport types
128
+ case Stackify.configuration.transport
129
+ when Stackify::DEFAULT
130
+ if Stackify.is_valid?
131
+ at_exit { make_remained_job }
132
+ t1 = Thread.new { Stackify.authorize }
133
+ case Stackify.configuration.mode
134
+ when MODES[:both]
135
+ t2 = start_logging
136
+ t3 = start_metrics
137
+ when MODES[:logging]
138
+ t2 = start_logging
139
+ when MODES[:metrics]
140
+ t3 = start_metrics
141
+ end
142
+
143
+ t1.join
144
+ t3.join if t3
145
+ else
146
+ Stackify.log_internal_error "Stackify is not properly configured! Errors: #{Stackify.configuration.errors}"
147
+ end
148
+ when Stackify::UNIX_SOCKET
149
+ case Stackify.configuration.mode
150
+ when MODES[:logging]
151
+ start_logging
152
+ when MODES[:both]
153
+ start_logging
154
+ start_metrics
155
+ when MODES[:metrics]
156
+ start_metrics
157
+ end
158
+ else
159
+ Stackify.log_internal_error "Stackify is not properly configured! Errors: #{Stackify.configuration.errors}"
120
160
  end
121
-
122
- t1.join
123
- t3.join if t3
124
- else
125
- Stackify.log_internal_error "Stackify is not properly configured! Errors: #{Stackify.configuration.errors}"
126
161
  end
127
162
  end
128
163
  end
@@ -3,35 +3,21 @@ module Stackify
3
3
 
4
4
  def initialize
5
5
  @@errors_governor = Stackify::ErrorsGovernor.new
6
+ @@transport = Stackify::TransportSelector.new(Stackify.configuration.transport).transport
6
7
  end
7
8
 
8
9
  def log level, msg, call_trace
9
- Stackify::Utils.do_only_if_authorized_and_mode_is_on Stackify::MODES[:logging] do
10
- if acceptable?(level, msg) && Stackify.working?
11
- worker = Stackify::AddMsgWorker.new
12
- task = log_message_task level, msg, call_trace
13
- worker.async_perform ScheduleDelay.new, task
14
- end
15
- end
10
+ task = log_message_task level, msg, call_trace
11
+ @@transport.log level, msg, call_trace, task
16
12
  end
17
13
 
18
14
  def log_exception level= :error, ex
19
- if ex.is_a?(Stackify::StackifiedError)
20
- Stackify::Utils.do_only_if_authorized_and_mode_is_on Stackify::MODES[:logging] do
21
- if acceptable?(level, ex.message) && Stackify.working?
22
- if @@errors_governor.can_send? ex
23
- worker = Stackify::AddMsgWorker.new
24
- task = log_exception_task level, ex
25
- worker.async_perform ScheduleDelay.new, task
26
- else
27
- Stackify.internal_log :warn,
28
- "LoggerClient: logging of exception with message \"#{ex.message}\" is skipped - flood_limit is exceeded"
29
- end
30
- end
31
- end
32
- else
33
- Stackify.log_internal_error 'LoggerClient: log_exception should get StackifiedError object'
34
- end
15
+ task = log_exception_task level, ex
16
+ @@transport.log_exception level, ex, task
17
+ end
18
+
19
+ def get_transport
20
+ @@transport
35
21
  end
36
22
 
37
23
  private
@@ -52,39 +38,11 @@ module Stackify
52
38
  end
53
39
 
54
40
  def log_message_task level, msg, call_trace, trans_id=nil, log_uuid=nil
55
- Stackify::ScheduleTask.new ({limit: 1}) do
56
- if %w(error fatal).include?(level)
57
- ex = if ruby_exception?(msg) && msg.class != Class
58
- msg.set_backtrace(call_trace)
59
- msg
60
- else
61
- e = StringException.new(msg)
62
- e.set_backtrace(call_trace)
63
- e
64
- end
65
- ex = StackifiedError.new(ex, binding())
66
- Stackify.msgs_queue << Stackify::MsgObject.new(level, ex.message, caller[0], trans_id, log_uuid, ex).to_h
67
- else
68
- Stackify.msgs_queue << Stackify::MsgObject.new(level, msg, caller[0], trans_id, log_uuid).to_h
69
- end
70
- end
41
+ @@transport.log_message_task level, msg, call_trace, trans_id, log_uuid
71
42
  end
72
43
 
73
44
  def log_exception_task level, ex, trans_id=nil, log_uuid=nil
74
- Stackify::ScheduleTask.new ({limit: 1}) do
75
- Stackify.msgs_queue << Stackify::MsgObject.new(level, ex.message, caller[0], trans_id, log_uuid, ex).to_h
76
- end
77
- end
78
-
79
- def ruby_exception? klass
80
- klass = klass.class == Class ? klass : klass.class
81
- klasses = [klass]
82
- while klass != Object do
83
- klasses << klass.superclass
84
- klass = klass.superclass
85
- end
86
- klasses.include?(Exception)
45
+ @@transport.log_exception_task level, ex, trans_id, log_uuid
87
46
  end
88
47
  end
89
-
90
48
  end
@@ -3,13 +3,102 @@ module Stackify
3
3
 
4
4
  LOGS_URI = URI("#{Stackify.configuration.base_api_url}/Log/Save")
5
5
 
6
+ def initialize
7
+ @@errors_governor = Stackify::ErrorsGovernor.new
8
+ end
9
+
10
+ def log level, msg, call_trace, task
11
+ Stackify::Utils.do_only_if_authorized_and_mode_is_on Stackify::MODES[:logging] do
12
+ if acceptable?(level, msg) && Stackify.working?
13
+ worker = Stackify::AddMsgWorker.new
14
+ worker.async_perform ScheduleDelay.new, task
15
+ end
16
+ end
17
+ end
18
+
19
+ def log_exception level= :error, ex, task
20
+ if ex.is_a?(Stackify::StackifiedError)
21
+ Stackify::Utils.do_only_if_authorized_and_mode_is_on Stackify::MODES[:logging] do
22
+ if acceptable?(level, ex.message) && Stackify.working?
23
+ if @@errors_governor.can_send? ex
24
+ worker = Stackify::AddMsgWorker.new
25
+ worker.async_perform ScheduleDelay.new, task
26
+ else
27
+ Stackify.internal_log :warn,
28
+ "LoggerClient: logging of exception with message \"#{ex.message}\" is skipped - flood_limit is exceeded"
29
+ end
30
+ end
31
+ end
32
+ else
33
+ Stackify.log_internal_error 'LoggerClient: log_exception should get StackifiedError object'
34
+ end
35
+ end
36
+
37
+ def has_error msg
38
+ !msg['Ex'].nil?
39
+ end
40
+
41
+ def get_epoch msg
42
+ msg['EpochMs']
43
+ end
44
+
45
+ def log_message_task level, msg, call_trace, trans_id=nil, log_uuid=nil
46
+ Stackify::ScheduleTask.new ({limit: 1}) do
47
+ if %w(error fatal).include?(level)
48
+ ex = if ruby_exception?(msg) && msg.class != Class
49
+ msg.set_backtrace(call_trace)
50
+ msg
51
+ else
52
+ e = StringException.new(msg)
53
+ e.set_backtrace(call_trace)
54
+ e
55
+ end
56
+ ex = StackifiedError.new(ex, binding())
57
+ Stackify.msgs_queue << Stackify::MsgObject.new(level, ex.message, caller[0], trans_id, log_uuid, ex).to_h
58
+ else
59
+ Stackify.msgs_queue << Stackify::MsgObject.new(level, msg, caller[0], trans_id, log_uuid).to_h
60
+ end
61
+ end
62
+ end
63
+
64
+ def log_exception_task level, ex, trans_id=nil, log_uuid=nil
65
+ Stackify::ScheduleTask.new ({limit: 1}) do
66
+ Stackify.msgs_queue << Stackify::MsgObject.new(level, ex.message, caller[0], trans_id, log_uuid, ex).to_h
67
+ end
68
+ end
69
+
6
70
  def send_logs msgs, attempts = 3
7
71
  worker = Stackify::LogsSenderWorker.new
8
72
  task = send_logs_task attempts, msgs
9
73
  worker.async_perform ScheduleDelay.new, task
10
74
  end
11
75
 
12
- private
76
+ private
77
+
78
+ def acceptable? level, msg
79
+ Stackify.is_valid? && is_correct_log_level?(level) &&
80
+ is_not_internal_log_message?(msg)
81
+ end
82
+
83
+ def is_not_internal_log_message? msg
84
+ msg.try(:index, ::Stackify::INTERNAL_LOG_PREFIX).nil?
85
+ end
86
+
87
+ def is_correct_log_level? level
88
+ config_level = Logger.const_get Stackify.configuration.log_level.to_s.upcase
89
+ current_level = Logger.const_get level.to_s.upcase
90
+ current_level >= config_level
91
+ end
92
+
93
+ def ruby_exception? klass
94
+ klass = klass.class == Class ? klass : klass.class
95
+ klasses = [klass]
96
+ while klass != Object do
97
+ klasses << klass.superclass
98
+ klass = klass.superclass
99
+ end
100
+ klasses.include?(Exception)
101
+ end
13
102
 
14
103
  def properties
15
104
  {
@@ -34,7 +34,7 @@ module Stackify
34
34
  Stackify.internal_log :info, '[MsgsQueue] All remained logs are going to be sent'
35
35
  Stackify.shutdown_all
36
36
  if self.length > 0
37
- Stackify.logs_sender.send_logs(pop_all)
37
+ Stackify.get_transport.send_logs(pop_all)
38
38
  Stackify.status = Stackify::STATUSES[:terminated]
39
39
  end
40
40
  end
@@ -47,7 +47,12 @@ module Stackify
47
47
  Stackify.internal_log :debug, "[MsgsQueue] add_msg() Newly created worker <#{@worker.name}>"
48
48
  end
49
49
  self.synchronize do
50
- Stackify::Utils.do_only_if_authorized_and_mode_is_on Stackify::MODES[:logging] do
50
+ case Stackify.configuration.transport
51
+ when Stackify::DEFAULT
52
+ Stackify::Utils.do_only_if_authorized_and_mode_is_on Stackify::MODES[:logging] do
53
+ old_push(msg)
54
+ end
55
+ when Stackify::UNIX_SOCKET
51
56
  old_push(msg)
52
57
  end
53
58
  end
@@ -109,13 +114,13 @@ module Stackify
109
114
  if length > 0
110
115
  msg = pop
111
116
  chunk << msg
112
- chunk_weight += (msg['Ex'].nil? ? LOG_SIZE : ERROR_SIZE)
113
- break if msg['EpochMs'] > started_at || CHUNK_MIN_WEIGHT > 50
117
+ chunk_weight += Stackify.get_transport.has_error(msg) ? ERROR_SIZE : LOG_SIZE
118
+ break if Stackify.get_transport.get_epoch(msg) > started_at || CHUNK_MIN_WEIGHT > 50
114
119
  else
115
120
  break
116
121
  end
117
122
  end
118
- Stackify.logs_sender.send_logs(chunk) if chunk.length > 0
123
+ Stackify.get_transport.send_logs(chunk) if chunk.length > 0
119
124
  chunk_weight
120
125
  end
121
126
  end
@@ -0,0 +1,17 @@
1
+ module Stackify
2
+ class TransportSelector
3
+
4
+ attr_reader :transport
5
+
6
+ def initialize type
7
+ case type
8
+ when Stackify::DEFAULT
9
+ Stackify::Utils.do_only_if_authorized_and_mode_is_on Stackify::MODES[:logging] do
10
+ @transport = Stackify::LogsSender.new
11
+ end
12
+ when Stackify::UNIX_SOCKET
13
+ @transport = Stackify::UnixSocketClient.new
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,97 @@
1
+ module Stackify
2
+ class UnixSocketClient
3
+
4
+ def initialize
5
+ Stackify.internal_log :info, '[UnixSocketClient]: initialize()'
6
+ @@errors_governor = Stackify::ErrorsGovernor.new
7
+ @@sender = Stackify::UnixSocketSender.new
8
+ end
9
+
10
+ def log level, msg, call_trace, task
11
+ if acceptable?(level, msg) && Stackify.working?
12
+ worker = Stackify::AddMsgWorker.new
13
+ worker.async_perform ScheduleDelay.new, task
14
+ end
15
+ end
16
+
17
+ def log_exception level= :error, ex, task
18
+ if ex.is_a?(Stackify::StackifiedError)
19
+ if acceptable?(level, ex.message) && Stackify.working?
20
+ if @@errors_governor.can_send? ex
21
+ worker = Stackify::AddMsgWorker.new
22
+ worker.async_perform ScheduleDelay.new, task
23
+ else
24
+ Stackify.internal_log :warn,
25
+ "UnixSocketClient: logging of exception with message \"#{ex.message}\" is skipped - flood_limit is exceeded"
26
+ end
27
+ end
28
+ else
29
+ Stackify.log_internal_error 'UnixSocketClient: log_exception should get StackifiedError object'
30
+ end
31
+ end
32
+
33
+ def has_error msg
34
+ !msg.error.nil?
35
+ end
36
+
37
+ def get_epoch msg
38
+ msg.date_millis
39
+ end
40
+
41
+ def send_logs msgs, attempts = 3
42
+ @@sender.send_logs msgs, attempts
43
+ end
44
+
45
+ def log_message_task level, msg, call_trace, trans_id=nil, log_uuid=nil
46
+ Stackify::ScheduleTask.new ({limit: 1}) do
47
+ if %w(error fatal).include?(level)
48
+ ex = if ruby_exception?(msg) && msg.class != Class
49
+ msg.set_backtrace(call_trace)
50
+ msg
51
+ else
52
+ e = StringException.new(msg)
53
+ e.set_backtrace(call_trace)
54
+ e
55
+ end
56
+ ex = StackifiedError.new(ex, binding())
57
+ Stackify.msgs_queue << Stackify::ProtobufLogObject.new(level, ex.message, caller[0], trans_id, log_uuid, ex).to_obj
58
+ else
59
+ Stackify.msgs_queue << Stackify::ProtobufLogObject.new(level, msg, caller[0], trans_id, log_uuid).to_obj
60
+ end
61
+ end
62
+ end
63
+
64
+ def log_exception_task level, ex, trans_id=nil, log_uuid=nil
65
+ Stackify::ScheduleTask.new ({limit: 1}) do
66
+ Stackify.msgs_queue << Stackify::ProtobufLogObject.new(level, ex.message, caller[0], trans_id, log_uuid, ex).to_obj
67
+ end
68
+ end
69
+
70
+ private
71
+
72
+ def acceptable? level, msg
73
+ Stackify.is_valid? && is_correct_log_level?(level) &&
74
+ is_not_internal_log_message?(msg)
75
+ end
76
+
77
+ def is_not_internal_log_message? msg
78
+ msg.try(:index, ::Stackify::INTERNAL_LOG_PREFIX).nil?
79
+ end
80
+
81
+ def is_correct_log_level? level
82
+ config_level = Logger.const_get Stackify.configuration.log_level.to_s.upcase
83
+ current_level = Logger.const_get level.to_s.upcase
84
+ current_level >= config_level
85
+ end
86
+
87
+ def ruby_exception? klass
88
+ klass = klass.class == Class ? klass : klass.class
89
+ klasses = [klass]
90
+ while klass != Object do
91
+ klasses << klass.superclass
92
+ klass = klass.superclass
93
+ end
94
+ klasses.include?(Exception)
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,79 @@
1
+ require 'net_http_unix'
2
+ require 'ostruct'
3
+
4
+ #
5
+ # This class will handle the sending of protobuf message to unix domain socket
6
+ #
7
+ module Stackify
8
+ class UnixSocketSender < Worker
9
+
10
+ # send_logs() Function to put the msg in the Worker
11
+ def send_logs msgs, attempts = 3
12
+ worker = Stackify::LogsSenderWorker.new('UnixSocketSender worker')
13
+ task = send_logs_task attempts, msgs
14
+ worker.async_perform ScheduleDelay.new, task
15
+ end
16
+
17
+ private
18
+
19
+ def properties
20
+ {
21
+ success_condition: lambda { |result| result.try(:status) == 200 },
22
+ limit: 1
23
+ }.dup
24
+ end
25
+
26
+ def send_logs_task attempts = nil, msgs
27
+ properties[:attempts] = attempts if attempts
28
+ Stackify::ScheduleTask.new properties do
29
+ data = create_log_group msgs
30
+ send_request data
31
+ end
32
+ end
33
+
34
+ # create_log_group() This function will create a log group protobuf object
35
+ # @msgs {Object} Protobuf message
36
+ # return {Object} Return an object
37
+ def create_log_group msgs
38
+ # @details {Object} it will return the properties based in Stackify.setup() configuration
39
+ details = Stackify::Utils.get_app_settings
40
+ log_group = Stackify::LogGroup.new
41
+ msgs.each do |msg|
42
+ log_group.logs << msg
43
+ end
44
+ log_group.environment = details['env']
45
+ log_group.server_name = details['server_name']
46
+ log_group.application_name = details['app_name']
47
+ log_group.application_location = details['app_location']
48
+ log_group.logger = 'Ruby logger'
49
+ log_group.platform = 'ruby'
50
+ log_group
51
+ end
52
+
53
+ # send_request() This function will send http request via unix domain socket
54
+ # @msgs {Object} Protobuf message
55
+ # return {Object} Return an object {status, message}
56
+ def send_request log_group
57
+ begin
58
+ # Convert data into binary and send it to unix domain socket
59
+ message = Stackify::LogGroup.encode(log_group)
60
+ client = NetX::HTTPUnix.new('unix://' + Stackify.configuration.unix_socket_path)
61
+ req = Net::HTTP::Post.new(Stackify.configuration.unix_socket_url)
62
+ req.set_content_type('application/x-protobuf')
63
+ req.body = message
64
+ response = client.request(req)
65
+ Stackify.internal_log :debug, "[UnixSocketSender] status_code = #{response.code}"
66
+ if response.code.to_i == 200
67
+ Stackify.internal_log :debug, "[UnixSocketSender]: Successfully send message via unix domain socket."
68
+ return OpenStruct.new({status: 200, msg: 'OK'})
69
+ else
70
+ Stackify.internal_log :debug, "[UnixSocketSender] Sending failed."
71
+ return OpenStruct.new({status: 500, msg: 'Not OK'})
72
+ end
73
+ rescue => exception
74
+ Stackify.log_internal_error "[UnixSocketSender] send_logs() Error: #{exception}"
75
+ return OpenStruct.new({status: 500, msg: exception})
76
+ end
77
+ end
78
+ end
79
+ end
@@ -3,14 +3,16 @@ module Stackify
3
3
  class Configuration
4
4
 
5
5
  attr_accessor :api_key, :app_name, :app_location, :env, :log_level, :logger,
6
- :proxy, :mode, :base_api_url, :api_enabled
6
+ :proxy, :mode, :base_api_url, :api_enabled, :transport, :errors
7
7
 
8
- attr_reader :errors, :send_interval, :flood_limit, :queue_max_size
8
+ attr_reader :send_interval, :flood_limit, :queue_max_size, :unix_socket_path, :unix_socket_url
9
9
 
10
10
  def initialize
11
11
  @base_api_url = 'https://api.stackify.com'
12
12
  @errors = []
13
+ @app_name = ''
13
14
  @api_key = ''
15
+ @transport = 'default'
14
16
  @env = :production
15
17
  @flood_limit = 100
16
18
  @queue_max_size = 10000
@@ -20,14 +22,25 @@ module Stackify
20
22
  @mode = MODES[:both]
21
23
  @logger = Logger.new(STDOUT)
22
24
  @logger.level = Logger::UNKNOWN
25
+ @unix_socket_path = '/usr/local/stackify/stackify.sock'
26
+ @unix_socket_url = '/log'
23
27
  end
24
28
 
25
29
  def is_valid?
26
- @errors = []
27
- validate_mode if validate_config_types
30
+ case Stackify.configuration.transport
31
+ when Stackify::DEFAULT
32
+ validate_default_transport
33
+ when Stackify::UNIX_SOCKET
34
+ validate_unix_domain_socket_transport
35
+ end
28
36
  @errors.empty?
29
37
  end
30
38
 
39
+ def validate_transport_type
40
+ return true if ['agent_socket', 'default'].include? @transport
41
+ @errors << 'Transport should be one of these values: [agent_socket, default]. Should be a String.'
42
+ end
43
+
31
44
  private
32
45
 
33
46
  def validate_config_types
@@ -36,6 +49,27 @@ module Stackify
36
49
  validate_mode_type
37
50
  end
38
51
 
52
+ # Perform validation if transport type is default
53
+ # Required parameters are: env, app_name, api_key, log_level
54
+ def validate_default_transport
55
+ validate_app_name &&
56
+ validate_transport_type &&
57
+ validate_api_key &&
58
+ validate_env &&
59
+ validate_log_level &&
60
+ validate_mode_type
61
+ end
62
+
63
+ # Perform validation if transport type is agent_socket
64
+ # Required parameters are: env, app_name, log_level
65
+ def validate_unix_domain_socket_transport
66
+ validate_env &&
67
+ validate_transport_type &&
68
+ validate_app_name &&
69
+ validate_log_level &&
70
+ validate_mode_type
71
+ end
72
+
39
73
  def validate_mode_type
40
74
  return true if @mode.is_a? Symbol
41
75
  @errors << 'Mode should be a Symbol'
@@ -43,7 +77,17 @@ module Stackify
43
77
 
44
78
  def validate_api_key
45
79
  return true if @api_key.is_a?(String) && !@api_key.empty?
46
- @errors << 'API_KEY should be a String and not empty'
80
+ @errors << 'Api_key should be a String and not empty'
81
+ end
82
+
83
+ def validate_app_name
84
+ return true if @app_name.is_a?(String) && !@app_name.empty?
85
+ @errors << 'App_name should be a String and not empty'
86
+ end
87
+
88
+ def validate_env
89
+ return true if @env.is_a?(Symbol) && !@env.empty?
90
+ @errors << 'Env should be a Symbol and not empty'
47
91
  end
48
92
 
49
93
  def validate_log_level
@@ -33,4 +33,13 @@ module Stackify::Utils
33
33
  found = exclude.select{|e| e =~ /#{cmd}/i}
34
34
  Stackify.configuration.api_enabled = false if found.count > 0
35
35
  end
36
+
37
+ def self.get_app_settings
38
+ @env = {
39
+ 'env' => Stackify.configuration.env,
40
+ 'app_name' => Stackify.configuration.app_name,
41
+ 'server_name' => Socket.gethostname,
42
+ 'app_location' => Stackify.configuration.app_location || Dir.pwd
43
+ }
44
+ end
36
45
  end
@@ -0,0 +1,85 @@
1
+ module Stackify
2
+ class ProtobufLogObject
3
+ def initialize level, msg, caller_str, trans_id=nil, log_uuid=nil, ex=nil
4
+ @level, @msg, @caller_str, @ex = level, msg, caller_str, ex, @trans_id = trans_id,
5
+ @log_uuid = log_uuid
6
+ end
7
+
8
+ # Create a LogMsgGroup Protobuf Object
9
+ def to_obj
10
+ begin
11
+ log = Stackify::LogGroup::Log.new
12
+ log.message = @msg
13
+ log.thread_name = Thread.current.object_id.to_s
14
+ log.date_millis = (Time.now.to_f * 1000).to_i
15
+ log.level = @level.to_s.upcase!
16
+ log.source_method = Stackify::Backtrace.method_name(@caller_str).to_s
17
+ log.source_line = Stackify::Backtrace.line_number(@caller_str).to_i
18
+ log.transaction_id = @trans_id unless @trans_id.nil?
19
+ log.id = @log_uuid unless @log_uuid.nil?
20
+ if @ex.try(:to_h)
21
+ ex = @ex.try(:to_h)
22
+ log_error = Stackify::LogGroup::Log::Error.new
23
+ log_error.date_millis = ex['OccurredEpochMillis'].to_i
24
+ if ex['EnvironmentDetail']
25
+ env = ex['EnvironmentDetail']
26
+ env_detail = Stackify::LogGroup::Log::Error::EnvironmentDetail.new
27
+ env_detail.device_name = env['DeviceName'].to_s
28
+ env_detail.application_name = env['AppName'].to_s
29
+ env_detail.application_location = env['AppLocation'].to_s
30
+ env_detail.configured_application_name = env['ConfiguredAppName'].to_s
31
+ env_detail.configured_environment_name = env['ConfiguredEnvironmentName'].to_s
32
+ log_error.environment_detail = env_detail
33
+ end
34
+ if ex['Error']
35
+ err = ex['Error']
36
+ error_item = Stackify::LogGroup::Log::Error::ErrorItem.new
37
+ error_item.message = err['Message'].to_s
38
+ error_item.error_type = err['ErrorType'].to_s
39
+ error_item.error_type_code = err['ErrorTypeCode'].to_s
40
+ if err['Data']
41
+ map_data = Google::Protobuf::Map.new(:string, :string)
42
+ err['Data'].each { |key, value| map_data["#{key}"] = value }
43
+ error_item.data = map_data
44
+ end
45
+ error_item.inner_error = err['InnerError']
46
+ error_item.source_method = err['SourceMethod'].to_s
47
+ if err['StackTrace']
48
+ stack = err['StackTrace']
49
+ stack.each do |stk|
50
+ trace_frame = Stackify::LogGroup::Log::Error::ErrorItem::TraceFrame.new
51
+ trace_frame.code_filename = stk['CodeFileName'].to_s
52
+ trace_frame.line_number = stk['LineNum'].to_i
53
+ trace_frame.method = stk['Method'].to_s
54
+ error_item.stacktrace.push(trace_frame)
55
+ end
56
+ end
57
+ log_error.error_item = error_item
58
+ end
59
+ if ex['WebRequestDetail']
60
+ req_details = ex['WebRequestDetail']
61
+ web_request = Stackify::LogGroup::Log::Error::WebRequestDetail.new
62
+ web_request.user_ip_address = req_details['UserIPAddress'].to_s
63
+ web_request.http_method = req_details['HttpMethod'].to_s
64
+ web_request.request_url = req_details['RequestUrl'].to_s
65
+ web_request.request_url_root = req_details['RequestUrlRoot'].to_s
66
+ web_request.referral_url = req_details['ReferralUrl'].to_s
67
+ web_request.post_data_raw = req_details['PostDataRaw'].to_s
68
+ log_error.web_request_detail = web_request
69
+ end
70
+ if !ex['ServerVariables'].empty?
71
+ map_server_vars = Google::Protobuf::Map.new(:string, :string)
72
+ ex['ServerVariables'].each { |key, value| map_server_vars["#{key.to_s}"] = value.to_s }
73
+ log_error.server_variables = map_server_vars
74
+ end
75
+
76
+ log.error = log_error
77
+ end
78
+ log
79
+ rescue => exception
80
+ Stackify.internal_log :info, "[ProtobufLogObject] Error: "
81
+ Stackify.internal_log :info, exception
82
+ end
83
+ end
84
+ end
85
+ end
@@ -1,3 +1,3 @@
1
1
  module Stackify
2
- VERSION = '1.0.15'
2
+ VERSION = '1.1.0'
3
3
  end
@@ -3,6 +3,8 @@ module Stackify
3
3
 
4
4
  def initialize name = 'LogsSender worker'
5
5
  super
6
+ @name = name
7
+ @name += " ##{self.id}"
6
8
  @type = :logs_send
7
9
  end
8
10
 
@@ -22,4 +22,6 @@ Gem::Specification.new do |spec|
22
22
  spec.add_development_dependency 'bundler', '~> 1.6'
23
23
  spec.add_development_dependency 'rake', '~> 0'
24
24
  spec.add_runtime_dependency 'faraday', '~> 0.8'
25
+ spec.add_runtime_dependency 'google-protobuf', '~> 3.0'
26
+ spec.add_runtime_dependency 'net_http_unix', '~> 0.2'
25
27
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stackify-api-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.15
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stackify
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-09-16 00:00:00.000000000 Z
11
+ date: 2019-10-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -52,6 +52,34 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0.8'
55
+ - !ruby/object:Gem::Dependency
56
+ name: google-protobuf
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: net_http_unix
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.2'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.2'
55
83
  description: Stackify Logs and Metrics API for Ruby
56
84
  email:
57
85
  - support@stackify.com
@@ -71,6 +99,7 @@ files:
71
99
  - lib/core_ext/object.rb
72
100
  - lib/generators/stackify/stackify_generator.rb
73
101
  - lib/generators/stackify/templates/stackify.rb
102
+ - lib/proto/stackify-agent.rb
74
103
  - lib/stackify-api-ruby.rb
75
104
  - lib/stackify/authorization/authorizable.rb
76
105
  - lib/stackify/authorization/authorization_client.rb
@@ -94,10 +123,14 @@ files:
94
123
  - lib/stackify/schedule_delay.rb
95
124
  - lib/stackify/schedule_task.rb
96
125
  - lib/stackify/scheduler.rb
126
+ - lib/stackify/transport_selector.rb
127
+ - lib/stackify/unix_socket_client.rb
128
+ - lib/stackify/unix_socket_sender.rb
97
129
  - lib/stackify/utils/backtrace.rb
98
130
  - lib/stackify/utils/configuration.rb
99
131
  - lib/stackify/utils/methods.rb
100
132
  - lib/stackify/utils/msg_object.rb
133
+ - lib/stackify/utils/protobuf_log_object.rb
101
134
  - lib/stackify/version.rb
102
135
  - lib/stackify/workers/add_msg_worker.rb
103
136
  - lib/stackify/workers/auth_worker.rb