diff --git a/log/formatting.go b/log/formatting.go index 581e5b3..d1dfd90 100644 --- a/log/formatting.go +++ b/log/formatting.go @@ -27,7 +27,7 @@ func (s severity) String() string { } } -func formatLine(line *logLine, useColor bool) string { +func formatLine(line *logLine, duplicates uint64, useColor bool) string { colorStart := "" colorEnd := "" @@ -40,14 +40,14 @@ func formatLine(line *logLine, useColor bool) string { var fLine string if line.line == 0 { - fLine = fmt.Sprintf("%s%s ? %s %s %03d%s %s", colorStart, line.time.Format("060102 15:04:05.000"), rightArrow, line.level.String(), counter, colorEnd, line.msg) + fLine = fmt.Sprintf("%s%s ? %s %s %03d%s%s %s", colorStart, line.time.Format("060102 15:04:05.000"), rightArrow, line.level.String(), counter, formatDuplicates(duplicates), colorEnd, line.msg) } else { fLen := len(line.file) fPartStart := fLen - 10 if fPartStart < 0 { fPartStart = 0 } - fLine = fmt.Sprintf("%s%s %s:%03d %s %s %03d%s %s", colorStart, line.time.Format("060102 15:04:05.000"), line.file[fPartStart:], line.line, rightArrow, line.level.String(), counter, colorEnd, line.msg) + fLine = fmt.Sprintf("%s%s %s:%03d %s %s %03d%s%s %s", colorStart, line.time.Format("060102 15:04:05.000"), line.file[fPartStart:], line.line, rightArrow, line.level.String(), counter, formatDuplicates(duplicates), colorEnd, line.msg) } if counter >= maxCount { @@ -56,3 +56,10 @@ func formatLine(line *logLine, useColor bool) string { return fLine } + +func formatDuplicates(duplicates uint64) string { + if duplicates == 0 { + return "" + } + return fmt.Sprintf(" [%dx]", duplicates+1) +} diff --git a/log/logging.go b/log/logging.go index 9f8558c..30b413c 100644 --- a/log/logging.go +++ b/log/logging.go @@ -42,6 +42,20 @@ type logLine struct { line int } +func (ll *logLine) Equal(ol *logLine) bool { + switch { + case ll.msg != ol.msg: + return false + case ll.file != ol.file: + return false + case ll.line != ol.line: + return false + case ll.level != ol.level: + return false + } + return true +} + const ( TraceLevel severity = 1 DebugLevel severity = 2 diff --git a/log/output.go b/log/output.go index 02225e7..5c71c1f 100644 --- a/log/output.go +++ b/log/output.go @@ -9,13 +9,10 @@ import ( "github.com/Safing/portbase/taskmanager" ) -func writeLine(line *logLine) { - - fmt.Println(formatLine(line, true)) - +func writeLine(line *logLine, duplicates uint64) { + fmt.Println(formatLine(line, duplicates, true)) // TODO: implement file logging and setting console/file logging // TODO: use https://github.com/natefinch/lumberjack - } func startWriter() { @@ -26,10 +23,16 @@ func startWriter() { func writer() { var line *logLine + var lastLine *logLine + var duplicates uint64 startedTask := false defer shutdownWaitGroup.Done() for { + // reset + line = nil + lastLine = nil + duplicates = 0 // wait until logs need to be processed select { @@ -47,7 +50,7 @@ func writer() { for { select { case line = <-logBuffer: - writeLine(line) + writeLine(line, duplicates) case <-time.After(10 * time.Millisecond): fmt.Println(fmt.Sprintf("%s%s %s EOF%s", InfoLevel.color(), time.Now().Format("060102 15:04:05.000"), leftArrow, endColor())) return @@ -60,7 +63,33 @@ func writer() { for { select { case line = <-logBuffer: - writeLine(line) + + // look-ahead for deduplication (best effort) + dedupLoop: + for { + // check if there is another line waiting + select { + case nextLine := <-logBuffer: + lastLine = line + line = nextLine + default: + break dedupLoop + } + + // deduplication + if !line.Equal(lastLine) { + // no duplicate + writeLine(lastLine, duplicates) + duplicates = 0 + } else { + // duplicate + duplicates++ + } + } + + // write actual line + writeLine(line, duplicates) + duplicates = 0 default: if startedTask { taskmanager.EndMicroTask() @@ -70,5 +99,11 @@ func writer() { } } + // back down a little + select { + case <-time.After(10 * time.Millisecond): + case <-shutdownSignal: + } + } }