github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/google.golang.org/appengine/log/log.go (about)

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