8000 Add support to configure colors for pods and containers (#306) · stern/stern@f4b2edc · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Commit f4b2edc

Browse files
author
Takashi Kusumi
authored
Add support to configure colors for pods and containers (#306)
1 parent d1b5d74 commit f4b2edc

File tree

5 files changed

+218
-9
lines changed

5 files changed

+218
-9
lines changed

README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ Supported Kubernetes resources are `pod`, `replicationcontroller`, `service`, `d
7676
`--completion` | | Output stern command-line completion code for the specified shell. Can be 'bash', 'zsh' or 'fish'.
7777
`--config` | `~/.config/stern/config.yaml` | Path to the stern config file
7878
`--container`, `-c` | `.*` | Container name when multiple containers in pod. (regular expression)
79+
`--container-colors` | | Specifies the colors used to highlight container names. Use the same format as --pod-colors. Defaults to the values of --pod-colors if omitted, and must match its length.
7980
`--container-state` | `all` | Tail containers with state in running, waiting, terminated, or all. 'all' matches all container states. To specify multiple states, repeat this or set comma-separated value.
8081
`--context` | | The name of the kubeconfig context to use
8182
`--diff-container`, `-d` | `false` | Display different colors for different containers.
@@ -94,6 +95,7 @@ Supported Kubernetes resources are `pod`, `replicationcontroller`, `service`, `d
9495
`--node` | | Node name to filter on.
9596
`--only-log-lines` | `false` | Print only log lines
9697
`--output`, `-o` | `default` | Specify predefined template. Currently support: [default, raw, json, extjson, ppextjson]
98+
`--pod-colors` | | Specifies the colors used to highlight pod names. Provide colors as a comma-separated list using SGR (Select Graphic Rendition) sequences, e.g., "91,92,93,94,95,96".
9799
`--prompt`, `-p` | `false` | Toggle interactive prompt for selecting 'app.kubernetes.io/instance' label values.
98100
`--selector`, `-l` | | Selector (label query) to filter on. If present, default to ".*" for the pod-query.
99101
`--show-hidden-options` | `false` | Print a list of hidden options.
@@ -197,6 +199,28 @@ The behavior and the default are different depending on the presence of the `--n
197199

198200
The combination of `--max-log-requests 1` and `--no-follow` will be helpful if you want to show logs in order.
199201

202+
### Customize highlight colors
203+
You can configure highlight colors for pods and containers in [the config file](#config-file) using a comma-separated list of [SGR (Select Graphic Rendition) sequences](https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters), as shown below. If you omit `container-colors`, the pod colors will be used as container colors as well.
204+
205+
```yaml
206+
# Green, Yellow, Blue, Magenta, Cyan, White
207+
pod-colors: "32,33,34,35,36,37"
208+
209+
# Colors with underline (4)
210+
# If empty, the pod colors will be used as container colors
211+
container-colors: "32;4,33;4,34;4,35;4,36;4,37;4"
212+
```
213+
214+
This format enables the use of various attributes, such as underline, background colors, 8-bit colors, and 24-bit colors, if your terminal supports them.
215+
216+
The equivalent flags `--pod-colors` and `--container-colors` are also available. The following command applies [24-bit colors](https://en.wikipedia.org/wiki/ANSI_escape_code#24-bit) using the `--pod-colors` flag.
217+
218+
```bash
219+
# Monokai theme
220+
podColors="38;2;255;97;136,38;2;169;220;118,38;2;255;216;102,38;2;120;220;232,38;2;171;157;242"
221+
stern --pod-colors "$podColors" deploy/app
222+
```
223+
200224
## Examples:
201225
Tail all logs from all namespaces
202226
```

cmd/cmd.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ type options struct {
8787
showHiddenOptions bool
8888
stdin bool
8989
diffContainer bool
90+
podColors []string
91+
containerColors []string
9092

9193
client kubernetes.Interface
9294
clientConfig clientcmd.ClientConfig
@@ -169,6 +171,9 @@ func (o *options) Run(cmd *cobra.Command) error {
169171
if err := o.setVerbosity(); err != nil {
170172
return err
171173
}
174+
if err := o.setColorList(); err != nil {
175+
return err
176+
}
172177

173178
config, err := o.sternConfig()
174179
if err != nil {
@@ -339,6 +344,13 @@ func (o *options) setVerbosity() error {
339344
return nil
340345
}
341346

347+
func (o *options) setColorList() error {
348+
if len(o.podColors) > 0 || len(o.containerColors) > 0 {
349+
return stern.SetColorList(o.podColors, o.containerColors)
350+
}
351+
return nil
352+
}
353+
342354
// overrideFlagSetDefaultFromConfig overrides the default value of the flagSets
343355
// from the config file
344356
func (o *options) overrideFlagSetDefaultFromConfig(fs *pflag.FlagSet) error {
@@ -438,6 +450,8 @@ func (o *options) AddFlags(fs *pflag.FlagSet) {
438450
fs.BoolVar(&o.showHiddenOptions, "show-hidden-options", o.showHiddenOptions, "Print a list of hidden options.")
439451
fs.BoolVar(&o.stdin, "stdin", o.stdin, "Parse logs from stdin. All Kubernetes related flags are ignored when it is set.")
440452
fs.BoolVarP(&o.diffContainer, "diff-container", "d", o.diffContainer, "Display different colors for different containers.")
453+
fs.StringSliceVar(&o.podColors, "pod-colors", o.podColors, "Specifies the colors used to highlight pod names. Provide colors as a comma-separated list using SGR (Select Graphic Rendition) sequences, e.g., \"91,92,93,94,95,96\".")
454+
fs.StringSliceVar(&o.containerColors, "container-colors", o.containerColors, "Specifies the colors used to highlight container names. Use the same format as --pod-colors. Defaults to the values of --pod-colors if omitted, and must match its length.")
441455

442456
fs.Lookup("timestamps").NoOptDefVal = "default"
443457
}

stern/color.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package stern
2+
3+
import (
4+
"errors"
5+
"strconv"
6+
"strings"
7+
8+
"github.com/fatih/color"
9+
)
10+
11+
var colorList = [][2]*color.Color{
12+
{color.New(color.FgHiCyan), color.New(color.FgCyan)},
13+
{color.New(color.FgHiGreen), color.New(color.FgGreen)},
14+
{color.New(color.FgHiMagenta), color.New(color.FgMagenta)},
15+
{color.New(color.FgHiYellow), color.New(color.FgYellow)},
16+
{color.New(color.FgHiBlue), color.New(color.FgBlue)},
17+
{color.New(color.FgHiRed), color.New(color.FgRed)},
18+
}
19+
20+
func SetColorList(podColors, containerColors []string) error {
21+
colors, err := parseColors(podColors, containerColors)
22+
if err != nil {
23+
return err
24+
}
25+
colorList = colors
26+
return nil
27+
}
28+
29+
func parseColors(podColors, containerColors []string) ([][2]*color.Color, error) {
30+
if len(podColors) == 0 {
31+
return nil, errors.New("pod-colors must not be empty")
32+
}
33+
if len(containerColors) == 0 {
34+
// if containerColors is empty, use podColors as containerColors
35+
return createColorPairs(podColors, podColors)
36+
}
37+
if len(containerColors) != len(podColors) {
38+
return nil, errors.New("pod-colors and container-colors must have the same length")
39+
}
40+
return createColorPairs(podColors, containerColors)
41+
}
42+
43+
func createColorPairs(podColors, containerColors []string) ([][2]*color.Color, error) {
44+
colorList := make([][2]*color.Color, 0, len(podColors))
45+
for i := 0; i < len(podColors); i++ {
46+
podColor, err := sgrSequenceToColor(podColors[i])
47+
if err != nil {
48+
return nil, err
49+
}
50+
containerColor, err := sgrSequenceToColor(containerColors[i])
51+
if err != nil {
52+
return nil, err
53+
}
54+
colorList = append(colorList, [2]*color.Color{podColor, containerColor})
55+
}
56+
return colorList, nil
57+
}
58+
59+
// sgrSequenceToColor converts a string representing SGR sequence
60+
// separated by ";" into a *color.Color instance.
61+
// For example, "31;4" means red foreground with underline.
62+
// https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
63+
func sgrSequenceToColor(s string) (*color.Color, error) {
64+
parts := strings.Split(s, ";")
65+
attrs := make([]color.Attribute, 0, len(parts))
66+
for _, part := range parts {
67+
attr, err := strconv.ParseInt(strings.TrimSpace(part), 10, 32)
68+
if err != nil {
69+
return nil, err
70+
}
71+
attrs = append(attrs, color.Attribute(attr))
72+
}
73+
return color.New(attrs...), nil
74+
}

stern/color_test.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package stern
2+
3+
import (
4+
"testing"
5+
6+
"github.com/fatih/color"
7+
)
8+
9+
func TestParseColors(t *testing.T) {
10+
tests := []struct {
11+
desc string
12+
podColors []string
13+
containerColors []string
14+
want [][2]*color.Color
15+
wantError bool
16+
}{
17+
{
18+
desc: "both pod and container colors are specified",
19+
podColors: []string{"91", "92", "93"},
20+
containerColors: []string{"31", "32", "33"},
21+
want: [][2]*color.Color{
22+
{color.New(color.FgHiRed), color.New(color.FgRed)},
23+
{color.New(color.FgHiGreen), color.New(color.FgGreen)},
24+
{color.New(color.FgHiYellow), color.New(color.FgYellow)},
25+
},
26+
},
27+
{
28+
desc: "only pod colors are specified",
29+
podColors: []string{"91", "92", "93"},
30+
containerColors: []string{},
31+
want: [][2]*color.Color{
32+
{color.New(color.FgHiRed), color.New(color.FgHiRed)},
33+
{color.New(color.FgHiGreen), color.New(color.FgHiGreen)},
34+
{color.New(color.FgHiYellow), color.New(color.FgHiYellow)},
35+
},
36+
},
37+
{
38+
desc: "multiple attributes",
39+
podColors: []string{"4;91"},
40+
containerColors: []string{"38;2;255;97;136"},
41+
want: [][2]*color.Color{
42+
{
43+
color.New(color.Underline, color.FgHiRed),
44+
color.New(38, 2, 255, 97, 136), // 24-bit color
45+
},
46+
},
47+
},
48+
{
49+
desc: "spaces are ignored",
50+
podColors: []string{" 91 ", "\t92\t"},
51+
containerColors: []string{},
52+
want: [][2]*color.Color{
53+
{color.New(color.FgHiRed), color.New(color.FgHiRed)},
54+
{color.New(color.FgHiGreen), color.New(color.FgHiGreen)},
55+
},
56+
},
57+
// error patterns
58+
{
59+
desc: "only container colors are specified",
60+
podColors: []string{},
61+
containerColors: []string{"31", "32", "33"},
62+
wantError: true,
63+
},
64+
{
65+
desc: "both pod and container colors are empty",
66+
podColors: []string{},
67+
containerColors: []string{},
68+
wantError: true,
69+
},
70+
{
71+
desc: "invalid color",
72+
podColors: []string{"a"},
73+
containerColors: []string{""},
74+
wantError: true,
75+
},
76+
}
77+
for _, tt := range tests {
78+
t.Run(tt.desc, func(t *testing.T) {
79+
colorList, err := parseColors(tt.podColors, tt.containerColors)
80+
81+
if tt.wantError {
82+
if err == nil {
83+
t.Error("expected err, but got nil")
84+
}
85+
return
86+
}
87+
if err != nil {
88+
t.Fatalf("unexpected err: %v", err)
89+
}
90+
91+
if len(tt.want) != len(colorList) {
92+
t.Fatalf("expected colorList of size %d, but got %d", len(tt.want), len(colorList))
93+
}
94+
95+
for i, wantPair := range tt.want {
96+
gotPair := colorList[i]
97+
if !wantPair[0].Equals(gotPair[0]) {
98+
t.Errorf("colorList[%d][0]: expected %v, but got %v", i, wantPair[0], gotPair[0])
99+
}
100+
if !wantPair[1].Equals(gotPair[1]) {
101+
t.Errorf("colorList[%d][1]: expected %v, but got %v", i, wantPair[1], gotPair[1])
102+
}
103+
}
104+
})
105+
}
106+
}

stern/tail.go

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,6 @@ func NewTail(clientset corev1client.CoreV1Interface, nodeName, namespace, podNam
8787
}
8888
}
8989

90-
var colorList = [][2]*color.Color{
91-
{color.New(color.FgHiCyan), color.New(color.FgCyan)},
92-
{color.New(color.FgHiGreen), color.New(color.FgGreen)},
93-
{color.New(color.FgHiMagenta), color.New(color.FgMagenta)},
94-
{color.New(color.FgHiYellow), color.New(color.FgYellow)},
95-
{color.New(color.FgHiBlue), color.New(color.FgBlue)},
96-
{color.New(color.FgHiRed), color.New(color.FgRed)},
97-
}
98-
9990
func determineColor(podName, containerName string, diffContainer bool) (podColor, containerColor *color.Color) {
10091
colors := colorList[colorIndex(podName)]
10192
if diffContainer {

0 commit comments

Comments
 (0)
0