/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2020 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
package nncp
import (
+ "bytes"
"fmt"
"os"
- "sort"
- "strings"
+ "sync"
"time"
+ "go.cypherpunks.ru/recfile"
"golang.org/x/sys/unix"
)
-type LogLevel string
+const LogFdPrefix = "FD:"
-type SDS map[string]interface{}
+var (
+ LogFd *os.File
+ LogFdLock sync.Mutex
+)
+
+type LE struct {
+ K string
+ V interface{}
+}
+type LEs []LE
-func sdFmt(who string, sds SDS) string {
- keys := make([]string, 0, len(sds))
- for k, _ := range sds {
- keys = append(keys, k)
+func (les LEs) Rec() string {
+ b := bytes.NewBuffer(make([]byte, 0, 1<<10))
+ w := recfile.NewWriter(b)
+ _, err := w.RecordStart()
+ if err != nil {
+ panic(err)
}
- sort.Strings(keys)
- result := make([]string, 0, 1+len(keys))
- result = append(result, "["+who)
- for _, k := range keys {
- var value string
- switch v := sds[k].(type) {
+ _, err = w.WriteFields(recfile.Field{
+ Name: "When",
+ Value: time.Now().UTC().Format(time.RFC3339Nano),
+ })
+ if err != nil {
+ panic(err)
+ }
+ for _, le := range les {
+ switch v := le.V.(type) {
case int, int8, uint8, int64, uint64:
- value = fmt.Sprintf("%d", v)
+ _, err = w.WriteFields(recfile.Field{
+ Name: le.K,
+ Value: fmt.Sprintf("%d", v),
+ })
+ case bool:
+ _, err = w.WriteFields(recfile.Field{
+ Name: le.K,
+ Value: fmt.Sprintf("%v", v),
+ })
+ case []string:
+ if len(v) > 0 {
+ _, err = w.WriteFieldMultiline(le.K, v)
+ }
default:
- value = fmt.Sprintf("%s", v)
+ _, err = w.WriteFields(recfile.Field{
+ Name: le.K,
+ Value: fmt.Sprintf("%s", v),
+ })
+ }
+ if err != nil {
+ panic(err)
}
- result = append(result, fmt.Sprintf(`%s="%s"`, k, value))
}
- return strings.Join(result, " ") + "]"
+ return b.String()
}
-func msgFmt(level LogLevel, who string, sds SDS, msg string) string {
- result := fmt.Sprintf(
- "%s %s %s",
- level,
- time.Now().UTC().Format(time.RFC3339Nano),
- sdFmt(who, sds),
- )
- if len(msg) > 0 {
- result += " " + msg
+func (ctx *Ctx) Log(rec string) {
+ if LogFd != nil {
+ LogFdLock.Lock()
+ LogFd.WriteString(rec)
+ LogFdLock.Unlock()
+ return
}
- return result + "\n"
-}
-
-func (ctx *Ctx) Log(msg string) {
fdLock, err := os.OpenFile(
ctx.LogPath+".lock",
os.O_CREATE|os.O_WRONLY,
fmt.Fprintln(os.Stderr, "Can not open log:", err)
return
}
- fd.WriteString(msg) // #nosec G104
- fd.Close() // #nosec G104
+ fd.WriteString(rec)
+ fd.Close()
}
-func (ctx *Ctx) LogD(who string, sds SDS, msg string) {
+func (ctx *Ctx) LogD(who string, les LEs, msg func(LEs) string) {
if !ctx.Debug {
return
}
- fmt.Fprint(os.Stderr, msgFmt(LogLevel("D"), who, sds, msg))
+ les = append(LEs{{"Debug", true}, {"Who", who}}, les...)
+ les = append(les, LE{"Msg", msg(les)})
+ fmt.Fprint(os.Stderr, les.Rec())
}
-func (ctx *Ctx) LogI(who string, sds SDS, msg string) {
- msg = msgFmt(LogLevel("I"), who, sds, msg)
- if !ctx.Quiet {
- fmt.Fprintln(os.Stderr, ctx.Humanize(msg))
+func (ctx *Ctx) LogI(who string, les LEs, msg func(LEs) string) {
+ les = append(LEs{{"Who", who}}, les...)
+ les = append(les, LE{"Msg", msg(les)})
+ rec := les.Rec()
+ if ctx.Debug {
+ fmt.Fprint(os.Stderr, rec)
}
- ctx.Log(msg)
-}
-
-func (ctx *Ctx) LogE(who string, sds SDS, err error, msg string) {
- sds["err"] = err.Error()
- msg = msgFmt(LogLevel("E"), who, sds, msg)
- if len(msg) > 2048 {
- msg = msg[:2048]
+ if !ctx.Quiet {
+ fmt.Fprintln(os.Stderr, ctx.HumanizeRec(rec))
}
- fmt.Fprintln(os.Stderr, ctx.Humanize(msg))
- ctx.Log(msg)
+ ctx.Log(rec)
}
-func SdsAdd(sds, add SDS) SDS {
- neu := SDS{}
- for k, v := range sds {
- neu[k] = v
+func (ctx *Ctx) LogE(who string, les LEs, err error, msg func(LEs) string) {
+ les = append(LEs{{"Err", err.Error()}, {"Who", who}}, les...)
+ les = append(les, LE{"Msg", msg(les)})
+ rec := les.Rec()
+ if ctx.Debug {
+ fmt.Fprint(os.Stderr, rec)
}
- for k, v := range add {
- neu[k] = v
+ if !ctx.Quiet {
+ fmt.Fprintln(os.Stderr, ctx.HumanizeRec(rec))
}
- return neu
+ ctx.Log(rec)
}