Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix logs tail issue when there are no logs #744

Merged
merged 6 commits into from
Apr 20, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 20 additions & 21 deletions internal/cli/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import (
"github.com/spf13/cobra"
)

// Pagination max out at 1000 entries in total
// https://auth0.com/docs/logs/retrieve-log-events-using-mgmt-api#limitations
const logsPerPageLimit = 1000

var (
logsFilter = Flag{
Name: "Filter",
Expand Down Expand Up @@ -63,7 +67,7 @@ func listLogsCmd(cli *cli) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
list, err := getLatestLogs(cli, inputs.Num, inputs.Filter)
if err != nil {
return fmt.Errorf("An unexpected error occurred while getting logs: %v", err)
return fmt.Errorf("failed to get logs: %w", err)
}

hasFilter := inputs.Filter != ""
Expand Down Expand Up @@ -100,28 +104,26 @@ func tailLogsCmd(cli *cli) *cobra.Command {
auth0 logs tail --filter "type:f" # See the full list of type codes at https://auth0.com/docs/logs/log-event-type-codes
auth0 logs tail -n 100`,
RunE: func(cmd *cobra.Command, args []string) error {
lastLogID := ""
list, err := getLatestLogs(cli, inputs.Num, inputs.Filter)
if err != nil {
return fmt.Errorf("An unexpected error occurred while getting logs: %v", err)
return fmt.Errorf("failed to get logs: %w", err)
}

// TODO(cyx): This is a hack for now to make the
// streaming work faster.
//
// Create a `set` to detect duplicates clientside.
set := make(map[string]struct{})
list = dedupeLogs(list, set)

if len(list) > 0 {
lastLogID = list[len(list)-1].GetLogID()
if len(list) == 0 {
cli.renderer.EmptyState("logs")
cli.renderer.Infof("To generate logs, run a test command like 'auth0 test login' or 'auth0 test token'")
return nil
}

logsCh := make(chan []*management.Log)

lastLogID := list[len(list)-1].GetLogID()

// Create a `set` to detect duplicates clientside.
set := make(map[string]struct{})
list = dedupeLogs(list, set)

go func() {
// This is pretty important and allows
// us to close / terminate the command.
defer close(logsCh)

for {
Expand All @@ -138,7 +140,7 @@ func tailLogsCmd(cli *cli) *cobra.Command {

list, err = cli.api.Log.List(queryParams...)
if err != nil {
cli.renderer.Errorf("An unexpected error occurred while getting logs: %v", err)
cli.renderer.Errorf("Failed to get latest logs: %v", err)
return
}

Expand All @@ -148,7 +150,7 @@ func tailLogsCmd(cli *cli) *cobra.Command {
}

if len(list) < 90 {
// Not a lot is happening, sleep on it
// Not a lot is happening, sleep on it.
time.Sleep(1 * time.Second)
}
}
Expand All @@ -168,11 +170,8 @@ func tailLogsCmd(cli *cli) *cobra.Command {
func getLatestLogs(cli *cli, n int, filter string) ([]*management.Log, error) {
page := 0
perPage := n

if perPage > 1000 {
// Pagination max out at 1000 entries in total
// https://auth0.com/docs/logs/retrieve-log-events-using-mgmt-api#limitations
perPage = 1000
if perPage > logsPerPageLimit {
perPage = logsPerPageLimit
}

queryParams := []management.RequestOption{
Expand Down
128 changes: 128 additions & 0 deletions internal/cli/logs_test.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,144 @@
package cli

import (
"bytes"
"fmt"
"io"
"testing"
"time"

"github.com/auth0/go-auth0/management"
"github.com/golang/mock/gomock"

"github.com/stretchr/testify/assert"

"github.com/auth0/auth0-cli/internal/auth0"
"github.com/auth0/auth0-cli/internal/auth0/mock"
"github.com/auth0/auth0-cli/internal/display"
)

func TestTailLogsCommand(t *testing.T) {
t.Run("it returns early with a message to generate logs when there are no logs", func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

logsAPI := mock.NewMockLogAPI(ctrl)
logsAPI.EXPECT().
List(gomock.Any()).
Return([]*management.Log{}, nil)

expectedResult := `No logs available.

▸ To generate logs, run a test command like 'auth0 test login' or 'auth0 test token'
`

stdout := &bytes.Buffer{}
cli := &cli{
renderer: &display.Renderer{
MessageWriter: stdout,
ResultWriter: io.Discard,
},
api: &auth0.API{Log: logsAPI},
}

cmd := tailLogsCmd(cli)
cmd.SetArgs([]string{"--number", "9000", "--filter", "user_id:123"})
err := cmd.Execute()

assert.NoError(t, err)
assert.Equal(t, expectedResult, stdout.String())
})

t.Run("it returns an error when it fails to get the logs on the first request", func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

logsAPI := mock.NewMockLogAPI(ctrl)
logsAPI.EXPECT().
List(gomock.Any()).
Return(nil, fmt.Errorf("generic error"))

cli := &cli{
api: &auth0.API{Log: logsAPI},
}

cmd := tailLogsCmd(cli)
cmd.SetArgs([]string{"--number", "9000", "--filter", "user_id:123"})
err := cmd.Execute()

assert.EqualError(t, err, "failed to get logs: generic error")
})

t.Run("it returns an error when it fails to get the logs on the 3rd request", func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

logsAPI := mock.NewMockLogAPI(ctrl)
logsAPI.EXPECT().
List(gomock.Any()).
Return(
[]*management.Log{
{
LogID: auth0.String("354234"),
Type: auth0.String("sapi"),
Description: auth0.String("Update branding settings"),
},
},
nil,
)

logsAPI.EXPECT().
List(gomock.Any()).
Return(
[]*management.Log{
{
LogID: auth0.String("354234"),
Type: auth0.String("sapi"),
Description: auth0.String("Update branding settings"),
},
{
LogID: auth0.String("354236"),
Type: auth0.String("sapi"),
Description: auth0.String("Update tenant settings"),
},
},
nil,
)

logsAPI.EXPECT().
List(gomock.Any()).
Return(nil, fmt.Errorf("generic error"))

expectedMessage := `
=== auth0-cli-tests.eu.auth0.com logs

▸ Failed to get latest logs: generic error
sergiught marked this conversation as resolved.
Show resolved Hide resolved
`
expectedResult := `TYPE DESCRIPTION DATE CONNECTION CLIENT
API Operation Update branding settings Jan 01 00:00:00.000 N/A N/A
`

message := &bytes.Buffer{}
result := &bytes.Buffer{}
cli := &cli{
renderer: &display.Renderer{
Tenant: "auth0-cli-tests.eu.auth0.com",
MessageWriter: message,
ResultWriter: result,
},
api: &auth0.API{Log: logsAPI},
}

cmd := tailLogsCmd(cli)
cmd.SetArgs([]string{"--number", "9000", "--filter", "user_id:123"})
err := cmd.Execute()
assert.NoError(t, err)

assert.Equal(t, expectedMessage, message.String())
assert.Equal(t, expectedResult, result.String())
})
}

func TestDedupeLogs(t *testing.T) {
t.Run("removes duplicate logs and sorts by date asc", func(t *testing.T) {
logs := []*management.Log{
Expand Down