github.com/Schaudge/grailbase@v0.0.0-20240223061707-44c758a471c0/eventlog/eventlog.go (about) 1 // Copyright 2020 GRAIL, Inc. All rights reserved. 2 // Use of this source code is governed by the Apache 2.0 3 // license that can be found in the LICENSE file. 4 5 // Package eventlog provides logging of semi-structured events, particularly in 6 // service of downstream analysis, e.g. when machines are started, when a user 7 // issues a command, when failures happen. 8 // 9 // For example, you can log events to CloudWatch Logs: 10 // 11 // sess := session.NewSession() 12 // cw := cloudwatchlogs.New(sess) 13 // e := cloudwatch.NewEventer(cw, "myLogGroup", "myLogStream") 14 // e.Event("rpcRetry", "org", "GRAIL", "retry", 0, "maxRetry", 10) 15 // e.Event("machineStopped", "addr", "192.168.1.1", "duration", 3600.0, "startTime": 1584140534) 16 // 17 // These events can now be analyzed and monitored using CloudWatch Logs tooling. 18 package eventlog 19 20 import ( 21 "bytes" 22 23 "github.com/Schaudge/grailbase/config" 24 "github.com/Schaudge/grailbase/log" 25 ) 26 27 func init() { 28 config.Register("eventer/nop", func(constr *config.Constructor[Nop]) { 29 constr.Doc = "eventer/nop configures a no-op event logger" 30 constr.New = func() (Nop, error) { 31 return Nop{}, nil 32 } 33 }) 34 config.Register("eventer/log-info", func(constr *config.Constructor[Log]) { 35 constr.Doc = "eventer/log-info configures an eventer that writes events at log level info" 36 constr.New = func() (Log, error) { 37 return Log(log.Info), nil 38 } 39 }) 40 // We do the most conservative thing by default, making event logging a 41 // no-op. 42 config.Default("eventer", "eventer/nop") 43 } 44 45 // Eventer is called to log events. 46 type Eventer interface { 47 // Event logs an event of typ with (key string, value interface{}) fields given in fieldPairs 48 // as k0, v0, k1, v1, ...kn, vn. For example: 49 // 50 // s.Event("machineStart", "addr", "192.168.1.2", "time", time.Now().Unix()) 51 // 52 // The value will be serialized as JSON. 53 // 54 // The key "eventType" is reserved. Field keys must be unique. Any violation will result 55 // in the event being dropped and logged. 56 // 57 // Implementations must be safe for concurrent use. 58 Event(typ string, fieldPairs ...interface{}) 59 } 60 61 // Nop is a no-op Eventer. 62 type Nop struct{} 63 64 var _ Eventer = Nop{} 65 66 func (Nop) String() string { 67 return "disabled" 68 } 69 70 // Event implements Eventer. 71 func (Nop) Event(_ string, _ ...interface{}) {} 72 73 // Log is an Eventer that writes events to the logger. It's intended for debugging/development. 74 type Log log.Level 75 76 var _ Eventer = Log(log.Debug) 77 78 // Event implements Eventer. 79 func (l Log) Event(typ string, fieldPairs ...interface{}) { 80 f := bytes.NewBufferString("eventlog: %s {") 81 for i := range fieldPairs { 82 f.WriteString("%v") 83 if i%2 == 0 { 84 f.WriteString(": ") 85 } else if i < len(fieldPairs)-1 { 86 f.WriteString(", ") 87 } 88 } 89 f.WriteByte('}') 90 log.Level(l).Printf(f.String(), append([]interface{}{typ}, fieldPairs...)...) 91 }