github.com/paketoio/libpak@v1.3.1/bard/logger.go (about) 1 /* 2 * Copyright 2018-2020 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * https://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package bard 18 19 import ( 20 "fmt" 21 "io" 22 "os" 23 "strings" 24 25 "github.com/buildpacks/libcnb" 26 "github.com/buildpacks/libcnb/poet" 27 "github.com/heroku/color" 28 ) 29 30 // TODO: Remove once TTY support is in place 31 func init() { 32 color.Enabled() 33 } 34 35 // Logger logs message to a writer. 36 type Logger struct { 37 poet.Logger 38 39 body io.Writer 40 header io.Writer 41 terminalBody io.Writer 42 terminalHeader io.Writer 43 title io.Writer 44 } 45 46 // Option is a function for configuring a Logger instance. 47 type Option func(logger Logger) Logger 48 49 // WithDebug configures the debug Writer. 50 func WithDebug(writer io.Writer) Option { 51 return func(logger Logger) Logger { 52 logger.Logger = poet.WithDebug(writer)(logger.Logger) 53 return logger 54 } 55 } 56 57 // NewLoggerWithOptions create a new instance of Logger. It configures the Logger with options. 58 func NewLoggerWithOptions(writer io.Writer, options ...Option) Logger { 59 l := Logger{ 60 Logger: poet.NewLogger(writer), 61 body: NewWriter(writer, WithAttributes(color.Faint), WithIndent(2)), 62 header: NewWriter(writer, WithIndent(1)), 63 terminalBody: NewWriter(writer, WithAttributes(color.FgRed, color.Bold), WithIndent(1)), 64 terminalHeader: NewWriter(writer, WithAttributes(color.FgRed)), 65 title: NewWriter(writer, WithAttributes(color.FgBlue)), 66 } 67 68 for _, option := range options { 69 l = option(l) 70 } 71 72 return l 73 } 74 75 // NewLogger creates a new instance of Logger. It configures debug logging if $BP_DEBUG is set. 76 func NewLogger(writer io.Writer) Logger { 77 var options []Option 78 79 if _, ok := os.LookupEnv("BP_DEBUG"); ok { 80 options = append(options, WithDebug(writer)) 81 } 82 83 return NewLoggerWithOptions(writer, options...) 84 } 85 86 // Body logs a message to the configured body writer. 87 func (l Logger) Body(format string, a ...interface{}) { 88 if !l.IsBodyEnabled() { 89 return 90 } 91 92 l.printf(l.body, format, a...) 93 } 94 95 // BodyWriter returns the configured body writer. 96 func (l Logger) BodyWriter() io.Writer { 97 return l.body 98 } 99 100 // IsBodyEnabled indicates whether body logging is enabled. 101 func (l Logger) IsBodyEnabled() bool { 102 return l.body != nil 103 } 104 105 // Header logs a message to the configured header writer. 106 func (l Logger) Header(format string, a ...interface{}) { 107 if !l.IsHeaderEnabled() { 108 return 109 } 110 111 l.printf(l.header, format, a...) 112 } 113 114 // HeaderWriter returns the configured header writer. 115 func (l Logger) HeaderWriter() io.Writer { 116 return l.header 117 } 118 119 // IsHeaderEnabled indicates whether header logging is enabled. 120 func (l Logger) IsHeaderEnabled() bool { 121 return l.header != nil 122 } 123 124 // IdentifiableError is an error associated with an Identifiable for logging purposes. 125 type IdentifiableError struct { 126 127 // Name is the name of the identified object. 128 Name string 129 130 // Description is the description of the identified object. 131 Description string 132 133 // Err is the nested error. 134 Err error 135 } 136 137 func (i IdentifiableError) Error() string { 138 return i.Err.Error() 139 } 140 141 // TerminalError logs a message to the configured terminal error writer. 142 func (l Logger) TerminalError(err IdentifiableError) { 143 if !l.IsTerminalErrorEnabled() { 144 return 145 } 146 147 l.printf(l.terminalHeader, "\n%s", IdentityFormatter{Name: err.Name, Description: err.Description}) 148 l.printf(l.terminalBody, "%s", err.Err) 149 } 150 151 // TerminalErrorWriter returns the configured terminal error writer. 152 func (l Logger) TerminalErrorWriter() io.Writer { 153 return l.terminalBody 154 } 155 156 // IsTerminalErrorEnabled indicates whether terminal error logging is enabled. 157 func (l Logger) IsTerminalErrorEnabled() bool { 158 return l.terminalHeader != nil && l.terminalBody != nil 159 } 160 161 // Title logs a message to the configured title writer. 162 func (l Logger) Title(buildpack libcnb.Buildpack) { 163 if !l.IsTitleEnabled() { 164 return 165 } 166 167 l.printf(l.title, "\n%s", IdentityFormatter{Name: buildpack.Info.Name, Description: buildpack.Info.Version}) 168 } 169 170 // TitleWriter returns the configured title writer. 171 func (l Logger) TitleWriter() io.Writer { 172 return l.title 173 } 174 175 // IsTitleEnabled indicates whether title logging is enabled. 176 func (l Logger) IsTitleEnabled() bool { 177 return l.title != nil 178 } 179 180 func (Logger) printf(writer io.Writer, format string, a ...interface{}) { 181 if !strings.HasSuffix(format, "\n") { 182 format = format + "\n" 183 } 184 185 _, _ = fmt.Fprintf(writer, format, a...) 186 }