rest-ftp-daemon 0.214.0 → 0.220.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,9 +1,12 @@
1
1
  module RestFtpDaemon
2
- class JobQueue < Queue
2
+ class JobQueue
3
+ include LoggerHelper
4
+ attr_reader :logger
3
5
 
4
6
  attr_reader :queue
5
7
  attr_reader :jobs
6
8
 
9
+
7
10
  if Settings.newrelic_enabled?
8
11
  include ::NewRelic::Agent::Instrumentation::ControllerInstrumentation
9
12
  end
@@ -24,7 +27,7 @@ module RestFtpDaemon
24
27
  # Identifiers generator
25
28
  @last_id = 0
26
29
  @prefix = Helpers.identifier JOB_IDENT_LEN
27
- info "queue initialized with prefix: #{@prefix}"
30
+ log_info "JobQueue initialized (prefix: #{@prefix})"
28
31
 
29
32
  # Mutex for counters
30
33
  @counters = {}
@@ -32,9 +35,7 @@ module RestFtpDaemon
32
35
  end
33
36
 
34
37
  def generate_id
35
- # rand(36**8).to_s(36)
36
38
  @mutex.synchronize do
37
- @last_id ||= 0
38
39
  @last_id += 1
39
40
  end
40
41
  prefixed_id @last_id
@@ -100,7 +101,7 @@ module RestFtpDaemon
100
101
  def find_by_id id, prefixed = false
101
102
  # Build a prefixed id if expected
102
103
  id = prefixed_id(id) if prefixed
103
- info "find_by_id (#{id}, #{prefixed}) > #{id}"
104
+ log_info "find_by_id (#{id}, #{prefixed}) > #{id}"
104
105
 
105
106
  # Search in jobs queues
106
107
  @jobs.select { |item| item.id == id }.last
@@ -184,8 +185,8 @@ module RestFtpDaemon
184
185
  next if job.updated_at > before
185
186
 
186
187
  # Ok, we have to clean it up ..
187
- info "expire [#{status.to_s}] [#{maxage}] > [#{job.id}] [#{job.updated_at}]"
188
- info " + unqueued" if @queue.delete(job)
188
+ log_info "expire [#{status.to_s}] [#{maxage}] > [#{job.id}] [#{job.updated_at}]"
189
+ log_info " + unqueued" if @queue.delete(job)
189
190
 
190
191
  true
191
192
  end
@@ -206,21 +207,13 @@ module RestFtpDaemon
206
207
  end
207
208
  end
208
209
 
209
- def info message, lines = []
210
- return if @logger.nil?
211
-
212
- # Forward to logger
213
- @logger.info_with_id message,
214
- id: @id,
215
- lines: lines,
216
- origin: self.class.to_s
217
- end
218
-
219
210
  if Settings.newrelic_enabled?
220
211
  add_transaction_tracer :push, :category => :task
221
212
  add_transaction_tracer :pop, :category => :task
222
213
  add_transaction_tracer :sort_queue!, :category => :task
223
214
  add_transaction_tracer :expire, :category => :task
215
+ add_transaction_tracer :counts_by_status, :category => :task
216
+ add_transaction_tracer :filter_jobs, :category => :task
224
217
  end
225
218
 
226
219
  end
@@ -1,29 +1,70 @@
1
1
  class Logger
2
2
 
3
3
  def info_with_id message, context = {}
4
- # Ensure context is a hash of options
4
+ # Ensure context is a hash of options and init
5
5
  context = {} unless context.is_a? Hash
6
+ context[:level] ||= Logger::DEBUG
6
7
 
7
- # Default context
8
- #add Logger::DEBUG, "info_with_id/context: #{context.inspect} | #{message}"
9
- context[:level] ||= 0
8
+ # Build prefixes depending on this context
9
+ prefix1 = build_prefix(context)
10
+ prefix2 = build_prefix() + ' | '
10
11
 
11
- # Common message header
12
- field_wid = "%#{-LOG_COL_WID.to_i}s" % context[:wid].to_s
13
- field_jid = "%#{-LOG_COL_JID.to_i}s" % context[:jid].to_s
14
- field_id = "%#{-LOG_COL_ID.to_i}s" % context[:id].to_s
15
- prefix = "#{field_wid} \t#{field_jid} \t#{field_id}\t#{' '*(context[:level].to_i+1)}"
12
+ # # Build output lines
13
+ # output = []
14
+ # output << prefix1 + message.strip
16
15
 
17
- # Send main message
18
- add Logger::INFO, prefix + message.to_s
16
+ # # Add optional lines
17
+ # context[:lines].each do |line|
18
+ # # line.strip!
19
+ # # next if line.empty?
20
+ # output << prefix2 + line[0..LOG_TRIM_LINE]
21
+ # end if context[:lines].is_a? Enumerable
19
22
 
20
- # Dump context lines if provided
21
- context[:lines].each do |line|
22
- line.strip!
23
- next if line.empty?
24
- add Logger::INFO, prefix + ' | ' + line[0..LOG_TRIM_LINE]
25
- end if context[:lines].is_a? Enumerable
23
+ # Use "context[:lines]" according to its type
24
+ lines = context[:lines]
26
25
 
26
+ if lines.is_a? Hash
27
+ output = build_from_hash prefix2, lines
28
+
29
+ elsif lines.is_a? Array
30
+ output = build_from_array prefix2, lines
31
+
32
+ else
33
+ output = []
34
+
35
+ end
36
+
37
+
38
+ # Prepend plain message to output
39
+ output.unshift (prefix1 + message.strip)
40
+
41
+ # Send all this to logger
42
+ add context[:level], output
27
43
  end
28
44
 
45
+ def build_prefix context = {}
46
+ LOG_FORMAT_MESSAGE % [
47
+ context[:wid].to_s,
48
+ context[:jid].to_s,
49
+ context[:id].to_s,
50
+ context[:level].to_i+1,
51
+ ]
52
+ end
53
+
54
+ protected
55
+
56
+ def build_from_array prefix, lines
57
+ lines.map do |value|
58
+ text = value.to_s.strip[0..LOG_TRIM_LINE]
59
+ "#{prefix}#{text}"
60
+ end
61
+ end
62
+
63
+ def build_from_hash prefix, lines
64
+ lines.map do |name, value|
65
+ text = value.to_s.strip[0..LOG_TRIM_LINE]
66
+ "#{prefix}#{name}: #{text}"
67
+ end
68
+ end
69
+
29
70
  end
@@ -0,0 +1,26 @@
1
+ module RestFtpDaemon
2
+ module LoggerHelper
3
+
4
+ protected
5
+
6
+ def log_info message, lines = []
7
+ logger.info_with_id message, log_context.merge({
8
+ from: self.class.to_s,
9
+ lines: lines,
10
+ level: Logger::INFO
11
+ })
12
+ end
13
+
14
+ def log_error message, lines = []
15
+ logger.info_with_id message, log_context.merge({
16
+ from: self.class.to_s,
17
+ lines: lines,
18
+ level: Logger::ERROR
19
+ })
20
+ end
21
+
22
+ def log_context
23
+ {}
24
+ end
25
+ end
26
+ end
@@ -1,6 +1,3 @@
1
- require 'singleton'
2
- require 'logger'
3
-
4
1
  module RestFtpDaemon
5
2
  class LoggerPool
6
3
  include Singleton
@@ -19,26 +16,29 @@ module RestFtpDaemon
19
16
  logfile ||= STDERR
20
17
 
21
18
  # Create the logger and return it
22
- logger = Logger.new(logfile, 'daily') #, 10, 1024000)
19
+ logger = Logger.new(logfile, LOG_ROTATION) #, 10, 1024000)
23
20
  logger.progname = pipe.to_s.upcase
24
21
 
25
22
  # And the formatter
26
- logger.formatter = proc do |severity, datetime, progname, message|
27
- # stamp = datetime.strftime("%Y-%m-%d %H:%M:%S")
28
- # field_pipe = "%-#{LOG_PIPE_LEN.to_i}s" % progname
29
- # "#{stamp}\t#{field_pipe}\t#{message}\n"
30
- "%s\t%-#{LOG_PIPE_LEN.to_i}s\t%s\n" % [
31
- datetime.strftime("%Y-%m-%d %H:%M:%S"),
23
+ logger.formatter = proc do |severity, datetime, progname, messages|
24
+ # Build common line prefix
25
+ prefix = LOG_FORMAT_PREFIX % [
26
+ datetime.strftime(LOG_FORMAT_TIME),
27
+ severity,
32
28
  progname,
33
- message,
34
29
  ]
30
+
31
+ # If we have a bunch of lines, prefix them and send them together
32
+ if messages.is_a? Array
33
+ messages.map { |line| prefix + line + LOG_NEWLINE}.join
34
+ else
35
+ prefix + messages.to_s + LOG_NEWLINE
36
+ end
35
37
  end
36
38
 
37
39
  # Finally return this logger
38
40
  logger
39
41
  end
40
42
 
41
- private
42
-
43
43
  end
44
44
  end
@@ -1,5 +1,8 @@
1
1
  module RestFtpDaemon
2
2
  class Notification
3
+ include LoggerHelper
4
+ attr_reader :logger
5
+
3
6
  attr_accessor :job_id
4
7
  attr_accessor :signal
5
8
  attr_accessor :error
@@ -18,10 +21,10 @@ module RestFtpDaemon
18
21
 
19
22
  # Check context
20
23
  if url.nil?
21
- info "skipping (missing url): #{params.inspect}"
24
+ log_info "skipping (missing url): #{params.inspect}"
22
25
  return
23
26
  elsif params[:event].nil?
24
- info "skipping (missing event): #{params.inspect}"
27
+ log_info "skipping (missing event): #{params.inspect}"
25
28
  return
26
29
  end
27
30
 
@@ -34,7 +37,7 @@ module RestFtpDaemon
34
37
  }
35
38
  body[:status] = params[:status] if params[:status].is_a? Enumerable
36
39
  @jid = params[:id]
37
- info "initialized"
40
+ log_info "initialized"
38
41
 
39
42
 
40
43
  # Send message in a thread
@@ -47,7 +50,7 @@ module RestFtpDaemon
47
50
  'User-Agent' => "#{APP_NAME} - #{APP_VER}"
48
51
  }
49
52
  data = body.to_json
50
- info "sending #{data}"
53
+ log_info "sending #{data}"
51
54
 
52
55
 
53
56
  # Prepare HTTP client
@@ -63,9 +66,9 @@ module RestFtpDaemon
63
66
  if response_lines.size > 1
64
67
  human_size = Helpers.format_bytes(response.body.bytesize, "B")
65
68
  #human_size = 0
66
- info "received [#{response.code}] #{human_size} (#{response_lines.size} lines)", response_lines
69
+ log_info "received [#{response.code}] #{human_size} (#{response_lines.size} lines)", response_lines
67
70
  else
68
- info "received [#{response.code}] #{response.body.strip}"
71
+ log_info "received [#{response.code}] #{response.body.strip}"
69
72
  end
70
73
 
71
74
  end
@@ -74,14 +77,11 @@ module RestFtpDaemon
74
77
 
75
78
  protected
76
79
 
77
- def info message, lines = []
78
- return if @logger.nil?
79
-
80
- @logger.info_with_id message,
81
- id: @id,
82
- jid: @jid,
83
- lines: lines,
84
- origin: self.class.to_s
80
+ def log_context
81
+ {
82
+ id: @id,
83
+ jid: @jid
84
+ }
85
85
  end
86
86
 
87
87
  end
@@ -25,7 +25,7 @@ class Settings < Settingslogic
25
25
  end
26
26
 
27
27
  def newrelic_enabled?
28
- Settings.newrelic.is_a?(Hash) && Settings.at(:newrelic, :license)
28
+ Settings.at(:debug, :newrelic)
29
29
  end
30
30
 
31
31
  def init_newrelic
@@ -38,10 +38,10 @@ class Settings < Settingslogic
38
38
  #Settings['newrelic']['enabled'] = true
39
39
 
40
40
  # License
41
- ENV['NEW_RELIC_LICENSE_KEY'] = Settings.at(:newrelic, :license)
41
+ ENV['NEW_RELIC_LICENSE_KEY'] = Settings.at(:debug, :newrelic)
42
42
 
43
43
  # Appname
44
- ENV['NEW_RELIC_APP_NAME'] = Settings.at(:newrelic, :appname) || "#{APP_NICK}-#{Settings.host}-#{APP_ENV}"
44
+ ENV['NEW_RELIC_APP_NAME'] = "#{APP_NICK}-#{Settings.host}-#{APP_ENV}"
45
45
 
46
46
  # Logfile
47
47
  ENV['NEW_RELIC_LOG'] = Settings.at(:logs, :newrelic)
@@ -0,0 +1,58 @@
1
+ module RestFtpDaemon
2
+ class Worker
3
+ include LoggerHelper
4
+ attr_reader :logger
5
+
6
+ if Settings.newrelic_enabled?
7
+ include ::NewRelic::Agent::Instrumentation::ControllerInstrumentation
8
+ end
9
+
10
+ def initialize wid
11
+ # Logger
12
+ @logger = RestFtpDaemon::LoggerPool.instance.get :workers
13
+
14
+ # Worker name
15
+ @wid = wid
16
+
17
+ # Set thread context
18
+ Thread.current.thread_variable_set :wid, wid
19
+ Thread.current.thread_variable_set :started_at, Time.now
20
+ worker_status :starting
21
+ end
22
+
23
+ protected
24
+
25
+ def log_context
26
+ {
27
+ wid: @wid,
28
+ tag_1_worker_object: true
29
+ }
30
+ end
31
+
32
+ def start
33
+ loop do
34
+ begin
35
+ work
36
+ rescue Exception => e
37
+ log_error "WORKER EXCEPTION: #{e.inspect}"
38
+ sleep 1
39
+ end
40
+ end
41
+ end
42
+
43
+ def worker_status status
44
+ Thread.current.thread_variable_set :status, status
45
+ Thread.current.thread_variable_set :updted_at, Time.now
46
+ end
47
+
48
+ def worker_jid jid
49
+ Thread.current.thread_variable_set :jid, jid
50
+ Thread.current.thread_variable_set :updted_at, Time.now
51
+ end
52
+
53
+ if Settings.newrelic_enabled?
54
+ add_transaction_tracer :work, :category => :task
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,49 @@
1
+ module RestFtpDaemon
2
+ class ConchitaWorker < Worker
3
+
4
+ def initialize wid = :conchita
5
+ # Generic worker initialize
6
+ super
7
+
8
+ # Conchita configuration
9
+ @conchita = Settings.conchita
10
+ if !@conchita.is_a? Hash
11
+ return log_info "ConchitaWorker: missing conchita.* configuration"
12
+ elsif @conchita[:timer].nil?
13
+ return log_info "ConchitaWorker: missing conchita.timer value"
14
+ end
15
+
16
+ # Start main loop
17
+ log_info "ConchitaWorker starting", @conchita
18
+ start
19
+ end
20
+
21
+ protected
22
+
23
+ def work
24
+ worker_status :cleaning
25
+
26
+ # Cleanup queues according to configured max-age
27
+ $queue.expire JOB_STATUS_FINISHED, maxage(JOB_STATUS_FINISHED)
28
+ $queue.expire JOB_STATUS_FAILED, maxage(JOB_STATUS_FAILED)
29
+ $queue.expire JOB_STATUS_QUEUED, maxage(JOB_STATUS_QUEUED)
30
+
31
+ # Force garbage collector
32
+ worker_status :collecting
33
+ GC.start if @conchita["garbage_collector"]
34
+
35
+ rescue Exception => e
36
+ log_error "EXCEPTION: #{e.inspect}"
37
+ sleep 1
38
+ else
39
+ # Sleep for a few seconds
40
+ worker_status :sleeping
41
+ sleep @conchita[:timer]
42
+ end
43
+
44
+ def maxage status
45
+ @conchita["clean_#{status.to_s}"] || 0
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,64 @@
1
+ module RestFtpDaemon
2
+ class JobWorker < Worker
3
+
4
+ def initialize wid
5
+ # Generic worker initialize
6
+ super
7
+
8
+ # Timeout config
9
+ @timeout = (Settings.transfer.timeout rescue nil) || DEFAULT_WORKER_TIMEOUT
10
+
11
+ # Start main loop
12
+ log_info "JobWorker starting", ["timeout: #{@timeout}"]
13
+ start
14
+ end
15
+
16
+ protected
17
+
18
+ def work
19
+ # Wait for a job to come into the queue
20
+ worker_status :waiting
21
+ log_info "waiting for a job"
22
+ job = $queue.pop
23
+
24
+ # Prepare the job for processing
25
+ worker_status :working
26
+ worker_jid job.id
27
+ log_info "working with job [#{job.id}]"
28
+ job.wid = Thread.current.thread_variable_get :wid
29
+
30
+ # Processs this job protected by a timeout
31
+ Timeout::timeout(@timeout, RestFtpDaemon::JobTimeout) do
32
+ job.process
33
+ end
34
+
35
+ # Processing done
36
+ worker_status :finished
37
+ log_info "finished with job [#{job.id}]"
38
+ worker_jid nil
39
+ job.wid = nil
40
+
41
+ # Increment total processed jobs count
42
+ $queue.counter_inc :jobs_processed
43
+
44
+ rescue RestFtpDaemon::JobTimeout => ex
45
+ log_error "JOB TIMED OUT", lines: ex.backtrace
46
+ worker_status :timeout
47
+ job.oops_you_stop_now ex unless job.nil?
48
+ sleep 1
49
+
50
+ rescue Exception => ex
51
+ log_error "JOB UNHDNALED EXCEPTION: #{ex.message}", lines: ex.backtrace
52
+ worker_status :crashed
53
+ job.oops_after_crash ex unless job.nil?
54
+ sleep 1
55
+
56
+ else
57
+ # Clean job status
58
+ worker_status :free
59
+ job.wid = nil
60
+
61
+ end
62
+
63
+ end
64
+ end