mirror of
https://github.com/amir20/dozzle.git
synced 2025-12-27 23:46:39 +01:00
feat: adds the ability to show a specific log from the past with a permanent link (#3958)
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"errors"
|
||||
"math"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
@@ -62,7 +63,6 @@ func (h *handler) fetchLogsBetweenDates(w http.ResponseWriter, r *http.Request)
|
||||
return
|
||||
}
|
||||
|
||||
buffer := utils.NewRingBuffer[*container.LogEvent](500)
|
||||
delta := max(to.Sub(from), time.Second*3)
|
||||
|
||||
var regex *regexp.Regexp
|
||||
@@ -82,8 +82,9 @@ func (h *handler) fetchLogsBetweenDates(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
minimum := 0
|
||||
if r.URL.Query().Has("minimum") {
|
||||
minimum, err = strconv.Atoi(r.URL.Query().Get("minimum"))
|
||||
buffer := utils.NewRingBuffer[*container.LogEvent](500)
|
||||
if r.URL.Query().Has("min") {
|
||||
minimum, err = strconv.Atoi(r.URL.Query().Get("min"))
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
@@ -93,6 +94,21 @@ func (h *handler) fetchLogsBetweenDates(w http.ResponseWriter, r *http.Request)
|
||||
http.Error(w, errors.New("minimum must be between 0 and buffer size").Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
buffer = utils.NewRingBuffer[*container.LogEvent](minimum)
|
||||
}
|
||||
|
||||
maxStart := math.MaxInt
|
||||
if r.URL.Query().Has("maxStart") {
|
||||
maxStart, err = strconv.Atoi(r.URL.Query().Get("maxStart"))
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if maxStart < 1 || maxStart > buffer.Size {
|
||||
http.Error(w, errors.New("invalid maxStart").Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
levels := make(map[string]struct{})
|
||||
@@ -120,7 +136,7 @@ func (h *handler) fetchLogsBetweenDates(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
for {
|
||||
if buffer.Len() > minimum {
|
||||
if minimum > 0 && buffer.Len() >= minimum {
|
||||
break
|
||||
}
|
||||
|
||||
@@ -157,6 +173,10 @@ func (h *handler) fetchLogsBetweenDates(w http.ResponseWriter, r *http.Request)
|
||||
break
|
||||
}
|
||||
|
||||
if buffer.Len() >= maxStart {
|
||||
break
|
||||
}
|
||||
|
||||
support_web.EscapeHTMLValues(event)
|
||||
buffer.Push(event)
|
||||
}
|
||||
@@ -166,13 +186,19 @@ func (h *handler) fetchLogsBetweenDates(w http.ResponseWriter, r *http.Request)
|
||||
break
|
||||
}
|
||||
|
||||
if minimum == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
from = from.Add(-delta)
|
||||
delta = delta * 2
|
||||
}
|
||||
|
||||
log.Debug().Int("buffer_size", buffer.Len()).Msg("sending logs to client")
|
||||
|
||||
for _, event := range buffer.Data() {
|
||||
data := buffer.Data()
|
||||
|
||||
for _, event := range data {
|
||||
if err := encoder.Encode(event); err != nil {
|
||||
log.Error().Err(err).Msg("error encoding log event")
|
||||
return
|
||||
|
||||
@@ -268,6 +268,7 @@ func Test_handler_between_dates_with_fill(t *testing.T) {
|
||||
q.Add("stderr", "true")
|
||||
q.Add("fill", "true")
|
||||
q.Add("levels", "info")
|
||||
q.Add("min", "10")
|
||||
|
||||
req.URL.RawQuery = q.Encode()
|
||||
|
||||
@@ -280,16 +281,22 @@ func Test_handler_between_dates_with_fill(t *testing.T) {
|
||||
mockedClient.On("ContainerLogsBetweenDates", mock.Anything, id, from, to, container.STDALL).
|
||||
Return(io.NopCloser(bytes.NewReader([]byte{})), nil).
|
||||
Once()
|
||||
|
||||
mockedClient.On("ContainerLogsBetweenDates", mock.Anything, id, time.Date(2017, time.December, 31, 14, 0, 0, 0, time.UTC), to, container.STDALL).
|
||||
Return(io.NopCloser(bytes.NewReader(data)), nil).
|
||||
Once()
|
||||
mockedClient.On("FindContainer", mock.Anything, id).Return(container.Container{ID: id}, nil)
|
||||
mockedClient.On("Host").Return(container.Host{
|
||||
ID: "localhost",
|
||||
})
|
||||
|
||||
mockedClient.On("ContainerLogsBetweenDates", mock.Anything, id, time.Date(2017, time.December, 30, 18, 0, 0, 0, time.UTC), to, container.STDALL).
|
||||
Return(io.NopCloser(bytes.NewReader(data)), nil).
|
||||
Once()
|
||||
|
||||
mockedClient.On("FindContainer", mock.Anything, id).Return(container.Container{ID: id, Created: time.Date(2017, time.December, 31, 10, 0, 0, 0, time.UTC)}, nil)
|
||||
mockedClient.On("Host").Return(container.Host{ID: "localhost"})
|
||||
|
||||
mockedClient.On("ListContainers", mock.Anything, mock.Anything).Return([]container.Container{
|
||||
{ID: id, Name: "test", Host: "localhost", State: "running"},
|
||||
}, nil)
|
||||
|
||||
mockedClient.On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- container.ContainerEvent")).Return(nil)
|
||||
|
||||
handler := createDefaultHandler(mockedClient)
|
||||
|
||||
Reference in New Issue
Block a user