Global code ovehaul - remove CAS and other, add 15 minute greet check

- Remove CAS, as it no longer works for us
- Remove other checks
- Implement 15 minute greet timeout check
- Fix chat logos
- Ditch thirdparty logger, use stdlib log facility wrapper for logging
This commit is contained in:
eleksir
2026-10-20 12:20:16 +03:00
parent 2f467af6cf
commit 0c34b6e781
1597 changed files with 6440 additions and 648118 deletions

71
internal/mylog/globals.go Normal file
View File

@@ -0,0 +1,71 @@
package mylog
import (
"errors"
"io"
"os"
"sync"
)
const (
// levelTrace is the most verbose level, below DEBUG.
levelTrace Level = iota
levelDebug
levelInfo
levelWarn
levelError
levelFatal
)
const (
// stampLayout is the timestamp layout. Sub-second precision is kept on
// purpose: the greeting sweeper reasons over 15-minute windows measured in
// microseconds, and the previous zap configuration truncated to whole
// seconds, which made window-edge debugging needlessly hard.
stampLayout = "2006-01-02 15:04:05.000"
// calldepth is reported to the stdlib logger. mylog never prints caller
// information, so the value only has to be consistent.
calldepth = 2
// filePerm is the mode for a newly created log file.
filePerm = 0o644
)
var (
// errClosed is returned when ReOpen is called on a closed logger.
errClosed = errors.New("cannot reopen a closed logger")
// errUnknownLevel is returned for a log level name mylog does not know.
errUnknownLevel = errors.New("unknown log level")
// levelNames renders each level as the uppercase token written to the log.
levelNames = map[Level]string{
levelTrace: "TRACE",
levelDebug: "DEBUG",
levelInfo: "INFO",
levelWarn: "WARN",
levelError: "ERROR",
levelFatal: "FATAL",
}
// exit is os.Exit, indirected so tests can observe the fatal path.
exit = os.Exit
// lock guards the package-global logger state. It is held for reading across
// the actual write, so that a concurrent Init cannot close the destination
// between the level check and the write.
lock sync.RWMutex
// current is the active logger. It is deliberately never nil: it is created
// working at package initialisation, writing to stderr at INFO. A nil logger
// would be the defect M2 in this codebase, and a valid default removes the
// state entirely rather than guarding against it.
current = newState(levelInfo, io.Writer(os.Stderr), "", nil, false)
// standard is the singleton Logger handed to modules that expect a
// *log.Logger. It is stateless; the package-global current does the work.
standard Logger = &adapter{}
)
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

59
internal/mylog/level.go Normal file
View File

@@ -0,0 +1,59 @@
package mylog
// The level vocabulary: what a level is called, how a name in free-form config
// text becomes a Level, and how the configured threshold is changed without
// reopening the destination.
import (
"fmt"
"strings"
)
// ParseLevel maps a level name to a Level. Parsing is case-insensitive and
// tolerates surrounding whitespace, because the level comes from free-form user
// text in the config file. An unknown name is an error: Init and SetLevel are
// programming-level calls where a typo should be loud.
func ParseLevel(s string) (Level, error) {
switch strings.ToUpper(strings.TrimSpace(s)) {
case "TRACE":
return levelTrace, nil
case "DEBUG":
return levelDebug, nil
case "INFO":
return levelInfo, nil
case "WARN":
return levelWarn, nil
case "ERROR":
return levelError, nil
case "FATAL":
return levelFatal, nil
default:
return levelInfo, fmt.Errorf("%w %q, want one of trace, debug, info, warn, error, fatal", errUnknownLevel, s)
}
}
// SetLevel changes the level without reopening the destination.
func SetLevel(loglevel string) error {
lvl, err := ParseLevel(loglevel)
if err != nil {
return err
}
lock.Lock()
defer lock.Unlock()
current.level = lvl
return nil
}
// name returns the uppercase token for the level.
func (l Level) name() string {
if n, ok := levelNames[l]; ok {
return n
}
return "UNKNOWN"
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

118
internal/mylog/log.go Normal file
View File

@@ -0,0 +1,118 @@
package mylog
// The destination lifecycle: opening the log, re-opening it for rotation, and
// closing it for good. The level vocabulary lives in level.go, the state in
// state.go, and the act of writing a line in write.go.
import (
"fmt"
"io"
stdlog "log"
"os"
)
// Init opens the log destination and sets the level.
//
// An empty filepath selects stderr. A non-empty path is opened appending, so
// an existing log is never truncated.
//
// Init is re-entrant: any previously open file is closed first, so repeated
// calls do not leak descriptors. If the new destination cannot be opened, Init
// returns an error and leaves the previous destination and level untouched —
// it deliberately does not fall back to stderr, because a silent fallback
// hides a broken log path from the operator.
func Init(loglevel string, filepath string) error {
lvl, err := ParseLevel(loglevel)
if err != nil {
return err
}
lock.Lock()
defer lock.Unlock()
dest, owned := io.Writer(os.Stderr), (*os.File)(nil)
if filepath != "" {
// The path comes from the operator's own config file, not from a
// network peer, and opening it is this function's entire purpose.
file, err := os.OpenFile(filepath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, filePerm) //nolint: gosec // G703
if err != nil {
return fmt.Errorf("unable to open log file %s: %w", filepath, err)
}
dest, owned = file, file
}
swapLocked(lvl, dest, filepath, owned)
// Keep the standard library's default logger pointed at the same
// destination, so third-party stdlib logging does not bypass our file.
stdlog.SetFlags(0)
stdlog.SetOutput(dest)
return nil
}
// ReOpen closes and re-opens the current log file, keeping its level. It exists
// for external rotation: the rotation tool moves the file aside, and ReOpen
// makes the logger pick up the new one.
//
// ReOpen on a closed logger is an error rather than a silent reopen, and on a
// stderr destination it is a no-op, since there is no file to re-open.
func ReOpen() error {
lock.RLock()
closed, path, lvl := current.closed, current.path, current.level
lock.RUnlock()
switch {
case closed:
return errClosed
case path == "":
return nil
default:
return Init(lvl.name(), path)
}
}
// Close closes the log destination. It is idempotent, and it is safe when the
// destination is stderr — stderr is not ours to close.
//
// After Close, logging calls are silently dropped rather than panicking or
// resurrecting the file: Close is terminal.
func Close() error {
lock.Lock()
defer lock.Unlock()
if current.closed {
return nil
}
current.closed = true
if current.owned == nil {
return nil
}
// The stdlib Logger has no Close — it does not own its writer — so the file
// we opened ourselves is closed directly.
file := current.owned
current.owned = nil
if file == nil {
return nil
}
return file.Close()
}
// swapLocked installs a new state, closing any file we previously owned.
// The caller must hold the write lock.
func swapLocked(lvl Level, dest io.Writer, path string, owned *os.File) {
if current.owned != nil {
_ = current.owned.Close()
}
current = newState(lvl, dest, path, owned, false)
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

View File

@@ -0,0 +1,28 @@
package mylog
// The standard-library-compatible Logger: what a module receives instead of
// depending on *log.Logger itself, and where its messages end up.
import "strings"
// Standard returns the Logger for modules that would otherwise depend on the
// standard library's *log.Logger. Messages passed to it are logged at debug
// level into the already-open destination.
//
// Returning the interface rather than the concrete *adapter is the whole point:
// it is the type such modules declare, so hiding it would defeat the
// substitution it exists for.
func Standard() Logger { //nolint:ireturn // deliberate public interface
return standard
}
// Output implements Logger, logging at debug level. The trailing newline of the
// standard library convention is trimmed so that mylog does not append a blank
// line to every message.
func (a *adapter) Output(_ int, s string) error {
write(levelDebug, strings.TrimRight(s, "\r\n"))
return nil
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

52
internal/mylog/state.go Normal file
View File

@@ -0,0 +1,52 @@
package mylog
// The logger state: the one struct every write goes through, how a new one is
// built, and the two accessors that read and replace it under the package lock.
// The state itself lives in globals.go with the rest of the package-level
// variables; this file holds only what a caller does with it.
import (
"io"
stdlog "log"
"os"
)
// newState builds a state whose stdlib logger writes to dest.
func newState(lvl Level, dest io.Writer, path string, owned *os.File, closed bool) *state {
return &state{
level: lvl,
dest: dest,
slog: newStdLogger(dest),
path: path,
owned: owned,
closed: closed,
}
}
// newStdLogger wraps a writer in a standard library logger. Flags are zeroed
// because the line format is rendered by mylog itself.
func newStdLogger(dest io.Writer) *stdLogger {
l := stdlog.New(dest, "", 0)
l.SetFlags(0)
return l
}
// getState returns the active logger state.
func getState() *state {
lock.RLock()
defer lock.RUnlock()
return current
}
// setState replaces the active state wholesale. Tests use it to restore the
// documented before-Init default.
func setState(s *state) {
lock.Lock()
defer lock.Unlock()
current = s
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

43
internal/mylog/types.go Normal file
View File

@@ -0,0 +1,43 @@
package mylog
// Types only. The level constants, the name table and Level.name live in
// globals.go and level.go respectively, because a file named "types" must hold
// nothing that is not a type.
import (
"io"
stdlog "log"
"os"
)
// Level is the severity of a log message. Levels are ordered, and a message is
// written when its own level is at or above the configured level.
type Level int
// stdLogger is the standard library logger doing the actual writing.
type stdLogger = stdlog.Logger
// Logger is the interface for modules that would otherwise depend on the
// standard library's *log.Logger. It is deliberately the smallest method set
// that *log.Logger satisfies, so widening a module's parameter type from
// *log.Logger to Logger keeps existing callers compiling.
type Logger interface {
Output(calldepth int, s string) error
}
// adapter is the Logger handed to such modules. Messages arrive through
// Output and are logged at debug level into the already-open destination.
type adapter struct{}
// state is everything the logger needs to write a line. It is held behind a
// lock so that a concurrent Init cannot close a destination mid-write.
type state struct {
level Level
dest io.Writer
slog *stdLogger
path string
owned *os.File
closed bool
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

View File

@@ -0,0 +1,63 @@
package mylog
import (
"fmt"
)
// Trace logs at the most verbose level, below DEBUG.
func Trace(args ...any) { write(levelTrace, fmt.Sprint(args...)) }
// Tracef logs a formatted message at the most verbose level, below DEBUG.
func Tracef(format string, args ...any) { write(levelTrace, fmt.Sprintf(format, args...)) }
// Debug logs at debug level.
func Debug(args ...any) { write(levelDebug, fmt.Sprint(args...)) }
// Debugf logs a formatted message at debug level.
func Debugf(format string, args ...any) { write(levelDebug, fmt.Sprintf(format, args...)) }
// Info logs at info level.
func Info(args ...any) { write(levelInfo, fmt.Sprint(args...)) }
// Infof logs a formatted message at info level.
func Infof(format string, args ...any) { write(levelInfo, fmt.Sprintf(format, args...)) }
// Warn logs at warn level.
func Warn(args ...any) { write(levelWarn, fmt.Sprint(args...)) }
// Warnf logs a formatted message at warn level.
func Warnf(format string, args ...any) { write(levelWarn, fmt.Sprintf(format, args...)) }
// Error logs at error level.
func Error(args ...any) { write(levelError, fmt.Sprint(args...)) }
// Errorf logs a formatted message at error level.
func Errorf(format string, args ...any) { write(levelError, fmt.Sprintf(format, args...)) }
// Fatal logs at fatal level, then exits the process with status 1.
//
// The destination is closed before exiting. os.Exit does not run deferred
// functions, so without this an explicit close a buffered tail could be lost —
// which is precisely the case where the message matters most.
func Fatal(args ...any) {
write(levelFatal, fmt.Sprint(args...))
exitAfterClose(1)
}
// Fatalf logs a formatted message at fatal level, then exits the process with
// status 1. The destination is closed first; see Fatal.
func Fatalf(format string, args ...any) {
write(levelFatal, fmt.Sprintf(format, args...))
exitAfterClose(1)
}
// exitAfterClose closes the logger and then exits, in that order.
func exitAfterClose(code int) {
_ = Close()
exit(code)
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

28
internal/mylog/write.go Normal file
View File

@@ -0,0 +1,28 @@
package mylog
// The one-line emit path: decide whether the line is wanted, render it, write
// it. Everything else in the package exists to reach these two functions.
import "time"
// write renders and writes one line if the level passes the filter and the
// logger is open. The read lock is held across the write so that a concurrent
// Init cannot close the destination underneath us.
func write(lvl Level, msg string) {
lock.RLock()
defer lock.RUnlock()
if current.closed || lvl < current.level {
return
}
_ = current.slog.Output(calldepth, render(lvl, msg))
}
// render builds one log line: a GMT+0 timestamp, the uppercase level, and the
// message verbatim, separated by exactly one space each.
func render(lvl Level, msg string) string {
return time.Now().UTC().Format(stampLayout) + " " + lvl.name() + " " + msg
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */