treewide: rename to hakurei
All checks were successful
Test / Create distribution (push) Successful in 43s
Test / Sandbox (push) Successful in 2m18s
Test / Hakurei (push) Successful in 3m10s
Test / Sandbox (race detector) (push) Successful in 3m30s
Test / Hakurei (race detector) (push) Successful in 4m43s
Test / Fpkg (push) Successful in 5m4s
Test / Flake checks (push) Successful in 1m12s
All checks were successful
Test / Create distribution (push) Successful in 43s
Test / Sandbox (push) Successful in 2m18s
Test / Hakurei (push) Successful in 3m10s
Test / Sandbox (race detector) (push) Successful in 3m30s
Test / Hakurei (race detector) (push) Successful in 4m43s
Test / Fpkg (push) Successful in 5m4s
Test / Flake checks (push) Successful in 1m12s
Fortify makes little sense for a container tool. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
81
internal/hlog/errors.go
Normal file
81
internal/hlog/errors.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package hlog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// baseError implements a basic error container
|
||||
type baseError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *baseError) Error() string { return e.Err.Error() }
|
||||
func (e *baseError) Unwrap() error { return e.Err }
|
||||
|
||||
// BaseError implements an error container with a user-facing message
|
||||
type BaseError struct {
|
||||
message string
|
||||
baseError
|
||||
}
|
||||
|
||||
// Message returns a user-facing error message
|
||||
func (e *BaseError) Message() string { return e.message }
|
||||
|
||||
// WrapErr wraps an error with a corresponding message.
|
||||
func WrapErr(err error, a ...any) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return wrapErr(err, fmt.Sprintln(a...))
|
||||
}
|
||||
|
||||
// WrapErrSuffix wraps an error with a corresponding message with err at the end of the message.
|
||||
func WrapErrSuffix(err error, a ...any) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return wrapErr(err, fmt.Sprintln(append(a, err)...))
|
||||
}
|
||||
|
||||
// WrapErrFunc wraps an error with a corresponding message returned by f.
|
||||
func WrapErrFunc(err error, f func(err error) string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return wrapErr(err, f(err))
|
||||
}
|
||||
|
||||
func wrapErr(err error, message string) *BaseError {
|
||||
return &BaseError{message, baseError{err}}
|
||||
}
|
||||
|
||||
var (
|
||||
baseErrorType = reflect.TypeFor[*BaseError]()
|
||||
)
|
||||
|
||||
func AsBaseError(err error, target **BaseError) bool {
|
||||
v := reflect.ValueOf(err)
|
||||
if !v.CanConvert(baseErrorType) {
|
||||
return false
|
||||
}
|
||||
|
||||
*target = v.Convert(baseErrorType).Interface().(*BaseError)
|
||||
return true
|
||||
}
|
||||
|
||||
func PrintBaseError(err error, fallback string) {
|
||||
var e *BaseError
|
||||
|
||||
if AsBaseError(err, &e) {
|
||||
if msg := e.Message(); strings.TrimSpace(msg) != "" {
|
||||
log.Print(msg)
|
||||
return
|
||||
}
|
||||
Verbose("*"+fallback, err)
|
||||
return
|
||||
}
|
||||
log.Println(fallback, err)
|
||||
}
|
||||
86
internal/hlog/hlog.go
Normal file
86
internal/hlog/hlog.go
Normal file
@@ -0,0 +1,86 @@
|
||||
// Package hlog provides various functions for output messages.
|
||||
package hlog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const (
|
||||
bufSize = 4 * 1024
|
||||
bufSizeMax = 16 * 1024 * 1024
|
||||
)
|
||||
|
||||
var o = &suspendable{w: os.Stderr}
|
||||
|
||||
// Prepare configures the system logger for [Suspend] and [Resume] to take effect.
|
||||
func Prepare(prefix string) { log.SetPrefix(prefix + ": "); log.SetFlags(0); log.SetOutput(o) }
|
||||
|
||||
type suspendable struct {
|
||||
w io.Writer
|
||||
s atomic.Bool
|
||||
|
||||
buf bytes.Buffer
|
||||
bufOnce sync.Once
|
||||
bufMu sync.Mutex
|
||||
dropped int
|
||||
}
|
||||
|
||||
func (s *suspendable) Write(p []byte) (n int, err error) {
|
||||
if !s.s.Load() {
|
||||
return s.w.Write(p)
|
||||
}
|
||||
s.bufOnce.Do(func() { s.prepareBuf() })
|
||||
|
||||
s.bufMu.Lock()
|
||||
defer s.bufMu.Unlock()
|
||||
|
||||
if l := len(p); s.buf.Len()+l > bufSizeMax {
|
||||
s.dropped += l
|
||||
return 0, syscall.ENOMEM
|
||||
}
|
||||
return s.buf.Write(p)
|
||||
}
|
||||
|
||||
func (s *suspendable) prepareBuf() { s.buf.Grow(bufSize) }
|
||||
func (s *suspendable) Suspend() bool { return o.s.CompareAndSwap(false, true) }
|
||||
func (s *suspendable) Resume() (resumed bool, dropped uintptr, n int64, err error) {
|
||||
if o.s.CompareAndSwap(true, false) {
|
||||
o.bufMu.Lock()
|
||||
defer o.bufMu.Unlock()
|
||||
|
||||
resumed = true
|
||||
dropped = uintptr(o.dropped)
|
||||
|
||||
o.dropped = 0
|
||||
n, err = io.Copy(s.w, &s.buf)
|
||||
s.buf = bytes.Buffer{}
|
||||
s.prepareBuf()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Suspend() bool { return o.Suspend() }
|
||||
func Resume() bool {
|
||||
resumed, dropped, _, err := o.Resume()
|
||||
if err != nil {
|
||||
// probably going to result in an error as well,
|
||||
// so this call is as good as unreachable
|
||||
log.Printf("cannot dump buffer on resume: %v", err)
|
||||
}
|
||||
if resumed && dropped > 0 {
|
||||
log.Fatalf("dropped %d bytes while output is suspended", dropped)
|
||||
}
|
||||
return resumed
|
||||
}
|
||||
|
||||
func BeforeExit() {
|
||||
if Resume() {
|
||||
log.Printf("beforeExit reached on suspended output")
|
||||
}
|
||||
}
|
||||
12
internal/hlog/msg.go
Normal file
12
internal/hlog/msg.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package hlog
|
||||
|
||||
type Output struct{}
|
||||
|
||||
func (Output) IsVerbose() bool { return Load() }
|
||||
func (Output) Verbose(v ...any) { Verbose(v...) }
|
||||
func (Output) Verbosef(format string, v ...any) { Verbosef(format, v...) }
|
||||
func (Output) WrapErr(err error, a ...any) error { return WrapErr(err, a...) }
|
||||
func (Output) PrintBaseErr(err error, fallback string) { PrintBaseError(err, fallback) }
|
||||
func (Output) Suspend() { Suspend() }
|
||||
func (Output) Resume() bool { return Resume() }
|
||||
func (Output) BeforeExit() { BeforeExit() }
|
||||
23
internal/hlog/verbose.go
Normal file
23
internal/hlog/verbose.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package hlog
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
var verbose = new(atomic.Bool)
|
||||
|
||||
func Load() bool { return verbose.Load() }
|
||||
func Store(v bool) { verbose.Store(v) }
|
||||
|
||||
func Verbosef(format string, v ...any) {
|
||||
if verbose.Load() {
|
||||
log.Printf(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
func Verbose(v ...any) {
|
||||
if verbose.Load() {
|
||||
log.Println(v...)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user