Introduce internal message IDs

For now, these can be used as cursors in the logs. Future patches will
introduce functions that perform log queries with message IDs.

The IDs are state-less tokens containing all the required information to
refer to an on-disk log line: network name, entity name, date and byte
offset. The byte offset doesn't need to point to the first byte of the
line, any byte will do (note, this makes it so message IDs aren't
necessarily unique, we may want to change that in the future).

These internal message IDs are not exposed to clients because we don't
support upstream message IDs yet.
This commit is contained in:
Simon Ser 2020-08-19 12:51:41 +02:00
parent 4dae0da59f
commit 8f29ae41a2
No known key found for this signature in database
GPG key ID: 0FDE7BE0E88F5E48
2 changed files with 53 additions and 9 deletions

View file

@ -3,6 +3,7 @@ package soju
import (
"bufio"
"fmt"
"io"
"os"
"path/filepath"
"strings"
@ -39,10 +40,48 @@ func logPath(network *network, entity string, t time.Time) string {
return filepath.Join(srv.LogPath, escapeFilename.Replace(user.Username), escapeFilename.Replace(network.GetName()), escapeFilename.Replace(entity), filename)
}
func (ml *messageLogger) Append(msg *irc.Message) error {
func parseMsgID(s string) (network, entity string, t time.Time, offset int64, err error) {
var year, month, day int
_, err = fmt.Sscanf(s, "%s %s %04d-%02d-%02d %d", &network, &entity, &year, &month, &day, &offset)
if err != nil {
return "", "", time.Time{}, 0, fmt.Errorf("invalid message ID: %v", err)
}
t = time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.Local)
return network, entity, t, offset, nil
}
func formatMsgID(network, entity string, t time.Time, offset int64) string {
year, month, day := t.Date()
return fmt.Sprintf("%s %s %04d-%02d-%02d %d", network, entity, year, month, day, offset)
}
// nextMsgID queries the message ID for the next message to be written to f.
func nextMsgID(network *network, entity string, t time.Time, f *os.File) (string, error) {
offset, err := f.Seek(0, io.SeekEnd)
if err != nil {
return "", err
}
return formatMsgID(network.GetName(), entity, t, offset), nil
}
// lastMsgID queries the last message ID for the given network, entity and
// date. The message ID returned may not refer to a valid message, but can be
// used in history queries.
func lastMsgID(network *network, entity string, t time.Time) (string, error) {
p := logPath(network, entity, t)
fi, err := os.Stat(p)
if os.IsNotExist(err) {
return formatMsgID(network.GetName(), entity, t, -1), nil
} else if err != nil {
return "", err
}
return formatMsgID(network.GetName(), entity, t, fi.Size()-1), nil
}
func (ml *messageLogger) Append(msg *irc.Message) (string, error) {
s := formatMessage(msg)
if s == "" {
return nil
return "", nil
}
var t time.Time
@ -50,7 +89,7 @@ func (ml *messageLogger) Append(msg *irc.Message) error {
var err error
t, err = time.Parse(serverTimeLayout, string(tag))
if err != nil {
return fmt.Errorf("failed to parse message time tag: %v", err)
return "", fmt.Errorf("failed to parse message time tag: %v", err)
}
t = t.In(time.Local)
} else {
@ -67,23 +106,28 @@ func (ml *messageLogger) Append(msg *irc.Message) error {
dir := filepath.Dir(path)
if err := os.MkdirAll(dir, 0700); err != nil {
return fmt.Errorf("failed to create logs directory %q: %v", dir, err)
return "", fmt.Errorf("failed to create logs directory %q: %v", dir, err)
}
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
if err != nil {
return fmt.Errorf("failed to open log file %q: %v", path, err)
return "", fmt.Errorf("failed to open log file %q: %v", path, err)
}
ml.path = path
ml.file = f
}
_, err := fmt.Fprintf(ml.file, "[%02d:%02d:%02d] %s\n", t.Hour(), t.Minute(), t.Second(), s)
msgID, err := nextMsgID(ml.network, ml.entity, t, ml.file)
if err != nil {
return fmt.Errorf("failed to log message to %q: %v", ml.path, err)
return "", fmt.Errorf("failed to generate message ID: %v", err)
}
return nil
_, err = fmt.Fprintf(ml.file, "[%02d:%02d:%02d] %s\n", t.Hour(), t.Minute(), t.Second(), s)
if err != nil {
return "", fmt.Errorf("failed to log message to %q: %v", ml.path, err)
}
return msgID, nil
}
func (ml *messageLogger) Close() error {

View file

@ -1623,7 +1623,7 @@ func (uc *upstreamConn) appendLog(entity string, msg *irc.Message) {
uc.messageLoggers[entity] = ml
}
if err := ml.Append(msg); err != nil {
if _, err := ml.Append(msg); err != nil {
uc.logger.Printf("failed to log message: %v", err)
}
}