2018-05-22 21:43:36 +02:00
|
|
|
// Copyright 2016-2018, Pulumi Corporation.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
2018-05-16 00:28:00 +02:00
|
|
|
|
|
|
|
package logging
|
|
|
|
|
|
|
|
// Wrapper around the glog API that allows us to intercept all logging calls and manipulate them as
|
|
|
|
// necessary. This is primarily used so we can make a best effort approach to filtering out secrets
|
|
|
|
// from any logs we emit before they get written to log-files/stderr.
|
|
|
|
//
|
|
|
|
// Code in pulumi should use this package instead of directly importing glog itself. If any glog
|
|
|
|
// methods are needed that are not exported from this, they can be added, with the caveat that they
|
|
|
|
// should be updated to properly filter as well before forwarding things along.
|
|
|
|
|
|
|
|
import (
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"strconv"
|
2019-08-14 15:20:56 +02:00
|
|
|
"strings"
|
2018-05-16 00:28:00 +02:00
|
|
|
"sync"
|
|
|
|
|
2019-07-31 20:23:33 +02:00
|
|
|
"github.com/golang/glog"
|
2018-05-16 00:28:00 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
type Filter interface {
|
|
|
|
Filter(s string) string
|
|
|
|
}
|
|
|
|
|
|
|
|
var LogToStderr = false // true if logging is being redirected to stderr.
|
|
|
|
var Verbose = 0 // >0 if verbose logging is enabled at a particular level.
|
|
|
|
var LogFlow = false // true to flow logging settings to child processes.
|
|
|
|
|
|
|
|
var rwLock sync.RWMutex
|
|
|
|
var filters []Filter
|
|
|
|
|
|
|
|
func V(level glog.Level) glog.Verbose {
|
|
|
|
return glog.V(level)
|
|
|
|
}
|
|
|
|
|
|
|
|
func Errorf(format string, args ...interface{}) {
|
2018-05-16 01:09:15 +02:00
|
|
|
glog.Errorf("%s", FilterString(fmt.Sprintf(format, args...)))
|
2018-05-16 00:28:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func Infof(format string, args ...interface{}) {
|
2018-05-16 01:09:15 +02:00
|
|
|
glog.Infof("%s", FilterString(fmt.Sprintf(format, args...)))
|
2018-05-16 00:28:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func Warningf(format string, args ...interface{}) {
|
2018-05-16 01:09:15 +02:00
|
|
|
glog.Warningf("%s", FilterString(fmt.Sprintf(format, args...)))
|
2018-05-16 00:28:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func Flush() {
|
|
|
|
glog.Flush()
|
|
|
|
}
|
|
|
|
|
|
|
|
// InitLogging ensures the logging library has been initialized with the given settings.
|
|
|
|
func InitLogging(logToStderr bool, verbose int, logFlow bool) {
|
|
|
|
// Remember the settings in case someone inquires.
|
|
|
|
LogToStderr = logToStderr
|
|
|
|
Verbose = verbose
|
|
|
|
LogFlow = logFlow
|
|
|
|
|
2018-08-20 20:05:49 +02:00
|
|
|
// glog uses golang's built in flags package to set configuration values, which is incompatible with how
|
2019-07-31 20:23:33 +02:00
|
|
|
// we use cobra. In order to accommodate this, we call flag.CommandLine.Parse() with an empty array and
|
|
|
|
// explicitly set the flags we care about here.
|
2019-07-31 20:29:00 +02:00
|
|
|
if !flag.Parsed() {
|
|
|
|
err := flag.CommandLine.Parse([]string{})
|
|
|
|
assertNoError(err)
|
|
|
|
}
|
2018-05-16 00:28:00 +02:00
|
|
|
if logToStderr {
|
2019-07-31 20:29:00 +02:00
|
|
|
err := flag.Lookup("logtostderr").Value.Set("true")
|
2018-05-16 00:28:00 +02:00
|
|
|
assertNoError(err)
|
|
|
|
}
|
|
|
|
if verbose > 0 {
|
2019-07-31 20:29:00 +02:00
|
|
|
err := flag.Lookup("v").Value.Set(strconv.Itoa(verbose))
|
2018-05-16 00:28:00 +02:00
|
|
|
assertNoError(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func assertNoError(err error) {
|
|
|
|
if err != nil {
|
|
|
|
failfast(err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func failfast(msg string) {
|
|
|
|
panic(fmt.Sprintf("fatal: %v", msg))
|
|
|
|
}
|
|
|
|
|
|
|
|
type nopFilter struct {
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *nopFilter) Filter(s string) string {
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
2019-08-14 15:20:56 +02:00
|
|
|
type replacerFilter struct {
|
|
|
|
replacer *strings.Replacer
|
2018-05-16 00:28:00 +02:00
|
|
|
}
|
|
|
|
|
2019-08-14 15:20:56 +02:00
|
|
|
func (f *replacerFilter) Filter(s string) string {
|
|
|
|
return f.replacer.Replace(s)
|
2018-05-16 00:28:00 +02:00
|
|
|
}
|
|
|
|
|
2018-05-16 01:09:15 +02:00
|
|
|
func AddGlobalFilter(filter Filter) {
|
2018-05-16 00:28:00 +02:00
|
|
|
rwLock.Lock()
|
|
|
|
filters = append(filters, filter)
|
|
|
|
rwLock.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
func CreateFilter(secrets []string, replacement string) Filter {
|
2019-08-14 15:20:56 +02:00
|
|
|
var items []string
|
2018-05-16 00:28:00 +02:00
|
|
|
for _, secret := range secrets {
|
|
|
|
// For short secrets, don't actually add them to the filter, this is a trade-off we make to prevent
|
|
|
|
// displaying `[secret]`. Travis does a similar thing, for example.
|
|
|
|
if len(secret) < 3 {
|
|
|
|
continue
|
|
|
|
}
|
2019-08-14 15:20:56 +02:00
|
|
|
items = append(items, secret, replacement)
|
2018-05-16 00:28:00 +02:00
|
|
|
}
|
2019-08-14 15:20:56 +02:00
|
|
|
if len(items) > 0 {
|
|
|
|
return &replacerFilter{replacer: strings.NewReplacer(items...)}
|
2018-05-16 00:28:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return &nopFilter{}
|
|
|
|
}
|
2018-05-16 01:09:15 +02:00
|
|
|
|
|
|
|
func FilterString(msg string) string {
|
|
|
|
var localFilters []Filter
|
|
|
|
rwLock.RLock()
|
|
|
|
localFilters = filters
|
|
|
|
rwLock.RUnlock()
|
|
|
|
|
|
|
|
for _, filter := range localFilters {
|
|
|
|
msg = filter.Filter(msg)
|
|
|
|
}
|
|
|
|
|
|
|
|
return msg
|
|
|
|
}
|