kubernetes 按格式不一致的经过时间字段排序(按事件发生后的实际时间排序k8s事件)

6qfn3psc  于 2022-12-17  发布在  Kubernetes
关注(0)|答案(7)|浏览(126)

编辑

我发现自己经常看一些事情是否已经停止发生。要做到这一点,按时间顺序看事件是有帮助的...
This solution * 似乎 * 工作,但格式仍然让我发疯...
我的解决方案“算是”奏效了-

kubectl get events |
  sed -E '/^[6789][0-9]s/{h; s/^(.).*/\1/; y/6789/0123/; s/^(.)/01m\1/;
                          x; s/^.(.*)/\1/; H;
                          x; s/\n//; };
          s/^10([0-9]s)/01m4\1/; s/^11([0-9]s)/01m5\1/; s/^([0-9]s)/00m0\1/; s/^([0-9]+s)/00m\1/;
          s/^([0-9]m)/0\1/; s/^([0-9]+m)([0-9]s)/\10\2/;
          s/^L/_L/;' | sort -r

......这对我来说似乎有点矫枉过正。
空格分隔的左对齐字段没有前导零,仅将秒报告为[0-9]+s(最长2米),然后报告为[0-9]+m[0-9]+s(最长5米),之后似乎仅报告[0-9]+m
有人有一个简短的,甚至可能是简单的,* 更容易阅读 * 的解决方案,工程?
没有工具的偏好(sedawkperl,原生bash等),只要它能工作,而且很可能已经安装在我需要工作的任何地方。
这不是一个高优先级,但似乎是一个有趣的小挑战,我想我会分享。
我的测试数据:

$: cat sample
LAST ...
28s ...
2m22s ...
46m ...
7s ...
75s ...
119s ...

具有所需输出的结果-

$: sed -E '/^[6789][0-9]s/{h; s/^(.).*/\1/; y/6789/0123/; s/^(.)/01m\1/;
                           x; s/^.(.*)/\1/; H;
                           x; s/\n//; };
           s/^10([0-9]s)/01m4\1/; s/^11([0-9]s)/01m5\1/; s/^([0-9]s)/00m0\1/; s/^([0-9]+s)/00m\1/;
           s/^([0-9]m)/0\1/; s/^([0-9]+m)([0-9]s)/\10\2/;
           s/^L/_L/;' sample | sort -r
_LAST ...
46m ...
02m22s ...
01m59s ...
01m15s ...
00m28s ...
00m07s ...

我随意地将现有的通用输出格式转换为标准化版本,只是为了让它可以方便地传递给团队中的其他成员。无论哪种方式,它都只是用来“目测”数据,所以只要易于阅读,其他格式就不是问题。
虽然理论上可以包括小时和天,这样的旧事件通常不会被这个工具报告,并且超出了这个问题的范围,如果需要,我可以推断出任何解决方案。因为我 * 可以 * 从this approach获得顺序,我真的只需要优雅的格式选项。
对Daweo的awk解决方案进行了笨拙的修改,并进行了格式化-

$: awk '/^[0-9]/{ if($1!~/m/){$1="0m" $1}; split($1,arr,/m/);
        t=arr[1]*60+arr[2]; m=(t-(t%60))/60; s=t-(m*60);
        m=sprintf("%02dm",m); if(s){ s=sprintf("%02ds",s) } else s="";
        $1=sprintf("%s%s",m,s); print; } /^L/{print "_"$0}' sample |
   sort -r
_LAST ...
46m ...
02m22s ...
01m59s ...
01m15s ...
00m28s ...
00m07s ...

其他人仍然感激。

qoefvg9y

qoefvg9y1#

我会先将所有内容转换为秒,然后将其打印为HH:MM:SS,例如:

$ cat tst.awk
BEGIN {
    split("h m s",denoms)
    fmts["s"] = fmts["m"] = fmts["h"] = "%02d"
    mults["s"] = 1
    mults["m"] = 60
    mults["h"] = 60 * 60
}
sub(/^L/,"_L") {
    print
    next
}
{
    time = $1

    secs = 0
    while ( match(time,/[0-9]+./) ) {
        value = substr(time,1,RLENGTH-1)
        denom = substr(time,RLENGTH)
        time  = substr(time,RLENGTH+1)
        secs += value * mults[denom]
    }

    for ( i=1; i in denoms; i++ ) {
        denom = denoms[i]
        out = (i>1 ? out ":" : "") sprintf(fmts[denom],int(secs/mults[denom]))
        secs %= mults[denom]
    }

    $1 = out

    print | "sort -r"
}
$ awk -f tst.awk sample
_LAST ...
00:46:00 ...
00:02:22 ...
00:01:59 ...
00:01:15 ...
00:00:28 ...
00:00:07 ...

显然,如果您想包括天,那么在BEGIN部分添加"d"的定义,对于其他更长的持续时间也是如此。

olhwl3o2

olhwl3o22#

我将按照以下方式利用GNU AWK完成此任务,让file.txt内容

2m36s ...
2m9s ...
28s ...
2m22s ...
2m6s ...
46m ...
7s ...
45m ...
3m9s ...
31m ...
16m ...
75s ...
74s ...
67s ...
46m ...
63s ...
2m15s ...
119s ...
16m ...
75s ...
74s ...
69s ...
46m ...
31m ...
16m ...
75s ...
62s ...

那么

awk '$1!~/m/{$1="0m" $1}{split($1,arr,/m/);$1=arr[1]*60+arr[2];print}' file.txt

给出输出

156 ...
129 ...
28 ...
142 ...
126 ...
2760 ...
7 ...
2700 ...
189 ...
1860 ...
960 ...
75 ...
74 ...
67 ...
2760 ...
63 ...
135 ...
119 ...
960 ...
75 ...
74 ...
69 ...
2760 ...
1860 ...
960 ...
75 ...
62 ...

说明:如果在第一个字段中没有m,则在m字符处使用split function,然后计算值:我将m之前的内容乘以60,以转换为秒,然后加上m之后的内容,得到以秒为单位的总数,对于没有秒部分的行,秒部分是空字符串,在算术中使用时会变为零。

awk '$1!~/m/{$1="0m" $1}{split($1,arr,/m/);$1=arr[1]*60+arr[2];print}' file.txt | sort -n

它给出输出

7 ...
28 ...
62 ...
63 ...
67 ...
69 ...
74 ...
74 ...
75 ...
75 ...
75 ...
119 ...
126 ...
129 ...
135 ...
142 ...
156 ...
189 ...
960 ...
960 ...
960 ...
1860 ...
1860 ...
2700 ...
2760 ...
2760 ...
2760 ...
  • (在GNU Awk 5.0.1和sort(GNU核心实用程序)8.30中测试)*
vshtjzan

vshtjzan3#

如果示例中显示了所有可能的时间格式,这可能会起作用。它显示了文件、输出和最终排序,为了清晰起见,它们粘贴在一起。
它查找m,乘以60并加上任何现有的秒数,如果没有找到m,它只打印秒数。

$ paste sample <(awk '/m/{split($1,ar,"m"); print ar[1] * 60 + ar[2]} 
                 !/m/{print $1 * 1}' sample) | sort -nk 3
7s ...  7
28s ... 28
62s ... 62
63s ... 63
67s ... 67
69s ... 69
74s ... 74
74s ... 74
75s ... 75
75s ... 75
75s ... 75
119s ...    119
2m6s ...    126
2m9s ...    129
2m15s ...   135
2m22s ...   142
2m36s ...   156
3m9s ...    189
16m ... 960
16m ... 960
16m ... 960
31m ... 1860
31m ... 1860
45m ... 2700
46m ... 2760
46m ... 2760
46m ... 2760
0md85ypi

0md85ypi4#

仅使用GNU awk:

awk '{match($1,  /[0-9]+m/, m); match($1, /[0-9]+s/, s)
    arr[m[0]*60 + s[0]] = $0
}
    END {
    n = asorti(arr, sorted, "@ind_num_asc")
    for(i = 1; i <= n; i++)
          print arr[sorted[i]]
}
' sample

看起来比一堆链条小汽车干净一点。上面写着:

LAST ...
7s ...
28s ...
62s ...
63s ...
67s ...
69s ...
74s ...
75s ...
119s ...
2m6s ...
2m9s ...
2m15s ...
2m22s ...
2m36s ...
3m9s ...
16m ...
31m ...
45m ...
46m ...

因为我实际上认为您希望最新的条目显示在顶部附近,如果不只是将 ind_num_asc 更改为 ind_num_desc 的话。

vxf3dgd4

vxf3dgd45#

使用sed

$ cat script.sed
1! {
        s/^[0-9]s/0m0&/
        /^[0-9]{2,}s/ {
                s/^6/01m0/
                s/^7/01m1/
                s/^8/01m2/
                s/^9/01m3/
                s/^10/01m4/
                s/^11/01m5/
                s/^[0-5][0-9]s/0m&/
        }
        s/^([0-9]m)([0-9]s)/0\10\2/
        s/^([0-9]+m)([0-9]s)/\10\2/
        s/^[0-9]m/0&/
}
1s/^/_/

要运行

$ sed -Ef script.sed input_file | sort -r
_LAST ...
46m ...
46m ...
46m ...
45m ...
31m ...
31m ...
16m ...
16m ...
16m ...
03m09s ...
02m36s ...
02m22s ...
02m15s ...
02m09s ...
02m06s ...
01m59s ...
01m15s ...
01m15s ...
01m15s ...
01m14s ...
01m14s ...
01m09s ...
01m07s ...
01m03s ...
01m02s ...
00m28s ...
00m07s ...
rggaifut

rggaifut6#

如果您正在寻找一个通用的实用程序来“规范化”报告的值,这里有一个演示该功能的“hack”。

#!/bin/bash

DBG=1

INPUT=`basename "$0" ".sh" `.input

cat >"${INPUT}" <<"EnDoFiNpUt"
2m36s ...
2m9s ...
28s ...
2m22s ...
2m6s ...
46m ...
7s ...
45m ...
3m9s ...
31m ...
16m ...
75s ...
74s ...
67s ...
46m ...
63s ...
2m15s ...
119s ...
16m ...
75s ...
74s ...
69s ...
46m ...
31m ...
16m ...
75s ...
62s ...
EnDoFiNpUt

#cat >"${INPUT}" <<"EnDoFiNpUt"
#119s ...
#EnDoFiNpUt

awk -v dbg="${DBG}" 'BEGIN{
    split("", times) ;
    items=0 ;
}{
    if( $0 == "" ){
        exit ;
    }else{
        if( dbg == 1 ){ print "\n"$0 | "cat >&2" ; } ;
        rem=$1 ;
        items++ ;

        posH=index( rem, "h" ) ;
        if( posH == 0 ){
            hr=0 ;
            if( dbg == 1 ){ print "\thr = "hr | "cat >&2" ; } ;

            posM=index( rem, "m" ) ;
            if( posM == 0 ){
                min=0 ;
                if( dbg == 1 ){ print "\tmin = "min | "cat >&2" ; } ;

                posS=index( rem, "s" ) ;
                if( posS == 0 ){
                    if( rem = "" ){
                        sec=0 ;
                    }else{
                        minX=sprintf("%d", rem/60 ) ;
                        sec=rem-minX*60 ;
                        min=min+minX ;
                        if( dbg == 1 && minX > 0 ){ print "\t\tmin = "min | "cat >&2" ; } ;
                    } ;
                }else{
                    beg=substr( rem, 1, posS-1) ;
                    if( rem = "" ){
                        sec=0 ;
                    }else{
                        minX=sprintf("%d", beg/60 ) ;
                        sec=beg-minX*60 ;
                        min=min+minX ;
                        if( dbg == 1 && minX > 0 ){ print "\t\tmin = "min | "cat >&2" ; } ;
                    } ;
                } ;
            }else{
                min=substr( rem, 1, posM-1) ;
                rem=substr( rem, posM+1 ) ;
                if( dbg == 1 ){ print "\tmin = "min | "cat >&2" ; } ;

                posS=index( rem, "s" ) ;
                if( posS == 0 ){
                    if( rem = "" ){
                        sec=0 ;
                    }else{
                        minX=sprintf("%d", rem/60 ) ;
                        sec=rem-minX*60 ;
                        min=min+minX ;
                        if( dbg == 1 && minX > 0 ){ print "\t\tmin = "min | "cat >&2" ; } ;
                    } ;
                }else{
                    beg=substr( rem, 1, posS-1) ;
                    if( rem = "" ){
                        sec=0 ;
                    }else{
                        minX=sprintf("%d", beg/60 ) ;
                        sec=beg-minX*60 ;
                        min=min+minX ;
                        if( dbg == 1 && minX > 0 ){ print "\t\tmin = "min | "cat >&2" ; } ;
                    } ;
                } ;
            } ;
        }else{
            hr=substr( rem, 1, posH-1) ;
            rem=substr( rem, posH+1 ) ;
            if( dbg == 1 ){ print "\thr = "hr | "cat >&2" ; } ;

            posM=index( rem, "m" ) ;
            if( posM == 0 ){
                min=0 ;
                if( dbg == 1 ){ print "\tmin = "min | "cat >&2" ; } ;

                posS=index( rem, "s" ) ;
                if( posS == 0 ){
                    if( rem = "" ){
                        sec=0 ;
                    }else{
                        minX=sprintf("%d", rem/60 ) ;
                        sec=rem-minX*60 ;
                        min=min+minX ;
                        if( dbg == 1 && minX > 0 ){ print "\t\tmin = "min | "cat >&2" ; } ;
                    } ;
                }else{
                    beg=substr( rem, 1, posS-1) ;
                    if( rem = "" ){
                        sec=0 ;
                    }else{
                        minX=sprintf("%d", beg/60 ) ;
                        sec=beg-minX*60 ;
                        min=min+minX ;
                        if( dbg == 1 && minX > 0 ){ print "\t\tmin = "min | "cat >&2" ; } ;
                    } ;
                } ;
            }else{
                min=substr( rem, 1, posM-1) ;
                rem=substr( rem, posM+1 ) ;
                if( dbg == 1 ){ print "\tmin = "min | "cat >&2" ; } ;

                posS=index( rem, "s" ) ;
                if( posS == 0 ){
                    if( rem = "" ){
                        sec=0 ;
                    }else{
                        minX=sprintf("%d", rem/60 ) ;
                        sec=rem-minX*60 ;
                        min=min+minX ;
                        if( dbg == 1 && minX > 0 ){ print "\t\tmin = "min | "cat >&2" ; } ;
                    } ;
                }else{
                    beg=substr( rem, 1, posS-1) ;
                    if( rem = "" ){
                        sec=0 ;
                    }else{
                        minX=sprintf("%d", beg/60 ) ;
                        sec=beg-minX*60 ;
                        min=min+minX ;
                        if( dbg == 1 && minX > 0 ){ print "\t\tmin = "min | "cat >&2" ; } ;
                    } ;
                } ;
            } ;
            if( dbg == 1 ){ print "\tsec = "sec | "cat >&2" ; } ;
        } ;
        times[items]=sprintf("%02dh%02dm%02ds", hr, min, sec ) ;
        if( dbg == 1 ){ print "\t"times[items] | "cat >&2" ; } ;
    } ;
}END{
    if( dbg == 1 ){ print "Normalized Values:" } ; 
    for( i=1 ; i <= items ; i++ ){
        print times[i] ;
    } ;
}' "${INPUT}" > "${INPUT}.out"

echo ""
cat "${INPUT}.out"

echo ""
echo "Sorted Values:"
grep -v 'Normalized' "${INPUT}.out" | sort -n
c8ib6hqw

c8ib6hqw7#

我想感谢大家的时间和贡献。
不出所料,我学到了一些东西
一位同事看到了这一点,并私下给我发来了一个解决方案 * 非常 * 类似于this solution,来自我在问题中引用的同一页,它帮助我理解了我的问题和解决它的内置方法。我在这里转发了一些解释,希望有人会发现它有用,也许可以帮助我完善自己的理解。
我对典型输出的主要问题是时间字段的格式(imo)* 可怕 * 不一致-我假设是为了简洁,这可能是好的-排序显然是按对象而不是时间(这在许多情况下也是有意义的),然后按对象的.lastTimestamp排序。
为了记录在案,kubectl get --help列出了(除此之外)

--sort-by='': If non-empty, sort list types using this field specification.

注意k8s对象定义中的The field specification is expressed as a JSONPath expression。并且

-o, --output='': Output format.

它有一个很长的选项列表,包括X1 M3 N1 X,它可以让你“自己卷”。
因此,我重新构建了正常的输出,用实际的、一致的时间戳字段替换了有问题的第一列,并更改了默认排序顺序。

kubectl get events -o custom-columns="TIMESTAMP:{.lastTimestamp},REASON:{.reason},TYPE:{.type},OBJ_NAME:{.involvedObject.name},MESSAGE:{.message}" --sort-by={.lastTimestamp,.type,.reason}

更棒的是,帮助文本直接引用了相关文档,所以我很快就能将其转换为模板文件:

$: cat $HOME/.kube/custCol.txt
TIMESTAMP      REASON  TYPE  OBJ_NAME             MESSAGE
lastTimestamp  reason  type  involvedObject.name  message

$: kubectl get events -o custom-columns-file=$HOME/.kube/custCol.txt --sort-by={.metadata.creationTimestamp,.type,.reason}

仍然很长,但不需要通过管道传输到另一个进程,我可以摸索逻辑。为了使它简洁,更容易输入和阅读,我做了一个别名-

alias events='kubectl get events -o custom-columns-file=$HOME/.kube/custCol.txt --sort-by={.metadata.creationTimestamp,.type,.reason}

现在,我可以通过在末尾添加群集和命名空间来指定它们-

$: events --context bulk -n bulk-sit1 | head -6
TIMESTAMP             REASON                 TYPE      OBJ_NAME                                     MESSAGE
2022-12-14T16:15:18Z   BackOff                Warning   seasonalsuspends-cron-job-1671034500-7c7md   Back-off restarting failed container
2022-12-14T16:00:29Z   BackOff                Warning   nonpays-cron-job-1671033600-7xhl7            Back-off restarting failed container
2022-12-14T16:06:51Z   BackoffLimitExceeded   Warning   nonpays-cron-job-1671033600                  Job has reached the specified backoff limit
2022-12-14T16:22:07Z   BackoffLimitExceeded   Warning   seasonalsuspends-cron-job-1671034500         Job has reached the specified backoff limit
2022-12-14T15:45:20Z   Completed              Normal    nonpays-cron-job-1671032700                  Job completed

我还没有弄清楚内置的过滤器,所以现在我有一个grep函数,用于警告-

warnings() { local args=(get events --sort-by={.lastTimestamp,.type,.reason} "$@" 
   -o custom-columns=TIMESTAMP:{.lastTimestamp},REASON:{.reason},TYPE:{.type},OBJ_NAME:{.involvedObject.name},MESSAGE:{.message});
  kubectl "${args[@]}" | grep ' Warning '
}

希望有人能用上它。

相关问题