]> 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         "fmt"
22         "os"
23         "sort"
24         "strings"
25         "time"
26
27         "golang.org/x/sys/unix"
28 )
29
30 type LogLevel string
31
32 type SDS map[string]interface{}
33
34 func sdFmt(who string, sds SDS) string {
35         keys := make([]string, 0, len(sds))
36         for k, _ := range sds {
37                 keys = append(keys, k)
38         }
39         sort.Strings(keys)
40         result := make([]string, 0, 1+len(keys))
41         result = append(result, "["+who)
42         for _, k := range keys {
43                 var value string
44                 switch v := sds[k].(type) {
45                 case int, int8, uint8, int64, uint64:
46                         value = fmt.Sprintf("%d", v)
47                 default:
48                         value = fmt.Sprintf("%s", v)
49                 }
50                 result = append(result, fmt.Sprintf(`%s="%s"`, k, value))
51         }
52         return strings.Join(result, " ") + "]"
53 }
54
55 func msgFmt(level LogLevel, who string, sds SDS, msg string) string {
56         result := fmt.Sprintf(
57                 "%s %s %s",
58                 level,
59                 time.Now().UTC().Format(time.RFC3339Nano),
60                 sdFmt(who, sds),
61         )
62         if len(msg) > 0 {
63                 result += " " + msg
64         }
65         return result + "\n"
66 }
67
68 func (ctx *Ctx) Log(msg string) {
69         fdLock, err := os.OpenFile(
70                 ctx.LogPath+".lock",
71                 os.O_CREATE|os.O_WRONLY,
72                 os.FileMode(0666),
73         )
74         if err != nil {
75                 fmt.Fprintln(os.Stderr, "Can not open lock for log:", err)
76                 return
77         }
78         defer fdLock.Close()
79         fdLockFd := int(fdLock.Fd())
80         err = unix.Flock(fdLockFd, unix.LOCK_EX)
81         if err != nil {
82                 fmt.Fprintln(os.Stderr, "Can not acquire lock for log:", err)
83                 return
84         }
85         defer unix.Flock(fdLockFd, unix.LOCK_UN)
86         fd, err := os.OpenFile(
87                 ctx.LogPath,
88                 os.O_CREATE|os.O_WRONLY|os.O_APPEND,
89                 os.FileMode(0666),
90         )
91         if err != nil {
92                 fmt.Fprintln(os.Stderr, "Can not open log:", err)
93                 return
94         }
95         fd.WriteString(msg) // #nosec G104
96         fd.Close()          // #nosec G104
97 }
98
99 func (ctx *Ctx) LogD(who string, sds SDS, msg string) {
100         if !ctx.Debug {
101                 return
102         }
103         fmt.Fprint(os.Stderr, msgFmt(LogLevel("D"), who, sds, msg))
104 }
105
106 func (ctx *Ctx) LogI(who string, sds SDS, msg string) {
107         msg = msgFmt(LogLevel("I"), who, sds, msg)
108         if !ctx.Quiet {
109                 fmt.Fprintln(os.Stderr, ctx.Humanize(msg))
110         }
111         ctx.Log(msg)
112 }
113
114 func (ctx *Ctx) LogE(who string, sds SDS, err error, msg string) {
115         sds["err"] = err.Error()
116         msg = msgFmt(LogLevel("E"), who, sds, msg)
117         if len(msg) > 2048 {
118                 msg = msg[:2048]
119         }
120         fmt.Fprintln(os.Stderr, ctx.Humanize(msg))
121         ctx.Log(msg)
122 }
123
124 func SdsAdd(sds, add SDS) SDS {
125         neu := SDS{}
126         for k, v := range sds {
127                 neu[k] = v
128         }
129         for k, v := range add {
130                 neu[k] = v
131         }
132         return neu
133 }