istio.io/istio@v0.0.0-20240520182934-d79c90f27776/istioctl/pkg/util/formatting/formatter.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package formatting 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "io" 21 "os" 22 "strings" 23 24 "github.com/mattn/go-isatty" 25 "sigs.k8s.io/yaml" 26 27 "istio.io/istio/pkg/config/analysis/diag" 28 "istio.io/istio/pkg/env" 29 ) 30 31 // Formatting options for Messages 32 const ( 33 LogFormat = "log" 34 JSONFormat = "json" 35 YAMLFormat = "yaml" 36 ) 37 38 var ( 39 MsgOutputFormatKeys = []string{LogFormat, JSONFormat, YAMLFormat} 40 MsgOutputFormats = make(map[string]bool) 41 termEnvVar = env.Register("TERM", "", "Specifies terminal type. Use 'dumb' to suppress color output") 42 ) 43 44 func init() { 45 for _, key := range MsgOutputFormatKeys { 46 MsgOutputFormats[key] = true 47 } 48 } 49 50 // Print output messages in the specified format with color options 51 func Print(ms diag.Messages, format string, colorize bool) (string, error) { 52 switch format { 53 case LogFormat: 54 return printLog(ms, colorize), nil 55 case JSONFormat: 56 return printJSON(ms) 57 case YAMLFormat: 58 return printYAML(ms) 59 default: 60 return "", fmt.Errorf("invalid format, expected one of %v but got %q", MsgOutputFormatKeys, format) 61 } 62 } 63 64 func printLog(ms diag.Messages, colorize bool) string { 65 logOutput := make([]string, 0, len(ms)) 66 for _, m := range ms { 67 logOutput = append(logOutput, render(m, colorize)) 68 } 69 return strings.Join(logOutput, "\n") 70 } 71 72 func printJSON(ms diag.Messages) (string, error) { 73 jsonOutput, err := json.MarshalIndent(ms, "", "\t") 74 return string(jsonOutput), err 75 } 76 77 func printYAML(ms diag.Messages) (string, error) { 78 yamlOutput, err := yaml.Marshal(ms) 79 return string(yamlOutput), err 80 } 81 82 // Formatting options for Message 83 var ( 84 colorPrefixes = map[diag.Level]string{ 85 diag.Info: "", // no special color for info messages 86 diag.Warning: "\033[33m", // yellow 87 diag.Error: "\033[1;31m", // bold red 88 } 89 ) 90 91 // render turns a Message instance into a string with an option of colored bash output 92 func render(m diag.Message, colorize bool) string { 93 return fmt.Sprintf("%s%v%s [%v]%s %s", 94 colorPrefix(m, colorize), m.Type.Level(), colorSuffix(colorize), 95 m.Type.Code(), m.Origin(), fmt.Sprintf(m.Type.Template(), m.Parameters...), 96 ) 97 } 98 99 func colorPrefix(m diag.Message, colorize bool) string { 100 if !colorize { 101 return "" 102 } 103 104 prefix, ok := colorPrefixes[m.Type.Level()] 105 if !ok { 106 return "" 107 } 108 return prefix 109 } 110 111 func colorSuffix(colorize bool) string { 112 if !colorize { 113 return "" 114 } 115 return "\033[0m" 116 } 117 118 func IstioctlColorDefault(writer io.Writer) bool { 119 if strings.EqualFold(termEnvVar.Get(), "dumb") { 120 return false 121 } 122 123 file, ok := writer.(*os.File) 124 if ok { 125 if !isatty.IsTerminal(file.Fd()) { 126 return false 127 } 128 } 129 130 return true 131 }