download kind works

This commit is contained in:
Mike Schwörer 2025-08-19 10:03:12 +02:00
parent e7293464c1
commit 0f9b423d2f
Signed by: Mikescher
GPG Key ID: D3C7172E0A70F8CF
14 changed files with 587 additions and 36 deletions

4
.gitignore vendored
View File

@ -35,4 +35,6 @@ atlassian-ide-plugin.xml
########## Custom ########## ########## Custom ##########
_out/* _out/*
script_old.sh

View File

@ -3,18 +3,29 @@ package app
import ( import (
"os" "os"
"os/signal" "os/signal"
"sync"
"syscall" "syscall"
"fyne.io/systray" "fyne.io/systray"
"git.blackforestbytes.com/BlackForestBytes/goext/syncext"
"mikescher.com/kpsync" "mikescher.com/kpsync"
"mikescher.com/kpsync/assets"
"mikescher.com/kpsync/log"
) )
type Application struct { type Application struct {
masterLock sync.Mutex
config kpsync.Config config kpsync.Config
trayReady bool trayReady bool
sigStopChan chan bool uploadRunning *syncext.AtomicBool
sigErrChan chan error
sigStopChan chan bool // keepass exited
sigErrChan chan error // fatal error
sigSyncLoopStopChan chan bool // stop sync loop
sigTermKeepassChan chan bool // stop keepass
dbFile string dbFile string
stateFile string stateFile string
@ -25,10 +36,14 @@ func NewApplication() *Application {
cfg := kpsync.LoadConfig() cfg := kpsync.LoadConfig()
return &Application{ return &Application{
config: cfg, masterLock: sync.Mutex{},
trayReady: false, config: cfg,
sigStopChan: make(chan bool, 128), uploadRunning: syncext.NewAtomicBool(false),
sigErrChan: make(chan error, 128), trayReady: false,
sigStopChan: make(chan bool, 128),
sigErrChan: make(chan error, 128),
sigSyncLoopStopChan: make(chan bool, 128),
sigTermKeepassChan: make(chan bool, 128),
} }
} }
@ -42,6 +57,9 @@ func (app *Application) Run() {
app.sigErrChan <- err app.sigErrChan <- err
return return
} }
app.setTrayStateDirect("Sleeping...", assets.IconDefault)
err = app.runSyncLoop() err = app.runSyncLoop()
if err != nil { if err != nil {
app.sigErrChan <- err app.sigErrChan <- err
@ -55,14 +73,27 @@ func (app *Application) Run() {
select { select {
case <-sigTerm: case <-sigTerm:
app.sigSyncLoopStopChan <- true
app.sigTermKeepassChan <- true
log.LogInfo("Stopping application (received SIGTERM signal)")
// TODO term // TODO term
case _ = <-app.sigErrChan: case err := <-app.sigErrChan:
app.sigSyncLoopStopChan <- true
app.sigTermKeepassChan <- true
log.LogInfo("Stopping application (received ERROR)")
log.LogError(err.Error(), err)
// TODO stop // TODO stop
case _ = <-app.sigStopChan: case _ = <-app.sigStopChan:
app.sigSyncLoopStopChan <- true
app.sigTermKeepassChan <- true
log.LogInfo("Stopping application (received STOP)")
// TODO stop // TODO stop
} }

View File

@ -1,11 +1,18 @@
package app package app
import ( import (
"errors"
"fmt" "fmt"
"os" "os"
"os/exec"
"path" "path"
"syscall"
"time"
"git.blackforestbytes.com/BlackForestBytes/goext/exerr" "git.blackforestbytes.com/BlackForestBytes/goext/exerr"
"git.blackforestbytes.com/BlackForestBytes/goext/langext"
"git.blackforestbytes.com/BlackForestBytes/goext/timeext"
"github.com/fsnotify/fsnotify"
"mikescher.com/kpsync/assets" "mikescher.com/kpsync/assets"
"mikescher.com/kpsync/log" "mikescher.com/kpsync/log"
) )
@ -20,6 +27,11 @@ func (app *Application) initSync() error {
app.dbFile = path.Join(app.config.WorkDir, path.Base(app.config.LocalFallback)) app.dbFile = path.Join(app.config.WorkDir, path.Base(app.config.LocalFallback))
app.stateFile = path.Join(app.config.WorkDir, "kpsync.state") app.stateFile = path.Join(app.config.WorkDir, "kpsync.state")
if isKeepassRunning() {
log.LogError("keepassxc is already running!", nil)
return exerr.New(exerr.TypeInternal, "keepassxc is already running").Build()
}
state := app.readState() state := app.readState()
needsDownload := true needsDownload := true
@ -29,16 +41,18 @@ func (app *Application) initSync() error {
if err != nil { if err != nil {
log.LogError("Failed to calculate local database checksum", err) log.LogError("Failed to calculate local database checksum", err)
} else if localCS == state.Checksum { } else if localCS == state.Checksum {
remoteETag, err := app.getRemoteETag() remoteETag, remoteLM, err := app.getRemoteETag()
if err != nil { if err != nil {
log.LogError("Failed to get remote ETag", err) log.LogError("Failed to get remote ETag", err)
} else if remoteETag == state.ETag { } else if remoteETag == state.ETag {
log.LogInfo(fmt.Sprintf("Found local database matching remote database - skip initial download")) log.LogInfo(fmt.Sprintf("Found local database matching remote database - skip initial download"))
log.LogInfo(fmt.Sprintf("Checksum (cached) := %s", state.Checksum)) log.LogInfo(fmt.Sprintf("Checksum (cached) := %s", state.Checksum))
log.LogInfo(fmt.Sprintf("Checksum (local) := %s", localCS)) log.LogInfo(fmt.Sprintf("Checksum (local) := %s", localCS))
log.LogInfo(fmt.Sprintf("ETag (cached) := %s", state.ETag)) log.LogInfo(fmt.Sprintf("ETag (cached) := %s", state.ETag))
log.LogInfo(fmt.Sprintf("ETag (remote) := %s", remoteETag)) log.LogInfo(fmt.Sprintf("ETag (remote) := %s", remoteETag))
log.LogInfo(fmt.Sprintf("LastModified (cached) := %s", state.LastModified.Format(time.RFC3339)))
log.LogInfo(fmt.Sprintf("LastModified (remote) := %s", remoteLM.Format(time.RFC3339)))
needsDownload = false needsDownload = false
} }
@ -46,22 +60,152 @@ func (app *Application) initSync() error {
} }
if needsDownload { if needsDownload {
func() { err = func() error {
fin := app.setTrayState("Downloading database", assets.IconDefault) fin := app.setTrayState("Downloading database", assets.IconDownload)
defer fin() defer fin()
log.LogInfo(fmt.Sprintf("Downloading remote database to %s", app.dbFile)) log.LogInfo(fmt.Sprintf("Downloading remote database to %s", app.dbFile))
etag, err := app.downloadDatabase() etag, lm, sha, sz, err := app.downloadDatabase()
if err != nil { if err != nil {
log.LogError("Failed to download remote database", err) log.LogError("Failed to download remote database", err)
app.sigErrChan <- exerr.Wrap(err, "Failed to download remote database").Build() return exerr.Wrap(err, "Failed to download remote database").Build()
return }
log.LogInfo(fmt.Sprintf("Downloaded remote database to %s", app.dbFile))
log.LogInfo(fmt.Sprintf("Checksum := %s", sha))
log.LogInfo(fmt.Sprintf("ETag := %s", etag))
log.LogInfo(fmt.Sprintf("Size := %s", langext.FormatBytes(sz)))
log.LogInfo(fmt.Sprintf("LastModified := %s", lm.Format(time.RFC3339)))
err = app.saveState(etag, lm, sha, sz)
if err != nil {
log.LogError("Failed to save state", err)
return exerr.Wrap(err, "Failed to save state").Build()
}
return nil
}()
if err != nil {
return exerr.Wrap(err, "").Build()
}
} else {
log.LogInfo(fmt.Sprintf("Skip download - use existing local database %s", app.dbFile))
}
go func() {
log.LogInfo("Starting keepassxc...")
cmd := exec.Command("keepassxc", app.dbFile)
go func() {
select {
case <-app.sigTermKeepassChan:
log.LogInfo("Received signal to terminate keepassxc")
if cmd != nil && cmd.Process != nil {
log.LogInfo(fmt.Sprintf("Terminating keepassxc %d", cmd.Process.Pid))
err := cmd.Process.Signal(syscall.SIGTERM)
if err != nil {
log.LogError("Failed to terminate keepassxc", err)
} else {
log.LogInfo("keepassxc terminated successfully")
}
} else {
log.LogInfo("No keepassxc process to terminate")
}
} }
}() }()
}
err := cmd.Start()
if err != nil {
log.LogError("Failed to start keepassxc", err)
app.sigErrChan <- exerr.Wrap(err, "Failed to start keepassxc").Build()
return
}
log.LogInfo(fmt.Sprintf("keepassxc started with PID %d", cmd.Process.Pid))
err = cmd.Wait()
exitErr := &exec.ExitError{}
if errors.As(err, &exitErr) {
log.LogInfo(fmt.Sprintf("keepass exited with code %d", exitErr.ExitCode()))
app.sigStopChan <- true
return
}
if err != nil {
log.LogError("Failed to run keepassxc", err)
app.sigErrChan <- exerr.Wrap(err, "Failed to run keepassxc").Build()
return
}
log.LogInfo("keepassxc exited successfully")
app.sigStopChan <- true
return
}()
return nil
} }
func (app *Application) runSyncLoop() error { func (app *Application) runSyncLoop() error {
watcher, err := fsnotify.NewWatcher()
if err != nil {
return exerr.Wrap(err, "failed to init file-watcher").Build()
}
defer func() { _ = watcher.Close() }()
err = watcher.Add(app.config.WorkDir)
if err != nil {
return exerr.Wrap(err, "").Build()
}
for {
select {
case <-app.sigSyncLoopStopChan:
log.LogInfo("Stopping sync loop (received signal)")
return nil
case event := <-watcher.Events:
log.LogInfo(fmt.Sprintf("inotify event: [%s] %s", event.Op.String(), event.Name))
if event.Has(fsnotify.Write) && event.Name == app.dbFile {
func() {
app.masterLock.Lock()
app.uploadRunning.Wait(false)
app.uploadRunning.Set(true)
app.masterLock.Unlock()
defer app.uploadRunning.Set(false)
log.LogInfo("Database file was modified")
log.LogInfo(fmt.Sprintf("Sleeping for %d seconds", app.config.Debounce))
time.Sleep(timeext.FromSeconds(app.config.Debounce))
state := app.readState()
localCS, err := app.calcLocalChecksum()
if err != nil {
log.LogError("Failed to calculate local database checksum", err)
return
}
if localCS == state.Checksum {
log.LogInfo("Local database still matches remote (via checksum) - no need to upload")
log.LogInfo(fmt.Sprintf("Checksum (remote/cached) := %s", state.Checksum))
log.LogInfo(fmt.Sprintf("Checksum (local) := %s", localCS))
return
}
//TODO upload with IfMatch
}()
}
case err := <-watcher.Errors:
log.LogError("Filewatcher reported an error", err)
}
}
} }

View File

@ -18,3 +18,41 @@ func (app *Application) initTray() {
systray.Run(trayOnReady, nil) systray.Run(trayOnReady, nil)
} }
func (app *Application) setTrayState(txt string, icon []byte) func() {
if !app.trayReady {
return func() {}
}
app.masterLock.Lock()
defer app.masterLock.Unlock()
systray.SetIcon(icon)
systray.SetTooltip(txt)
fin := func() {
app.masterLock.Lock()
defer app.masterLock.Unlock()
if !app.trayReady {
return
}
systray.SetIcon(assets.IconDefault)
systray.SetTooltip("Sleeping...")
}
return fin
}
func (app *Application) setTrayStateDirect(txt string, icon []byte) {
if !app.trayReady {
return
}
app.masterLock.Lock()
defer app.masterLock.Unlock()
systray.SetIcon(icon)
systray.SetTooltip(txt)
}

View File

@ -3,9 +3,13 @@ package app
import ( import (
"encoding/json" "encoding/json"
"os" "os"
"strings"
"time"
"git.blackforestbytes.com/BlackForestBytes/goext/cryptext" "git.blackforestbytes.com/BlackForestBytes/goext/cryptext"
"git.blackforestbytes.com/BlackForestBytes/goext/exerr" "git.blackforestbytes.com/BlackForestBytes/goext/exerr"
"github.com/shirou/gopsutil/v3/process"
"mikescher.com/kpsync/log"
) )
func fileExists(p string) bool { func fileExists(p string) bool {
@ -17,12 +21,16 @@ func fileExists(p string) bool {
} }
type State struct { type State struct {
ETag string `json:"etag"` ETag string `json:"etag"`
Size int64 `json:"size"` Size int64 `json:"size"`
Checksum string `json:"checksum"` Checksum string `json:"checksum"`
LastModified time.Time `json:"lastModified"`
} }
func (app *Application) readState() *State { func (app *Application) readState() *State {
app.masterLock.Lock()
defer app.masterLock.Unlock()
bin, err := os.ReadFile(app.stateFile) bin, err := os.ReadFile(app.stateFile)
if err != nil { if err != nil {
return nil return nil
@ -37,6 +45,30 @@ func (app *Application) readState() *State {
return &state return &state
} }
func (app *Application) saveState(eTag string, lastModified time.Time, checksum string, size int64) error {
app.masterLock.Lock()
defer app.masterLock.Unlock()
obj := State{
ETag: eTag,
Size: size,
Checksum: checksum,
LastModified: lastModified,
}
bin, err := json.MarshalIndent(obj, "", " ")
if err != nil {
return exerr.Wrap(err, "Failed to marshal state").Build()
}
err = os.WriteFile(app.stateFile, bin, 0644)
if err != nil {
return exerr.Wrap(err, "Failed to write state file").Build()
}
return nil
}
func (app *Application) calcLocalChecksum() (string, error) { func (app *Application) calcLocalChecksum() (string, error) {
bin, err := os.ReadFile(app.dbFile) bin, err := os.ReadFile(app.dbFile)
if err != nil { if err != nil {
@ -45,3 +77,24 @@ func (app *Application) calcLocalChecksum() (string, error) {
return cryptext.BytesSha256(bin), nil return cryptext.BytesSha256(bin), nil
} }
func isKeepassRunning() bool {
proc, err := process.Processes()
if err != nil {
log.LogError("failed to query existing keepass process", err)
return false
}
for _, p := range proc {
name, err := p.Name()
if err != nil {
continue
}
if strings.ToLower(name) == "keepass" || strings.ToLower(name) == "keepassxc" {
return true
}
}
return false
}

View File

@ -1,5 +1,99 @@
package app package app
func (app *Application) initSync() { import (
"io"
"net/http"
"os"
"time"
"git.blackforestbytes.com/BlackForestBytes/goext/cryptext"
"git.blackforestbytes.com/BlackForestBytes/goext/exerr"
"git.blackforestbytes.com/BlackForestBytes/goext/timeext"
)
func (app *Application) downloadDatabase() (string, time.Time, string, int64, error) {
client := http.Client{Timeout: 90 * time.Second}
req, err := http.NewRequest("GET", app.config.WebDAVURL, nil)
if err != nil {
return "", time.Time{}, "", 0, exerr.Wrap(err, "").Build()
}
req.SetBasicAuth(app.config.WebDAVUser, app.config.WebDAVPass)
resp, err := client.Do(req)
if err != nil {
return "", time.Time{}, "", 0, exerr.Wrap(err, "Failed to download remote database").Build()
}
if resp.StatusCode != http.StatusOK {
return "", time.Time{}, "", 0, exerr.New(exerr.TypeInternal, "Failed to download remote database").Int("sc", resp.StatusCode).Build()
}
bin, err := io.ReadAll(resp.Body)
if err != nil {
return "", time.Time{}, "", 0, exerr.Wrap(err, "Failed to read response body").Build()
}
etag := resp.Header.Get("ETag")
if etag == "" {
return "", time.Time{}, "", 0, exerr.New(exerr.TypeInternal, "ETag header is missing").Build()
}
lmStr := resp.Header.Get("Last-Modified")
lm, err := time.Parse("Mon, 02 Jan 2006 15:04:05 MST", lmStr)
if err != nil {
return "", time.Time{}, "", 0, exerr.Wrap(err, "Failed to parse Last-Modified header").Build()
}
lm = lm.In(timeext.TimezoneBerlin)
sha := cryptext.BytesSha256(bin)
sz := int64(len(bin))
err = os.WriteFile(app.dbFile, bin, 0644)
if err != nil {
return "", time.Time{}, "", 0, exerr.Wrap(err, "Failed to write database file").Build()
}
return etag, lm, sha, sz, nil
}
func (app *Application) getRemoteETag() (string, time.Time, error) {
client := http.Client{Timeout: 90 * time.Second}
req, err := http.NewRequest("HEAD", app.config.WebDAVURL, nil)
if err != nil {
return "", time.Time{}, exerr.Wrap(err, "").Build()
}
req.SetBasicAuth(app.config.WebDAVUser, app.config.WebDAVPass)
resp, err := client.Do(req)
if err != nil {
return "", time.Time{}, exerr.Wrap(err, "Failed to download remote database").Build()
}
if resp.StatusCode != http.StatusOK {
return "", time.Time{}, exerr.New(exerr.TypeInternal, "Failed to download remote database").Int("sc", resp.StatusCode).Build()
}
etag := resp.Header.Get("ETag")
if etag == "" {
return "", time.Time{}, exerr.New(exerr.TypeInternal, "ETag header is missing").Build()
}
lmStr := resp.Header.Get("Last-Modified")
lm, err := time.Parse("Mon, 02 Jan 2006 15:04:05 MST", lmStr)
if err != nil {
return "", time.Time{}, exerr.Wrap(err, "Failed to parse Last-Modified header").Build()
}
lm = lm.In(timeext.TimezoneBerlin)
return etag, lm, nil
} }

View File

@ -4,7 +4,7 @@ import (
_ "embed" _ "embed"
) )
//go:embed IconInit.png //go:embed iconInit.png
var IconInit []byte var IconInit []byte
//go:embed iconDefault.png //go:embed iconDefault.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 323 B

BIN
assets/iconDownload.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 B

BIN
assets/iconInit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 B

View File

@ -48,26 +48,26 @@ func LoadConfig() Config {
flag.Parse() flag.Parse()
if strings.HasSuffix(configPath, "~") { if strings.HasPrefix(configPath, "~") {
usr, err := user.Current() usr, err := user.Current()
if err != nil { if err != nil {
log.FatalErr("Failed to query users home directory", err) log.FatalErr("Failed to query users home directory", err)
} }
fmt.Println(usr.HomeDir) fmt.Println(usr.HomeDir)
configPath = strings.TrimSuffix(configPath, "~") configPath = strings.TrimPrefix(configPath, "~")
configPath = fmt.Sprintf("%s/%s", usr.HomeDir, configPath) configPath = fmt.Sprintf("%s/%s", usr.HomeDir, configPath)
} }
if _, err := os.Stat(configPath); os.IsNotExist(err) && configPath != "" { if _, err := os.Stat(configPath); os.IsNotExist(err) && configPath != "" {
_ = os.WriteFile(configPath, langext.Must(json.Marshal(Config{ _ = os.WriteFile(configPath, langext.Must(json.MarshalIndent(Config{
WebDAVURL: "https://your-nextcloud-domain.example/remote.php/dav/files/keepass.kdbx", WebDAVURL: "https://your-nextcloud-domain.example/remote.php/dav/files/keepass.kdbx",
WebDAVUser: "", WebDAVUser: "",
WebDAVPass: "", WebDAVPass: "",
LocalFallback: "", LocalFallback: "",
WorkDir: "/tmp/kpsync", WorkDir: "/tmp/kpsync",
Debounce: 3500, Debounce: 3500,
})), 0644) }, "", " ")), 0644)
} }
cfgBin, err := os.ReadFile(configPath) cfgBin, err := os.ReadFile(configPath)

50
go.mod
View File

@ -3,8 +3,50 @@ module mikescher.com/kpsync
go 1.24.6 go 1.24.6
require ( require (
fyne.io/systray v1.11.0 // indirect fyne.io/systray v1.11.0
git.blackforestbytes.com/BlackForestBytes/goext v0.0.594 // indirect git.blackforestbytes.com/BlackForestBytes/goext v0.0.594
github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/fsnotify/fsnotify v1.9.0
golang.org/x/sys v0.34.0 // indirect github.com/shirou/gopsutil/v3 v3.24.5
)
require (
github.com/bytedance/sonic v1.13.3 // indirect
github.com/bytedance/sonic/loader v0.3.0 // indirect
github.com/cloudwego/base64x v0.1.5 // indirect
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect
github.com/gin-gonic/gin v1.10.1 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.27.0 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/rs/xid v1.6.0 // indirect
github.com/rs/zerolog v1.34.0 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.3.0 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.mongodb.org/mongo-driver v1.17.4 // indirect
golang.org/x/arch v0.19.0 // indirect
golang.org/x/crypto v0.40.0 // indirect
golang.org/x/net v0.42.0 // indirect
golang.org/x/sync v0.16.0 // indirect
golang.org/x/sys v0.34.0 // indirect
golang.org/x/text v0.27.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
) )

138
go.sum
View File

@ -2,7 +2,145 @@ fyne.io/systray v1.11.0 h1:D9HISlxSkx+jHSniMBR6fCFOUjk1x/OOOJLa9lJYAKg=
fyne.io/systray v1.11.0/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs= fyne.io/systray v1.11.0/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs=
git.blackforestbytes.com/BlackForestBytes/goext v0.0.594 h1:tFbvEAe7FMvLuW9/RuXXlxsi8/Ve7xVrFxZnk8e/0yU= git.blackforestbytes.com/BlackForestBytes/goext v0.0.594 h1:tFbvEAe7FMvLuW9/RuXXlxsi8/Ve7xVrFxZnk8e/0yU=
git.blackforestbytes.com/BlackForestBytes/goext v0.0.594/go.mod h1:vczjjViG013HjA5Ka3VTE7axDgqMChn1EsvEVg9LZnU= git.blackforestbytes.com/BlackForestBytes/goext v0.0.594/go.mod h1:vczjjViG013HjA5Ka3VTE7axDgqMChn1EsvEVg9LZnU=
github.com/bytedance/sonic v1.13.3 h1:MS8gmaH16Gtirygw7jV91pDCN33NyMrPbN7qiYhEsF0=
github.com/bytedance/sonic v1.13.3/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA=
github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw=
go.mongodb.org/mongo-driver v1.17.4/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
golang.org/x/arch v0.19.0 h1:LmbDQUodHThXE+htjrnmVD73M//D9GTH6wFZjyDkjyU=
golang.org/x/arch v0.19.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=

View File

@ -1,5 +1,9 @@
package log package log
import (
"git.blackforestbytes.com/BlackForestBytes/goext/exerr"
)
//TODO //TODO
func Fatal(msg string) { func Fatal(msg string) {
@ -8,15 +12,20 @@ func Fatal(msg string) {
func FatalErr(msg string, err error) { func FatalErr(msg string, err error) {
if err != nil { if err != nil {
panic("ERROR: " + msg + "\n" + err.Error()) println("FATAL: " + msg)
println(" " + err.Error())
println(exerr.FromError(err).FormatLog(exerr.LogPrintOverview))
panic(0)
} else { } else {
panic("ERROR: " + msg) panic("FATAL: " + msg)
} }
} }
func LogError(msg string, err error) { func LogError(msg string, err error) {
if err != nil { if err != nil {
println("ERROR: " + msg + "\n" + err.Error()) println("ERROR: " + msg)
println(" " + err.Error())
println(exerr.FromError(err).FormatLog(exerr.LogPrintOverview))
} else { } else {
println("ERROR: " + msg) println("ERROR: " + msg)
} }