【问题标题】:Redirecting CGI error output from STDERR to a file (python AND perl)将 CGI 错误输出从 STDERR 重定向到文件(python AND perl)
【发布时间】:2010-12-19 09:08:51
【问题描述】:

我正在将一个网站移动到 Hostmonster 并询问服务器日志的位置,以便我可以自动扫描它以查找 CGI 错误。有人告诉我,“很抱歉,我们没有 cgi 错误转到您有权访问的任何文件。”

出于组织原因,我坚持使用 Hostmonster 和这个糟糕的政策,所以作为一种解决方法,我想也许我会修改 CGI 脚本以将 STDERR 重定向到自定义日志文件。

我有很多脚本 (269),所以我需要在 Python 和 Perl 中使用一种简单的方法将 STDERR 重定向到自定义日志文件。

如果有多个脚本同时失败,则可以显式或隐式地解决文件锁定问题,因为理论上共享的 CGI 错误日志文件可以由多个脚本同时写入。

(我想使用共享错误日志,这样我就可以每晚通过电子邮件将其内容发送给自己,然后将其存档或删除。)

我知道我可能必须修改每个文件 (grrr),这就是为什么我正在寻找一些优雅的东西,而这些东西只有几行代码。谢谢。

【问题讨论】:

  • 另一个问题是他们可能也不允许你创建文件。
  • 我很确定您可以在您的家庭或网络 (public_html) 树中创建文件。我想到的另一个方面是,如果我有 269 个脚本并且要附加到自己的日志文件中,也许我需要担心自定义日志文件上的文件锁定
  • 这是我的另一个疯狂想法,它要么很酷,要么很疯狂。这些脚本都以 #!/usr/bin/python 或 perl 开头。如果我创建了自己的包装器,例如 /home/user/bin/mypython 或 myperl,会怎样。然后可以重定向stderr并将脚本的其余部分传递给“真正的”perl或python。如果这实际上是一个好主意(不是一个可怕的主意),我只是不知道“将 [cgi] 脚本的其余部分传递给真正的 perl/python”的机制。
  • (我突然想到这可能是不可移植的,因为某些主机,甚至可能是 Hostmonster,可能不喜欢 CGI 脚本调用类似 /home/user/bin/myperl 之类的奇怪东西 - 他们可能有安全问题,即使它们本身确实不应该)
  • 关于许多错误日志/文件锁定的问题:只需在发送电子邮件之前附加或压缩文件 -> 问题解决了吗?如果您想按时间顺序记录日志事件,只需编写一个简单的脚本来遍历日志。

标签: python perl cgi stderr hostmonster


【解决方案1】:

Python:cgitb。在脚本的顶部,在其他导入之前:

import cgitb
cgitb.enable(False, '/home/me/www/myapp/logs/errors')

('errors' 是 Web 服务器用户具有写入权限的目录。)

【讨论】:

  • 这很酷,因为它使用了一个内置模块并且不需要更多的代码行。唯一的问题是,似乎每个回溯/错误转储都会写入自己的文件。虽然这很好地解决了文件锁定问题(我在发布使用自定义错误日志的问题后突然想到,我不得不担心脚本可能同时失败,因此文件锁定),我有点想要一个共享我可以定期扫描错误日志文件(然后通过电子邮件发送给自己,然后归档)。
【解决方案2】:

在 Perl 中尝试CGI::Carp

BEGIN { 
use CGI::Carp qw(carpout); 
use diagnostics;
open(LOG, ">errors.txt"); 
carpout(LOG);
close(LOG);
}

use CGI::Carp qw(fatalsToBrowser);

【讨论】:

    【解决方案3】:

    对于 Perl,只需关闭并重新打开 STDERR 以指向您选择的文件。

    close STDERR;
    open STDERR, '>>', '/path/to/your/log.txt' 
      or die "Couldn't redirect STDERR: $!";
    
    warn "this will go to log.txt";
    

    或者,您可以查看像 File::Tee 这样的文件句柄多路复用器。

    【讨论】:

    • 这是一个基本的,可能是愚蠢的问题,但是通过简单的打开/追加写入 STDERR,Perl 是否隐式处理文件锁定?我问是因为我在发布问题后突然想到,理论上使用共享日志文件我可能不得不考虑多个脚本一次失败的可能性(因此,文件锁定问题)
    • 在Unix上,如果你以O_APPEND模式打开一个文件,seek和write操作是以原子的方式进行的,因此不需要锁定。
    【解决方案4】:

    Python 具有您可能想要研究的 sys.stderr 模块。

    >>>help(sys.__stderr__.read)
    Help on built-in function read:
    
    read(...)
        read([size]) -> read at most size bytes, returned as a string.
    
        If the size argument is negative or omitted, read until EOF is reached.
        Notice that when in non-blocking mode, less data than what was requested
        may be returned, even if no size parameter was given.
    

    您可以将它的输出存储在一个字符串中并将该字符串写入文件。

    希望对你有帮助

    【讨论】:

    • 如果我理解正确,你是说 sys.__stderr__.read() 会让我在发生错误后检索 stderr 流的内容。但是我是否不需要知道要检索的错误消息的长度才能得到它?也许我没有正确理解你的建议。
    • 我认为您指的是 [size] 参数。 '[]' 表示该参数是可选的。正如帮助文本所说,如果没有指定大小,那么它会一直读取直到达到 EOF。我对此的理解是,如果你不指定大小,那么你会得到整个错误
    【解决方案5】:

    蟒蛇:

    import sys
    
    sys.stderr = open('file_path_with_write_permission/filename', 'a')
    

    【讨论】:

    • 您的意思可能是'a' 模式。否则,每个请求都会覆盖该文件。
    【解决方案6】:

    在我的 Perl CGI 程序中,我通常有

    BEGIN {
      open(STDERR,'>>','stderr.log');
    }
    

    就在 shebang 行和“使用严格;使用警告;”之后。如果需要,您可以将 $0 附加到文件名。但这并不能解决多程序问题,因为一个程序的多个副本可能会同时运行。对于每个程序组,我通常只有几个输出文件。

    【讨论】:

    • 这不是你的错,因为你不可能知道这一点,但我没有采用这个解决方案的原因是因为我想在错误日志路径的构造中使用一个变量,并且我很久以前就知道 Perl 并没有真正允许在 BEGIN 块中使用变量。 (显然可以使用外部包,但对我来说似乎太复杂/太脆弱了。)
    【解决方案7】:

    我最终采用的解决方案类似于以下,靠近我所有脚本的顶部:

    Perl:

    open(STDERR,">>","/path/to/my/cgi-error.log")
        or die "Could not redirect STDERR: $OS_ERROR";
    

    Python:

    sys.stderr = open("/path/to/my/cgi-error.log", "a")
    

    显然在 Perl 中,您不需要在重新打开 STDERR 句柄之前关闭它。

    通常我会作为最佳实践关闭它,但正如我在问题中所说,我有 269 个脚本,我正在尝试将更改最小化。 (另外,重新打开打开的文件句柄似乎更 Perlish,听起来很糟糕。)

    如果其他人将来有类似的事情,我将立即更新我的所有脚本:

    Perl:

    find . -type f -name "*.pl" -exec perl -pi.bak -e 's%/usr/bin/perl%/usr/bin/perl\nopen(STDERR,">>","/path/to/my/cgi-error.log")\n    or die "Could not redirect STDERR: \$OS_ERROR";%' {} \;
    

    Python:

    find . -type f -name "*.py" -exec perl -pi.bak -e 's%^(import os, sys.*)%$1\nsys.stderr = open("/path/to/my/cgi-error.log", "a")%' {} \;
    

    我发布这些命令的原因是,我花了很多语法按摩才能使这些命令正常工作(例如,将 Couldn't 更改为 Could not,将 #!/usr/bin/perl 更改为 just /usr/bin/perl 所以 shell 不会将 ! 解释为历史字符,使用 $OS_ERROR 而不是 $! 等)

    感谢所有评论的人。由于没有人同时回答 Perl 和 Python,我不能真正“接受”任何给定的答案,但我确实给了那些引导我走向正确方向的答案。再次感谢!

    【讨论】:

      猜你喜欢
      • 2012-02-29
      • 1970-01-01
      • 2015-06-23
      • 2021-06-29
      • 1970-01-01
      • 1970-01-01
      • 2014-07-22
      • 2018-02-08
      • 2019-06-14
      相关资源
      最近更新 更多