Hi, I use the ready_pipe branch. I set "logger" to log into Syslog, but the issue I''ll explain here is the same even if I log to a file. I runned Unicorn daemonized. Now imagine I do a typo in my Rack config.ru (I use preload=true). When sending USR2 to the master it fails (ok) but the raised exception is not shown in the logger, instead it''s lost as $stderr is /dev/null. Even worse, imagine Unicorn is not running and I start it daemonized (with a init script). Due to the typo in config.ru I see: "master failed to start, check stderr log for details" But the message is useless since stderr is /dev/null so I se nothing in Syslog. So I suggest the following: If the process fails after setting the logger, then rescue any exception, log it to the logger and raise it as normal: rescue => err logger.fatal "#{err.class}: #{err.message}\n#{err.backtrace.join("\n")}" raise err end I''m trying to figure where exactly to include such code but it seems a bit complex. Any tip please? Thanks a lot. -- I?aki Baz Castillo <ibc at aliax.net>
El S?bado, 2 de Enero de 2010, I?aki Baz Castillo escribi?:> Hi, I use the ready_pipe branch. I set "logger" to log into Syslog, but the > issue I''ll explain here is the same even if I log to a file. > > I runned Unicorn daemonized. Now imagine I do a typo in my Rack config.ru > (I use preload=true). When sending USR2 to the master it fails (ok) but > the raised exception is not shown in the logger, instead it''s lost as > $stderr is /dev/null. > > Even worse, imagine Unicorn is not running and I start it daemonized (with > a init script). Due to the typo in config.ru I see: > > "master failed to start, check stderr log for details" > > But the message is useless since stderr is /dev/null so I se nothing in > Syslog. > > > So I suggest the following: > > If the process fails after setting the logger, then rescue any exception, > log it to the logger and raise it as normal: > > rescue => err > logger.fatal "#{err.class}: > #{err.message}\n#{err.backtrace.join("\n")}" raise err > end > > I''m trying to figure where exactly to include such code but it seems a bit > complex. Any tip please? > > Thanks a lot.A simpler approach would be removing this line at the end of launcher.rb: Unicorn::Configurator::DEFAULTS[:stderr_path] = "/dev/null" With this, errors would be displayed in stderr. Of course for workers stderr should be reopened to point to /dev/null, but IMHO not for master process. Perhaps errors preventing the master process to start should be displayed to stderr instead of to the logger (as it could be not set yet). Opinnions? -- I?aki Baz Castillo <ibc at aliax.net>
El S?bado, 2 de Enero de 2010, I?aki Baz Castillo escribi?:> A simpler approach would be removing this line at the end of launcher.rb: > > Unicorn::Configurator::DEFAULTS[:stderr_path] = "/dev/null" > > With this, errors would be displayed in stderr. Of course for workers > stderr should be reopened to point to /dev/null, but IMHO not for master > process. > > Perhaps errors preventing the master process to start should be displayed > to stderr instead of to the logger (as it could be not set yet).And the other already available workaround is setting the "stderr_path". Unfortunatelly $stderr.reopen requires a file as parameter so is not possible to use Syslog or any other logger, is it? BTW, how to reset the $stderr so it points again to the default one (the terminal)? -- I?aki Baz Castillo <ibc at aliax.net>
I?aki Baz Castillo <ibc at aliax.net> wrote:> El S?bado, 2 de Enero de 2010, I?aki Baz Castillo escribi?: > > A simpler approach would be removing this line at the end of launcher.rb: > > > > Unicorn::Configurator::DEFAULTS[:stderr_path] = "/dev/null" > > > > With this, errors would be displayed in stderr. Of course for workers > > stderr should be reopened to point to /dev/null, but IMHO not for master > > process. > > > > Perhaps errors preventing the master process to start should be displayed > > to stderr instead of to the logger (as it could be not set yet). > > And the other already available workaround is setting the "stderr_path". > Unfortunatelly $stderr.reopen requires a file as parameter so is not possible > to use Syslog or any other logger, is it?Hi I?aki, Unfortunately, the stock Syslog module isn''t compatible with the Ruby Logger implementation. Try grabbing the "SyslogLogger" gem and using that instead. I haven''t had any experience with SyslogLogger, but a cursory look says it''s OK. I would always keep stderr_path set somewhere in your config file since by default "rack.errors" points to it, and it''s the default place for Kernel#warn to output to. If you really want to, you may be able to subclass SyslogLogger and give it IO-like methods (write/puts/flush) are require for Rack::Lint compatibility and then set $stderr: # Totally untested, stick this in your Unicorn config file and let # us know if it works or blows up badly: require ''syslog_logger'' class MySyslogLogger < SyslogLogger alias puts error alias write error def flush; self; end end $stderr = MySyslogLogger.new(''foo'')> BTW, how to reset the $stderr so it points again to the default one (the > terminal)?You would''ve had to dup stderr in the config file and keep it referenced before it got redirected. But a daemonized process shouldn''t be holding onto terminals... -- Eric Wong
El Domingo, 3 de Enero de 2010, Eric Wong escribi?:> Unfortunately, the stock Syslog module isn''t compatible with the Ruby > Logger implementation. Try grabbing the "SyslogLogger" gem and using > that instead. I haven''t had any experience with SyslogLogger, but a > cursory look says it''s OK.Yes, in fact I already use a "bit" customized SyslogLogger :)> If you really want to, you may be able to subclass SyslogLogger and give > it IO-like methods (write/puts/flush) are require for Rack::Lint > compatibility and then set $stderr: > > # Totally untested, stick this in your Unicorn config file and let > # us know if it works or blows up badly: > > require ''syslog_logger'' > > class MySyslogLogger < SyslogLogger > alias puts error > alias write error > def flush; self; end > end > > $stderr = MySyslogLogger.new(''foo'')Really great! It works fine :)> > BTW, how to reset the $stderr so it points again to the default one (the > > terminal)? > > You would''ve had to dup stderr in the config file and keep it referenced > before it got redirected. But a daemonized process shouldn''t be holding > onto terminals...Really thanks a lot. -- I?aki Baz Castillo <ibc at aliax.net>
El Domingo, 3 de Enero de 2010, Eric Wong escribi?:> If you really want to, you may be able to subclass SyslogLogger and give > it IO-like methods (write/puts/flush) are require for Rack::Lint > compatibility and then set $stderr: > > # Totally untested, stick this in your Unicorn config file and let > # us know if it works or blows up badly: > > require ''syslog_logger'' > > class MySyslogLogger < SyslogLogger > alias puts error > alias write error > def flush; self; end > end > > $stderr = MySyslogLogger.new(''foo'') >They would be also needed these methods: def reopen(v); true; end def sync=(v); v; end -- I?aki Baz Castillo <ibc at aliax.net>
El Domingo, 3 de Enero de 2010, Eric Wong escribi?:> # Totally untested, stick this in your Unicorn config file and let > # us know if it works or blows up badly: > > require ''syslog_logger'' > > class MySyslogLogger < SyslogLogger > alias puts error > alias write error > def flush; self; end > end > > $stderr = MySyslogLogger.new(''foo'')Unfortunatelly it doesn''w work very well. First of all Sysloglogger relies on a constant called SYSLOG created in "initialize" method so it''s required to add the above methods to SyslogLogger class rather than subclass. Anyhow, the main problem is that by doing "$stderr = SyslogLogger.new(''foo'')" the std error is not redirected as /proc/PID/fd still shows: 2 -> /dev/pts/2 Of course by calling $stderr.puts "error" the above works, but when a real error raised it''s is printed in the screen (/dev/pts/2). I strongly think that IO#reopen is required. IO#reopen accepts a string (file path) or other IO, so "converting" SyslogLogger into a IO is required. I''ll try to achieve it. Regards. -- I?aki Baz Castillo <ibc at aliax.net>
I?aki Baz Castillo <ibc at aliax.net> wrote:> El Domingo, 3 de Enero de 2010, Eric Wong escribi?: > > > # Totally untested, stick this in your Unicorn config file and let > > # us know if it works or blows up badly: > > > > require ''syslog_logger'' > > > > class MySyslogLogger < SyslogLogger > > alias puts error > > alias write error > > def flush; self; end > > end > > > > $stderr = MySyslogLogger.new(''foo'') > > Unfortunatelly it doesn''w work very well. First of all Sysloglogger relies on > a constant called SYSLOG created in "initialize" method so it''s required to > add the above methods to SyslogLogger class rather than subclass. > > Anyhow, the main problem is that by doing "$stderr = SyslogLogger.new(''foo'')" > the std error is not redirected as /proc/PID/fd still shows: > > 2 -> /dev/pts/2Yup, that''s expected. _Assigning_ $stderr does not do a redirect at the OS-level, only at th Ruby level (sufficient for env["rack.errors"], Kernel#warn).> Of course by calling $stderr.puts "error" the above works, but when a real > error raised it''s is printed in the screen (/dev/pts/2).> I strongly think that IO#reopen is required. IO#reopen accepts a string (file > path) or other IO, so "converting" SyslogLogger into a IO is required. > > I''ll try to achieve it.Yes, you need to do something that''ll dup()/dup2() internally like IO#reopen to replace fd=2. But when you have the raw file descriptor, you won''t get syslog formatting... The only way I can think of is to redirect fd=2 to the stdin of a separate process to add formatting in... r, w = IO.pipe fork { $stdin.reopen(r) w.close exec(''ruby'', ''-rsyslog'', ''-ane'', ''BEGIN{Syslog.open("foo")}; Syslog.warning $_'') } r.close $stderr.reopen(w) Not pretty... -- Eric Wong
El Lunes, 4 de Enero de 2010, Eric Wong escribi?:> I?aki Baz Castillo <ibc at aliax.net> wrote: > > El Domingo, 3 de Enero de 2010, Eric Wong escribi?: > > > # Totally untested, stick this in your Unicorn config file and let > > > # us know if it works or blows up badly: > > > > > > require ''syslog_logger'' > > > > > > class MySyslogLogger < SyslogLogger > > > alias puts error > > > alias write error > > > def flush; self; end > > > end > > > > > > $stderr = MySyslogLogger.new(''foo'') > > > > Unfortunatelly it doesn''w work very well. First of all Sysloglogger > > relies on a constant called SYSLOG created in "initialize" method so it''s > > required to add the above methods to SyslogLogger class rather than > > subclass. > > > > Anyhow, the main problem is that by doing "$stderr > > SyslogLogger.new(''foo'')" the std error is not redirected as /proc/PID/fd > > still shows: > > > > 2 -> /dev/pts/2 > > Yup, that''s expected. _Assigning_ $stderr does not do a redirect at the > OS-level, only at th Ruby level (sufficient for env["rack.errors"], > Kernel#warn). > > > Of course by calling $stderr.puts "error" the above works, but when a > > real error raised it''s is printed in the screen (/dev/pts/2). > > > > I strongly think that IO#reopen is required. IO#reopen accepts a string > > (file path) or other IO, so "converting" SyslogLogger into a IO is > > required. > > > > I''ll try to achieve it. > > Yes, you need to do something that''ll dup()/dup2() internally like > IO#reopen to replace fd=2. But when you have the raw file descriptor, > you won''t get syslog formatting... > > The only way I can think of is to redirect fd=2 to the stdin of a > separate process to add formatting in... > > r, w = IO.pipe > fork { > $stdin.reopen(r) > w.close > exec(''ruby'', ''-rsyslog'', ''-ane'', > ''BEGIN{Syslog.open("foo")}; Syslog.warning $_'') > } > r.close > $stderr.reopen(w) > > Not pretty...Thanks. Definitively it seems too much complex so I''ll change myidea and will use a normal text file for stderr when running daemonized. Thanks a lot. -- I?aki Baz Castillo <ibc at aliax.net>