mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-04 00:42:24 -06:00 
			
		
		
		
	* add back exif-terminator and use only for jpeg,png,webp * fix arguments passed to terminateExif() * pull in latest exif-terminator * fix test * update processed img --------- Co-authored-by: tobi <tobi.smethurst@protonmail.com>
		
			
				
	
	
		
			537 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			537 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package log
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	e "errors"
 | 
						|
	"fmt"
 | 
						|
	"strings"
 | 
						|
	"sync"
 | 
						|
 | 
						|
	"text/template"
 | 
						|
 | 
						|
	"github.com/go-errors/errors"
 | 
						|
	"golang.org/x/net/context"
 | 
						|
)
 | 
						|
 | 
						|
// TODO(dustin): Finish symbol documentation
 | 
						|
 | 
						|
// Config severity integers.
 | 
						|
const (
 | 
						|
	LevelDebug   = iota
 | 
						|
	LevelInfo    = iota
 | 
						|
	LevelWarning = iota
 | 
						|
	LevelError   = iota
 | 
						|
)
 | 
						|
 | 
						|
// Config severity names.
 | 
						|
const (
 | 
						|
	LevelNameDebug   = "debug"
 | 
						|
	LevelNameInfo    = "info"
 | 
						|
	LevelNameWarning = "warning"
 | 
						|
	LevelNameError   = "error"
 | 
						|
)
 | 
						|
 | 
						|
// Seveirty name->integer map.
 | 
						|
var (
 | 
						|
	LevelNameMap = map[string]int{
 | 
						|
		LevelNameDebug:   LevelDebug,
 | 
						|
		LevelNameInfo:    LevelInfo,
 | 
						|
		LevelNameWarning: LevelWarning,
 | 
						|
		LevelNameError:   LevelError,
 | 
						|
	}
 | 
						|
 | 
						|
	LevelNameMapR = map[int]string{
 | 
						|
		LevelDebug:   LevelNameDebug,
 | 
						|
		LevelInfo:    LevelNameInfo,
 | 
						|
		LevelWarning: LevelNameWarning,
 | 
						|
		LevelError:   LevelNameError,
 | 
						|
	}
 | 
						|
)
 | 
						|
 | 
						|
// Errors
 | 
						|
var (
 | 
						|
	ErrAdapterAlreadyRegistered = e.New("adapter already registered")
 | 
						|
	ErrFormatEmpty              = e.New("format is empty")
 | 
						|
	ErrExcludeLevelNameInvalid  = e.New("exclude bypass-level is invalid")
 | 
						|
	ErrNoAdapterConfigured      = e.New("no default adapter configured")
 | 
						|
	ErrAdapterIsNil             = e.New("adapter is nil")
 | 
						|
	ErrConfigurationNotLoaded   = e.New("can not configure because configuration is not loaded")
 | 
						|
)
 | 
						|
 | 
						|
// Other
 | 
						|
var (
 | 
						|
	includeFilters    = make(map[string]bool)
 | 
						|
	useIncludeFilters = false
 | 
						|
	excludeFilters    = make(map[string]bool)
 | 
						|
	useExcludeFilters = false
 | 
						|
 | 
						|
	adapters = make(map[string]LogAdapter)
 | 
						|
 | 
						|
	// TODO(dustin): !! Finish implementing this.
 | 
						|
	excludeBypassLevel = -1
 | 
						|
)
 | 
						|
 | 
						|
// Add global include filter.
 | 
						|
func AddIncludeFilter(noun string) {
 | 
						|
	includeFilters[noun] = true
 | 
						|
	useIncludeFilters = true
 | 
						|
}
 | 
						|
 | 
						|
// Remove global include filter.
 | 
						|
func RemoveIncludeFilter(noun string) {
 | 
						|
	delete(includeFilters, noun)
 | 
						|
	if len(includeFilters) == 0 {
 | 
						|
		useIncludeFilters = false
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Add global exclude filter.
 | 
						|
func AddExcludeFilter(noun string) {
 | 
						|
	excludeFilters[noun] = true
 | 
						|
	useExcludeFilters = true
 | 
						|
}
 | 
						|
 | 
						|
// Remove global exclude filter.
 | 
						|
func RemoveExcludeFilter(noun string) {
 | 
						|
	delete(excludeFilters, noun)
 | 
						|
	if len(excludeFilters) == 0 {
 | 
						|
		useExcludeFilters = false
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func AddAdapter(name string, la LogAdapter) {
 | 
						|
	if _, found := adapters[name]; found == true {
 | 
						|
		Panic(ErrAdapterAlreadyRegistered)
 | 
						|
	}
 | 
						|
 | 
						|
	if la == nil {
 | 
						|
		Panic(ErrAdapterIsNil)
 | 
						|
	}
 | 
						|
 | 
						|
	adapters[name] = la
 | 
						|
 | 
						|
	if GetDefaultAdapterName() == "" {
 | 
						|
		SetDefaultAdapterName(name)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func ClearAdapters() {
 | 
						|
	adapters = make(map[string]LogAdapter)
 | 
						|
	SetDefaultAdapterName("")
 | 
						|
}
 | 
						|
 | 
						|
type LogAdapter interface {
 | 
						|
	Debugf(lc *LogContext, message *string) error
 | 
						|
	Infof(lc *LogContext, message *string) error
 | 
						|
	Warningf(lc *LogContext, message *string) error
 | 
						|
	Errorf(lc *LogContext, message *string) error
 | 
						|
}
 | 
						|
 | 
						|
// TODO(dustin): !! Also populate whether we've bypassed an exception so that
 | 
						|
//                  we can add a template macro to prefix an exclamation of
 | 
						|
//                  some sort.
 | 
						|
type MessageContext struct {
 | 
						|
	Level         *string
 | 
						|
	Noun          *string
 | 
						|
	Message       *string
 | 
						|
	ExcludeBypass bool
 | 
						|
}
 | 
						|
 | 
						|
type LogContext struct {
 | 
						|
	Logger *Logger
 | 
						|
	Ctx    context.Context
 | 
						|
}
 | 
						|
 | 
						|
type Logger struct {
 | 
						|
	isConfigured bool
 | 
						|
	an           string
 | 
						|
	la           LogAdapter
 | 
						|
	t            *template.Template
 | 
						|
	systemLevel  int
 | 
						|
	noun         string
 | 
						|
}
 | 
						|
 | 
						|
func NewLoggerWithAdapterName(noun string, adapterName string) (l *Logger) {
 | 
						|
	l = &Logger{
 | 
						|
		noun: noun,
 | 
						|
		an:   adapterName,
 | 
						|
	}
 | 
						|
 | 
						|
	return l
 | 
						|
}
 | 
						|
 | 
						|
func NewLogger(noun string) (l *Logger) {
 | 
						|
	l = NewLoggerWithAdapterName(noun, "")
 | 
						|
 | 
						|
	return l
 | 
						|
}
 | 
						|
 | 
						|
func (l *Logger) Noun() string {
 | 
						|
	return l.noun
 | 
						|
}
 | 
						|
 | 
						|
func (l *Logger) Adapter() LogAdapter {
 | 
						|
	return l.la
 | 
						|
}
 | 
						|
 | 
						|
var (
 | 
						|
	configureMutex sync.Mutex
 | 
						|
)
 | 
						|
 | 
						|
func (l *Logger) doConfigure(force bool) {
 | 
						|
	configureMutex.Lock()
 | 
						|
	defer configureMutex.Unlock()
 | 
						|
 | 
						|
	if l.isConfigured == true && force == false {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	if IsConfigurationLoaded() == false {
 | 
						|
		Panic(ErrConfigurationNotLoaded)
 | 
						|
	}
 | 
						|
 | 
						|
	if l.an == "" {
 | 
						|
		l.an = GetDefaultAdapterName()
 | 
						|
	}
 | 
						|
 | 
						|
	// If this is empty, then no specific adapter was given or no system
 | 
						|
	// default was configured (which implies that no adapters were registered).
 | 
						|
	// All of our logging will be skipped.
 | 
						|
	if l.an != "" {
 | 
						|
		la, found := adapters[l.an]
 | 
						|
		if found == false {
 | 
						|
			Panic(fmt.Errorf("adapter is not valid: %s", l.an))
 | 
						|
		}
 | 
						|
 | 
						|
		l.la = la
 | 
						|
	}
 | 
						|
 | 
						|
	// Set the level.
 | 
						|
 | 
						|
	systemLevel, found := LevelNameMap[levelName]
 | 
						|
	if found == false {
 | 
						|
		Panic(fmt.Errorf("log-level not valid: [%s]", levelName))
 | 
						|
	}
 | 
						|
 | 
						|
	l.systemLevel = systemLevel
 | 
						|
 | 
						|
	// Set the form.
 | 
						|
 | 
						|
	if format == "" {
 | 
						|
		Panic(ErrFormatEmpty)
 | 
						|
	}
 | 
						|
 | 
						|
	if t, err := template.New("logItem").Parse(format); err != nil {
 | 
						|
		Panic(err)
 | 
						|
	} else {
 | 
						|
		l.t = t
 | 
						|
	}
 | 
						|
 | 
						|
	l.isConfigured = true
 | 
						|
}
 | 
						|
 | 
						|
func (l *Logger) flattenMessage(lc *MessageContext, format *string, args []interface{}) (string, error) {
 | 
						|
	m := fmt.Sprintf(*format, args...)
 | 
						|
 | 
						|
	lc.Message = &m
 | 
						|
 | 
						|
	var b bytes.Buffer
 | 
						|
	if err := l.t.Execute(&b, *lc); err != nil {
 | 
						|
		return "", err
 | 
						|
	}
 | 
						|
 | 
						|
	return b.String(), nil
 | 
						|
}
 | 
						|
 | 
						|
func (l *Logger) allowMessage(noun string, level int) bool {
 | 
						|
	if _, found := includeFilters[noun]; found == true {
 | 
						|
		return true
 | 
						|
	}
 | 
						|
 | 
						|
	// If we didn't hit an include filter and we *had* include filters, filter
 | 
						|
	// it out.
 | 
						|
	if useIncludeFilters == true {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
 | 
						|
	if _, found := excludeFilters[noun]; found == true {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
 | 
						|
	return true
 | 
						|
}
 | 
						|
 | 
						|
func (l *Logger) makeLogContext(ctx context.Context) *LogContext {
 | 
						|
	return &LogContext{
 | 
						|
		Ctx:    ctx,
 | 
						|
		Logger: l,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
type LogMethod func(lc *LogContext, message *string) error
 | 
						|
 | 
						|
func (l *Logger) log(ctx context.Context, level int, lm LogMethod, format string, args []interface{}) error {
 | 
						|
	if l.systemLevel > level {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	// Preempt the normal filter checks if we can unconditionally allow at a
 | 
						|
	// certain level and we've hit that level.
 | 
						|
	//
 | 
						|
	// Notice that this is only relevant if the system-log level is letting
 | 
						|
	// *anything* show logs at the level we came in with.
 | 
						|
	canExcludeBypass := level >= excludeBypassLevel && excludeBypassLevel != -1
 | 
						|
	didExcludeBypass := false
 | 
						|
 | 
						|
	n := l.Noun()
 | 
						|
 | 
						|
	if l.allowMessage(n, level) == false {
 | 
						|
		if canExcludeBypass == false {
 | 
						|
			return nil
 | 
						|
		} else {
 | 
						|
			didExcludeBypass = true
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	levelName, found := LevelNameMapR[level]
 | 
						|
	if found == false {
 | 
						|
		Panic(fmt.Errorf("level not valid: (%d)", level))
 | 
						|
	}
 | 
						|
 | 
						|
	levelName = strings.ToUpper(levelName)
 | 
						|
 | 
						|
	lc := &MessageContext{
 | 
						|
		Level:         &levelName,
 | 
						|
		Noun:          &n,
 | 
						|
		ExcludeBypass: didExcludeBypass,
 | 
						|
	}
 | 
						|
 | 
						|
	if s, err := l.flattenMessage(lc, &format, args); err != nil {
 | 
						|
		return err
 | 
						|
	} else {
 | 
						|
		lc := l.makeLogContext(ctx)
 | 
						|
		if err := lm(lc, &s); err != nil {
 | 
						|
			panic(err)
 | 
						|
		}
 | 
						|
 | 
						|
		return e.New(s)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (l *Logger) Debugf(ctx context.Context, format string, args ...interface{}) {
 | 
						|
	l.doConfigure(false)
 | 
						|
 | 
						|
	if l.la != nil {
 | 
						|
		l.log(ctx, LevelDebug, l.la.Debugf, format, args)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (l *Logger) Infof(ctx context.Context, format string, args ...interface{}) {
 | 
						|
	l.doConfigure(false)
 | 
						|
 | 
						|
	if l.la != nil {
 | 
						|
		l.log(ctx, LevelInfo, l.la.Infof, format, args)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (l *Logger) Warningf(ctx context.Context, format string, args ...interface{}) {
 | 
						|
	l.doConfigure(false)
 | 
						|
 | 
						|
	if l.la != nil {
 | 
						|
		l.log(ctx, LevelWarning, l.la.Warningf, format, args)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (l *Logger) mergeStack(err interface{}, format string, args []interface{}) (string, []interface{}) {
 | 
						|
	if format != "" {
 | 
						|
		format += "\n%s"
 | 
						|
	} else {
 | 
						|
		format = "%s"
 | 
						|
	}
 | 
						|
 | 
						|
	var stackified *errors.Error
 | 
						|
	stackified, ok := err.(*errors.Error)
 | 
						|
	if ok == false {
 | 
						|
		stackified = errors.Wrap(err, 2)
 | 
						|
	}
 | 
						|
 | 
						|
	args = append(args, stackified.ErrorStack())
 | 
						|
 | 
						|
	return format, args
 | 
						|
}
 | 
						|
 | 
						|
func (l *Logger) Errorf(ctx context.Context, errRaw interface{}, format string, args ...interface{}) {
 | 
						|
	l.doConfigure(false)
 | 
						|
 | 
						|
	var err interface{}
 | 
						|
 | 
						|
	if errRaw != nil {
 | 
						|
		_, ok := errRaw.(*errors.Error)
 | 
						|
		if ok == true {
 | 
						|
			err = errRaw
 | 
						|
		} else {
 | 
						|
			err = errors.Wrap(errRaw, 1)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if l.la != nil {
 | 
						|
		if errRaw != nil {
 | 
						|
			format, args = l.mergeStack(err, format, args)
 | 
						|
		}
 | 
						|
 | 
						|
		l.log(ctx, LevelError, l.la.Errorf, format, args)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (l *Logger) ErrorIff(ctx context.Context, errRaw interface{}, format string, args ...interface{}) {
 | 
						|
	if errRaw == nil {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	var err interface{}
 | 
						|
 | 
						|
	_, ok := errRaw.(*errors.Error)
 | 
						|
	if ok == true {
 | 
						|
		err = errRaw
 | 
						|
	} else {
 | 
						|
		err = errors.Wrap(errRaw, 1)
 | 
						|
	}
 | 
						|
 | 
						|
	l.Errorf(ctx, err, format, args...)
 | 
						|
}
 | 
						|
 | 
						|
func (l *Logger) Panicf(ctx context.Context, errRaw interface{}, format string, args ...interface{}) {
 | 
						|
	l.doConfigure(false)
 | 
						|
 | 
						|
	var err interface{}
 | 
						|
 | 
						|
	_, ok := errRaw.(*errors.Error)
 | 
						|
	if ok == true {
 | 
						|
		err = errRaw
 | 
						|
	} else {
 | 
						|
		err = errors.Wrap(errRaw, 1)
 | 
						|
	}
 | 
						|
 | 
						|
	if l.la != nil {
 | 
						|
		format, args = l.mergeStack(err, format, args)
 | 
						|
		err = l.log(ctx, LevelError, l.la.Errorf, format, args)
 | 
						|
	}
 | 
						|
 | 
						|
	Panic(err.(error))
 | 
						|
}
 | 
						|
 | 
						|
func (l *Logger) PanicIff(ctx context.Context, errRaw interface{}, format string, args ...interface{}) {
 | 
						|
	if errRaw == nil {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	var err interface{}
 | 
						|
 | 
						|
	_, ok := errRaw.(*errors.Error)
 | 
						|
	if ok == true {
 | 
						|
		err = errRaw
 | 
						|
	} else {
 | 
						|
		err = errors.Wrap(errRaw, 1)
 | 
						|
	}
 | 
						|
 | 
						|
	l.Panicf(ctx, err.(error), format, args...)
 | 
						|
}
 | 
						|
 | 
						|
func Wrap(err interface{}) *errors.Error {
 | 
						|
	es, ok := err.(*errors.Error)
 | 
						|
	if ok == true {
 | 
						|
		return es
 | 
						|
	} else {
 | 
						|
		return errors.Wrap(err, 1)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func Errorf(message string, args ...interface{}) *errors.Error {
 | 
						|
	err := fmt.Errorf(message, args...)
 | 
						|
	return errors.Wrap(err, 1)
 | 
						|
}
 | 
						|
 | 
						|
func Panic(err interface{}) {
 | 
						|
	_, ok := err.(*errors.Error)
 | 
						|
	if ok == true {
 | 
						|
		panic(err)
 | 
						|
	} else {
 | 
						|
		panic(errors.Wrap(err, 1))
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func Panicf(message string, args ...interface{}) {
 | 
						|
	err := Errorf(message, args...)
 | 
						|
	Panic(err)
 | 
						|
}
 | 
						|
 | 
						|
func PanicIf(err interface{}) {
 | 
						|
	if err == nil {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	_, ok := err.(*errors.Error)
 | 
						|
	if ok == true {
 | 
						|
		panic(err)
 | 
						|
	} else {
 | 
						|
		panic(errors.Wrap(err, 1))
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Is checks if the left ("actual") error equals the right ("against") error.
 | 
						|
// The right must be an unwrapped error (the kind that you'd initialize as a
 | 
						|
// global variable). The left can be a wrapped or unwrapped error.
 | 
						|
func Is(actual, against error) bool {
 | 
						|
	// If it's an unwrapped error.
 | 
						|
	if _, ok := actual.(*errors.Error); ok == false {
 | 
						|
		return actual == against
 | 
						|
	}
 | 
						|
 | 
						|
	return errors.Is(actual, against)
 | 
						|
}
 | 
						|
 | 
						|
// Print is a utility function to prevent the caller from having to import the
 | 
						|
// third-party library.
 | 
						|
func PrintError(err error) {
 | 
						|
	wrapped := Wrap(err)
 | 
						|
	fmt.Printf("Stack:\n\n%s\n", wrapped.ErrorStack())
 | 
						|
}
 | 
						|
 | 
						|
// PrintErrorf is a utility function to prevent the caller from having to
 | 
						|
// import the third-party library.
 | 
						|
func PrintErrorf(err error, format string, args ...interface{}) {
 | 
						|
	wrapped := Wrap(err)
 | 
						|
 | 
						|
	fmt.Printf(format, args...)
 | 
						|
	fmt.Printf("\n")
 | 
						|
	fmt.Printf("Stack:\n\n%s\n", wrapped.ErrorStack())
 | 
						|
}
 | 
						|
 | 
						|
func init() {
 | 
						|
	if format == "" {
 | 
						|
		format = defaultFormat
 | 
						|
	}
 | 
						|
 | 
						|
	if levelName == "" {
 | 
						|
		levelName = defaultLevelName
 | 
						|
	}
 | 
						|
 | 
						|
	if includeNouns != "" {
 | 
						|
		for _, noun := range strings.Split(includeNouns, ",") {
 | 
						|
			AddIncludeFilter(noun)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if excludeNouns != "" {
 | 
						|
		for _, noun := range strings.Split(excludeNouns, ",") {
 | 
						|
			AddExcludeFilter(noun)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if excludeBypassLevelName != "" {
 | 
						|
		var found bool
 | 
						|
		if excludeBypassLevel, found = LevelNameMap[excludeBypassLevelName]; found == false {
 | 
						|
			panic(ErrExcludeLevelNameInvalid)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 |