查看多个节点上的日志:multitail脚本

日志分布在多个节点上,想要实时查看多个日志输出的话可以用这个脚本。这个脚本模拟了multitail的效果:

$ cat multitail.sh

#!/bin/bash
set -f

PROG_NAME=$0

usage() {
    echo "Usage: $PROG_NAME ip1,ip2,ip3... file1 file2 file3 ..."
    exit 1
}

if [ $# -lt 2 ]; then
    usage
fi

COMMAND="tail -f"

IP_LIST=$1
shift && FILES=("$@")

for file in ${FILES[*]}; do
    COMMAND="$COMMAND $file"
done

SED="sed"
if [[ $OSTYPE == *darwin* ]]; then
  which gsed 
  if [ $? -eq 0 ];then
    SED="gsed"
  else
    echo "mac os need gsed, please install gnu-sed." 
    exit 1
  fi
fi

for ip in $(echo "$IP_LIST" | tr ',' '\n'); do
  if [ ${#FILES[@]} -gt 1 ];then
    ssh user@$ip "$COMMAND" | $SED 's/\(==> \)/\1'"$ip:"'/' &
  else
    #ssh user@$ip "$COMMAND" | $SED '0~10a===='"$ip"'====' &
    ssh user@$ip "$COMMAND" | $SED 's/^/'"$ip "'/' &
  fi
done

CHILD_PIDS=$(ps -ef | grep $$ | grep -v grep | awk '$3=='"$$"'{print $2}' | xargs)

# CTRL-C to stop
trap 'kill $CHILD_PIDS >/dev/null 2>&1' SIGINT SIGTERM 
wait

使用方式:

$ ./multitail.sh 192.168.10.1,192.168.10.2 /data/app1/a.log /data/app2/b.log

可以配合ack命令对一些关键字高亮:

$ ./multitail.sh ip1,ip2 log1 log2 | ack --passthru login

注意,依赖ssh执行远程命令,所以前提是执行脚本的机器必须与目标ip打通ssh,不需要密码。

推荐一个更好用的diff命令:icdiff

icdiff王福强推荐的一个很酷的可以用来替代系统默认的diff工具的命令行比较工具,效果跟filemeger这些有的一比,关键是在shell下使用很方便。我现在已经用它替代默认的diff,并替代了git diff的效果,如下:

在 .gitconfig 里添加

【diff】#防止模板渲染转义,请替换为英文的中括号
    external = git-diff-wrapper.sh

脚本内容:

$ cat git-diff-wrapper.sh
#!/bin/bash
/usr/local/bin/icdiff $2 $5

想要忽略这个外部diff而使用官方的diff效果的话:

$ git diff --no-ext-diff

scala雾中风景(21): auto-tupling与auto-detupling

前几天和王福强讨论一个偏函数的问题,聊到了auto tupling的问题,以前有过几篇blog是跟这个相关的。参考:scala雾中风景(16): println(1,2,3)为什么work?scala雾中风景(17): toSet()的谜题;

其实当方法参数是Unit类型时,传入的参数不是Unit的话编译器最后自动生成一个”()” 也属于auto tupling的机制,只是之前不知道它们是同一种机制造成的,参考:scala雾中风景(4): Unit类型, 和 scala雾中风景(8): 高阶函数与Unit的谜题

不过在跟afo讨论的过程中发现另一个现象,用下面的例子来看:

class A{
    val p: Function[(Int,Int),String]  = { case (a:Int,b:Int) => "OK" }
    val p2: Function2[Int,Int,String]  = { case (a:Int,b:Int) => "OK" }
}

这里面的偏函数{ case (a:Int,b:Int) => "OK" } 即可以声明为Function类型,也可以声明为Function2类型。

最初我以为这里编译器是把偏函数类型向上造型,第一个能通过,第二个通不过,因为PartialFunction类型是继承自A=>B这种只有一个参数的函数类型,即Function[A,B],见scala源码:

trait PartialFunction[-A, +B] extends (A => B) 

所以,偏函数对象可以造型为Function,不能造型为Function2,因为不是它的子类;但实际中这么写却能编译通过,也没有给出任何警告,觉得有些蹊跷,确认了一下后边的偏函数{ case (x,y) ... } 里面case后边的小括号在模式匹配中是被当成Tuple对待的,猜测背后是类型推断时期做了一些适配的事情。

scala> import scala.tools.reflect.ToolBox

scala> val tb =  scala.reflect.runtime.currentMirror.mkToolBox()

scala> tb.parse("val p2: Function[(Int,Int),String]  = { case (a:Int,b:Int) => \"OK\" }")
res0: tb.u.Tree =
val p2: Function[scala.Tuple2[Int, Int], String] = <empty> match {
  case scala.Tuple2((a @ (_: Int)), (b @ (_: Int))) => "OK"
}

scala> tb.parse("val p2: Function2[Int,Int,String]  = { case (a:Int,b:Int) => \"OK\" }")
res1: tb.u.Tree =
val p2: Function2[Int, Int, String] = <empty> match {
  case scala.Tuple2((a @ (_: Int)), (b @ (_: Int))) => "OK"
}

今天有空用typer-debug参数分析了一下过程, 发现在类型推断的时候,也存在把一个tuple类型适配为(adapt)若干个方法参数的过程;这个过程跟auto tupling相反,称为auto detupling;通过编译器的-Ytyper-debug选项,分别编译这两种写法:

class A{
    val p: Function[(Int,Int),String]  = { case (a:Int,b:Int) => "OK" }
}

$ scala-2.11.4/bin/scalac -Yno-adapted-args -Ytyper-debug  A.scala >a 2>&1

class A{
    val p: Function2[Int,Int,String]  = { case (a:Int,b:Int) => "OK" }
}

$ scala-2.11.4/bin/scalac -Yno-adapted-args -Ytyper-debug  B.scala >b 2>&1   

然后通过diff工具来比较:

可以看到左边红色是Function2的声明,高亮部分:Tuple2确实被适配为了2个方法参数:

[adapt] Tuple2.type adapted to [T1, T2](_1: T1, _2: T2)(T1, T2)

遗憾的是,不像auto tupling可以通过-Yno-adapted-args编译选项禁止,auto detupling没有参数可以禁止掉,或许有我没有找到。