Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,14 @@ Flags:
--config string config file (default is $HOME/.spinner.yaml)
-d, --debug Print debug logging
-h, --help help for spinner
-t, --tail string Path to file to tail and pipe to STDOUT.
-t, --tail string Path to file to tail and pipe to STDOUT
-o, --out Path to file to write response body if site response status >= 300

Service Usage:
spinner service [name] [flags]

Site Usage:
spinner site [url] [flags]
spinner site [url] [counter limit] [time to exit] [flags]
```


Expand All @@ -51,4 +52,4 @@ Spinner.
spinner.exe service W3SVC -t c:\\iislog\\W3SVC\\u_extend1.log
```

The Linux build is experimental and does not include the `service` command.
The Linux build is experimental and does not include the `service` command.
2 changes: 2 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (

var cfgFile string
var tailFile string
var outFile string
var debugFlag bool

//func Kill(err error) {
Expand Down Expand Up @@ -72,6 +73,7 @@ func init() {
RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.spinner.yaml)")

RootCmd.PersistentFlags().StringVarP(&tailFile, "tail", "t", "", "Path to file to tail and pipe to STDOUT.")
RootCmd.PersistentFlags().StringVarP(&outFile, "out", "o", "", "Path to file to write errors out to.")
RootCmd.PersistentFlags().BoolVarP(&debugFlag, "debug", "d", false, "Print debug logging")
}

Expand Down
112 changes: 94 additions & 18 deletions cmd/site.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@
package cmd

import (
"bufio"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"path/filepath"
"strconv"
s "strings"
"time"

Expand All @@ -27,8 +30,8 @@ import (

var urlFlag string

func queryPage(u string) {

func queryPage(u, cl, tte string) {
var c int64
var fullURL string

if s.HasPrefix(u, "http://") || s.HasPrefix(u, "https://") {
Expand All @@ -37,41 +40,110 @@ func queryPage(u string) {
fullURL = "http://" + u
}

fmt.Println("Full URL being monitored:", fullURL)
log.Println("Full URL being monitored:", fullURL, "Counter limit:", cl)

for {
cl, err := strconv.ParseInt(cl, 10, 8)
if err != nil {
log.Fatal(err)
}

tte, err := strconv.ParseInt(tte, 10, 8)
if err != nil {
log.Fatal(err)
}

resp, err := http.Get(fullURL)
// Set 30 second timeout for page GET
tr := &http.Transport{
IdleConnTimeout: 30 * time.Second,
DisableCompression: true,
}

client := &http.Client{Transport: tr}
resp, err := client.Get(fullURL)
if err != nil {
log.Fatal("An error occurred during the request:", err)
}

if resp.StatusCode >= 300 {
rd, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
c++
if c >= cl {
rd, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
// Convert rd to string and remove carriage returns from output
rs := s.Replace(string(rd), "\r", "", -1)
// Remove line feeds from output
rs = s.Replace(rs, "\n", "", -1)

// Output to file if path is set
if outFile != "" {
d, _ := filepath.Split(outFile)
err := os.MkdirAll(d, os.ModePerm)
if err != nil {
panic(err)
}

f, err := os.OpenFile(outFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
if err != nil {
panic(err)
}
defer f.Close()

ls := time.Now().Format("2006-01-02 15:04:05") + " [spinner-app] " + rs + "\n"

b := bufio.NewWriterSize(f, 10000)
_, err = b.WriteString(ls)
if err != nil {
log.Println(err)
}

err = b.Flush()
if err != nil {
log.Println(err)
}
}

// Output to stdout
log.Println("Status Code:", resp.StatusCode, " Body:", rs)
// Sleep for n second(s) allowing output error to stdout to be picked up
// by monitoring software/container (if needed)
time.Sleep(time.Duration(tte) * time.Second)
log.Fatalln("Spinner shutting down, status code was:", resp.StatusCode)
} else {
log.Println("Status Code:", resp.StatusCode, "Counter count:", c)
}
// Convert rd to string and remove line breaks to output on single line
rs := s.Replace(string(rd), "\r\n", "", -1)
log.Fatalf("Status Code: %v Body: %s", resp.StatusCode, rs)
} else if debugFlag {
c = 0
log.Println("Status Code:", resp.StatusCode)
}

resp.Body.Close()

time.Sleep(1000 * time.Millisecond)
time.Sleep(1 * time.Second)
}
}

// siteCmd represents the site command
var siteCmd = &cobra.Command{
Use: "site [url]",
Use: "site [url] [counter limit] [time to exit]",
Short: "Watch a Site",
Aliases: []string{"url", "address"},
Example: "spinner.exe site http://localhost -t c:\\iislog\\W3SVC\\u_extend1.log",
Long: `Poll Web Site by Get request and terminate this process if
the a >300 status code is returned.
Example: "spinner.exe site http://localhost 5 5 -t c:\\iislog\\W3SVC\\u_extend1.log -o c:\\logs\\spinner.log",
Long: `Poll Web Site by Get request and terminate this process if the a >300
status code is returned.

Counter Limit (default 1) is the number of times the site being monitored can
be down before spinner exits.

Time to Exit (default 1) is the time (in seconds) after the response body is
logged to stdout before spinner will shutdown. This can be useful
if the monitoring software does not catch the error quick enough.

OutFile is the location path to a file to write out the response body if
the response status is >= 300. This can be useful in troubleshooting why
the application is exiting.

Use this as the entrypoint for a container to stop the container if
the given service stops.`,
Expand All @@ -86,9 +158,13 @@ the given service stops.`,
if tailFile != "" {
go TailLog()
}

queryPage(args[0])

if len(args) == 1 {
queryPage(args[0], "1", "1")
} else if len(args) == 2 {
queryPage(args[0], args[1], "1")
} else {
queryPage(args[0], args[1], args[2])
}
},
}

Expand Down