osbase/logtree: add WithBacklogOrder

By moving the log entry reversal to the logtree.LogReader,
the behavior of getEntries and scanEntries changed. This is
fine as there are no other consumers for these functions outside of
logtree and the reader keeps the previous behavior, but also allows
to skip it.

Change-Id: If1fddd14d43aca64009655b978d6a05dd910eb50
Reviewed-on: https://review.monogon.dev/c/monogon/+/4447
Tested-by: Jenkins CI
Reviewed-by: Lorenz Brun <lorenz@monogon.tech>
diff --git a/osbase/logtree/journal.go b/osbase/logtree/journal.go
index c6cc503..55977bf 100644
--- a/osbase/logtree/journal.go
+++ b/osbase/logtree/journal.go
@@ -5,7 +5,6 @@
 
 import (
 	"errors"
-	"sort"
 	"strings"
 	"sync"
 
@@ -197,10 +196,6 @@
 		cur = cur.prevGlobal
 	}
 
-	// Reverse entries back into chronological order.
-	sort.SliceStable(res, func(i, j int) bool {
-		return i > j
-	})
 	return
 }
 
@@ -233,10 +228,6 @@
 		cur = cur.prevLocal
 	}
 
-	// Reverse entries back into chronological order.
-	sort.SliceStable(res, func(i, j int) bool {
-		return i > j
-	})
 	return
 }
 
diff --git a/osbase/logtree/journal_test.go b/osbase/logtree/journal_test.go
index 826f2e7..944e4fb 100644
--- a/osbase/logtree/journal_test.go
+++ b/osbase/logtree/journal_test.go
@@ -38,7 +38,7 @@
 		t.Fatalf("wanted %d entries, got %d", want, got)
 	}
 	for i, entry := range entries {
-		want := fmt.Sprintf("test %d", (9000-defaultDNQuota)+i)
+		want := fmt.Sprintf("test %d", 9000-1-i)
 		got := strings.Join(entry.leveled.messages, "\n")
 		if want != got {
 			t.Fatalf("wanted entry %q, got %q", want, got)
diff --git a/osbase/logtree/logtree_access.go b/osbase/logtree/logtree_access.go
index 1582a8f..6c42452 100644
--- a/osbase/logtree/logtree_access.go
+++ b/osbase/logtree/logtree_access.go
@@ -5,11 +5,19 @@
 
 import (
 	"errors"
+	"slices"
 	"sync/atomic"
 
 	"source.monogon.dev/go/logging"
 )
 
+type BacklogOrder int
+
+const (
+	BacklogOrderLatestFirst BacklogOrder = iota
+	BacklogOrderOldestFirst
+)
+
 // LogReadOption describes options for the LogTree.Read call.
 type LogReadOption func(*logReaderOptions)
 
@@ -17,6 +25,7 @@
 	withChildren               bool
 	withStream                 bool
 	withBacklog                int
+	withBacklogOrder           BacklogOrder
 	onlyLeveled                bool
 	onlyRaw                    bool
 	leveledWithMinimumSeverity logging.Severity
@@ -54,6 +63,12 @@
 	return func(lro *logReaderOptions) { lro.withBacklog = count }
 }
 
+// WithBacklogOrder makes Read return log entries in the given order,
+// default being latest messages first.
+func WithBacklogOrder(order BacklogOrder) LogReadOption {
+	return func(lro *logReaderOptions) { lro.withBacklogOrder = order }
+}
+
 // BacklogAllAvailable makes WithBacklog return all backlogged log data that
 // logtree possesses.
 const BacklogAllAvailable int = -1
@@ -156,6 +171,11 @@
 		}
 	}
 
+	if lro.withBacklogOrder == BacklogOrderLatestFirst {
+		// Reverse entries back into chronological order.
+		slices.Reverse(entries)
+	}
+
 	lr := &LogReader{}
 	if lro.withStream {
 		sub := &subscriber{