starling 0.9.3 → 0.9.8

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.
data/CHANGELOG ADDED
@@ -0,0 +1,49 @@
1
+ == 0.9.8
2
+ * add fix to enable relative paths <david@motorator.com>
3
+ * fix tests so they don't run 10 times due to a stupid bug with how the server is forked <seth@mojodna.net>
4
+ * fix some other tests <romeda@gmail.com>
5
+ * fix some error messages <romeda@gmail.com>
6
+ * probably some other things <romeda@gmail.com>
7
+
8
+ == 0.9.7.9
9
+ * properly complain if the spool directory isn't writable <seth@mojodna.net>
10
+ * assume group and user privileges in a working order <seth@mojodna.net>
11
+ * support string user / group names in addition to uid/gids <seth@mojodna.net>
12
+
13
+ == 0.9.7.7
14
+ * added init.d scripts for redhat and ubuntu by Mike Perham <mperham@gmail.com>
15
+ * fixed dependencies for SyslogLogger, eventmachine and memcache-client by Mike Perham <mperham@gmail.com>
16
+ * added starling_top script to monitor starling server by Mike Perham <mperham@gmail.com>
17
+ * fixed starling_top to use 22122 as port by Abdul-Rahman Advany <abdulrahman@advany.com>
18
+
19
+ == 0.9.7.6 2008-06-24
20
+ * removed client code (will be added to different project) by Abdul-Rahman Advany <abdulrahman@advany.com>
21
+
22
+ == 0.9.7.5 2008-05-04
23
+ * added worker class, using starling client you can now run them in background by Abdul-Rahman Advany <abdulrahman@advany.com>
24
+ - handles creation of threadpool
25
+ - handles fetching of messages and passing these to the threads
26
+ - handles pushing of processed messages to starling again if needed
27
+
28
+ == 0.9.7 2008-05-03
29
+ * merged branch of AnotherBritt and Glenn Rempe by Abdul-Rahman Advany <abdulrahman@advany.com>
30
+ * rspeced tests by Abdul-Rahman Advany <abdulrahman@advany.com>
31
+
32
+ == 0.9.6 2008-04-30
33
+ * logging of message lifecycle as :age by AnotherBritt <?>
34
+ * added some extra logging options by AnotherBritt <?>
35
+ * added some test for epoll by AnotherBritt <?>
36
+
37
+ == 0.9.5.4 2008-04-28
38
+
39
+ * Bumped version number by Glenn Rempe <glenn.rempe@gmail.com>
40
+ * Purged all old RubyForge config. Now GitHub friendly by Glenn Rempe <glenn.rempe@gmail.com>
41
+ * New gemspec for GitHub gem auto-build-serve by Glenn Rempe <glenn.rempe@gmail.com>
42
+
43
+ == 0.9.4 2008-01-31 ==
44
+ * Evented code added using EventMachine by Chris Wanstrath <chris@ozmm.org>
45
+
46
+ == 2007-11-02
47
+
48
+ * Initial release
49
+
@@ -1,4 +1,4 @@
1
- Copyright (c) 2007 Blaine Cook, Twitter, Inc.
1
+ Copyright (c) 2007 FIXME full name
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.rdoc ADDED
@@ -0,0 +1,106 @@
1
+ = Name
2
+
3
+ Starling - a light weight server for reliable distributed message passing.
4
+
5
+ = Description
6
+
7
+ Starling is a powerful but simple messaging server that enables reliable
8
+ distributed queuing with an absolutely minimal overhead. It speaks the
9
+ MemCache protocol for maximum cross-platform compatibility. Any language
10
+ that speaks MemCache can take advantage of Starling's queue facilities.
11
+
12
+ = Installation
13
+
14
+ This fork of the Starling source is hosted at GitHub and can be found at:
15
+
16
+ http://github.com/starling/starling/tree/master
17
+
18
+ The original source was to be found at RubyForge but no longer exists there.
19
+
20
+ GitHub serves gems prefixed by a username to differentiate different forks.
21
+ This project can be installed with:
22
+
23
+ # THIS COMMAND ONE TIME ONLY
24
+ gem sources -a http://gems.github.com/
25
+
26
+ # As often as you like
27
+ sudo gem install starling-starling
28
+
29
+ See http://gems.github.com/ if you want more info about GitHub and gems.
30
+
31
+ = Quick Start Usage
32
+
33
+ # View the Starling help and usage message
34
+ starling --help
35
+
36
+ # In a console window start the Starling server. By default
37
+ # it runs verbosely in the foreground, listening on 127.0.0.1:22122
38
+ # and stores its files under /tmp/starling:
39
+ starling
40
+
41
+ # In a new console test the put and get of messages on a queue:
42
+
43
+ irb
44
+ >> require 'starling'
45
+ => true
46
+ >> starling = Starling.new('127.0.0.1:22122')
47
+ => MemCache: 1 servers, 1 buckets, ns: nil, ro: false
48
+ >> starling.set('my_queue', 12345)
49
+ => nil
50
+ >> starling.get('my_queue')
51
+ => 12345
52
+
53
+ # You can do a simple loop over a queue with something like:
54
+ >> loop { puts starling.get('my_queue'); sleep 1 }
55
+ 12345
56
+ nil
57
+ nil
58
+ ...
59
+
60
+ For more information run the following in a new console:
61
+
62
+ 'gem server'
63
+
64
+ This will start a gem server on http://localhost:8808/ which you can view in your
65
+ browser to see the RDocs for the gem. Or generate rdocs by running the following
66
+ in a new console:
67
+
68
+ 'rdoc'
69
+
70
+ = Using fiveruns memcache-client
71
+
72
+ memcache-client from fiveruns has a couple of fixed added like supporting failover and retry on failure.
73
+
74
+ This fork of the memcache-client source is hosted at GitHub and can be found at:
75
+
76
+ http://github.com/fiveruns/memcache-client/tree/master
77
+
78
+ It can be installed using:
79
+
80
+ # THIS COMMAND ONE TIME ONLY
81
+ gem sources -a http://gems.github.com/
82
+
83
+ # As often as you like
84
+ sudo gem install fiveruns-memcache-client
85
+
86
+ = Known Issues
87
+
88
+ * Starling is "slow" as far as messaging systems are concerned. In practice,
89
+ it's fast enough.
90
+
91
+ == TODO
92
+
93
+ * Implement memcached instead of memcache as a client interface (to make it faster)
94
+
95
+ = Authors
96
+
97
+ * Blaine Cook <romeda@gmail.com>
98
+ * Chris Wanstrath <chris@ozmm.org>
99
+ * AnotherBritt <?>
100
+ * Glenn Rempe <?>
101
+ * Abdul-Rahman Advany <abdulrahman@advany.com>
102
+
103
+ = Copyright
104
+
105
+ Starling - a light-weight server for reliable distributed message passing.
106
+ Copyright 2007-2008 Blaine Cook <blaine@twitter.com>, Twitter Inc.
data/Rakefile CHANGED
@@ -1,4 +1,22 @@
1
- require 'config/requirements'
2
- require 'config/hoe' # setup Hoe + all gem configuration
3
-
4
- Dir['tasks/**/*.rake'].each { |rake| load rake }
1
+ require 'rubygems'
2
+ require 'rake/rdoctask'
3
+ require 'spec/rake/spectask'
4
+
5
+ task :default => :spec
6
+
7
+ task :install do
8
+ sh %{gem build starling.gemspec}
9
+ sh %{sudo gem install starling-*.gem}
10
+ end
11
+
12
+ Spec::Rake::SpecTask.new do |t|
13
+ t.ruby_opts = ['-rtest/unit']
14
+ t.spec_files = FileList['spec/*_spec.rb']
15
+ t.fail_on_error = true
16
+ end
17
+
18
+ Rake::RDocTask.new do |rd|
19
+ rd.main = "README.rdoc"
20
+ rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
21
+ rd.rdoc_dir = 'doc'
22
+ end
data/bin/starling CHANGED
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'starling/runner'
3
+ require 'starling/server_runner'
4
+
4
5
  StarlingServer::Runner.run
6
+
data/bin/starling_top ADDED
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'ostruct'
5
+ require 'socket'
6
+
7
+ @options = OpenStruct.new
8
+ @options.hostname = 'localhost'
9
+ @options.port = 22122
10
+
11
+ op = OptionParser.new do |opts|
12
+ opts.banner = "STARLING TOP\nUsage: startop [options]"
13
+ opts.separator "General Options:"
14
+ opts.on("-h HOSTNAME", "--hostname=HOSTNAME", "Hostname [default: localhost]") do |h|
15
+ @options.hostname = h
16
+ end
17
+ opts.on("-p PORT", "--port=PORT", Integer, "Port [default: 22122]") do |p|
18
+ @options.port = p
19
+ end
20
+ opts.on_tail("--help", "Show this message") do
21
+ puts opts
22
+ exit
23
+ end
24
+ end
25
+ op.parse!
26
+
27
+
28
+ def stats_data
29
+ data = ''
30
+ sock = TCPSocket.new(@options.hostname, @options.port)
31
+ sock.print("stats\r\n")
32
+ sock.flush
33
+ # memcached does not close the socket once it is done writing
34
+ # the stats data. We need to read line by line until we detect
35
+ # the END line and then stop/close on our side.
36
+ stats = sock.gets
37
+ while true
38
+ data += stats
39
+ break if stats.strip == 'END'
40
+ stats = sock.gets
41
+ end
42
+ sock.close
43
+ data
44
+ end
45
+
46
+ def parse(stats_data)
47
+ stats = []
48
+ stats_data.each_line do |line|
49
+ stats << "#{$1}: #{$2}" if line =~ /STAT (\w+) (\S+)/
50
+ end
51
+ stats.sort
52
+ end
53
+
54
+ stats = parse(stats_data)
55
+ stats.each do |stat|
56
+ puts stat
57
+ end
@@ -0,0 +1,9 @@
1
+ starling:
2
+ port: 22122
3
+ pid_file: /tmp/starling/starling.pid
4
+ queue_path: /tmp/starling/spool
5
+ timeout: 0
6
+ syslog_channel: starling-tampopo
7
+ log_level: DEBUG
8
+ daemonize: true
9
+
@@ -0,0 +1,63 @@
1
+ #!/bin/bash
2
+ #
3
+ # starling This shell script takes care of starting and stopping
4
+ # the starling server
5
+ # chkconfig: 345 98 98
6
+ # description: The starling queue server
7
+
8
+ #determine where the 'pidof' executable is located
9
+ if [ -e /bin/pidof ]; then
10
+ PIDOF="/bin/pidof"
11
+ elif [ -e /sbin/pidof ]; then
12
+ PIDOF="/sbin/pidof"
13
+ elif [ -e /usr/local/bin/pidof ]; then
14
+ PIDOF="/usr/local/bin/pidof"
15
+ elif [ -e /bin/pgrep ]; then
16
+ PIDOF="/bin/pgrep"
17
+ elif [ -e /usr/bin/pgrep ]; then
18
+ PIDOF="/usr/bin/pgrep"
19
+ elif [ -e /usr/local/bin/pgrep ]; then
20
+ PIDOF="/usr/local/bin/pgrep"
21
+ else
22
+ echo "Could not find pidof or pgrep"
23
+ fi
24
+
25
+ PROGDIR="/usr/bin"
26
+ PROGNAME="starling"
27
+ OPTIONS="-u nobody -g nobody -L /var/log/starling.log -q /var/spool/starling"
28
+
29
+ start() {
30
+ pid=`$PIDOF $PROGNAME`
31
+ if [ "$pid" != "" ]; then
32
+ echo "$PROGDIR$PROGNAME already running: $pid"
33
+ else
34
+ echo "Starting $PROGDIR$PROGNAME"
35
+ cd $PROGDIR
36
+ nohup $PROGDIR$PROGNAME $OPTIONS &
37
+ fi
38
+ }
39
+
40
+ stop() {
41
+ pid=`$PIDOF $PROGNAME`
42
+ if [ "$pid" != "" ]; then
43
+ echo "Stopping $PROGDIR$PROGNAME: $pid"
44
+ kill $pid
45
+ else
46
+ echo "$PROGDIR$PROGNAME not running"
47
+ fi
48
+ }
49
+
50
+ case "$1" in
51
+ start)
52
+ start
53
+ ;;
54
+ stop)
55
+ stop
56
+ ;;
57
+ restart)
58
+ stop
59
+ sleep 3
60
+ start
61
+ ;;
62
+
63
+ esac
@@ -0,0 +1,71 @@
1
+ #! /bin/sh
2
+ ### BEGIN INIT INFO
3
+ # Provides: starling
4
+ # Required-Start: $local_fs $remote_fs
5
+ # Required-Stop: $local_fs $remote_fs
6
+ # Default-Start: 2 3 4 5
7
+ # Default-Stop: S 0 1 6
8
+ # Short-Description: Starling queue server
9
+ # Description: The Starling distributed, transactional queue server
10
+ ### END INIT INFO
11
+ # Author: Twitter
12
+ # Version: 0.9.7.7
13
+
14
+ set -e
15
+
16
+ DUSER=starling
17
+ DGROUP=starling
18
+ LOGFILE=/var/log/starling/starling.log
19
+ SPOOLDIR=/var/spool/starling
20
+ PORT=22122
21
+ LISTEN=0.0.0.0
22
+ PIDFILE=/var/run/starling/starling.pid
23
+
24
+ PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
25
+ NAME=starling
26
+ DESC="Starling"
27
+ INSTALL_DIR=/usr/local/bin
28
+ DAEMON=$INSTALL_DIR/$NAME
29
+ SCRIPTNAME=/etc/init.d/$NAME
30
+ OPTS="-h $LISTEN -p $PORT -d -q $SPOOLDIR -P $PIDFILE -L $LOGFILE"
31
+
32
+ . /lib/lsb/init-functions
33
+
34
+
35
+ # Gracefully exit if the package has been removed.
36
+ test -x $DAEMON || exit 0
37
+
38
+ d_start() {
39
+ log_begin_msg "Starting Starling Server..."
40
+ start-stop-daemon -c $DUSER:$DGROUP --start --quiet --pidfile $PIDFILE --exec $DAEMON \
41
+ -- $OPTS || log_end_msg 1
42
+ log_end_msg 0
43
+ }
44
+
45
+ d_stop() {
46
+ log_begin_msg "Stopping Starling Server..."
47
+ start-stop-daemon -c $DUSER:$DGROUP --stop --quiet --pidfile $PIDFILE \
48
+ || log_end_msg 1
49
+ log_end_msg 0
50
+ }
51
+
52
+ case "$1" in
53
+ start)
54
+ d_start
55
+ ;;
56
+ stop)
57
+ d_stop
58
+ ;;
59
+ restart|force-reload|reload)
60
+ d_stop
61
+ sleep 2
62
+ d_start
63
+ ;;
64
+ *)
65
+ echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2
66
+ exit 3
67
+ ;;
68
+ esac
69
+
70
+ exit 0
71
+
data/lib/starling.rb CHANGED
@@ -6,7 +6,7 @@ class Starling < MemCache
6
6
 
7
7
  ##
8
8
  # fetch an item from a queue.
9
-
9
+
10
10
  def get(*args)
11
11
  loop do
12
12
  response = super(*args)
@@ -16,13 +16,13 @@ class Starling < MemCache
16
16
  end
17
17
 
18
18
  ##
19
- # insert +value+ into +queue+.
19
+ # insert +value+ into +queue+.
20
20
  #
21
21
  # +expiry+ is expressed as a UNIX timestamp
22
- #
22
+ #
23
23
  # If +raw+ is true, +value+ will not be Marshalled. If +raw+ = :yaml, +value+
24
24
  # will be serialized with YAML, instead.
25
-
25
+
26
26
  def set(queue, value, expiry = 0, raw = false)
27
27
  retries = 0
28
28
  begin
@@ -4,8 +4,8 @@ module StarlingServer
4
4
  # This is an internal class that's used by Starling::Server to handle the
5
5
  # MemCache protocol and act as an interface between the Server and the
6
6
  # QueueCollection.
7
-
8
- class Handler
7
+
8
+ class Handler < EventMachine::Connection
9
9
 
10
10
  DATA_PACK_FMT = "Ia*".freeze
11
11
 
@@ -13,18 +13,18 @@ module StarlingServer
13
13
  ERR_UNKNOWN_COMMAND = "CLIENT_ERROR bad command line format\r\n".freeze
14
14
 
15
15
  # GET Responses
16
- GET_COMMAND = /^get (.{1,250})\r\n$/
16
+ GET_COMMAND = /\Aget (.{1,250})\s*\r\n/m
17
17
  GET_RESPONSE = "VALUE %s %s %s\r\n%s\r\nEND\r\n".freeze
18
18
  GET_RESPONSE_EMPTY = "END\r\n".freeze
19
19
 
20
20
  # SET Responses
21
- SET_COMMAND = /^set (.{1,250}) ([0-9]+) ([0-9]+) ([0-9]+)\r\n$/
21
+ SET_COMMAND = /\Aset (.{1,250}) ([0-9]+) ([0-9]+) ([0-9]+)\r\n/m
22
22
  SET_RESPONSE_SUCCESS = "STORED\r\n".freeze
23
23
  SET_RESPONSE_FAILURE = "NOT STORED\r\n".freeze
24
24
  SET_CLIENT_DATA_ERROR = "CLIENT_ERROR bad data chunk\r\nERROR\r\n".freeze
25
25
 
26
26
  # STAT Response
27
- STATS_COMMAND = /stats\r\n$/
27
+ STATS_COMMAND = /\Astats\r\n/m
28
28
  STATS_RESPONSE = "STAT pid %d
29
29
  STAT uptime %d
30
30
  STAT time %d
@@ -47,77 +47,118 @@ STAT limit_maxbytes %d
47
47
  QUEUE_STATS_RESPONSE = "STAT queue_%s_items %d
48
48
  STAT queue_%s_total_items %d
49
49
  STAT queue_%s_logsize %d
50
- STAT queue_%s_expired_items %d\n".freeze
50
+ STAT queue_%s_expired_items %d
51
+ STAT queue_%s_age %d\r\n".freeze
52
+
53
+ SHUTDOWN_COMMAND = /\Ashutdown\r\n/m
54
+
55
+
56
+ @@next_session_id = 1
51
57
 
52
58
  ##
53
59
  # Creates a new handler for the MemCache protocol that communicates with a
54
60
  # given client.
55
61
 
56
- def initialize(client, server, queue_collection)
57
- @client = client
58
- @server = server
59
- @queue_collection = queue_collection
60
- @expiry_stats = Hash.new(0)
62
+ def initialize(options = {})
63
+ @opts = options
61
64
  end
62
65
 
63
66
  ##
64
67
  # Process incoming commands from the attached client.
65
68
 
66
- def run
67
- while running? do
68
- process_command(@client.readline)
69
- Thread.current[:last_activity] = Time.now
69
+ def post_init
70
+ @stash = []
71
+ @data = ""
72
+ @data_buf = ""
73
+ @server = @opts[:server]
74
+ @logger = StarlingServer::Base.logger
75
+ @expiry_stats = Hash.new(0)
76
+ @expected_length = nil
77
+ @server.stats[:total_connections] += 1
78
+ set_comm_inactivity_timeout @opts[:timeout]
79
+ @queue_collection = @opts[:queue]
80
+
81
+ @session_id = @@next_session_id
82
+ @@next_session_id += 1
83
+
84
+ peer = Socket.unpack_sockaddr_in(get_peername)
85
+ #@logger.debug "(#{@session_id}) New session from #{peer[1]}:#{peer[0]}"
86
+ end
87
+
88
+ def receive_data(incoming)
89
+ @server.stats[:bytes_read] += incoming.size
90
+ @data << incoming
91
+
92
+ while data = @data.slice!(/.*?\r\n/m)
93
+ response = process(data)
70
94
  end
95
+
96
+ send_data response if response
71
97
  end
72
98
 
73
- private
99
+ def process(data)
100
+ data = @data_buf + data if @data_buf.size > 0
101
+ # our only non-normal state is consuming an object's data
102
+ # when @expected_length is present
103
+ if @expected_length && data.size == @expected_length
104
+ response = set_data(data)
105
+ @data_buf = ""
106
+ return response
107
+ elsif @expected_length
108
+ @data_buf = data
109
+ return
110
+ end
111
+
112
+ case data
113
+ when SET_COMMAND
114
+ @server.stats[:set_requests] += 1
115
+ set($1, $2, $3, $4.to_i)
116
+ when GET_COMMAND
117
+ @server.stats[:get_requests] += 1
118
+ get($1)
119
+ when STATS_COMMAND
120
+ stats
121
+ when SHUTDOWN_COMMAND
122
+ # no point in responding, they'll never get it.
123
+ Runner::shutdown
124
+ else
125
+ logger.warn "Unknown command: #{data}."
126
+ respond ERR_UNKNOWN_COMMAND
127
+ end
128
+ rescue => e
129
+ logger.error "Error handling request: #{e}."
130
+ logger.debug e.backtrace.join("\n")
131
+ respond GET_RESPONSE_EMPTY
132
+ end
74
133
 
75
- def running?
76
- !Thread.current[:shutdown]
134
+ def unbind
135
+ #@logger.debug "(#{@session_id}) connection ends"
77
136
  end
78
137
 
138
+ private
79
139
  def respond(str, *args)
80
140
  response = sprintf(str, *args)
81
141
  @server.stats[:bytes_written] += response.length
82
- @client.write response
142
+ response
83
143
  end
84
144
 
85
- def process_command(command)
86
- begin
87
- @server.stats[:bytes_read] += command.length
88
- case command
89
- when SET_COMMAND
90
- @server.stats[:set_requests] += 1
91
- set($1, $2, $3, $4.to_i)
92
- when GET_COMMAND
93
- @server.stats[:get_requests] += 1
94
- get($1)
95
- when STATS_COMMAND
96
- stats
97
- else
98
- logger.warn "Unknown command: #{command[0,4]}.\nFull command was #{command}."
99
- respond ERR_UNKNOWN_COMMAND
100
- end
101
- rescue => e
102
- logger.error "Error handling request: #{e}."
103
- logger.debug e.backtrace.join("\n")
104
- respond GET_RESPONSE_EMPTY
105
- end
145
+ def set(key, flags, expiry, len)
146
+ @expected_length = len + 2
147
+ @stash = [ key, flags, expiry ]
148
+ nil
106
149
  end
107
150
 
108
- def set(key, flags, expiry, len)
109
- data = @client.read(len)
110
- data_end = @client.read(2)
111
- @server.stats[:bytes_read] += len + 2
112
- if data_end == "\r\n" && data.size == len
113
- internal_data = [expiry.to_i, data].pack(DATA_PACK_FMT)
114
- if @queue_collection.put(key, internal_data)
115
- respond SET_RESPONSE_SUCCESS
116
- else
117
- respond SET_RESPONSE_FAILURE
118
- end
151
+ def set_data(incoming)
152
+ key, flags, expiry = @stash
153
+ data = incoming.slice(0...@expected_length-2)
154
+ @stash = []
155
+ @expected_length = nil
156
+
157
+ internal_data = [expiry.to_i, data].pack(DATA_PACK_FMT)
158
+ if @queue_collection.put(key, internal_data)
159
+ respond SET_RESPONSE_SUCCESS
119
160
  else
120
- respond SET_CLIENT_DATA_ERROR
161
+ respond SET_RESPONSE_FAILURE
121
162
  end
122
163
  end
123
164
 
@@ -141,7 +182,7 @@ STAT queue_%s_expired_items %d\n".freeze
141
182
  end
142
183
 
143
184
  def stats
144
- respond STATS_RESPONSE,
185
+ respond STATS_RESPONSE,
145
186
  Process.pid, # pid
146
187
  Time.now - @server.stats(:start_time), # uptime
147
188
  Time.now.to_i, # time
@@ -169,13 +210,13 @@ STAT queue_%s_expired_items %d\n".freeze
169
210
  k, v.length,
170
211
  k, v.total_items,
171
212
  k, v.logsize,
172
- k, @expiry_stats[k])
213
+ k, @expiry_stats[k],
214
+ k, v.current_age)
173
215
  end
174
216
  end
175
217
 
176
218
  def logger
177
- @server.logger
219
+ @logger
178
220
  end
179
-
180
221
  end
181
222
  end