]> Cypherpunks.ru repositories - nncp.git/blob - src/log.go
Merge branch 'develop'
[nncp.git] / src / log.go
1 /*
2 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
3 Copyright (C) 2016-2021 Sergey Matveev <stargrave@stargrave.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, version 3 of the License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 package nncp
19
20 import (
21         "bytes"
22         "fmt"
23         "os"
24         "sync"
25         "time"
26
27         "go.cypherpunks.ru/recfile"
28         "golang.org/x/sys/unix"
29 )
30
31 const LogFdPrefix = "FD:"
32
33 var (
34         LogFd     *os.File
35         LogFdLock sync.Mutex
36 )
37
38 type LE struct {
39         K string
40         V interface{}
41 }
42 type LEs []LE
43
44 func (les LEs) Rec() string {
45         fields := make([]recfile.Field, 0, len(les)+1)
46         fields = append(fields, recfile.Field{
47                 Name: "When", Value: time.Now().UTC().Format(time.RFC3339Nano),
48         })
49         var val string
50         for _, le := range les {
51                 switch v := le.V.(type) {
52                 case int, int8, uint8, int64, uint64:
53                         val = fmt.Sprintf("%d", v)
54                 case bool:
55                         val = fmt.Sprintf("%v", v)
56                 default:
57                         val = fmt.Sprintf("%s", v)
58                 }
59                 fields = append(fields, recfile.Field{Name: le.K, Value: val})
60         }
61         b := bytes.NewBuffer(make([]byte, 0, 1<<10))
62         w := recfile.NewWriter(b)
63         _, err := w.RecordStart()
64         if err != nil {
65                 panic(err)
66         }
67         _, err = w.WriteFields(fields...)
68         if err != nil {
69                 panic(err)
70         }
71         return b.String()
72 }
73
74 func (ctx *Ctx) Log(rec string) {
75         if LogFd != nil {
76                 LogFdLock.Lock()
77                 LogFd.WriteString(rec)
78                 LogFdLock.Unlock()
79                 return
80         }
81         fdLock, err := os.OpenFile(
82                 ctx.LogPath+".lock",
83                 os.O_CREATE|os.O_WRONLY,
84                 os.FileMode(0666),
85         )
86         if err != nil {
87                 fmt.Fprintln(os.Stderr, "Can not open lock for log:", err)
88                 return
89         }
90         defer fdLock.Close()
91         fdLockFd := int(fdLock.Fd())
92         err = unix.Flock(fdLockFd, unix.LOCK_EX)
93         if err != nil {
94                 fmt.Fprintln(os.Stderr, "Can not acquire lock for log:", err)
95                 return
96         }
97         defer unix.Flock(fdLockFd, unix.LOCK_UN)
98         fd, err := os.OpenFile(
99                 ctx.LogPath,
100                 os.O_CREATE|os.O_WRONLY|os.O_APPEND,
101                 os.FileMode(0666),
102         )
103         if err != nil {
104                 fmt.Fprintln(os.Stderr, "Can not open log:", err)
105                 return
106         }
107         fd.WriteString(rec)
108         fd.Close()
109 }
110
111 func (ctx *Ctx) LogD(who string, les LEs, msg func(LEs) string) {
112         if !ctx.Debug {
113                 return
114         }
115         les = append(LEs{{"Debug", true}, {"Who", who}}, les...)
116         les = append(les, LE{"Msg", msg(les)})
117         fmt.Fprint(os.Stderr, les.Rec())
118 }
119
120 func (ctx *Ctx) LogI(who string, les LEs, msg func(LEs) string) {
121         les = append(LEs{{"Who", who}}, les...)
122         les = append(les, LE{"Msg", msg(les)})
123         rec := les.Rec()
124         if !ctx.Quiet {
125                 fmt.Fprintln(os.Stderr, ctx.HumanizeRec(rec))
126         }
127         ctx.Log(rec)
128 }
129
130 func (ctx *Ctx) LogE(who string, les LEs, err error, msg func(LEs) string) {
131         les = append(LEs{{"Err", err.Error()}, {"Who", who}}, les...)
132         les = append(les, LE{"Msg", msg(les)})
133         rec := les.Rec()
134         if !ctx.Quiet {
135                 fmt.Fprintln(os.Stderr, ctx.HumanizeRec(rec))
136         }
137         ctx.Log(rec)
138 }