119 lines
3.7 KiB
Go
119 lines
3.7 KiB
Go
package companion
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type ConsoleLogStreamClient interface {
|
|
StreamLogEvents(context.Context, func(LogStreamEvent) error) error
|
|
}
|
|
|
|
type ConsoleLogStore interface {
|
|
EnsureSchema(context.Context) error
|
|
StoreConsoleRecords(context.Context, []ConsoleRecord) (int, error)
|
|
StoreSemanticEventBatch(context.Context, SemanticEventBatch) (int, error)
|
|
}
|
|
|
|
type ConsoleLogCollector struct {
|
|
Client ConsoleLogStreamClient
|
|
Store ConsoleLogStore
|
|
ServerInstanceID string
|
|
CorrelationSecret string
|
|
Backoff time.Duration
|
|
OnSemanticEvents func(context.Context, SemanticEventBatch) error
|
|
}
|
|
|
|
func NewConsoleLogCollector(client ConsoleLogStreamClient, store ConsoleLogStore, serverInstanceID string, correlationSecret string) *ConsoleLogCollector {
|
|
return &ConsoleLogCollector{Client: client, Store: store, ServerInstanceID: serverInstanceID, CorrelationSecret: correlationSecret, Backoff: 2 * time.Second}
|
|
}
|
|
|
|
func (collector *ConsoleLogCollector) Run(ctx context.Context) error {
|
|
if collector == nil || collector.Client == nil || collector.Store == nil || strings.TrimSpace(collector.ServerInstanceID) == "" {
|
|
return fmt.Errorf("console log collector is not configured")
|
|
}
|
|
if err := collector.Store.EnsureSchema(ctx); err != nil {
|
|
return err
|
|
}
|
|
backoff := collector.Backoff
|
|
if backoff <= 0 {
|
|
backoff = 2 * time.Second
|
|
}
|
|
for {
|
|
err := collector.Client.StreamLogEvents(ctx, collector.handleEvent(ctx))
|
|
if ctx.Err() != nil {
|
|
return ctx.Err()
|
|
}
|
|
if err != nil {
|
|
select {
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
case <-time.After(backoff):
|
|
}
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
|
|
func (collector *ConsoleLogCollector) handleEvent(ctx context.Context) func(LogStreamEvent) error {
|
|
return func(event LogStreamEvent) error {
|
|
record, ok := consoleRecordFromLogEvent(collector.ServerInstanceID, event)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
if _, err := collector.Store.StoreConsoleRecords(ctx, []ConsoleRecord{record}); err != nil {
|
|
return err
|
|
}
|
|
batch := ParseConsoleRecords(collector.ServerInstanceID, []ConsoleRecord{record}, collector.CorrelationSecret)
|
|
if _, err := collector.Store.StoreSemanticEventBatch(ctx, batch); err != nil {
|
|
return err
|
|
}
|
|
if collector.OnSemanticEvents != nil {
|
|
if err := collector.OnSemanticEvents(ctx, batch); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func consoleRecordFromLogEvent(serverInstanceID string, event LogStreamEvent) (ConsoleRecord, bool) {
|
|
if event.ServerInstanceID != serverInstanceID || event.Entry.Seq == 0 || strings.TrimSpace(event.Entry.Line) == "" {
|
|
return ConsoleRecord{}, false
|
|
}
|
|
stream := consoleStreamName(event.StreamKey)
|
|
if stream == "" {
|
|
if !knownPluginLogStream(event.StreamKey) {
|
|
return ConsoleRecord{}, false
|
|
}
|
|
stream = strings.ToLower(strings.TrimSpace(event.StreamKey))
|
|
}
|
|
occurredAt := event.Entry.Timestamp
|
|
if occurredAt.IsZero() {
|
|
occurredAt = time.Now().UTC()
|
|
}
|
|
return ConsoleRecord{ServerID: serverInstanceID, Stream: stream, Sequence: event.Entry.Seq, OccurredAt: occurredAt.UTC(), Text: event.Entry.Line}, true
|
|
}
|
|
|
|
func knownPluginLogStream(streamKey string) bool {
|
|
switch strings.ToLower(strings.TrimSpace(streamKey)) {
|
|
case "scum.login", "scum.chat", "scum.server", "scum.kill", "scum.trade", "scum.admin", "scum.performance":
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func consoleStreamName(streamKey string) string {
|
|
key := strings.ToLower(strings.TrimSpace(streamKey))
|
|
if strings.Contains(key, "stderr") || strings.HasSuffix(key, ".err") || strings.HasSuffix(key, "-err") {
|
|
return "stderr"
|
|
}
|
|
if strings.Contains(key, "stdout") || strings.Contains(key, "console") || strings.HasSuffix(key, ".out") || strings.HasSuffix(key, "-out") {
|
|
return "stdout"
|
|
}
|
|
return ""
|
|
}
|