package sshlog import ( "time" "github.com/sirupsen/logrus" capnp "zombiezen.com/go/capnproto2" "zombiezen.com/go/capnproto2/pogs" ) // SessionLogger will buffer and write events to disk using capnp proto for session replay type SessionLogger struct { logger *Logger encoder *capnp.Encoder } type sessionLogData struct { Timestamp string // The UTC timestamp of when the log occurred Content []byte // The shell output } // NewSessionLogger creates a new session logger by encapsulating a Logger object and writing capnp encoded messages to it func NewSessionLogger(filename string, logger *logrus.Logger, flushInterval time.Duration, maxFileSize int64) (*SessionLogger, error) { l, err := NewLogger(filename, logger, flushInterval, maxFileSize) if err != nil { return nil, err } sessionLogger := &SessionLogger{ logger: l, encoder: capnp.NewEncoder(l), } return sessionLogger, nil } // Writes to a log buffer. Implements the io.Writer interface. func (l *SessionLogger) Write(p []byte) (n int, err error) { return l.writeSessionLog(&sessionLogData{ Timestamp: time.Now().UTC().Format(time.RFC3339), Content: p, }) } // Close drains anything left in the buffer and cleans up any resources still // in use. func (l *SessionLogger) Close() error { return l.logger.Close() } func (l *SessionLogger) writeSessionLog(p *sessionLogData) (int, error) { msg, seg, err := capnp.NewMessage(capnp.SingleSegment(nil)) if err != nil { return 0, err } log, err := NewRootSessionLog(seg) if err != nil { return 0, err } log.SetTimestamp(p.Timestamp) log.SetContent(p.Content) if err := l.encoder.Encode(msg); err != nil { return 0, err } return len(p.Content), nil } func unmarshalSessionLog(s SessionLog) (*sessionLogData, error) { p := new(sessionLogData) err := pogs.Extract(p, SessionLog_TypeID, s.Struct) return p, err }