rest-ftp-daemon 0.214.0 → 0.220.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.
@@ -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