1
0
mirror of https://github.com/amir20/dozzle.git synced 2025-12-21 21:33:18 +01:00

chore: cleans up event generator

This commit is contained in:
Amir Raminfar
2025-10-13 16:52:11 -07:00
parent 573ee5ed03
commit cabe33a70f
6 changed files with 87 additions and 43 deletions

View File

@@ -60,10 +60,11 @@ func (s *server) StreamLogs(in *pb.StreamLogsRequest, out pb.AgentService_Stream
return err return err
} }
events := make(chan *container.LogEvent)
dockerReader := docker.NewLogReader(reader, c.Tty) dockerReader := docker.NewLogReader(reader, c.Tty)
g := container.NewEventGenerator(out.Context(), dockerReader, c) g := container.NewEventGenerator(out.Context(), dockerReader, c, events)
for event := range g.Events { for event := range events {
out.Send(&pb.StreamLogsResponse{ out.Send(&pb.StreamLogsResponse{
Event: logEventToPb(event), Event: logEventToPb(event),
}) })
@@ -88,12 +89,13 @@ func (s *server) LogsBetweenDates(in *pb.LogsBetweenDatesRequest, out pb.AgentSe
return err return err
} }
events := make(chan *container.LogEvent)
dockerReader := docker.NewLogReader(reader, c.Tty) dockerReader := docker.NewLogReader(reader, c.Tty)
g := container.NewEventGenerator(out.Context(), dockerReader, c) g := container.NewEventGenerator(out.Context(), dockerReader, c, events)
for { for {
select { select {
case event := <-g.Events: case event := <-events:
out.Send(&pb.StreamLogsResponse{ out.Send(&pb.StreamLogsResponse{
Event: logEventToPb(event), Event: logEventToPb(event),
}) })

View File

@@ -17,7 +17,7 @@ import (
) )
type EventGenerator struct { type EventGenerator struct {
Events chan *LogEvent events chan<- *LogEvent
Errors chan error Errors chan error
reader LogReader reader LogReader
next *LogEvent next *LogEvent
@@ -33,12 +33,12 @@ type LogReader interface {
Read() (string, StdType, error) Read() (string, StdType, error)
} }
func NewEventGenerator(ctx context.Context, reader LogReader, container Container) *EventGenerator { func NewEventGenerator(ctx context.Context, reader LogReader, container Container, events chan<- *LogEvent) *EventGenerator {
generator := &EventGenerator{ generator := &EventGenerator{
reader: reader, reader: reader,
buffer: make(chan *LogEvent, 100), buffer: make(chan *LogEvent, 100),
Errors: make(chan error, 1), Errors: make(chan error, 1),
Events: make(chan *LogEvent), events: events,
containerID: container.ID, containerID: container.ID,
ctx: ctx, ctx: ctx,
} }
@@ -69,14 +69,12 @@ loop:
checkPosition(current, next) checkPosition(current, next)
select { select {
case g.Events <- current: case g.events <- current:
case <-g.ctx.Done(): case <-g.ctx.Done():
break loop break loop
} }
} }
close(g.Events)
g.wg.Done() g.wg.Done()
} }
@@ -114,6 +112,10 @@ func (g *EventGenerator) peek() *LogEvent {
} }
} }
func (g *EventGenerator) Wait() {
g.wg.Wait()
}
func createEvent(message string, streamType StdType) *LogEvent { func createEvent(message string, streamType StdType) *LogEvent {
h := fnv.New32a() h := fnv.New32a()
h.Write([]byte(message)) h.Write([]byte(message))

View File

@@ -16,8 +16,9 @@ import (
func TestEventGenerator_Events_tty(t *testing.T) { func TestEventGenerator_Events_tty(t *testing.T) {
input := "example input" input := "example input"
g := NewEventGenerator(context.Background(), makeFakeReader(input, STDOUT), Container{Tty: true}) events := make(chan *LogEvent, 1)
event := <-g.Events NewEventGenerator(context.Background(), makeFakeReader(input, STDOUT), Container{Tty: true}, events)
event := <-events
require.NotNil(t, event, "Expected event to not be nil, but got nil") require.NotNil(t, event, "Expected event to not be nil, but got nil")
assert.Equal(t, input, event.Message) assert.Equal(t, input, event.Message)
@@ -26,8 +27,9 @@ func TestEventGenerator_Events_tty(t *testing.T) {
func TestEventGenerator_Events_non_tty(t *testing.T) { func TestEventGenerator_Events_non_tty(t *testing.T) {
input := "example input" input := "example input"
g := NewEventGenerator(context.Background(), makeFakeReader(input, STDOUT), Container{Tty: false}) events := make(chan *LogEvent, 1)
event := <-g.Events NewEventGenerator(context.Background(), makeFakeReader(input, STDOUT), Container{Tty: false}, events)
event := <-events
require.NotNil(t, event, "Expected event to not be nil, but got nil") require.NotNil(t, event, "Expected event to not be nil, but got nil")
assert.Equal(t, input, event.Message) assert.Equal(t, input, event.Message)
@@ -36,9 +38,12 @@ func TestEventGenerator_Events_non_tty(t *testing.T) {
func TestEventGenerator_Events_non_tty_close_channel(t *testing.T) { func TestEventGenerator_Events_non_tty_close_channel(t *testing.T) {
input := "example input" input := "example input"
g := NewEventGenerator(context.Background(), makeFakeReader(input, STDOUT), Container{Tty: false}) events := make(chan *LogEvent, 1)
<-g.Events g := NewEventGenerator(context.Background(), makeFakeReader(input, STDOUT), Container{Tty: false}, events)
_, ok := <-g.Events <-events
g.wg.Wait() // Wait for goroutines to finish
close(events) // Close the channel manually since generator no longer owns it
_, ok := <-events
assert.False(t, ok, "Expected channel to be closed") assert.False(t, ok, "Expected channel to be closed")
} }
@@ -46,8 +51,9 @@ func TestEventGenerator_Events_non_tty_close_channel(t *testing.T) {
func TestEventGenerator_Events_routines_done(t *testing.T) { func TestEventGenerator_Events_routines_done(t *testing.T) {
input := "example input" input := "example input"
g := NewEventGenerator(context.Background(), makeFakeReader(input, STDOUT), Container{Tty: false}) events := make(chan *LogEvent, 1)
<-g.Events g := NewEventGenerator(context.Background(), makeFakeReader(input, STDOUT), Container{Tty: false}, events)
<-events
assert.False(t, waitTimeout(&g.wg, 1*time.Second), "Expected routines to be done") assert.False(t, waitTimeout(&g.wg, 1*time.Second), "Expected routines to be done")
} }

View File

@@ -58,9 +58,17 @@ func (d *DockerClientService) LogsBetweenDates(ctx context.Context, c container.
return nil, err return nil, err
} }
events := make(chan *container.LogEvent)
dockerReader := docker.NewLogReader(reader, c.Tty) dockerReader := docker.NewLogReader(reader, c.Tty)
g := container.NewEventGenerator(ctx, dockerReader, c) g := container.NewEventGenerator(ctx, dockerReader, c, events)
return g.Events, nil
// Start a goroutine to close the channel when EventGenerator completes
go func() {
defer close(events)
g.Wait()
}()
return events, nil
} }
func (d *DockerClientService) StreamLogs(ctx context.Context, c container.Container, from time.Time, stdTypes container.StdType, events chan<- *container.LogEvent) error { func (d *DockerClientService) StreamLogs(ctx context.Context, c container.Container, from time.Time, stdTypes container.StdType, events chan<- *container.LogEvent) error {
@@ -70,16 +78,25 @@ func (d *DockerClientService) StreamLogs(ctx context.Context, c container.Contai
} }
dockerReader := docker.NewLogReader(reader, c.Tty) dockerReader := docker.NewLogReader(reader, c.Tty)
g := container.NewEventGenerator(ctx, dockerReader, c) g := container.NewEventGenerator(ctx, dockerReader, c, events)
for event := range g.Events {
events <- event
}
// Create a channel to signal when EventGenerator completes
done := make(chan struct{})
go func() {
g.Wait()
close(done)
}()
// Wait for either an error, completion, or context cancellation
select { select {
case e := <-g.Errors: case e := <-g.Errors:
return e return e
default: case <-done:
return nil // EventGenerator completed successfully
close(events)
return io.EOF
case <-ctx.Done():
return ctx.Err()
} }
} }

View File

@@ -51,9 +51,17 @@ func (k *K8sClientService) LogsBetweenDates(ctx context.Context, c container.Con
return nil, err return nil, err
} }
events := make(chan *container.LogEvent)
k8sReader := k8s.NewLogReader(reader) k8sReader := k8s.NewLogReader(reader)
g := container.NewEventGenerator(ctx, k8sReader, c) g := container.NewEventGenerator(ctx, k8sReader, c, events)
return g.Events, nil
// Start a goroutine to close the channel when EventGenerator completes
go func() {
defer close(events)
g.Wait()
}()
return events, nil
} }
func (k *K8sClientService) RawLogs(ctx context.Context, container container.Container, from time.Time, to time.Time, stdTypes container.StdType) (io.ReadCloser, error) { func (k *K8sClientService) RawLogs(ctx context.Context, container container.Container, from time.Time, to time.Time, stdTypes container.StdType) (io.ReadCloser, error) {
@@ -67,16 +75,25 @@ func (k *K8sClientService) StreamLogs(ctx context.Context, c container.Container
} }
k8sReader := k8s.NewLogReader(reader) k8sReader := k8s.NewLogReader(reader)
g := container.NewEventGenerator(ctx, k8sReader, c) g := container.NewEventGenerator(ctx, k8sReader, c, events)
for event := range g.Events {
events <- event
}
// Create a channel to signal when EventGenerator completes
done := make(chan struct{})
go func() {
g.Wait()
close(done)
}()
// Wait for either an error, completion, or context cancellation
select { select {
case e := <-g.Errors: case e := <-g.Errors:
return e return e
default: case <-done:
return nil // EventGenerator completed successfully
close(events)
return io.EOF
case <-ctx.Done():
return ctx.Err()
} }
} }

View File

@@ -164,13 +164,13 @@ stdout or stderr is required
/* snapshot: Test_handler_streamLogs_happy */ /* snapshot: Test_handler_streamLogs_happy */
:ping :ping
data: {"m":"INFO Testing logs...\n","ts":0,"id":3835490584,"l":"info","s":"stdout","c":"123456"}
event: container-event event: container-event
data: {"name":"container-stopped","host":"localhost","actorId":"123456","time":"<removed>"} data: {"name":"container-stopped","host":"localhost","actorId":"123456","time":"<removed>"}
data: {"m":"INFO Testing logs...\n","ts":0,"id":3835490584,"l":"info","s":"stdout","c":"123456"}
/* snapshot: Test_handler_streamLogs_happy_container_stopped */ /* snapshot: Test_handler_streamLogs_happy_container_stopped */
@@ -182,9 +182,9 @@ data: {"name":"container-stopped","host":"localhost","actorId":"123456","time":"
/* snapshot: Test_handler_streamLogs_happy_with_id */ /* snapshot: Test_handler_streamLogs_happy_with_id */
:ping :ping
data: {"m":"INFO Testing logs...","rm":"INFO Testing logs...","ts":1589396137772,"id":2908612274,"l":"info","s":"stdout","c":"123456"}
id: 1589396137772
event: container-event event: container-event
data: {"name":"container-stopped","host":"localhost","actorId":"123456","time":"<removed>"} data: {"name":"container-stopped","host":"localhost","actorId":"123456","time":"<removed>"}
data: {"m":"INFO Testing logs...","rm":"INFO Testing logs...","ts":1589396137772,"id":2908612274,"l":"info","s":"stdout","c":"123456"}
id: 1589396137772