log.go (4234B)
1 package log 2 3 import ( 4 "context" 5 "encoding/binary" 6 "time" 7 8 "git.defalsify.org/vise.git/logging" 9 "git.defalsify.org/vise.git/lang" 10 "git.defalsify.org/vise.git/db" 11 ) 12 13 var ( 14 logg logging.Logger = logging.NewVanilla().WithDomain("logdb") 15 ) 16 17 // TODO: add a formatted dumper for the logdb 18 type logDb struct { 19 db.Db 20 logDb db.Db 21 } 22 23 type LogEntry struct { 24 Key []byte 25 Val []byte 26 SessionId string 27 When time.Time 28 Pfx uint8 29 } 30 31 // NewLogDb creates a wrapper for the main Db in the first argument, which write an entry for every Put to the second database. 32 // 33 // All interface methods operate like normal on the main Db. 34 // 35 // Errors writing to the log database are ignored (but logged). 36 // 37 // The Put is recorded in the second database under a chronologically sorted session key: 38 // 39 // `db.DATATYPE_UNKNOWN | sessionId | "_" | Big-endian uint64 representation of nanoseconds of time of put` 40 // 41 // The value is stored as: 42 // 43 // `varint(length(key)) | key | value` 44 func NewLogDb(mainDb db.Db, subDb db.Db) *logDb { 45 subDb.Base().AllowUnknownPrefix() 46 return &logDb{ 47 Db: mainDb, 48 logDb: subDb, 49 } 50 } 51 52 // Start implements Db 53 func (ldb *logDb) Start(ctx context.Context) error { 54 err := ldb.Db.Start(ctx) 55 if err != nil { 56 return err 57 } 58 err = ldb.logDb.Start(ctx) 59 if err != nil { 60 logg.DebugCtxf(ctx, "logdb start fail", "ctx", ctx, "err", err) 61 } 62 return nil 63 } 64 65 // Stop implements Db 66 func (ldb *logDb) Stop(ctx context.Context) error { 67 err := ldb.logDb.Stop(ctx) 68 if err != nil { 69 logg.DebugCtxf(ctx, "logdb stop fail", "ctx", ctx, "err", err) 70 } 71 return ldb.Db.Stop(ctx) 72 } 73 74 // Connect implements Db. 75 func (ldb *logDb) Connect(ctx context.Context, connStr string) error { 76 err := ldb.Db.Connect(ctx, connStr) 77 if err != nil { 78 return err 79 } 80 err = ldb.logDb.Connect(ctx, connStr) 81 if err != nil { 82 ldb.Db.Close(ctx) 83 } 84 return err 85 } 86 87 // SetLanguage implements Db. 88 func (ldb *logDb) SetLanguage(ln *lang.Language) { 89 ldb.Db.SetLanguage(ln) 90 ldb.logDb.SetLanguage(ln) 91 } 92 93 // SetSession implements Db. 94 func (ldb *logDb) SetSession(sessionId string) { 95 ldb.Db.SetSession(sessionId) 96 ldb.logDb.SetSession(sessionId) 97 } 98 99 // Base implement Db. 100 func (ldb *logDb) Base() *db.DbBase { 101 return ldb.Db.Base() 102 } 103 104 // create the chronological logentry key to store the put under. 105 func (ldb *logDb) toLogDbEntry(ctx context.Context, key []byte, val []byte) ([]byte, []byte) { 106 var innerKey []byte 107 var innerValKey []byte 108 var innerValVal []byte 109 var l []byte 110 111 lk, err := ldb.Base().ToKey(ctx, key) 112 if err != nil { 113 return nil, nil 114 } 115 if lk.Translation == nil { 116 innerValKey = lk.Default 117 } else { 118 innerValKey = lk.Translation 119 } 120 121 l = make([]byte, 8) 122 c := binary.PutUvarint(l, uint64(len(innerValKey))) 123 innerValKey = append(l[:c], innerValKey...) 124 innerValKey = append(innerValKey, val...) 125 126 innerKey = make([]byte, 8) 127 t := time.Now().UnixNano() 128 binary.BigEndian.PutUint64(innerKey, uint64(t)) 129 return innerKey, append(innerValKey, innerValVal...) 130 } 131 132 // ToLogDbEntry decodes a logdb entry to a structure containing the relevant metadata aswell as the original key and value pass by the client. 133 func (ldb *logDb) ToLogDbEntry(ctx context.Context, key []byte, val []byte) (LogEntry, error) { 134 var err error 135 136 key = key[1:] 137 tb := key[len(key)-8:] 138 nsecs := binary.BigEndian.Uint64(tb[:8]) 139 nsecPart := int64(nsecs % 1000000000) 140 secPart := int64(nsecs / 1000000000) 141 t := time.Unix(secPart, nsecPart) 142 143 sessionId := key[:len(key)-8] 144 145 l, c := binary.Uvarint(val) 146 lk := val[c:uint64(c)+l] 147 v := val[uint64(c)+l:] 148 pfx := lk[0] 149 k, err := ldb.Base().DecodeKey(ctx, lk) 150 if err != nil { 151 return LogEntry{}, err 152 } 153 sessionId = lk[1:len(lk)-len(k)-1] 154 return LogEntry{ 155 Key: k, 156 Val: v, 157 SessionId: string(sessionId), 158 When: t, 159 Pfx: pfx, 160 }, nil 161 } 162 163 // Put implements Db. 164 func (ldb *logDb) Put(ctx context.Context, key []byte, val []byte) error { 165 ldb.logDb.SetPrefix(db.DATATYPE_UNKNOWN) 166 err := ldb.Db.Put(ctx, key, val) 167 if err != nil { 168 return err 169 } 170 key, val = ldb.toLogDbEntry(ctx, key, val) 171 if key == nil { 172 logg.DebugCtxf(ctx, "logdb kv fail", "key", key, "err", err) 173 return nil 174 } 175 err = ldb.logDb.Put(ctx, key, val) 176 if err != nil { 177 logg.DebugCtxf(ctx, "logdb put fail", "key", key, "err", err) 178 } 179 return nil 180 }