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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Gemfile.lock +1 -1
- data/bin/rest-ftp-daemon +1 -1
- data/config.ru +1 -1
- data/lib/rest-ftp-daemon.rb +9 -5
- data/lib/rest-ftp-daemon/api/dashboard.rb +1 -1
- data/lib/rest-ftp-daemon/api/debug.rb +6 -19
- data/lib/rest-ftp-daemon/api/jobs.rb +11 -11
- data/lib/rest-ftp-daemon/api/root.rb +8 -5
- data/lib/rest-ftp-daemon/api/status.rb +1 -1
- data/lib/rest-ftp-daemon/constants.rb +7 -3
- data/lib/rest-ftp-daemon/exceptions.rb +1 -0
- data/lib/rest-ftp-daemon/job.rb +40 -39
- data/lib/rest-ftp-daemon/job_queue.rb +10 -17
- data/lib/rest-ftp-daemon/logger.rb +58 -17
- data/lib/rest-ftp-daemon/logger_helper.rb +26 -0
- data/lib/rest-ftp-daemon/logger_pool.rb +13 -13
- data/lib/rest-ftp-daemon/notification.rb +14 -14
- data/lib/rest-ftp-daemon/settings.rb +3 -3
- data/lib/rest-ftp-daemon/worker.rb +58 -0
- data/lib/rest-ftp-daemon/worker_conchita.rb +49 -0
- data/lib/rest-ftp-daemon/worker_job.rb +64 -0
- data/lib/rest-ftp-daemon/worker_pool.rb +48 -104
- data/rest-ftp-daemon.yml.sample +1 -4
- metadata +5 -3
- data/lib/rest-ftp-daemon/api/routes.rb +0 -16
- data/lib/rest-ftp-daemon/conchita.rb +0 -65
@@ -1,9 +1,12 @@
|
|
1
1
|
module RestFtpDaemon
|
2
|
-
class JobQueue
|
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
|
-
|
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
|
-
|
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
|
-
|
188
|
-
|
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
|
-
#
|
8
|
-
|
9
|
-
|
8
|
+
# Build prefixes depending on this context
|
9
|
+
prefix1 = build_prefix(context)
|
10
|
+
prefix2 = build_prefix() + ' | '
|
10
11
|
|
11
|
-
#
|
12
|
-
|
13
|
-
|
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
|
-
#
|
18
|
-
|
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
|
-
#
|
21
|
-
context[:lines]
|
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,
|
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,
|
27
|
-
#
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
24
|
+
log_info "skipping (missing url): #{params.inspect}"
|
22
25
|
return
|
23
26
|
elsif params[:event].nil?
|
24
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
69
|
+
log_info "received [#{response.code}] #{human_size} (#{response_lines.size} lines)", response_lines
|
67
70
|
else
|
68
|
-
|
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
|
78
|
-
|
79
|
-
|
80
|
-
@
|
81
|
-
|
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.
|
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(:
|
41
|
+
ENV['NEW_RELIC_LICENSE_KEY'] = Settings.at(:debug, :newrelic)
|
42
42
|
|
43
43
|
# Appname
|
44
|
-
ENV['NEW_RELIC_APP_NAME'] =
|
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
|