github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/api/common/logs.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package common 5 6 import ( 7 "fmt" 8 "net/url" 9 "time" 10 11 "github.com/juju/errors" 12 "github.com/juju/loggo" 13 14 "github.com/juju/juju/api/base" 15 "github.com/juju/juju/apiserver/params" 16 ) 17 18 // DebugLogParams holds parameters for WatchDebugLog that control the 19 // filtering of the log messages. If the structure is zero initialized, the 20 // entire log file is sent back starting from the end, and until the user 21 // closes the connection. 22 type DebugLogParams struct { 23 // IncludeEntity lists entity tags to include in the response. Tags may 24 // finish with a '*' to match a prefix e.g.: unit-mysql-*, machine-2. If 25 // none are set, then all lines are considered included. 26 IncludeEntity []string 27 // IncludeModule lists logging modules to include in the response. If none 28 // are set all modules are considered included. If a module is specified, 29 // all the submodules also match. 30 IncludeModule []string 31 // ExcludeEntity lists entity tags to exclude from the response. As with 32 // IncludeEntity the values may finish with a '*'. 33 ExcludeEntity []string 34 // ExcludeModule lists logging modules to exclude from the resposne. If a 35 // module is specified, all the submodules are also excluded. 36 ExcludeModule []string 37 // Limit defines the maximum number of lines to return. Once this many 38 // have been sent, the socket is closed. If zero, all filtered lines are 39 // sent down the connection until the client closes the connection. 40 Limit uint 41 // Backlog tells the server to try to go back this many lines before 42 // starting filtering. If backlog is zero and replay is false, then there 43 // may be an initial delay until the next matching log message is written. 44 Backlog uint 45 // Level specifies the minimum logging level to be sent back in the response. 46 Level loggo.Level 47 // Replay tells the server to start at the start of the log file rather 48 // than the end. If replay is true, backlog is ignored. 49 Replay bool 50 // NoTail tells the server to only return the logs it has now, and not 51 // to wait for new logs to arrive. 52 NoTail bool 53 // StartTime should be a time in the past - only records with a 54 // log time on or after StartTime will be returned. 55 StartTime time.Time 56 } 57 58 func (args DebugLogParams) URLQuery() url.Values { 59 attrs := url.Values{ 60 "includeEntity": args.IncludeEntity, 61 "includeModule": args.IncludeModule, 62 "excludeEntity": args.ExcludeEntity, 63 "excludeModule": args.ExcludeModule, 64 } 65 if args.Replay { 66 attrs.Set("replay", fmt.Sprint(args.Replay)) 67 } 68 if args.NoTail { 69 attrs.Set("noTail", fmt.Sprint(args.NoTail)) 70 } 71 if args.Limit > 0 { 72 attrs.Set("maxLines", fmt.Sprint(args.Limit)) 73 } 74 if args.Backlog > 0 { 75 attrs.Set("backlog", fmt.Sprint(args.Backlog)) 76 } 77 if args.Level != loggo.UNSPECIFIED { 78 attrs.Set("level", fmt.Sprint(args.Level)) 79 } 80 if !args.StartTime.IsZero() { 81 attrs.Set("startTime", args.StartTime.Format(time.RFC3339Nano)) 82 } 83 return attrs 84 } 85 86 // LogMessage is a structured logging entry. 87 type LogMessage struct { 88 Entity string 89 Timestamp time.Time 90 Severity string 91 Module string 92 Location string 93 Message string 94 } 95 96 // StreamDebugLog requests the specified debug log records from the 97 // server and returns a channel of the messages that come back. 98 func StreamDebugLog(source base.StreamConnector, args DebugLogParams) (<-chan LogMessage, error) { 99 // TODO(babbageclunk): this isn't cancellable - if the caller stops 100 // reading from the channel (because it has an error, for example), 101 // the goroutine will be leaked. This is OK when used from the command 102 // line, but is a problem if it happens in jujud. Change it to accept 103 // a stop channel and use a read deadline so that the client can stop 104 // it. https://pad.lv/1644084 105 106 // Prepare URL query attributes. 107 attrs := args.URLQuery() 108 109 connection, err := source.ConnectStream("/log", attrs) 110 if err != nil { 111 return nil, errors.Trace(err) 112 } 113 114 messages := make(chan LogMessage) 115 go func() { 116 defer close(messages) 117 118 for { 119 var msg params.LogMessage 120 err := connection.ReadJSON(&msg) 121 if err != nil { 122 return 123 } 124 messages <- LogMessage{ 125 Entity: msg.Entity, 126 Timestamp: msg.Timestamp, 127 Severity: msg.Severity, 128 Module: msg.Module, 129 Location: msg.Location, 130 Message: msg.Message, 131 } 132 } 133 }() 134 135 return messages, nil 136 }