Skip to content

Commit

Permalink
feat: retrieve multiple service logs stream at once via cli (#2426)
Browse files Browse the repository at this point in the history
## Description
https://www.loom.com/share/aa413be508564af9831a67420d0fb934



## Is this change user facing?
YES
  • Loading branch information
tedim52 authored May 7, 2024
1 parent df806ff commit b071ba2
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func NewHistoricalServiceIdentifierArgWithValidationDisabled(
return &args.ArgConfig{
Key: serviceIdentifierArgKey,
IsOptional: isOptional,
DefaultValue: "",
DefaultValue: []string{},
IsGreedy: isGreedy,
ArgCompletionProvider: args.NewManualCompletionsProvider(getCompletionsForExistingAndHistoricalServices(enclaveIdentifierArgKey)),
ValidationFunc: noValidationFunc,
Expand Down
1 change: 1 addition & 0 deletions cli/cli/command_framework/lowlevel/args/consts_for_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ var validArgsConfig = []*ArgConfig{
IsGreedy: true,
},
}

var validTokens = []string{
arg1Value,
arg2Value,
Expand Down
84 changes: 68 additions & 16 deletions cli/cli/commands/service/logs/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package logs
import (
"context"
"fmt"
"github.com/fatih/color"
"github.com/kurtosis-tech/kurtosis/api/golang/core/lib/services"
"github.com/kurtosis-tech/kurtosis/api/golang/engine/kurtosis_engine_rpc_api_bindings"
"github.com/kurtosis-tech/kurtosis/api/golang/engine/lib/kurtosis_context"
Expand All @@ -34,10 +35,12 @@ const (
isEnclaveIdArgGreedy = false

serviceIdentifierArgKey = "service"
isServiceIdentifierArgOptional = false
isServiceIdentifierArgGreedy = false
isServiceIdentifierArgOptional = true // don't need to pass this in if they use the return all services flag
isServiceIdentifierArgGreedy = true

shouldFollowLogsFlagKey = "follow"
returnAllServiceLogs = "all-services"
allServicesWildcard = "*"
returnNumLogsFlagKey = "num"
returnAllLogsFlagKey = "all"
matchTextFilterFlagKey = "match"
Expand All @@ -55,12 +58,25 @@ const (
commonInstructionInMatchFlags = "Important: " + matchTextFilterFlagKey + " and " + matchRegexFilterFlagKey + " flags cannot be used at the same time. You should either use one or the other."
)

type ColorPrinter func(a ...interface{}) string

var colorList = []ColorPrinter{
color.New(color.FgBlue).SprintFunc(),
color.New(color.FgCyan).SprintFunc(),
color.New(color.FgGreen).SprintFunc(),
color.New(color.FgMagenta).SprintFunc(),
color.New(color.FgYellow).SprintFunc(),
color.New(color.FgHiRed).SprintFunc(),
color.New(color.FgHiBlue).SprintFunc(),
color.New(color.FgHiWhite).SprintFunc(),
}

var doNotFilterLogLines *kurtosis_context.LogLineFilter = nil

var defaultShouldFollowLogs = strconv.FormatBool(false)
var defaultInvertMatchFilterFlagValue = strconv.FormatBool(false)

var defaultShouldReturnAllLogs = strconv.FormatBool(false)
var defaultShouldReturnAllServiceLog = strconv.FormatBool(false)
var defaultNumLogLinesFlagValue = strconv.Itoa(defaultNumLogLines)

var ServiceLogsCmd = &engine_consuming_kurtosis_command.EngineConsumingKurtosisCommand{
Expand Down Expand Up @@ -121,6 +137,13 @@ var ServiceLogsCmd = &engine_consuming_kurtosis_command.EngineConsumingKurtosisC
Type: flags.FlagType_Bool,
Default: defaultInvertMatchFilterFlagValue,
},
{
Key: returnAllServiceLogs,
Usage: "Returns service log streams for all logs in an enclave",
Shorthand: "x",
Type: flags.FlagType_Bool,
Default: defaultShouldReturnAllServiceLog,
},
},
Args: []*args.ArgConfig{
enclave_id_arg.NewHistoricalEnclaveIdentifiersArgWithValidationDisabled(
Expand All @@ -131,8 +154,8 @@ var ServiceLogsCmd = &engine_consuming_kurtosis_command.EngineConsumingKurtosisC
service_identifier_arg.NewHistoricalServiceIdentifierArgWithValidationDisabled(
serviceIdentifierArgKey,
enclaveIdentifierArgKey,
isServiceIdentifierArgGreedy,
isServiceIdentifierArgOptional,
isServiceIdentifierArgGreedy,
),
},
RunFunc: run,
Expand All @@ -151,10 +174,20 @@ func run(
return stacktrace.Propagate(err, "An error occurred getting the enclave identifier using arg key '%v'", enclaveIdentifierArgKey)
}

serviceIdentifier, err := args.GetNonGreedyArg(serviceIdentifierArgKey)
shouldReturnAllServiceLogs, err := flags.GetBool(returnAllServiceLogs)
if err != nil {
return stacktrace.Propagate(err, "An error occurred getting the 'all-services' flag using key '%v'", returnAllServiceLogs)
}

var serviceIdentifiers []string
serviceIdentifiers, err = args.GetGreedyArg(serviceIdentifierArgKey)
if err != nil {
return stacktrace.Propagate(err, "An error occurred getting the service identifier using arg key '%v'", serviceIdentifierArgKey)
}
// if no service identifiers were passed or just the wildcard was passed, default to returning all
if len(serviceIdentifiers) == 0 || (len(serviceIdentifiers) == 1 && serviceIdentifiers[0] == allServicesWildcard) { //
shouldReturnAllServiceLogs = true
}

shouldFollowLogs, err := flags.GetBool(shouldFollowLogsFlagKey)
if err != nil {
Expand Down Expand Up @@ -191,10 +224,27 @@ func run(
return stacktrace.Propagate(err, "An error occurred connecting to the local Kurtosis engine")
}

serviceUuid := getEnclaveAndServiceUuidForIdentifiers(kurtosisCtx, ctx, enclaveIdentifier, serviceIdentifier)
if shouldReturnAllServiceLogs {
enclaveCtx, err := kurtosisCtx.GetEnclaveContext(ctx, enclaveIdentifier)
if err != nil {
return stacktrace.Propagate(err, "An error occurred retrieving enclave context for '%v'", enclaveIdentifier)
}

userServiceUuids := map[services.ServiceUUID]bool{
serviceUuid: true,
allServiceIdentifiers, err := enclaveCtx.GetExistingAndHistoricalServiceIdentifiers(ctx)
if err != nil {
return stacktrace.Propagate(err, "An error occurred retrieving service identifiers for enclave '%v'", enclaveIdentifier)
}
serviceIdentifiers = allServiceIdentifiers.GetOrderedListOfNames()
}

userServiceUuids := map[services.ServiceUUID]bool{}
serviceUuids := map[services.ServiceUUID]string{}
serviceColorPrinterMap := map[string]ColorPrinter{}
for idx, serviceIdentifier := range serviceIdentifiers {
serviceUuid := getEnclaveAndServiceUuidForIdentifiers(kurtosisCtx, ctx, enclaveIdentifier, serviceIdentifier)
serviceUuids[serviceUuid] = serviceIdentifier
serviceColorPrinterMap[serviceIdentifier] = colorList[idx%len(colorList)]
userServiceUuids[serviceUuid] = true
}

logLineFilter, err := getLogLineFilterFromFilterFlagValues(matchTextStr, matchRegexStr, invertMatch)
Expand Down Expand Up @@ -226,14 +276,16 @@ func run(
}

userServiceLogsByUuid := serviceLogsStreamContent.GetServiceLogsByServiceUuids()

userServiceLogs, found := userServiceLogsByUuid[serviceUuid]
if !found {
return stacktrace.NewError("Expected to find logs for user service with UUID '%v' on user service logs map '%+v' but was not found; this should never happen, and is a bug in Kurtosis", serviceUuid, userServiceLogsByUuid)
}

for _, serviceLog := range userServiceLogs {
out.PrintOutLn(serviceLog.GetContent())
for serviceUuid, serviceIdentifier := range serviceUuids {
userServiceLogs, found := userServiceLogsByUuid[serviceUuid]
if !found {
return stacktrace.NewError("Expected to find logs for user service with UUID '%v' on user service logs map '%+v' but was not found; this should never happen, and is a bug in Kurtosis", serviceUuid, userServiceLogsByUuid)
}

for _, serviceLog := range userServiceLogs {
colorPrinter := serviceColorPrinterMap[serviceIdentifier]
out.PrintOutLn(fmt.Sprintf("%v %v", colorPrinter("[%v]", serviceIdentifier), serviceLog.GetContent()))
}
}
case <-interruptChan:
logrus.Debugf("Received signal interruption in service logs Kurtosis CLI command")
Expand Down
9 changes: 5 additions & 4 deletions docs/docs/cli-reference/service-logs.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ sidebar_label: service logs
slug: /service-logs
---

To print the logs for a service, run:
To print the logs for services in an enclave, run:


```bash
kurtosis service logs $THE_ENCLAVE_IDENTIFIER $THE_SERVICE_IDENTIFIER
kurtosis service logs $THE_ENCLAVE_IDENTIFIER $THE_SERVICE_IDENTIFIER1 $THE_SERVICE_IDENTIFIER2 $THE_SERVICE_IDENTIFIER3
```
where `$THE_ENCLAVE_IDENTIFIER` and the `$THE_SERVICE_IDENTIFIER` are [resource identifiers](../advanced-concepts/resource-identifier.md) for the enclave and service, respectively. The service identifier (name or UUID) is printed upon inspecting an enclave.
where `$THE_ENCLAVE_IDENTIFIER` and the `$THE_SERVICE_IDENTIFIER` are [resource identifiers](../advanced-concepts/resource-identifier.md) for the enclave and services, respectively. The service identifier (name or UUID) is printed upon inspecting an enclave.
:::

:::note Number of log lines
Expand All @@ -23,7 +23,8 @@ Kurtosis will keep logs for up to 4 weeks before removing them to prevent logs f
The following optional arguments can be used:
1. `-a`, `--all` can be used to retrieve all logs.
1. `-n`, `--num=uint32` can be used to retrieve X last log lines. (eg. `-n 10` will retrieve last 10 log lines, similar to `tail -n 10`)
1. `-f`, `-follow` can be added to continue following the logs, similar to `tail -f`.
1. `-f`, `--follow` can be added to continue following the logs, similar to `tail -f`.
1. `-x`, `--all-services` can be used to retrieve logs for all services in an enclave. Another option is to pass in the escaped wildcard operator like so `kurtosis service logs enclave-name '*'`
1. `--match=text` can be used for filtering the log lines containing the text.
1. `--regex-match="regex"` can be used for filtering the log lines containing the regex. This filter will also work for text but will have degraded performance.
1. `-v`, `--invert-match` can be used to invert the filter condition specified by either `--match` or `--regex-match`. Log lines NOT containing the match will be returned.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,6 @@ func (service *EngineConnectServerService) Clean(ctx context.Context, connectArg
}

func (service *EngineConnectServerService) GetServiceLogs(ctx context.Context, connectArgs *connect.Request[kurtosis_engine_rpc_api_bindings.GetServiceLogsArgs], stream *connect.ServerStream[kurtosis_engine_rpc_api_bindings.GetServiceLogsResponse]) error {

args := connectArgs.Msg
enclaveIdentifier := args.GetEnclaveIdentifier()
enclaveUuid, err := service.enclaveManager.GetEnclaveUuidForEnclaveIdentifier(context.Background(), enclaveIdentifier)
Expand Down

0 comments on commit b071ba2

Please sign in to comment.