Index: state/apiserver/debugger/debugger.go |
=== added file 'state/apiserver/debugger/debugger.go' |
--- state/apiserver/debugger/debugger.go 1970-01-01 00:00:00 +0000 |
+++ state/apiserver/debugger/debugger.go 2014-01-23 14:14:49 +0000 |
@@ -0,0 +1,97 @@ |
+// Copyright 2014 Canonical Ltd. |
+// Licensed under the AGPLv3, see LICENCE file for details. |
+ |
+package debugger |
+ |
+import ( |
+ "bytes" |
+ "errors" |
+ |
+ "launchpad.net/loggo" |
+ |
+ "launchpad.net/juju-core/state" |
+ "launchpad.net/juju-core/state/api/params" |
+ "launchpad.net/juju-core/state/apiserver/common" |
+ "launchpad.net/juju-core/state/watcher" |
+ "launchpad.net/juju-core/utils/tailer" |
+) |
+ |
+var ( |
+ logger = loggo.GetLogger("juju.api.debugger") |
+ |
+ logLocation = "/var/log/juju/all-machines.log" |
+) |
+ |
+const maxLogLines = 256 |
fwereade
2014/01/23 16:22:58
How long does it take for this to be filled in a m
mue
2014/01/23 16:41:52
Yeah, no problem, has been a first shot. 2^16 too
|
+ |
+// Debugger defines the methods on the debugger API end point. |
+type Debugger interface { |
+ WatchDebugLog(args params.EntityLogRequests) params.StringsWatchResults |
+} |
+ |
+// DebuggerAPI implements the Debugger interface and is the concrete |
+// implementation of the api end point. |
+type DebuggerAPI struct { |
+ state *state.State |
+ resources *common.Resources |
+ authorizer common.Authorizer |
+} |
+ |
+var _ Debugger = (*DebuggerAPI)(nil) |
+ |
+// NewDebuggerAPI creates a new server-side debugger API end point. |
+func NewDebuggerAPI( |
+ st *state.State, |
+ resources *common.Resources, |
+ authorizer common.Authorizer, |
+) (*DebuggerAPI, error) { |
+ return &DebuggerAPI{state: st, resources: resources, authorizer: authorizer}, nil |
+} |
+ |
+// WatchDebugLog starts a StringsWatcher to watch the debug log for entries |
+// regarding the machines or units passed in args. |
+func (api *DebuggerAPI) WatchDebugLog(args params.EntityLogRequests) params.StringsWatchResults { |
+ result := params.StringsWatchResults{ |
+ Results: make([]params.StringsWatchResult, len(args.Entities)), |
+ } |
+ for i, entity := range args.Entities { |
+ debugResult, err := api.watchOneDebugLog(entity) |
+ result.Results[i] = debugResult |
+ result.Results[i].Error = common.ServerError(err) |
+ } |
+ return result |
+} |
+ |
+// watchOneDebugLog starts a StringWatcher for the log entries of |
+// the environment or one machine or unit. |
+func (api *DebuggerAPI) watchOneDebugLog(entity params.EntityLogRequest) (params.StringsWatchResult, error) { |
+ nothing := params.StringsWatchResult{} |
+ // Check the tag and prepare the filter. |
+ var filter tailer.FilterFunc |
+ if entity.Tag == "" { |
+ return nothing, errors.New("cannot watch debug log without tag") |
+ } |
+ environ, err := api.state.Environment() |
+ if err != nil { |
+ return nothing, err |
+ } |
+ if entity.Tag != environ.Tag() { |
fwereade
2014/01/23 16:22:58
I think we should filter out tags that aren't mach
mue
2014/01/23 16:41:52
Would reduce the amount of data too, good. But in
|
+ prefix := []byte(entity.Tag + ":") |
+ filter = func(line []byte) bool { |
+ return bytes.HasPrefix(line, prefix) |
+ } |
+ } |
+ // Start the log tailer. |
+ logTailer, err := startLogTailer(logLocation, entity.Lines, maxLogLines, filter) |
+ if err != nil { |
+ return nothing, err |
+ } |
+ // Consume the initial event and forward it to the result. |
+ if changes, ok := <-logTailer.Changes(); ok { |
fwereade
2014/01/23 16:22:58
Is this guaranteed to return an empty []string if
mue
2014/01/23 16:41:52
See tests. Also testing on EC2 showed a fine behav
|
+ return params.StringsWatchResult{ |
+ StringsWatcherId: api.resources.Register(logTailer), |
+ Changes: changes, |
+ }, nil |
+ } |
+ return nothing, watcher.MustErr(logTailer) |
+} |