Skip to content

Commit 766554c

Browse files
committed
fix: Align template variables with Docker nomenclature
The .ContainerName was in fact the service name when using compose. It's now the actual container name and the service name is available as .ServiceName. For convenience .ServiceName is set to the container name when using plain docker. When support for Swarm is added the naming becomes inconsistent as the name for the same concept changes between Swarm and non-Swarm. A compose project is the equivalent of a Swarm namespace/stack. So the .ComposeProject is changed to .Namespace as it's a quite neutral name for both setups. The .ComposeColor is changed accordingly. Similarly, in compose the container instance is called container number whereas in Swarm it's called a slot. The new variable to be used for both is .ContainerNumber.
1 parent ebe6d98 commit 766554c

File tree

12 files changed

+147
-109
lines changed

12 files changed

+147
-109
lines changed

README.md

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,16 @@ The `container query` is a regular expression of the container name; you could p
3939
`--color` | `auto` | Force set color output. 'auto': colorize if tty attached, 'always': always colorize, 'never': never colorize.
4040
`--completion` | | Output tailfin command-line completion code for the specified shell. Can be 'bash', 'zsh' or 'fish'.
4141
`--compose` | `[]` | Compose project name to match (regular expression)
42-
`--compose-colors` | | Specifies the colors used to highlight container names. Provide colors as a comma-separated list using SGR (Select Graphic Rendition) sequences, e.g., "91,92,93,94,95,96".
4342
`--config` | `~/.config/tailfin/config.yaml` | Path to the tailfin config file
44-
`--container-colors` | | Specifies the colors used to highlight compose project names. Use the same format as --container-colors. Defaults to the values of --container-colors if omitted, and must match its length.
43+
`--container-colors` | | Specifies the colors used to highlight container names. Provide colors as a comma-separated list using SGR (Select Graphic Rendition) sequences, e.g., "91,92,93,94,95,96".
4544
`--exclude`, `-e` | `[]` | Log lines to exclude. (regular expression)
4645
`--exclude-container`, `-E` | `[]` | Container name to exclude. (regular expression)
4746
`--highlight`, `-H` | `[]` | Log lines to highlight. (regular expression)
4847
`--image`, `-m` | `[]` | Images to match (regular expression)
4948
`--include`, `-i` | `[]` | Log lines to include. (regular expression)
5049
`--label`, `-l` | `[]` | Label query to filter on. One key or `key=value` per flag instance.
5150
`--max-log-requests` | `-1` | Maximum number of concurrent logs to request. Defaults to 50, but 5 when specifying --no-follow
51+
`--namespace-colors` | | Specifies the colors used to highlight namespace (compose project). Use the same format as --container-colors. Defaults to the values of --container-colors if omitted, and must match its length.
5252
`--no-follow` | `false` | Exit when all logs have been shown.
5353
`--only-log-lines` | `false` | Print only log lines
5454
`--output`, `-o` | `default` | Specify predefined template. Currently support: [default, raw, json, extjson, ppextjson]
@@ -95,19 +95,22 @@ It accepts a custom template through the `--template` flag, which will be
9595
compiled to a Go template and then used for every log message. This Go template
9696
will receive the following struct:
9797

98-
| property | type | description |
99-
|-----------------|--------|------------------------------------------------|
100-
| `Message` | string | The log message itself |
101-
| `ComposeProject`| string | The name of the docker compose project, if any |
102-
| `ContainerName` | string | The name of the container |
98+
| property | type | Docker | Compose |
99+
|------------------|--------|----------------|---------------------------|
100+
| `Message` | string | Log message | Log message |
101+
| `ContainerName` | string | Container name | Container name |
102+
| `ServiceName` | string | Container name | Service name |
103+
| `Namespace` | string | - | Compose project name |
104+
| `ContainerNumber`| string | - | Container number |
105+
<!-- TODO:Labels --->
103106

104107
The following functions are available within the template (besides the [builtin
105108
functions](https://golang.org/pkg/text/template/#hdr-Functions)):
106109

107110
| func | arguments | description |
108111
|-----------------|-----------------------|-----------------------------------------------------------------------------------|
109112
| `json` | `object` | Marshal the object and output it as a json text |
110-
| `color` | `color.Color, string` | Wrap the text in color (.ContainerColor and .ComposeColor provided) |
113+
| `color` | `color.Color, string` | Wrap the text in color (.ContainerColor and .NamespaceColor provided) |
111114
| `parseJSON` | `string` | Parse string as JSON |
112115
| `tryParseJSON` | `string` | Attempt to parse string as JSON, return nil on failure |
113116
| `extractJSONParts` | `string, ...string` | Parse string as JSON and concatenate the given keys. |
@@ -145,21 +148,21 @@ You can configure highlight colors for compose projects and containers in [the c
145148

146149
```yaml
147150
# Green, Yellow, Blue, Magenta, Cyan, White
148-
compose-colors: "32,33,34,35,36,37"
151+
namespace-colors: "32,33,34,35,36,37"
149152
150153
# Colors with underline (4)
151-
# If empty, the compose colors will be used as container colors
154+
# If empty, the namespace will be used as container colors
152155
container-colors: "32;4,33;4,34;4,35;4,36;4,37;4"
153156
```
154157

155158
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.
156159

157-
The equivalent flags `--compose-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 `--compose-colors` flag.
160+
The equivalent flags `--namespace-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 `--namespace-colors` flag.
158161

159162
```bash
160163
# Monokai theme
161-
composeColors="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"
162-
tailfin --compose-colors "$composeColors" app
164+
namespaceColor="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"
165+
tailfin --namespace-colors "$namespaceColor" app
163166
```
164167

165168
## Examples:
@@ -217,25 +220,25 @@ tailfin backend -o raw
217220
Output using a custom template:
218221
219222
```
220-
tailfin --template '{{printf "%s (%s/%s)\n" .Message .ComposeProject .ContainerName}}' backend
223+
tailfin --template '{{printf "%s (%s/%s)\n" .Message .Namespace .ContainerName}}' backend
221224
```
222225
223226
Output using a custom template with tailfin-provided colors:
224227
225228
```
226-
tailfin --template '{{.Message}} ({{color .ComposeColor .ComposeProject}}/{{color .ContainerColor .ContainerName}}){{"\n"}}' backend
229+
tailfin --template '{{.Message}} ({{color .NamespaceColor .Namespace}}/{{color .ContainerColor .ContainerName}}){{"\n"}}' backend
227230
```
228231
229232
Output using a custom template with `parseJSON`:
230233
231234
```
232-
tailfin --template='{{.ComposeProject}}/{{.ContainerName}} {{with $d := .Message | parseJSON}}[{{$d.level}}] {{$d.message}}{{end}}{{"\n"}}' backend
235+
tailfin --template='{{.Namespace}}/{{.ContainerName}} {{with $d := .Message | parseJSON}}[{{$d.level}}] {{$d.message}}{{end}}{{"\n"}}' backend
233236
```
234237
235238
Output using a custom template that tries to parse JSON or fallbacks to raw format:
236239
237240
```
238-
tailfin --template='{{.ComposeProject}}/{{.ContainerName}} {{ with $msg := .Message | tryParseJSON }}[{{ colorGreen (toRFC3339Nano $msg.ts) }}] {{ levelColor $msg.level }} ({{ colorCyan $msg.caller }}) {{ $msg.msg }}{{ else }} {{ .Message }} {{ end }}{{"\n"}}' backend
241+
tailfin --template='{{.Namespace}}/{{.ContainerName}} {{ with $msg := .Message | tryParseJSON }}[{{ colorGreen (toRFC3339Nano $msg.ts) }}] {{ levelColor $msg.level }} ({{ colorCyan $msg.caller }}) {{ $msg.msg }}{{ else }} {{ .Message }} {{ end }}{{"\n"}}' backend
239242
```
240243
241244
Load custom template from file:

cmd/tailfincmd/cmd.go

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,9 @@ type options struct {
7272
configFilePath string
7373
stdin bool
7474
containerColors []string
75-
composeColors []string
75+
namespaceColor []string
7676
//containerStates []string
7777
//selector string
78-
//compose []string
7978

8079
dockerClient *dockerclient.Client
8180
}
@@ -263,8 +262,8 @@ func (o *options) setVerbosity() error {
263262
}
264263

265264
func (o *options) setColorList() error {
266-
if len(o.containerColors) > 0 || len(o.composeColors) > 0 {
267-
return stern.SetColorList(o.composeColors, o.containerColors)
265+
if len(o.containerColors) > 0 || len(o.namespaceColor) > 0 {
266+
return stern.SetColorList(o.namespaceColor, o.containerColors)
268267
}
269268
return nil
270269
}
@@ -356,10 +355,9 @@ func (o *options) AddFlags(fs *pflag.FlagSet) {
356355
fs.IntVar(&o.verbosity, "verbosity", o.verbosity, "Number of the log level verbosity")
357356
fs.BoolVarP(&o.version, "version", "v", o.version, "Print the version and exit.")
358357
fs.BoolVar(&o.stdin, "stdin", o.stdin, "Parse logs from stdin. All Docker related flags are ignored when it is set.")
359-
fs.StringSliceVar(&o.containerColors, "compose-colors", o.containerColors, "Specifies the colors used to highlight container names. Provide colors as a comma-separated list using SGR (Select Graphic Rendition) sequences, e.g., \"91,92,93,94,95,96\".")
360-
fs.StringSliceVar(&o.composeColors, "container-colors", o.composeColors, "Specifies the colors used to highlight compose project names. Use the same format as --container-colors. Defaults to the values of --container-colors if omitted, and must match its length.")
358+
fs.StringSliceVar(&o.containerColors, "container-colors", o.containerColors, "Specifies the colors used to highlight container names. Provide colors as a comma-separated list using SGR (Select Graphic Rendition) sequences, e.g., \"91,92,93,94,95,96\".")
359+
fs.StringSliceVar(&o.namespaceColor, "namespace-colors", o.namespaceColor, "Specifies the colors used to highlight namespace (compose project). Use the same format as --container-colors. Defaults to the values of --container-colors if omitted, and must match its length.")
361360
// TODO: --context for docker context? Seems to be a `docker` thing, not a dockerd thing.
362-
// TODO: --ignore-compose to make it unaware of compose (e.g. use full container name)
363361
// TODO --prompt??
364362

365363
fs.Lookup("timestamps").NoOptDefVal = "default"
@@ -377,15 +375,15 @@ func (o *options) generateTemplate() (*template.Template, error) {
377375
if t == "" {
378376
switch o.output {
379377
case "default":
380-
t = "{{if .ComposeProject}}{{color .ComposeColor .ComposeProject}} {{end}}{{color .ContainerColor .ContainerName}} {{.Message}}"
378+
t = "{{if .Namespace}}{{color .NamespaceColor .Namespace}} {{end}}{{color .ContainerColor .ServiceName}} {{.Message}}"
381379
case "raw":
382380
t = "{{.Message}}"
383381
case "json":
384382
t = "{{json .}}"
385383
case "extjson":
386-
t = "{\"compose\": \"{{if .ComposeProject}}{{color .ComposeColor .ComposeProject}}{{end}}\", \"container\": \"{{color .ContainerColor .ContainerName}}\", \"message\": {{extjson .Message}}}"
384+
t = "{\"namespace\": \"{{if .Namespace}}{{color .NamespaceColor .Namespace}}{{end}}\", \"service\": \"{{color .ContainerColor .ServiceName}}\", \"message\": {{extjson .Message}}}"
387385
case "ppextjson":
388-
t = "{\n \"compose\": \"{{if .ComposeProject}}{{color .ComposeColor .ComposeProject}}{{end}}\",\n \"container\": \"{{color .ContainerColor .ContainerName}}\",\n \"message\": {{extjson .Message}}\n}"
386+
t = "{\n \"namespace\": \"{{if .Namespace}}{{color .NamespaceColor .Namespace}}{{end}}\",\n \"service\": \"{{color .ContainerColor .ServiceName}}\",\n \"message\": {{extjson .Message}}\n}"
389387
default:
390388
return nil, errors.New("output should be one of 'default', 'raw', 'json', 'extjson', and 'ppextjson'")
391389
}

cmd/tailfincmd/cmd_test.go

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ func TestOptionsGenerateTemplate(t *testing.T) {
208208
return o
209209
}(),
210210
"default message",
211-
"compose1 container1 default message\n",
211+
"compose1 service1 default message\n",
212212
false,
213213
true,
214214
},
@@ -234,7 +234,7 @@ func TestOptionsGenerateTemplate(t *testing.T) {
234234
return o
235235
}(),
236236
"json message",
237-
`{"message":"json message","container":"container1","compose":"compose1"}
237+
`{"message":"json message","container":"container1","service":"service1","namespace":"compose1","number":"0"}
238238
`,
239239
false,
240240
true,
@@ -248,7 +248,7 @@ func TestOptionsGenerateTemplate(t *testing.T) {
248248
return o
249249
}(),
250250
`{"msg":"extjson message"}`,
251-
`{"compose": "compose1", "container": "container1", "message": {"msg":"extjson message"}}
251+
`{"namespace": "compose1", "service": "service1", "message": {"msg":"extjson message"}}
252252
`,
253253
false,
254254
true,
@@ -263,8 +263,8 @@ func TestOptionsGenerateTemplate(t *testing.T) {
263263
}(),
264264
`{"msg":"ppextjson message"}`,
265265
`{
266-
"compose": "compose1",
267-
"container": "container1",
266+
"namespace": "compose1",
267+
"service": "service1",
268268
"message": {"msg":"ppextjson message"}
269269
}
270270
`,
@@ -288,12 +288,12 @@ func TestOptionsGenerateTemplate(t *testing.T) {
288288
"template",
289289
func() *options {
290290
o := NewOptions(streams)
291-
o.template = "Message={{.Message}} ComposeProject={{.ComposeProject}} ContainerName={{.ContainerName}}"
291+
o.template = "Message={{.Message}} Namespace={{.Namespace}} ContainerName={{.ContainerName}}"
292292

293293
return o
294294
}(),
295295
"template message", // no new line
296-
"Message=template message ComposeProject=compose1 ContainerName=container1",
296+
"Message=template message Namespace=compose1 ContainerName=container1",
297297
false,
298298
true,
299299
},
@@ -380,11 +380,14 @@ func TestOptionsGenerateTemplate(t *testing.T) {
380380
log := stern.Log{
381381
Message: tt.message,
382382
ContainerName: "container1",
383+
ServiceName: "container1",
383384
ContainerColor: color.New(color.FgBlue),
384385
}
385386
if tt.withCompose {
386-
log.ComposeProject = "compose1"
387-
log.ComposeColor = color.New(color.FgBlue)
387+
log.Namespace = "compose1"
388+
log.ServiceName = "service1"
389+
log.ContainerNumber = "0"
390+
log.NamespaceColor = color.New(color.FgBlue)
388391
}
389392
tmpl, err := tt.o.generateTemplate()
390393

cmd/tailfincmd/test.tpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{{color .ComposeColor .ComposeProject}} {{color .ContainerColor .ContainerName}} {{ with $msg := .Message | tryParseJSON }}[{{ colorGreen (toRFC3339Nano (toUTC $msg.ts)) }}] {{ levelColor $msg.level }} {{ $msg.msg }}{{ else }}{{ .Message }}{{ end }}
1+
{{color .NamespaceColor .Namespace}} {{color .ContainerColor .ContainerName}} {{ with $msg := .Message | tryParseJSON }}[{{ colorGreen (toRFC3339Nano (toUTC $msg.ts)) }}] {{ levelColor $msg.level }} {{ $msg.msg }}{{ else }}{{ .Message }}{{ end }}

stern/color.go

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,41 +24,41 @@ func colorIndex(name string) uint32 {
2424
return hash.Sum32() % uint32(len(colorList))
2525
}
2626

27-
func SetColorList(composeColors, containerColors []string) error {
28-
colors, err := parseColors(composeColors, containerColors)
27+
func SetColorList(namespaceColor, containerColors []string) error {
28+
colors, err := parseColors(namespaceColor, containerColors)
2929
if err != nil {
3030
return err
3131
}
3232
colorList = colors
3333
return nil
3434
}
3535

36-
func parseColors(composeColors, containerColors []string) ([][2]*color.Color, error) {
37-
if len(composeColors) == 0 {
38-
return nil, errors.New("compose-colors must not be empty")
36+
func parseColors(namespaceColor, containerColors []string) ([][2]*color.Color, error) {
37+
if len(namespaceColor) == 0 {
38+
return nil, errors.New("namespace-colors must not be empty")
3939
}
4040
if len(containerColors) == 0 {
41-
// if containerColors is empty, use composeColors as containerColors
42-
return createColorPairs(composeColors, composeColors)
41+
// if containerColors is empty, use namespaceColor as containerColors
42+
return createColorPairs(namespaceColor, namespaceColor)
4343
}
44-
if len(containerColors) != len(composeColors) {
45-
return nil, errors.New("compose-colors and container-colors must have the same length")
44+
if len(containerColors) != len(namespaceColor) {
45+
return nil, errors.New("namespace-colors and container-colors must have the same length")
4646
}
47-
return createColorPairs(composeColors, containerColors)
47+
return createColorPairs(namespaceColor, containerColors)
4848
}
4949

50-
func createColorPairs(composeColors, containerColors []string) ([][2]*color.Color, error) {
51-
colorList := make([][2]*color.Color, 0, len(composeColors))
52-
for i := 0; i < len(composeColors); i++ {
53-
composeColor, err := sgrSequenceToColor(composeColors[i])
50+
func createColorPairs(namespaceColor, containerColors []string) ([][2]*color.Color, error) {
51+
colorList := make([][2]*color.Color, 0, len(namespaceColor))
52+
for i := 0; i < len(namespaceColor); i++ {
53+
namespaceColor, err := sgrSequenceToColor(namespaceColor[i])
5454
if err != nil {
5555
return nil, err
5656
}
5757
containerColor, err := sgrSequenceToColor(containerColors[i])
5858
if err != nil {
5959
return nil, err
6060
}
61-
colorList = append(colorList, [2]*color.Color{composeColor, containerColor})
61+
colorList = append(colorList, [2]*color.Color{namespaceColor, containerColor})
6262
}
6363
return colorList, nil
6464
}

stern/color_test.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ import (
99
func TestParseColors(t *testing.T) {
1010
tests := []struct {
1111
desc string
12-
composeColors []string
12+
namespaceColors []string
1313
containerColors []string
1414
want [][2]*color.Color
1515
wantError bool
1616
}{
1717
{
1818
desc: "both compose and container colors are specified",
19-
composeColors: []string{"91", "92", "93"},
19+
namespaceColors: []string{"91", "92", "93"},
2020
containerColors: []string{"31", "32", "33"},
2121
want: [][2]*color.Color{
2222
{color.New(color.FgHiRed), color.New(color.FgRed)},
@@ -26,7 +26,7 @@ func TestParseColors(t *testing.T) {
2626
},
2727
{
2828
desc: "only compose colors are specified",
29-
composeColors: []string{"91", "92", "93"},
29+
namespaceColors: []string{"91", "92", "93"},
3030
containerColors: []string{},
3131
want: [][2]*color.Color{
3232
{color.New(color.FgHiRed), color.New(color.FgHiRed)},
@@ -36,7 +36,7 @@ func TestParseColors(t *testing.T) {
3636
},
3737
{
3838
desc: "multiple attributes",
39-
composeColors: []string{"4;91"},
39+
namespaceColors: []string{"4;91"},
4040
containerColors: []string{"38;2;255;97;136"},
4141
want: [][2]*color.Color{
4242
{
@@ -47,7 +47,7 @@ func TestParseColors(t *testing.T) {
4747
},
4848
{
4949
desc: "spaces are ignored",
50-
composeColors: []string{" 91 ", "\t92\t"},
50+
namespaceColors: []string{" 91 ", "\t92\t"},
5151
containerColors: []string{},
5252
want: [][2]*color.Color{
5353
{color.New(color.FgHiRed), color.New(color.FgHiRed)},
@@ -57,26 +57,26 @@ func TestParseColors(t *testing.T) {
5757
// error patterns
5858
{
5959
desc: "only container colors are specified",
60-
composeColors: []string{},
60+
namespaceColors: []string{},
6161
containerColors: []string{"31", "32", "33"},
6262
wantError: true,
6363
},
6464
{
6565
desc: "both compose and container colors are empty",
66-
composeColors: []string{},
66+
namespaceColors: []string{},
6767
containerColors: []string{},
6868
wantError: true,
6969
},
7070
{
7171
desc: "invalid color",
72-
composeColors: []string{"a"},
72+
namespaceColors: []string{"a"},
7373
containerColors: []string{""},
7474
wantError: true,
7575
},
7676
}
7777
for _, tt := range tests {
7878
t.Run(tt.desc, func(t *testing.T) {
79-
colorList, err := parseColors(tt.composeColors, tt.containerColors)
79+
colorList, err := parseColors(tt.namespaceColors, tt.containerColors)
8080

8181
if tt.wantError {
8282
if err == nil {

stern/docker_stern.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ func RunDocker(ctx context.Context, client *dockerclient.Client, config *DockerC
3434
ContainerConfig{
3535
target.Id,
3636
target.Name,
37+
target.ServiceName,
3738
target.ComposeProject,
39+
target.ContainerNumber,
3840
target.Tty,
3941
},
4042
config.Template,

0 commit comments

Comments
 (0)