mirror of
https://github.com/stashapp/stash.git
synced 2025-12-06 16:34:02 +01:00
* Enable safe linters Enable the linters dogsled, rowserrcheck, and sqlclosecheck. These report no errors currently in the code base. Enable misspell. Misspell finds two spelling mistakes in comments, which are fixed by the patch as well. Add and sort linters which are relatively safe to add over time. Comment them out for now. * Close the response body If we can get a HTTP response, it has a body which ought to be closed. By doing so, we avoid potentially leaking connections. * Enable the exportloopref linter There are two places in the code with these warnings. Fix them while enabling the linter. * Remove redundant types in tests If a slice already determines the type, the inner type declaration is redundant. Remove the inner declarations. * Mark autotag test cases as parallel Autotag test cases is by far the outlier when it comes to test time. While go test runs test cases in parallel, it doesn't do so inside a given package, unless one marks the test cases as parallel. This change provides a significant speedup on a 8-core machine for test runs.
192 lines
3.5 KiB
Go
192 lines
3.5 KiB
Go
package logger
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
type PluginLogLevel struct {
|
|
char byte
|
|
Name string
|
|
}
|
|
|
|
// Valid Level values.
|
|
var (
|
|
TraceLevel = PluginLogLevel{
|
|
char: 't',
|
|
Name: "trace",
|
|
}
|
|
DebugLevel = PluginLogLevel{
|
|
char: 'd',
|
|
Name: "debug",
|
|
}
|
|
InfoLevel = PluginLogLevel{
|
|
char: 'i',
|
|
Name: "info",
|
|
}
|
|
WarningLevel = PluginLogLevel{
|
|
char: 'w',
|
|
Name: "warning",
|
|
}
|
|
ErrorLevel = PluginLogLevel{
|
|
char: 'e',
|
|
Name: "error",
|
|
}
|
|
ProgressLevel = PluginLogLevel{
|
|
char: 'p',
|
|
Name: "progress",
|
|
}
|
|
NoneLevel = PluginLogLevel{
|
|
Name: "none",
|
|
}
|
|
)
|
|
|
|
var validLevels = []PluginLogLevel{
|
|
TraceLevel,
|
|
DebugLevel,
|
|
InfoLevel,
|
|
WarningLevel,
|
|
ErrorLevel,
|
|
ProgressLevel,
|
|
NoneLevel,
|
|
}
|
|
|
|
const startLevelChar byte = 1
|
|
const endLevelChar byte = 2
|
|
|
|
func (l PluginLogLevel) prefix() string {
|
|
return string([]byte{
|
|
startLevelChar,
|
|
byte(l.char),
|
|
endLevelChar,
|
|
})
|
|
}
|
|
|
|
func (l PluginLogLevel) Log(args ...interface{}) {
|
|
if l.char == 0 {
|
|
return
|
|
}
|
|
|
|
argsToUse := []interface{}{
|
|
l.prefix(),
|
|
}
|
|
argsToUse = append(argsToUse, args...)
|
|
fmt.Fprintln(os.Stderr, argsToUse...)
|
|
}
|
|
|
|
func (l PluginLogLevel) Logf(format string, args ...interface{}) {
|
|
if l.char == 0 {
|
|
return
|
|
}
|
|
|
|
formatToUse := string(l.prefix()) + format + "\n"
|
|
fmt.Fprintf(os.Stderr, formatToUse, args...)
|
|
}
|
|
|
|
// PluginLogLevelFromName returns the Level that matches the provided name or nil if
|
|
// the name does not match a valid value.
|
|
func PluginLogLevelFromName(name string) *PluginLogLevel {
|
|
for _, l := range validLevels {
|
|
if l.Name == name {
|
|
return &l
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// DetectLogLevel returns the Level and the logging string for a provided line
|
|
// of plugin output. It parses the string for logging control characters and
|
|
// determines the log level, if present. If not present, the plugin output
|
|
// is returned unchanged with a nil Level.
|
|
func DetectLogLevel(line string) (*PluginLogLevel, string) {
|
|
if len(line) < 4 || line[0] != startLevelChar || line[2] != endLevelChar {
|
|
return nil, line
|
|
}
|
|
|
|
char := line[1]
|
|
var level *PluginLogLevel
|
|
for _, l := range validLevels {
|
|
if l.char == char {
|
|
l := l // Make a copy of the loop variable
|
|
level = &l
|
|
break
|
|
}
|
|
}
|
|
|
|
if level == nil {
|
|
return nil, line
|
|
}
|
|
|
|
line = strings.TrimSpace(line[3:])
|
|
|
|
return level, line
|
|
}
|
|
|
|
type PluginLogger struct {
|
|
Prefix string
|
|
DefaultLogLevel *PluginLogLevel
|
|
ProgressChan chan float64
|
|
}
|
|
|
|
func (log *PluginLogger) HandleStderrLine(line string) {
|
|
level, ll := DetectLogLevel(line)
|
|
|
|
// if no log level, just output to info
|
|
if level == nil {
|
|
if log.DefaultLogLevel != nil {
|
|
level = log.DefaultLogLevel
|
|
} else {
|
|
level = &InfoLevel
|
|
}
|
|
}
|
|
|
|
switch *level {
|
|
case TraceLevel:
|
|
Trace(log.Prefix, ll)
|
|
case DebugLevel:
|
|
Debug(log.Prefix, ll)
|
|
case InfoLevel:
|
|
Info(log.Prefix, ll)
|
|
case WarningLevel:
|
|
Warn(log.Prefix, ll)
|
|
case ErrorLevel:
|
|
Error(log.Prefix, ll)
|
|
case ProgressLevel:
|
|
p, err := strconv.ParseFloat(ll, 64)
|
|
if err != nil {
|
|
Errorf("Error parsing progress value '%s': %s", ll, err.Error())
|
|
} else {
|
|
// only pass progress through if channel present
|
|
if log.ProgressChan != nil {
|
|
// don't block on this
|
|
select {
|
|
case log.ProgressChan <- p:
|
|
default:
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (log *PluginLogger) HandlePluginStdErr(pluginStdErr io.ReadCloser) {
|
|
// pipe plugin stderr to our logging
|
|
scanner := bufio.NewScanner(pluginStdErr)
|
|
for scanner.Scan() {
|
|
str := scanner.Text()
|
|
if str != "" {
|
|
log.HandleStderrLine(str)
|
|
}
|
|
}
|
|
|
|
str := scanner.Text()
|
|
if str != "" {
|
|
log.HandleStderrLine(str)
|
|
}
|
|
|
|
pluginStdErr.Close()
|
|
}
|