From 2f275b72b2248bb7c522327115acb17223b8387b Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Thu, 26 Jul 2018 01:14:39 -0400 Subject: [PATCH] Improve the output of `kubectl get events` Events have long shown the most data of the core objects in their output, but that data is of varying use to a user. Following the principle that events are intended for the system to communicate information back to the user, and that Message is the primary human readable field, this commit alters the default columns to ensure event is shown with the most width. 1. Events are no longer sorted in the printer (this was a bug and was broken with paging and server side rendering) 2. Only the last seen, type, reason, kind, and message fields are shown by default, which makes the message prominent 3. Source, subobject, count, and first seen are only shown under `-o wide` 4. The duration fields were changed to be the more precise output introduced for job duration (2-3 sig figs) --- pkg/printers/internalversion/printers.go | 55 ++++++++++++------- pkg/printers/internalversion/printers_test.go | 4 +- pkg/registry/core/pod/storage/storage_test.go | 2 +- .../pkg/util/duration/duration.go | 18 +++++- 4 files changed, 53 insertions(+), 26 deletions(-) diff --git a/pkg/printers/internalversion/printers.go b/pkg/printers/internalversion/printers.go index 499a0e7c020..1ef024267ca 100644 --- a/pkg/printers/internalversion/printers.go +++ b/pkg/printers/internalversion/printers.go @@ -21,7 +21,6 @@ import ( "fmt" "io" "net" - "sort" "strconv" "strings" "time" @@ -44,7 +43,6 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/duration" "k8s.io/apimachinery/pkg/util/sets" - "k8s.io/kubernetes/pkg/api/events" "k8s.io/kubernetes/pkg/apis/apps" "k8s.io/kubernetes/pkg/apis/autoscaling" "k8s.io/kubernetes/pkg/apis/batch" @@ -236,15 +234,15 @@ func AddHandlers(h printers.PrintHandler) { eventColumnDefinitions := []metav1beta1.TableColumnDefinition{ {Name: "Last Seen", Type: "string", Description: apiv1.Event{}.SwaggerDoc()["lastTimestamp"]}, - {Name: "First Seen", Type: "string", Description: apiv1.Event{}.SwaggerDoc()["firstTimestamp"]}, - {Name: "Count", Type: "string", Description: apiv1.Event{}.SwaggerDoc()["count"]}, - {Name: "Name", Type: "string", Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]}, - {Name: "Kind", Type: "string", Description: apiv1.Event{}.InvolvedObject.SwaggerDoc()["kind"]}, - {Name: "Subobject", Type: "string", Description: apiv1.Event{}.InvolvedObject.SwaggerDoc()["fieldPath"]}, {Name: "Type", Type: "string", Description: apiv1.Event{}.SwaggerDoc()["type"]}, {Name: "Reason", Type: "string", Description: apiv1.Event{}.SwaggerDoc()["reason"]}, - {Name: "Source", Type: "string", Description: apiv1.Event{}.SwaggerDoc()["source"]}, + {Name: "Kind", Type: "string", Description: apiv1.Event{}.InvolvedObject.SwaggerDoc()["kind"]}, + {Name: "Source", Type: "string", Priority: 1, Description: apiv1.Event{}.SwaggerDoc()["source"]}, {Name: "Message", Type: "string", Description: apiv1.Event{}.SwaggerDoc()["message"]}, + {Name: "Subobject", Type: "string", Priority: 1, Description: apiv1.Event{}.InvolvedObject.SwaggerDoc()["fieldPath"]}, + {Name: "First Seen", Type: "string", Priority: 1, Description: apiv1.Event{}.SwaggerDoc()["firstTimestamp"]}, + {Name: "Count", Type: "string", Priority: 1, Description: apiv1.Event{}.SwaggerDoc()["count"]}, + {Name: "Name", Type: "string", Priority: 1, Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]}, } h.TableHandler(eventColumnDefinitions, printEvent) h.TableHandler(eventColumnDefinitions, printEventList) @@ -515,7 +513,7 @@ func translateTimestampSince(timestamp metav1.Time) string { return "" } - return duration.ShortHumanDuration(time.Since(timestamp.Time)) + return duration.HumanDuration(time.Since(timestamp.Time)) } // translateTimestampUntil returns the elapsed time until timestamp in @@ -525,7 +523,7 @@ func translateTimestampUntil(timestamp metav1.Time) string { return "" } - return duration.ShortHumanDuration(time.Until(timestamp.Time)) + return duration.HumanDuration(time.Until(timestamp.Time)) } var ( @@ -1328,25 +1326,42 @@ func printEvent(obj *api.Event, options printers.PrintOptions) ([]metav1beta1.Ta Object: runtime.RawExtension{Object: obj}, } // While watching event, we should print absolute time. - var FirstTimestamp, LastTimestamp string + var firstTimestamp, lastTimestamp string if options.AbsoluteTimestamps { - FirstTimestamp = obj.FirstTimestamp.String() - LastTimestamp = obj.LastTimestamp.String() + firstTimestamp = obj.FirstTimestamp.String() + lastTimestamp = obj.LastTimestamp.String() } else { - FirstTimestamp = translateTimestampSince(obj.FirstTimestamp) - LastTimestamp = translateTimestampSince(obj.LastTimestamp) + firstTimestamp = translateTimestampSince(obj.FirstTimestamp) + lastTimestamp = translateTimestampSince(obj.LastTimestamp) + } + if options.Wide { + row.Cells = append(row.Cells, + lastTimestamp, + obj.Type, + obj.Reason, + obj.InvolvedObject.Kind, + formatEventSource(obj.Source), + strings.TrimSpace(obj.Message), + obj.InvolvedObject.FieldPath, + firstTimestamp, + int64(obj.Count), + obj.Name, + ) + } else { + row.Cells = append(row.Cells, + lastTimestamp, + obj.Type, + obj.Reason, + obj.InvolvedObject.Kind, + strings.TrimSpace(obj.Message), + ) } - row.Cells = append(row.Cells, LastTimestamp, FirstTimestamp, - int64(obj.Count), obj.Name, obj.InvolvedObject.Kind, - obj.InvolvedObject.FieldPath, obj.Type, obj.Reason, - formatEventSource(obj.Source), obj.Message) return []metav1beta1.TableRow{row}, nil } // Sorts and prints the EventList in a human-friendly format. func printEventList(list *api.EventList, options printers.PrintOptions) ([]metav1beta1.TableRow, error) { - sort.Sort(events.SortableEvents(list.Items)) rows := make([]metav1beta1.TableRow, 0, len(list.Items)) for i := range list.Items { r, err := printEvent(&list.Items[i], options) diff --git a/pkg/printers/internalversion/printers_test.go b/pkg/printers/internalversion/printers_test.go index 73553d57878..8042ce0183c 100644 --- a/pkg/printers/internalversion/printers_test.go +++ b/pkg/printers/internalversion/printers_test.go @@ -1927,7 +1927,7 @@ func TestTranslateTimestampSince(t *testing.T) { {"unknown", translateTimestampSince(metav1.Time{}), ""}, {"30 seconds ago", translateTimestampSince(metav1.Time{Time: time.Now().Add(-3e10)}), "30s"}, {"5 minutes ago", translateTimestampSince(metav1.Time{Time: time.Now().Add(-3e11)}), "5m"}, - {"an hour ago", translateTimestampSince(metav1.Time{Time: time.Now().Add(-6e12)}), "1h"}, + {"an hour ago", translateTimestampSince(metav1.Time{Time: time.Now().Add(-6e12)}), "100m"}, {"2 days ago", translateTimestampSince(metav1.Time{Time: time.Now().UTC().AddDate(0, 0, -2)}), "2d"}, {"months ago", translateTimestampSince(metav1.Time{Time: time.Now().UTC().AddDate(0, 0, -90)}), "90d"}, {"10 years ago", translateTimestampSince(metav1.Time{Time: time.Now().UTC().AddDate(-10, 0, 0)}), "10y"}, @@ -1952,7 +1952,7 @@ func TestTranslateTimestampUntil(t *testing.T) { {"unknown", translateTimestampUntil(metav1.Time{}), ""}, {"in 30 seconds", translateTimestampUntil(metav1.Time{Time: time.Now().Add(3e10 + buf)}), "30s"}, {"in 5 minutes", translateTimestampUntil(metav1.Time{Time: time.Now().Add(3e11 + buf)}), "5m"}, - {"in an hour", translateTimestampUntil(metav1.Time{Time: time.Now().Add(6e12 + buf)}), "1h"}, + {"in an hour", translateTimestampUntil(metav1.Time{Time: time.Now().Add(6e12 + buf)}), "100m"}, {"in 2 days", translateTimestampUntil(metav1.Time{Time: time.Now().UTC().AddDate(0, 0, 2).Add(buf)}), "2d"}, {"in months", translateTimestampUntil(metav1.Time{Time: time.Now().UTC().AddDate(0, 0, 90).Add(buf)}), "90d"}, {"in 10 years", translateTimestampUntil(metav1.Time{Time: time.Now().UTC().AddDate(10, 0, 0).Add(buf)}), "10y"}, diff --git a/pkg/registry/core/pod/storage/storage_test.go b/pkg/registry/core/pod/storage/storage_test.go index 1b919009a5c..c9581980b24 100644 --- a/pkg/registry/core/pod/storage/storage_test.go +++ b/pkg/registry/core/pod/storage/storage_test.go @@ -463,7 +463,7 @@ func TestConvertToTableList(t *testing.T) { out: &metav1beta1.Table{ ColumnDefinitions: columns, Rows: []metav1beta1.TableRow{ - {Cells: []interface{}{"foo", "1/2", "Pending", int64(10), "1y", "10.1.2.3", "test-node", "nominated-node"}, Object: runtime.RawExtension{Object: pod1}}, + {Cells: []interface{}{"foo", "1/2", "Pending", int64(10), "370d", "10.1.2.3", "test-node", "nominated-node"}, Object: runtime.RawExtension{Object: pod1}}, }, }, }, diff --git a/staging/src/k8s.io/apimachinery/pkg/util/duration/duration.go b/staging/src/k8s.io/apimachinery/pkg/util/duration/duration.go index 0b88ab6c1fe..961ec5ed8b2 100644 --- a/staging/src/k8s.io/apimachinery/pkg/util/duration/duration.go +++ b/staging/src/k8s.io/apimachinery/pkg/util/duration/duration.go @@ -57,17 +57,29 @@ func HumanDuration(d time.Duration) string { } minutes := int(d / time.Minute) if minutes < 10 { - return fmt.Sprintf("%dm%ds", minutes, int(d/time.Second)%60) + s := int(d/time.Second) % 60 + if s == 0 { + return fmt.Sprintf("%dm", minutes) + } + return fmt.Sprintf("%dm%ds", minutes, s) } else if minutes < 60*3 { return fmt.Sprintf("%dm", minutes) } hours := int(d / time.Hour) if hours < 8 { - return fmt.Sprintf("%dh%dm", hours, int(d/time.Minute)%60) + m := int(d/time.Minute) % 60 + if m == 0 { + return fmt.Sprintf("%dh", hours) + } + return fmt.Sprintf("%dh%dm", hours, m) } else if hours < 48 { return fmt.Sprintf("%dh", hours) } else if hours < 24*8 { - return fmt.Sprintf("%dd%dh", hours/24, hours%24) + h := hours % 24 + if h == 0 { + return fmt.Sprintf("%dd", hours/24) + } + return fmt.Sprintf("%dd%dh", hours/24, h) } else if hours < 24*365*2 { return fmt.Sprintf("%dd", hours/24) } else if hours < 24*365*8 {