Jenkins控制台输出不是实时的

54

我对Jenkins还不太熟悉,但我遇到了一个简单但很烦人的问题。当我在Jenkins上运行作业(构建)时,我会触发Ruby命令来执行我的测试脚本。

问题是Jenkins没有实时从控制台显示输出。这是触发器日志。

Building in workspace /var/lib/jenkins/workspace/foo_bar
No emails were triggered.
[foo_bar] $ /bin/sh -xe /tmp/hudson4042436272524123595.sh
+ ruby /var/lib/jenkins/test-script.rb

基本上,它会在构建完成之前一直挂在此输出上,然后才显示全部输出。有趣的是,这不是一致的行为,有时它按照应该的方式工作。但大多数情况下,没有实时控制台输出。

Jenkins版本:1.461


1
你正在运行的服务器有多强大,这个脚本需要执行多长时间?这听起来像是由于服务器过载而导致的延迟。当我的Jenkins主节点运行到满负荷时,我曾经看到过类似的症状。 - CIGuy
谢谢您的关注,这确实有道理。在这种情况下,我们正在谈论EC2实例“small”,http://aws.amazon.com/ec2/instance-types/,但它只是运行的进程。您是否需要更多? - Haris Krajina
这取决于有多少作业正在运行,但是如果您在小实例上同时运行多个作业,我会预计会出现延迟。 - CIGuy
1
我有一个类似的问题,然而,机器超规格但利用率不高。在Jenkins之外运行Python脚本可以实时按预期工作。该作业调用一个Python脚本,其尾随另一个进程的日志文件。日志文件实时更新,Jenkins输出以块形式倒出,好像正在等待填充缓冲区。 - Craig
嗨,Craig,你是对的,输出缓冲是个问题。使用 STDOUT.sync=true 解决了这个问题。 - Haris Krajina
我在使用Maven时遇到了同样的问题。只有在调用结束后,所有Maven调用的输出才会被刷新。还有其他人遇到这个问题吗? - Marty
9个回答

74

为了澄清一些答案:

  • rubypython 或其他任何明智的脚本语言都会缓冲输出;这是为了尽可能减少 IO 操作;向磁盘写入数据很慢,向控制台写入数据也很慢...
  • 通常情况下,数据会在缓冲区中积累到足够多时自动执行 flush() 操作,并特殊处理换行符。例如:如果不带换行符地写入一个字符串,然后调用 sleep() 方法,直到 sleep() 执行完毕才会写入该字符串(我只是以 sleep 作为示例,请随意替换为任何其他昂贵的系统调用)。

例如,以下代码将等待 8 秒钟,打印一行文本,再等待 5 秒钟,然后打印第二行文本。

from time import sleep

def test():
    print "ok",
    time.sleep(3)
    print "now",
    time.sleep(5)
    print "done"
    time.sleep(5)
    print "again"

test()
  • 对于 rubySTDOUT.sync = true 会开启 autoflush;所有写入 STDOUT 的操作都会被紧跟着执行 flush()。这将解决你的问题,但会导致更多的IO。

STDOUT.sync = true
  • 对于python,你可以使用python -u或环境变量PYTHONUNBUFFERED来使stdin/stdout/stout不缓冲,但是还有其他的解决方案,它们不会改变stdinstderr

  • export PYTHONUNBUFFERED=1
    
  • 对于perl,你可以使用autoflush

  • autoflush STDOUT 1;
    

    16

    确保你的脚本正在刷新它的标准输出和错误输出。在我的情况下,我遇到了类似于你描述的缓冲问题,但我是使用Python语言。以下Python代码解决了我的问题:

    import sys
    sys.stdout.flush()
    

    我不是 Ruby 程序员,但谷歌给出以下结果:

    $stdout.flush
    

    嗨,Craig,谢谢你的回答。STDOUT.flush$stdout.flush都可以使用。问题是,你需要在脚本中多次使用这些命令来在需要时刷新缓冲区。我将发布一个解决方案,基本上涵盖了同步缓冲区刷新到输出的内容。 - Haris Krajina
    谢谢,它真的解决了我的问题,因为它在shell下运行良好而不需要刷新,所以我没有考虑过这个。 - Larry Cai

    9

    我认为 python -u 也同样有效。

    例如,在批处理命令中:

    python -u foo.py
    

    4
    最简单的解决方案是打开将缓冲区同步到输出的功能。 @Craig在他的答案中提到了这个,但是有一行代码的解决方案可以覆盖整个脚本,并且不需要您多次刷新缓冲区。
    只需写入:
    STDOUT.sync = true
    

    其背后的逻辑很简单,为了避免多次使用IO操作输出,输出被缓冲。要禁用此功能,请使用

    STDOUT.sync = false
    

    这是 Ruby 的解决方案。

    3

    其他答案都是针对特定程序的,但我在这里找到了一个更通用的解决方案:

    https://unix.stackexchange.com/a/25378

    您可以使用stdbuf来改变任何程序的缓冲行为。
    在我的情况下,我正在通过teegrep将输出从shell脚本管道传输到控制台或基于内容的文件中。像OP描述的那样,控制台一直挂起。这解决了问题:
    ./slowly_parse.py login.csv |tee >(grep -v LOG: > out.csv) | stdbuf -oL -eL grep LOG:
    

    最终我发现只需在grep命令中加入--line-buffered参数即可获得相同的结果:

    ./slowly_parse.py login.csv |tee >(grep -v LOG: > out.csv) | grep --line-buffered LOG:
    

    3
    其他答案已经正确地指出,您需要确保标准输出未被缓冲。
    另一件需要注意的事情是Jenkins本身逐行缓冲。如果您有一个运行缓慢的进程会输出单个字符(例如,打印成功测试的“.”和错误测试的“E”的nunit测试套件摘要),您将在行末才能看到结果。
    [对于我运行在Windows机器上的Jenkins 1.572也是如此。]

    1

    0

    操作系统天生会缓冲输出数据以节省CPU,Jenkins也是如此。

    看起来您正在使用shell命令运行Ruby脚本 -
    我建议通过专用插件直接运行您的Ruby脚本:

    Jenkins Ruby Plugin

    (可能需要安装它)


    我会尝试这个,需要一些时间。谢谢。 - Haris Krajina
    你好,这个不起作用 :( 尝试了一样的结果。而且解决方案并不是很实际,因为 Ruby 代码保存在 Jenkins 工作中,这样版本控制就更加困难了。 - Haris Krajina
    1
    我同意这种解决方案“非源代码控制”的本质 - 最好在Jenkins之外管理任何脚本,并从作业中调用它。(对于Jenkins中的shell / batch块也是如此) - Gonen

    0
    Python在输出追踪信息时会进行缓冲,并在脚本结束时一次性打印出来,以减少在控制台上进行写入的次数,因为向控制台写入是比较慢的。
    在你的追踪信息之后,你可以使用下面的命令。它会将之前排队的所有追踪信息刷新到控制台上。

    sys.stdout.flush()


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接