Files
free-turn-proxy/mobile/events.go
samosvalishe a7ec2b949a feat(mobile): отключаемый кольцевой буфер логов
Хосту, который ведёт лог через EventSink, кольцо для DumpLogs не нужно.
2026-09-19 23:54:42 +03:00

88 lines
2.3 KiB
Go

package mobile
import (
"fmt"
"sync/atomic"
"time"
"github.com/samosvalishe/free-turn-proxy/internal/logx"
"github.com/samosvalishe/free-turn-proxy/internal/session"
)
const (
LevelDebug = "debug"
LevelInfo = "info"
LevelWarn = "warn"
LevelError = "error"
)
// EventSink - приёмник событий сессии, реализуемый хостом (Java/ObjC).
// OnLog может вызываться конкурентно; методы не должны блокировать исполнение.
type EventSink interface {
OnState(state string, streams, total int, errMsg string)
OnLog(level, msg string, unixMillis int64)
// OnCaptcha передаёт URL для решения капчи вручную (пустой url - закрыть окно).
OnCaptcha(url string)
}
var sink atomic.Pointer[EventSink]
// SetEventSink регистрирует приёмник событий (nil отключает push-канал).
func SetEventSink(s EventSink) {
if s == nil {
sink.Store(nil)
return
}
sink.Store(&s)
}
func currentSink() EventSink {
if p := sink.Load(); p != nil {
return *p
}
return nil
}
func emitCaptcha(url string) {
if s := currentSink(); s != nil {
s.OnCaptcha(url)
}
}
type observer struct{}
func (observer) OnPhase(phase session.Phase, streams, total int, errMsg string) {
if s := currentSink(); s != nil {
s.OnState(string(phase), streams, total, errMsg)
}
}
func coreLog() logx.Logger { return &sinkLogger{buf: sharedLogBuf} }
// sinkLogger дублирует логи в кольцевой буфер и EventSink.
type sinkLogger struct {
debug bool
buf *logBuffer
}
func (l *sinkLogger) write(level, format string, v ...any) {
msg := fmt.Sprintf(format, v...)
now := time.Now()
if !logBufOff.Load() {
l.buf.append(now.Format("15:04:05") + " [" + level + "] " + msg)
}
if s := currentSink(); s != nil {
s.OnLog(level, msg, now.UnixMilli())
}
}
func (l *sinkLogger) Debugf(format string, v ...any) {
if l.debug {
l.write(LevelDebug, format, v...)
}
}
func (l *sinkLogger) Infof(format string, v ...any) { l.write(LevelInfo, format, v...) }
func (l *sinkLogger) Warnf(format string, v ...any) { l.write(LevelWarn, format, v...) }
func (l *sinkLogger) Errorf(format string, v ...any) { l.write(LevelError, format, v...) }
func (l *sinkLogger) DebugEnabled() bool { return l.debug }