Skip to content

Commit

Permalink
Simplify filespec definition
Browse files Browse the repository at this point in the history
  • Loading branch information
gvalkov committed Jul 18, 2018
1 parent 78fa3e2 commit c6c51bf
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 48 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ docker-build:
sudo docker build -t gvalkov/tailon .

README.md:
go build
ed $@ <<< $$'/BEGIN HELP/+2,/END HELP/-2d\n/BEGIN HELP/+1r !./tailon --help 2>&1\n,w'
sed -i 's/[ \t]*$$//' $@

Expand Down
35 changes: 19 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,11 @@ docker run --rm gvalkov/tailon --help

## Usage

Tailon is a command-line program that spawns a local HTTP server, which in turn
Tailon is a command-line program that starts a local HTTP server, which in turn
streams the output of commands such as `tail` and `grep`. It can be configured
from its command-line interface or through the convenience of a [toml] config
file.
file. Some options, like adding new commands, are only available through the
configuration file.

To get started, run tailon with the list of files that you wish to monitor.

Expand Down Expand Up @@ -71,30 +72,32 @@ Tailon is a webapp for looking at and searching through files and streams.
Tailon can be configured through a config file or with command-line flags.
The command-line interface expects one or more filespec arguments, which
specify the files or directories to be served. The expected format is:
specify the files to be served. The expected format is:
[[glob|dir|file],alias=name,group=name,]<path>
[alias=name,group=name]<spec>
The default filespec is 'file' and points to a single, possibly non-existent
file. The file name in the UI can be overwritten with the 'alias=' specifier.
where <spec> can be a file name, glob or directory. The optional 'alias='
and 'group=' specifiers change the display name of the files in the UI and
the group in which they appear.
The 'glob' filespec evaluates to the list of files that match a shell file
name pattern. The pattern is evaluated each time the file list is refreshed.
An 'alias' specifier overwrites the parent directory of each matched file in
the UI. Note that quoting is necessary to prevent shell expansion.
A file specifier points to a single, possibly non-existent file. The file
name in the UI can be overwritten with 'alias='. For example:
tailon "glob,/var/log/apache/*.log" "glob,alias=apache,/var/log/apache/*.log"
tailon alias=error.log,/var/log/apache/error.log
The 'dir' specifier evaluates to all files in a directory.
A glob evaluates to the list of files that match a shell file name pattern.
The pattern is evaluated each time the file list is refreshed. An 'alias='
specifier overwrites the parent directory of each matched file in the UI.
tailon dir,/var/log/apache
tailon "/var/log/apache/*.log" "alias=nginx,/var/log/nginx/*.log"
The "group=" specifier sets the group in which files appear in the file
dropdown of the UI.
If a directory is given, all files under it are served recursively.
tailon /var/log/apache/ /var/log/nginx/
Example usage:
tailon file1.txt file2.txt file3.txt
tailon alias=messages,/var/log/messages "glob:/var/log/*.log"
tailon alias=messages,/var/log/messages "/var/log/*.log"
tailon -b localhost:8080 -c config.toml
For information on usage through the configuration file, please refer to the
Expand Down
63 changes: 35 additions & 28 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,30 +23,32 @@ const scriptEpilog = `
Tailon can be configured through a config file or with command-line flags.
The command-line interface expects one or more filespec arguments, which
specify the files or directories to be served. The expected format is:
specify the files to be served. The expected format is:
[[glob|dir|file],alias=name,group=name,]<path>
[alias=name,group=name]<spec>
The default filespec is 'file' and points to a single, possibly non-existent
file. The file name in the UI can be overwritten with the 'alias=' specifier.
where <spec> can be a file name, glob or directory. The optional 'alias='
and 'group=' specifiers change the display name of the files in the UI and
the group in which they appear.
The 'glob' filespec evaluates to the list of files that match a shell file
name pattern. The pattern is evaluated each time the file list is refreshed.
An 'alias' specifier overwrites the parent directory of each matched file in
the UI. Note that quoting is necessary to prevent shell expansion.
A file specifier points to a single, possibly non-existent file. The file
name in the UI can be overwritten with 'alias='. For example:
tailon "glob,/var/log/apache/*.log" "glob,alias=apache,/var/log/apache/*.log"
tailon alias=error.log,/var/log/apache/error.log
The 'dir' specifier evaluates to all files in a directory.
A glob evaluates to the list of files that match a shell file name pattern.
The pattern is evaluated each time the file list is refreshed. An 'alias='
specifier overwrites the parent directory of each matched file in the UI.
tailon dir,/var/log/apache
tailon "/var/log/apache/*.log" "alias=nginx,/var/log/nginx/*.log"
The "group=" specifier sets the group in which files appear in the file
dropdown of the UI.
If a directory is given, all files under it are served recursively.
tailon /var/log/apache/ /var/log/nginx/
Example usage:
tailon file1.txt file2.txt file3.txt
tailon alias=messages,/var/log/messages "glob:/var/log/*.log"
tailon alias=messages,/var/log/messages "/var/log/*.log"
tailon -b localhost:8080 -c config.toml
For information on usage through the configuration file, please refer to the
Expand Down Expand Up @@ -146,21 +148,32 @@ type FileSpec struct {
}

// Parse a string into a filespec. Example inputs are:
// file,alias=1,group=2,/var/log/messages
// /var/log/messages
// glob,/var/log/*
// alias=1,group=2,/var/log/messages
// /var/log/
// /var/log/*
func parseFileSpec(spec string) (FileSpec, error) {
var filespec FileSpec
var path string
parts := strings.Split(spec, ",")

// If no specifiers are given, default is file.
if length := len(parts); length == 1 {
return FileSpec{spec, "file", "", ""}, nil
path = parts[0]
} else {
// The last part is the path. We'll probably need a more robust
// solution in the future.
path, parts = parts[len(parts)-1], parts[:len(parts)-1]
}

// The last part is the path. We'll probably need a more robust
// solution in the future.
path, parts := parts[len(parts)-1], parts[:len(parts)-1]
if strings.ContainsAny(path, "*?[]") {
filespec.Type = "glob"
} else {
stat, err := os.Lstat(path)
if os.IsNotExist(err) || stat.Mode().IsRegular() {
filespec.Type = "file"
} else if stat.Mode().IsDir() {
filespec.Type = "dir"
}
}

for _, part := range parts {
if strings.HasPrefix(part, "group=") {
Expand All @@ -169,13 +182,7 @@ func parseFileSpec(spec string) (FileSpec, error) {
filespec.Group = group
} else if strings.HasPrefix(part, "alias=") {
filespec.Alias = strings.SplitN(part, "=", 2)[1]
} else {
switch part {
case "file", "dir", "glob":
filespec.Type = part
}
}

}

if filespec.Type == "" {
Expand Down
8 changes: 4 additions & 4 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ func TestCliFileSpec(t *testing.T) {
t.Fatalf("%s != %s", b, res)
}

a, b = "glob,alias=2,/var/log/*.log", FileSpec{"/var/log/*.log", "glob", "2", ""}
a, b = "alias=2,/var/log/*.log", FileSpec{"/var/log/*.log", "glob", "2", ""}
if res, err := parseFileSpec(a); err != nil || res != b {
t.Fatalf("%s != %s", b, res)
}

a, b = "dir,alias=1,group=\"a b\",/var/log/", FileSpec{"/var/log/", "dir", "1", "a b"}
a, b = "alias=1,group=\"a b\",/var/log/", FileSpec{"/var/log/", "dir", "1", "a b"}
if res, err := parseFileSpec(a); err != nil || res != b {
t.Fatalf("%s != %s", b, res)
}
Expand All @@ -36,7 +36,7 @@ func getAliases(entries []*ListEntry) []string {
}

func TestListingWildcard(t *testing.T) {
spec, _ := parseFileSpec("glob,testdata/ex1/var/log/*.log")
spec, _ := parseFileSpec("testdata/ex1/var/log/*.log")
lst := createListing([]FileSpec{spec})

if len(lst["__default__"]) != 4 {
Expand All @@ -48,7 +48,7 @@ func TestListingWildcard(t *testing.T) {
t.Fatal()
}

spec, _ = parseFileSpec("glob,alias=logs,testdata/ex1/var/log/*.log")
spec, _ = parseFileSpec("alias=logs,testdata/ex1/var/log/*.log")
lst = createListing([]FileSpec{spec})

aliases = getAliases(lst["__default__"])
Expand Down

0 comments on commit c6c51bf

Please sign in to comment.