// Logs returns the log entries parsed from the log files stored on // the server. Log entries are returned in reverse chronological order. The // following options are available: // * "starttime" query parameter filters the log entries to only ones that // occurred on or after the "starttime". Defaults to a day ago. // * "endtime" query parameter filters the log entries to only ones that // occurred before on on the "endtime". Defaults to the current time. // * "pattern" query parameter filters the log entries by the provided regexp // pattern if it exists. Defaults to nil. // * "max" query parameter is the hard limit of the number of returned log // entries. Defaults to defaultMaxLogEntries. // * "level" query parameter filters the log entries to be those of the // corresponding severity level or worse. Defaults to "info". func (s *statusServer) Logs( _ context.Context, req *serverpb.LogsRequest, ) (*serverpb.LogEntriesResponse, error) { log.Flush() var sev log.Severity if len(req.Level) == 0 { sev = log.Severity_INFO } else { var sevFound bool sev, sevFound = log.SeverityByName(req.Level) if !sevFound { return nil, fmt.Errorf("level could not be determined: %s", req.Level) } } startTimestamp, err := parseInt64WithDefault( req.StartTime, timeutil.Now().AddDate(0, 0, -1).UnixNano()) if err != nil { return nil, grpc.Errorf(codes.InvalidArgument, "StartTime could not be parsed: %s", err) } endTimestamp, err := parseInt64WithDefault(req.EndTime, timeutil.Now().UnixNano()) if err != nil { return nil, grpc.Errorf(codes.InvalidArgument, "EndTime could not be parsed: %s", err) } if startTimestamp > endTimestamp { return nil, grpc.Errorf(codes.InvalidArgument, "StartTime: %d should not be greater than endtime: %d", startTimestamp, endTimestamp) } maxEntries, err := parseInt64WithDefault(req.Max, defaultMaxLogEntries) if err != nil { return nil, grpc.Errorf(codes.InvalidArgument, "Max could not be parsed: %s", err) } if maxEntries < 1 { return nil, grpc.Errorf(codes.InvalidArgument, "Max: %d should be set to a value greater than 0", maxEntries) } var regex *regexp.Regexp if len(req.Pattern) > 0 { if regex, err = regexp.Compile(req.Pattern); err != nil { return nil, grpc.Errorf(codes.InvalidArgument, "regex pattern could not be compiled: %s", err) } } entries, err := log.FetchEntriesFromFiles(sev, startTimestamp, endTimestamp, int(maxEntries), regex) if err != nil { return nil, err } return &serverpb.LogEntriesResponse{Entries: entries}, nil }
// LogFilesList returns a list of available log files. func (s *statusServer) LogFilesList( ctx context.Context, req *serverpb.LogFilesListRequest, ) (*serverpb.LogFilesListResponse, error) { ctx = s.AnnotateCtx(ctx) nodeID, local, err := s.parseNodeID(req.NodeId) if err != nil { return nil, grpc.Errorf(codes.InvalidArgument, err.Error()) } if !local { status, err := s.dialNode(nodeID) if err != nil { return nil, err } return status.LogFilesList(ctx, req) } log.Flush() logFiles, err := log.ListLogFiles() if err != nil { return nil, err } return &serverpb.LogFilesListResponse{Files: logFiles}, err }
// LogFile returns a single log file. func (s *statusServer) LogFile( ctx context.Context, req *serverpb.LogFileRequest, ) (*serverpb.LogEntriesResponse, error) { ctx = s.AnnotateCtx(ctx) nodeID, local, err := s.parseNodeID(req.NodeId) if err != nil { return nil, grpc.Errorf(codes.InvalidArgument, err.Error()) } if !local { status, err := s.dialNode(nodeID) if err != nil { return nil, err } return status.LogFile(ctx, req) } log.Flush() reader, err := log.GetLogReader(req.File, true /* restricted */) if reader == nil || err != nil { return nil, fmt.Errorf("log file %s could not be opened: %s", req.File, err) } defer reader.Close() var entry log.Entry var resp serverpb.LogEntriesResponse decoder := log.NewEntryDecoder(reader) for { if err := decoder.Decode(&entry); err != nil { if err == io.EOF { break } return nil, err } resp.Entries = append(resp.Entries, entry) } return &resp, nil }