Support for filtering logs by resource
This commit is contained in:
parent
8fd11f26b8
commit
9648444b05
10
cmd/logs.go
10
cmd/logs.go
|
@ -18,6 +18,7 @@ func newLogsCmd() *cobra.Command {
|
|||
var stack string
|
||||
var follow bool
|
||||
var since string
|
||||
var resource string
|
||||
|
||||
logsCmd := &cobra.Command{
|
||||
Use: "logs",
|
||||
|
@ -29,6 +30,11 @@ func newLogsCmd() *cobra.Command {
|
|||
}
|
||||
|
||||
startTime := parseRelativeDuration(since)
|
||||
var resourceFilter *operations.ResourceFilter
|
||||
if resource != "" {
|
||||
var rf = operations.ResourceFilter(resource)
|
||||
resourceFilter = &rf
|
||||
}
|
||||
|
||||
// IDEA: This map will grow forever as new log entries are found. We may need to do a more approximate
|
||||
// approach here to ensure we don't grow memory unboundedly while following logs.
|
||||
|
@ -41,6 +47,7 @@ func newLogsCmd() *cobra.Command {
|
|||
for {
|
||||
logs, err := backend.GetLogs(stackName, operations.LogQuery{
|
||||
StartTime: startTime,
|
||||
Resource: resourceFilter,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -72,6 +79,9 @@ func newLogsCmd() *cobra.Command {
|
|||
logsCmd.PersistentFlags().StringVar(
|
||||
&since, "since", "",
|
||||
"Only return logs newer than a relative duration ('5s', '2m', '3h'). Defaults to returning all logs.")
|
||||
logsCmd.PersistentFlags().StringVarP(
|
||||
&resource, "resource", "r", "",
|
||||
"Only return logs for the requested resource ('name', 'type::name' or full URN). Defaults to returning all logs.")
|
||||
|
||||
return logsCmd
|
||||
}
|
||||
|
|
|
@ -11,12 +11,31 @@ type LogEntry struct {
|
|||
Message string
|
||||
}
|
||||
|
||||
// ResourceFilter specifies a specific resource or subset of resources. It can be provided in three formats:
|
||||
// - Full URN: "<namespace>::<alloc>::<type>::<name>"
|
||||
// - Type + Name: "<type>::<name>"
|
||||
// - Name: "<name>"
|
||||
type ResourceFilter string
|
||||
|
||||
// Query is a filter on logs, in the format '<name>=<val>,<name>=>val>' where <name> is one of the following:
|
||||
// - `URN`
|
||||
// - `QualifiedName`
|
||||
// - `Name`
|
||||
// - `Type`
|
||||
// - `Message`
|
||||
type Query string
|
||||
|
||||
// LogQuery represents the parameters to a log query operation.
|
||||
// All fields are optional, leaving them off returns all logs.
|
||||
type LogQuery struct {
|
||||
// StartTime is an optional time indiciating that only logs from after this time should be produced.
|
||||
StartTime *time.Time
|
||||
EndTime *time.Time
|
||||
Query *string
|
||||
// EndTime is an optional time indiciating that only logs from before this time should be produced.
|
||||
EndTime *time.Time
|
||||
// Query is a string indicating a filter to apply to the logs - query syntax TBD
|
||||
Query *string
|
||||
// Resource is a string indicating that logs should be limited toa resource of resoruces
|
||||
Resource *ResourceFilter
|
||||
}
|
||||
|
||||
// MetricName is a handle to a metric supported by a Pulumi Framework resources
|
||||
|
|
|
@ -87,29 +87,34 @@ var _ Provider = (*resourceOperations)(nil)
|
|||
|
||||
// GetLogs gets logs for a Resource
|
||||
func (ops *resourceOperations) GetLogs(query LogQuery) (*[]LogEntry, error) {
|
||||
opsProvider, err := ops.getOperationsProvider()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if opsProvider != nil {
|
||||
// If this resource has an operations provider - use it and don't recur into children. It is the responsibility
|
||||
// of it's GetLogs implementation to aggregate all logs from children, either by passing them through or by
|
||||
// filtering specific content out.
|
||||
logsResult, err := opsProvider.GetLogs(query)
|
||||
// Only get logs for this resource if it matches the resource filter query
|
||||
if ops.matchesResourceFilter(query.Resource) {
|
||||
// Try to get an operations provider for this resource, it may be `nil`
|
||||
opsProvider, err := ops.getOperationsProvider()
|
||||
if err != nil {
|
||||
return logsResult, err
|
||||
return nil, err
|
||||
}
|
||||
if logsResult != nil {
|
||||
return logsResult, nil
|
||||
if opsProvider != nil {
|
||||
// If this resource has an operations provider - use it and don't recur into children. It is the
|
||||
// responsibility of it's GetLogs implementation to aggregate all logs from children, either by passing them
|
||||
// through or by filtering specific content out.
|
||||
logsResult, err := opsProvider.GetLogs(query)
|
||||
if err != nil {
|
||||
return logsResult, err
|
||||
}
|
||||
if logsResult != nil {
|
||||
return logsResult, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
// If this resource did not choose to provide it's own logs, recur into children and collect + aggregate their logs.
|
||||
var logs []LogEntry
|
||||
for _, child := range ops.resource.children {
|
||||
childOps := &resourceOperations{
|
||||
resource: child,
|
||||
config: ops.config,
|
||||
}
|
||||
// TODO: Parallelize these calls to child GetLogs
|
||||
// IDEA: Parallelize these calls to child GetLogs
|
||||
childLogs, err := childOps.GetLogs(query)
|
||||
if err != nil {
|
||||
return &logs, err
|
||||
|
@ -146,6 +151,31 @@ func (ops *resourceOperations) GetLogs(query LogQuery) (*[]LogEntry, error) {
|
|||
return &retLogs, nil
|
||||
}
|
||||
|
||||
// matchesResourceFilter determines whether this resource matches the provided resource filter.
|
||||
func (ops *resourceOperations) matchesResourceFilter(filter *ResourceFilter) bool {
|
||||
if filter == nil {
|
||||
// No filter, all resources match it.
|
||||
return true
|
||||
}
|
||||
if ops.resource == nil || ops.resource.state == nil {
|
||||
return false
|
||||
}
|
||||
urn := ops.resource.state.URN
|
||||
if resource.URN(*filter) == urn {
|
||||
// The filter matched the full URN
|
||||
return true
|
||||
}
|
||||
if string(*filter) == string(urn.Type())+"::"+string(urn.Name()) {
|
||||
// The filter matched the '<type>::<name>' part of the URN
|
||||
return true
|
||||
}
|
||||
if tokens.QName(*filter) == urn.Name() {
|
||||
// The filter matched the '<name>' part of the URN
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ListMetrics lists metrics for a Resource
|
||||
func (ops *resourceOperations) ListMetrics() []MetricName {
|
||||
return []MetricName{}
|
||||
|
|
Loading…
Reference in a new issue