log.go 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. // Copyright 2011 Google 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 log provides the means of querying an application's logs from
  6. within an App Engine application.
  7. Example:
  8. c := appengine.NewContext(r)
  9. query := &log.Query{
  10. AppLogs: true,
  11. Versions: []string{"1"},
  12. }
  13. for results := query.Run(c); ; {
  14. record, err := results.Next()
  15. if err == log.Done {
  16. log.Infof(c, "Done processing results")
  17. break
  18. }
  19. if err != nil {
  20. log.Errorf(c, "Failed to retrieve next log: %v", err)
  21. break
  22. }
  23. log.Infof(c, "Saw record %v", record)
  24. }
  25. */
  26. package log // import "google.golang.org/appengine/log"
  27. import (
  28. "errors"
  29. "fmt"
  30. "strings"
  31. "time"
  32. "github.com/golang/protobuf/proto"
  33. "golang.org/x/net/context"
  34. "google.golang.org/appengine"
  35. "google.golang.org/appengine/internal"
  36. pb "google.golang.org/appengine/internal/log"
  37. )
  38. // Query defines a logs query.
  39. type Query struct {
  40. // Start time specifies the earliest log to return (inclusive).
  41. StartTime time.Time
  42. // End time specifies the latest log to return (exclusive).
  43. EndTime time.Time
  44. // Offset specifies a position within the log stream to resume reading from,
  45. // and should come from a previously returned Record's field of the same name.
  46. Offset []byte
  47. // Incomplete controls whether active (incomplete) requests should be included.
  48. Incomplete bool
  49. // AppLogs indicates if application-level logs should be included.
  50. AppLogs bool
  51. // ApplyMinLevel indicates if MinLevel should be used to filter results.
  52. ApplyMinLevel bool
  53. // If ApplyMinLevel is true, only logs for requests with at least one
  54. // application log of MinLevel or higher will be returned.
  55. MinLevel int
  56. // Versions is the major version IDs whose logs should be retrieved.
  57. // Logs for specific modules can be retrieved by the specifying versions
  58. // in the form "module:version"; the default module is used if no module
  59. // is specified.
  60. Versions []string
  61. // A list of requests to search for instead of a time-based scan. Cannot be
  62. // combined with filtering options such as StartTime, EndTime, Offset,
  63. // Incomplete, ApplyMinLevel, or Versions.
  64. RequestIDs []string
  65. }
  66. // AppLog represents a single application-level log.
  67. type AppLog struct {
  68. Time time.Time
  69. Level int
  70. Message string
  71. }
  72. // Record contains all the information for a single web request.
  73. type Record struct {
  74. AppID string
  75. ModuleID string
  76. VersionID string
  77. RequestID []byte
  78. IP string
  79. Nickname string
  80. AppEngineRelease string
  81. // The time when this request started.
  82. StartTime time.Time
  83. // The time when this request finished.
  84. EndTime time.Time
  85. // Opaque cursor into the result stream.
  86. Offset []byte
  87. // The time required to process the request.
  88. Latency time.Duration
  89. MCycles int64
  90. Method string
  91. Resource string
  92. HTTPVersion string
  93. Status int32
  94. // The size of the request sent back to the client, in bytes.
  95. ResponseSize int64
  96. Referrer string
  97. UserAgent string
  98. URLMapEntry string
  99. Combined string
  100. Host string
  101. // The estimated cost of this request, in dollars.
  102. Cost float64
  103. TaskQueueName string
  104. TaskName string
  105. WasLoadingRequest bool
  106. PendingTime time.Duration
  107. Finished bool
  108. AppLogs []AppLog
  109. // Mostly-unique identifier for the instance that handled the request if available.
  110. InstanceID string
  111. }
  112. // Result represents the result of a query.
  113. type Result struct {
  114. logs []*Record
  115. context context.Context
  116. request *pb.LogReadRequest
  117. resultsSeen bool
  118. err error
  119. }
  120. // Next returns the next log record,
  121. func (qr *Result) Next() (*Record, error) {
  122. if qr.err != nil {
  123. return nil, qr.err
  124. }
  125. if len(qr.logs) > 0 {
  126. lr := qr.logs[0]
  127. qr.logs = qr.logs[1:]
  128. return lr, nil
  129. }
  130. if qr.request.Offset == nil && qr.resultsSeen {
  131. return nil, Done
  132. }
  133. if err := qr.run(); err != nil {
  134. // Errors here may be retried, so don't store the error.
  135. return nil, err
  136. }
  137. return qr.Next()
  138. }
  139. // Done is returned when a query iteration has completed.
  140. var Done = errors.New("log: query has no more results")
  141. // protoToAppLogs takes as input an array of pointers to LogLines, the internal
  142. // Protocol Buffer representation of a single application-level log,
  143. // and converts it to an array of AppLogs, the external representation
  144. // of an application-level log.
  145. func protoToAppLogs(logLines []*pb.LogLine) []AppLog {
  146. appLogs := make([]AppLog, len(logLines))
  147. for i, line := range logLines {
  148. appLogs[i] = AppLog{
  149. Time: time.Unix(0, *line.Time*1e3),
  150. Level: int(*line.Level),
  151. Message: *line.LogMessage,
  152. }
  153. }
  154. return appLogs
  155. }
  156. // protoToRecord converts a RequestLog, the internal Protocol Buffer
  157. // representation of a single request-level log, to a Record, its
  158. // corresponding external representation.
  159. func protoToRecord(rl *pb.RequestLog) *Record {
  160. offset, err := proto.Marshal(rl.Offset)
  161. if err != nil {
  162. offset = nil
  163. }
  164. return &Record{
  165. AppID: *rl.AppId,
  166. ModuleID: rl.GetModuleId(),
  167. VersionID: *rl.VersionId,
  168. RequestID: rl.RequestId,
  169. Offset: offset,
  170. IP: *rl.Ip,
  171. Nickname: rl.GetNickname(),
  172. AppEngineRelease: string(rl.GetAppEngineRelease()),
  173. StartTime: time.Unix(0, *rl.StartTime*1e3),
  174. EndTime: time.Unix(0, *rl.EndTime*1e3),
  175. Latency: time.Duration(*rl.Latency) * time.Microsecond,
  176. MCycles: *rl.Mcycles,
  177. Method: *rl.Method,
  178. Resource: *rl.Resource,
  179. HTTPVersion: *rl.HttpVersion,
  180. Status: *rl.Status,
  181. ResponseSize: *rl.ResponseSize,
  182. Referrer: rl.GetReferrer(),
  183. UserAgent: rl.GetUserAgent(),
  184. URLMapEntry: *rl.UrlMapEntry,
  185. Combined: *rl.Combined,
  186. Host: rl.GetHost(),
  187. Cost: rl.GetCost(),
  188. TaskQueueName: rl.GetTaskQueueName(),
  189. TaskName: rl.GetTaskName(),
  190. WasLoadingRequest: rl.GetWasLoadingRequest(),
  191. PendingTime: time.Duration(rl.GetPendingTime()) * time.Microsecond,
  192. Finished: rl.GetFinished(),
  193. AppLogs: protoToAppLogs(rl.Line),
  194. InstanceID: string(rl.GetCloneKey()),
  195. }
  196. }
  197. // Run starts a query for log records, which contain request and application
  198. // level log information.
  199. func (params *Query) Run(c context.Context) *Result {
  200. req, err := makeRequest(params, internal.FullyQualifiedAppID(c), appengine.VersionID(c))
  201. return &Result{
  202. context: c,
  203. request: req,
  204. err: err,
  205. }
  206. }
  207. func makeRequest(params *Query, appID, versionID string) (*pb.LogReadRequest, error) {
  208. req := &pb.LogReadRequest{}
  209. req.AppId = &appID
  210. if !params.StartTime.IsZero() {
  211. req.StartTime = proto.Int64(params.StartTime.UnixNano() / 1e3)
  212. }
  213. if !params.EndTime.IsZero() {
  214. req.EndTime = proto.Int64(params.EndTime.UnixNano() / 1e3)
  215. }
  216. if len(params.Offset) > 0 {
  217. var offset pb.LogOffset
  218. if err := proto.Unmarshal(params.Offset, &offset); err != nil {
  219. return nil, fmt.Errorf("bad Offset: %v", err)
  220. }
  221. req.Offset = &offset
  222. }
  223. if params.Incomplete {
  224. req.IncludeIncomplete = &params.Incomplete
  225. }
  226. if params.AppLogs {
  227. req.IncludeAppLogs = &params.AppLogs
  228. }
  229. if params.ApplyMinLevel {
  230. req.MinimumLogLevel = proto.Int32(int32(params.MinLevel))
  231. }
  232. if params.Versions == nil {
  233. // If no versions were specified, default to the default module at
  234. // the major version being used by this module.
  235. if i := strings.Index(versionID, "."); i >= 0 {
  236. versionID = versionID[:i]
  237. }
  238. req.VersionId = []string{versionID}
  239. } else {
  240. req.ModuleVersion = make([]*pb.LogModuleVersion, 0, len(params.Versions))
  241. for _, v := range params.Versions {
  242. var m *string
  243. if i := strings.Index(v, ":"); i >= 0 {
  244. m, v = proto.String(v[:i]), v[i+1:]
  245. }
  246. req.ModuleVersion = append(req.ModuleVersion, &pb.LogModuleVersion{
  247. ModuleId: m,
  248. VersionId: proto.String(v),
  249. })
  250. }
  251. }
  252. if params.RequestIDs != nil {
  253. ids := make([][]byte, len(params.RequestIDs))
  254. for i, v := range params.RequestIDs {
  255. ids[i] = []byte(v)
  256. }
  257. req.RequestId = ids
  258. }
  259. return req, nil
  260. }
  261. // run takes the query Result produced by a call to Run and updates it with
  262. // more Records. The updated Result contains a new set of logs as well as an
  263. // offset to where more logs can be found. We also convert the items in the
  264. // response from their internal representations to external versions of the
  265. // same structs.
  266. func (r *Result) run() error {
  267. res := &pb.LogReadResponse{}
  268. if err := internal.Call(r.context, "logservice", "Read", r.request, res); err != nil {
  269. return err
  270. }
  271. r.logs = make([]*Record, len(res.Log))
  272. r.request.Offset = res.Offset
  273. r.resultsSeen = true
  274. for i, log := range res.Log {
  275. r.logs[i] = protoToRecord(log)
  276. }
  277. return nil
  278. }
  279. func init() {
  280. internal.RegisterErrorCodeMap("logservice", pb.LogServiceError_ErrorCode_name)
  281. }