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  }