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

Add tailing functionality #9

Merged
merged 12 commits into from
Jul 10, 2024
9 changes: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ Small standalone command line tool to retrieve and search recent app
server logs from [Solarwinds].

### This is v1 of the swo-cli and it supports ONLY logs search.
### This is v1 of the swo-cli and it DOES NOT support tailing.

Supports optional Boolean search queries. Example:
Supports optional Boolean search queries and polling for new events (like "tail -f"). Example:

$ swo logs "(www OR db) (nginx OR pgsql) -accepted"
$ swo logs -f "(www OR db) (nginx OR pgsql) -accepted"

## Quick Start

Expand Down Expand Up @@ -114,7 +113,7 @@ If you frequently pipe output to a certain command, create a function which
accepts optional arguments, invokes `swo` with any arguments, and pipes
output to that command. For example, this `swocolor` function will pipe to `lnav`:

$ function swocolor() { swo logs $* | lnav; }
$ function swocolor() { swo logs -f $* | lnav; }

Add the `function` line to your `~/.bashrc`. It can be invoked with search
parameters:
Expand Down Expand Up @@ -167,7 +166,7 @@ even though one is for 4 words (AND) while the other is for a phrase:

### Multiple API tokens

To use multiple API tokens (such as for separate home and work SolarWinds Observability
To use multiple API tokens (such as for separate home and work SolarWinds Observability
accounts), create a `.swo-cli.yml` configuration file in each project's
working directory and invoke the CLI in that directory. The CLI checks for
`.swo-cli.yml` in the current working directory prior to using
Expand Down
3 changes: 2 additions & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
| Version | Supported |
|---------| ------------------ |
| 1.1.x | :white_check_mark: |
| 1.2.x | :white_check_mark: |

## Reporting a Vulnerability

To report a vulnerability, please email [email protected]. Details about our security and disclosure policies can be found at https://www.solarwinds.com/information-security.
To report a vulnerability, please email [email protected]. Details about our security and disclosure policies can be found at https://www.solarwinds.com/information-security.
7 changes: 4 additions & 3 deletions cmd/swo/main.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package main

import (
"github.com/solarwinds/swo-cli/logs"
"github.com/urfave/cli/v2"
"log"
"os"

"github.com/solarwinds/swo-cli/logs"
"github.com/urfave/cli/v2"
)

var version = "v1.1.2"
var version = "v1.2.0"

func main() {
app := &cli.App{
Expand Down
24 changes: 19 additions & 5 deletions logs/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,12 @@ func (c *Client) prepareRequest(ctx context.Context, nextPage string) (*http.Req
params := url.Values{}
if nextPage == "" {
logsEndpoint, err = url.JoinPath(c.opts.ApiUrl, "v1/logs")
params.Add("direction", "forward")
if c.opts.follow {
params.Add("direction", "tail")
} else {
params.Add("direction", "forward")
}

params.Add("pageSize", "1000")

if c.opts.group != "" {
Expand Down Expand Up @@ -99,6 +104,10 @@ func (c *Client) prepareRequest(ctx context.Context, nextPage string) (*http.Req
if err != nil {
return nil, err
}

if c.opts.follow {
params.Del("endTime")
}
}

if err != nil {
Expand Down Expand Up @@ -187,15 +196,20 @@ func (c *Client) Run(ctx context.Context) error {
return err
}

if logs.NextPage == "" {
break
}
Comment on lines +199 to +201

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moving this up here isn't necessarily safe. If there are events sent back in the last page, this will drop them. (The API might not do this currently, but it probably will in the future.)

I think we probably want this to go back to its original spot, and then the time.Sleep can go at the end?


if c.opts.follow && len(logs.Logs) == 0 {
time.Sleep(2 * time.Second)
continue
}

err = c.printResult(logs.Logs)
if err != nil {
return fmt.Errorf("failed to print result: %w", err)
}

if logs.NextPage == "" {
break
}

nextPage = logs.NextPage
}

Expand Down
13 changes: 8 additions & 5 deletions logs/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@ package logs

import (
"context"

"github.com/urfave/cli/v2"
)

var flags = []cli.Flag{
&cli.StringFlag{Name: "group", Aliases: []string{"g"}, Usage: "group ID to search"},
&cli.StringFlag{Name: "group", Aliases: []string{"g"}, Usage: "group name to search"},
&cli.StringFlag{Name: "min-time", Usage: "earliest time to search from", Value: "1 hour ago"},
&cli.StringFlag{Name: "max-time", Usage: "latest time to search from"},
&cli.StringFlag{Name: "system", Aliases: []string{"s"}, Usage: "system to search"},
&cli.BoolFlag{Name: "json", Aliases: []string{"j"}, Usage: "output raw JSON", Value: false},
&cli.BoolFlag{Name: "follow", Aliases: []string{"f"}, Usage: "enable live tailing", Value: false},
}

func run(cCtx *cli.Context) error {
Expand All @@ -22,6 +24,7 @@ func run(cCtx *cli.Context) error {
maxTime: cCtx.String("max-time"),
minTime: cCtx.String("min-time"),
json: cCtx.Bool("json"),
follow: cCtx.Bool("follow"),
ApiUrl: cCtx.String("api-url"),
Token: cCtx.String("api-token"),
}
Expand All @@ -46,14 +49,14 @@ func NewLogsCommand() *cli.Command {
Usage: "command-line search for SolarWinds Observability log management service",
Flags: flags,
ArgsUsage: `

EXAMPLES:
swo logs something
swo logs 1.2.3 Failure
swo logs -s ns1 "connection refused"
swo logs "(www OR db) (nginx OR pgsql) -accepted"
swo logs -g <SWO_GROUP_ID> "(nginx OR pgsql) -accepted"
swo logs --min-time 'yesterday at noon' --max-time 'today at 4am' -g <SWO_GROUP_ID>
swo logs -f "(www OR db) (nginx OR pgsql) -accepted"
swo logs -f -g <SWO_GROUP_NAME> "(nginx OR pgsql) -accepted"
swo logs --min-time 'yesterday at noon' --max-time 'today at 4am' -g <SWO_GROUP_NAME>
swo logs -- -redis
`,
Action: run,
Expand Down
10 changes: 10 additions & 0 deletions logs/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ type Options struct {
maxTime string
minTime string
json bool
follow bool

ApiUrl string `yaml:"api-url"`
Token string `yaml:"token"`
Expand All @@ -68,6 +69,15 @@ func (opts *Options) Init(args []string) error {
opts.minTime = result
}

if opts.follow { // set maxTime to <now - 10s> when 'follow' flag is set, it is used only for the first request
result, err := parseTime(time.Now().Add(-10 * time.Second).Format(time.RFC3339))
if err != nil {
return errors.Join(errMaxTimeFlag, err)
}

opts.maxTime = result
}

if opts.maxTime != "" {
result, err := parseTime(opts.maxTime)
if err != nil {
Expand Down