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  }