source: code/trunk/logger.go@ 246

Last change on this file since 246 was 235, checked in by contact, 5 years ago

Log TOPIC messages

See [1].

[1]: https://github.com/znc/znc/blob/82c4ad8362c6b2f4406770c54a5e10a40fdba548/modules/log.cpp#L486

File size: 3.1 KB
Line 
1package soju
2
3import (
4 "fmt"
5 "os"
6 "path/filepath"
7 "strings"
8 "time"
9
10 "gopkg.in/irc.v3"
11)
12
13type messageLogger struct {
14 conn *upstreamConn
15 entity string
16
17 filename string
18 file *os.File
19}
20
21func newMessageLogger(uc *upstreamConn, entity string) *messageLogger {
22 return &messageLogger{
23 conn: uc,
24 entity: entity,
25 }
26}
27
28func (ml *messageLogger) Append(msg *irc.Message) error {
29 s := formatMessage(msg)
30 if s == "" {
31 return nil
32 }
33
34 // TODO: parse time from msg.Tags["time"], if available
35
36 // TODO: enforce maximum open file handles (LRU cache of file handles)
37 // TODO: handle non-monotonic clock behaviour
38 now := time.Now()
39 year, month, day := now.Date()
40 filename := fmt.Sprintf("%04d-%02d-%02d.log", year, month, day)
41 if ml.filename != filename {
42 if ml.file != nil {
43 ml.file.Close()
44 }
45
46 // TODO: handle/forbid network/entity names with illegal path characters
47 dir := filepath.Join(ml.conn.srv.LogPath, ml.conn.user.Username, ml.conn.network.GetName(), ml.entity)
48 if err := os.MkdirAll(dir, 0700); err != nil {
49 return fmt.Errorf("failed to create logs directory %q: %v", dir, err)
50 }
51
52 path := filepath.Join(dir, filename)
53 f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
54 if err != nil {
55 return fmt.Errorf("failed to open log file %q: %v", path, err)
56 }
57
58 ml.filename = filename
59 ml.file = f
60 }
61
62 _, err := fmt.Fprintf(ml.file, "[%02d:%02d:%02d] %s\n", now.Hour(), now.Minute(), now.Second(), s)
63 if err != nil {
64 return fmt.Errorf("failed to log message to %q: %v", ml.filename, err)
65 }
66 return nil
67}
68
69func (ml *messageLogger) Close() error {
70 if ml.file == nil {
71 return nil
72 }
73 return ml.file.Close()
74}
75
76// formatMessage formats a message log line. It assumes a well-formed IRC
77// message.
78func formatMessage(msg *irc.Message) string {
79 switch strings.ToUpper(msg.Command) {
80 case "NICK":
81 return fmt.Sprintf("*** %s is now known as %s", msg.Prefix.Name, msg.Params[0])
82 case "JOIN":
83 return fmt.Sprintf("*** Joins: %s (%s@%s)", msg.Prefix.Name, msg.Prefix.User, msg.Prefix.Host)
84 case "PART":
85 var reason string
86 if len(msg.Params) > 1 {
87 reason = msg.Params[1]
88 }
89 return fmt.Sprintf("*** Parts: %s (%s@%s) (%s)", msg.Prefix.Name, msg.Prefix.User, msg.Prefix.Host, reason)
90 case "KICK":
91 nick := msg.Params[1]
92 var reason string
93 if len(msg.Params) > 2 {
94 reason = msg.Params[2]
95 }
96 return fmt.Sprintf("*** %s was kicked by %s (%s)", nick, msg.Prefix.Name, reason)
97 case "QUIT":
98 var reason string
99 if len(msg.Params) > 0 {
100 reason = msg.Params[0]
101 }
102 return fmt.Sprintf("*** Quits: %s (%s@%s) (%s)", msg.Prefix.Name, msg.Prefix.User, msg.Prefix.Host, reason)
103 case "TOPIC":
104 var topic string
105 if len(msg.Params) > 1 {
106 topic = msg.Params[1]
107 }
108 return fmt.Sprintf("*** %s changes topic to '%s'", msg.Prefix.Name, topic)
109 case "MODE":
110 return fmt.Sprintf("*** %s sets mode: %s", msg.Prefix.Name, strings.Join(msg.Params[1:], " "))
111 case "NOTICE":
112 return fmt.Sprintf("-%s- %s", msg.Prefix.Name, msg.Params[1])
113 case "PRIVMSG":
114 return fmt.Sprintf("<%s> %s", msg.Prefix.Name, msg.Params[1])
115 default:
116 return ""
117 }
118}
Note: See TracBrowser for help on using the repository browser.