(v0.1.1) Automated packaging of release by Packagr
This commit is contained in:
+26
@@ -0,0 +1,26 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
|
||||
|
||||
.DS_Store
|
||||
.vendor
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.0
|
||||
- 1.1
|
||||
- 1.3
|
||||
- 1.4
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2013 Kevin van Zonneveld <kevin@vanzonneveld.net>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the “Softwareâ€), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
.PHONY: test
|
||||
test:
|
||||
go test -v ./...
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
logstreamer [![Build Status][BuildStatusIMGURL]][BuildStatusURL]
|
||||
===============
|
||||
[![Flattr][FlattrIMGURL]][FlattrURL]
|
||||
|
||||
[BuildStatusIMGURL]: https://secure.travis-ci.org/kvz/logstreamer.png?branch=master
|
||||
[BuildStatusURL]: //travis-ci.org/kvz/logstreamer "Build Status"
|
||||
[FlattrIMGURL]: http://api.flattr.com/button/flattr-badge-large.png
|
||||
[FlattrURL]: https://flattr.com/submit/auto?user_id=kvz&url=github.com/kvz/logstreamer&title=logstreamer&language=&tags=github&category=software
|
||||
|
||||
Prefixes streams (e.g. stdout or stderr) in Go.
|
||||
|
||||
If you are executing a lot of (remote) commands, you may want to indent all of their
|
||||
output, prefix the loglines with hostnames, or mark anything that was thrown to stderr
|
||||
red, so you can spot errors more easily.
|
||||
|
||||
For this purpose, Logstreamer was written.
|
||||
|
||||
You pass 3 arguments to `NewLogstreamer()`:
|
||||
|
||||
- Your `*log.Logger`
|
||||
- Your desired prefix (`"stdout"` and `"stderr"` prefixed have special meaning)
|
||||
- If the lines should be recorded `true` or `false`. This is useful if you want to retrieve any errors.
|
||||
|
||||
This returns an interface that you can point `exec.Command`'s `cmd.Stderr` and `cmd.Stdout` to.
|
||||
All bytes that are written to it are split by newline and then prefixed to your specification.
|
||||
|
||||
**Don't forget to call `Flush()` or `Close()` if the last line of the log
|
||||
might not end with a newline character!**
|
||||
|
||||
A typical usage pattern looks like this:
|
||||
|
||||
```go
|
||||
// Create a logger (your app probably already has one)
|
||||
logger := log.New(os.Stdout, "--> ", log.Ldate|log.Ltime)
|
||||
|
||||
// Setup a streamer that we'll pipe cmd.Stdout to
|
||||
logStreamerOut := NewLogstreamer(logger, "stdout", false)
|
||||
defer logStreamerOut.Close()
|
||||
// Setup a streamer that we'll pipe cmd.Stderr to.
|
||||
// We want to record/buffer anything that's written to this (3rd argument true)
|
||||
logStreamerErr := NewLogstreamer(logger, "stderr", true)
|
||||
defer logStreamerErr.Close()
|
||||
|
||||
// Execute something that succeeds
|
||||
cmd := exec.Command(
|
||||
"ls",
|
||||
"-al",
|
||||
)
|
||||
cmd.Stderr = logStreamerErr
|
||||
cmd.Stdout = logStreamerOut
|
||||
|
||||
// Reset any error we recorded
|
||||
logStreamerErr.FlushRecord()
|
||||
|
||||
// Execute command
|
||||
err := cmd.Start()
|
||||
```
|
||||
|
||||
## Test
|
||||
|
||||
```bash
|
||||
$ cd src/pkg/logstreamer/
|
||||
$ go test
|
||||
```
|
||||
|
||||
Here I issue two local commands, `ls -al` and `ls nonexisting`:
|
||||
|
||||

|
||||
|
||||
Over at [Transloadit](http://transloadit.com) we use it for streaming remote commands.
|
||||
Servers stream command output over SSH back to me, and every line is prefixed with a date, their hostname & marked red in case they
|
||||
wrote to stderr.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT license, see `LICENSE.txt`.
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
package logstreamer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Logstreamer struct {
|
||||
Logger *log.Logger
|
||||
buf *bytes.Buffer
|
||||
// If prefix == stdout, colors green
|
||||
// If prefix == stderr, colors red
|
||||
// Else, prefix is taken as-is, and prepended to anything
|
||||
// you throw at Write()
|
||||
prefix string
|
||||
// if true, saves output in memory
|
||||
record bool
|
||||
persist string
|
||||
|
||||
// Adds color to stdout & stderr if terminal supports it
|
||||
colorOkay string
|
||||
colorFail string
|
||||
colorReset string
|
||||
}
|
||||
|
||||
func NewLogstreamerForWriter(prefix string, writer io.Writer) *Logstreamer {
|
||||
logger := log.New(writer, prefix, 0)
|
||||
return NewLogstreamer(logger, "", false)
|
||||
}
|
||||
|
||||
func NewLogstreamerForStdout(prefix string) *Logstreamer {
|
||||
// logger := log.New(os.Stdout, prefix, log.Ldate|log.Ltime)
|
||||
logger := log.New(os.Stdout, prefix, 0)
|
||||
return NewLogstreamer(logger, "", false)
|
||||
}
|
||||
|
||||
func NewLogstreamerForStderr(prefix string) *Logstreamer {
|
||||
logger := log.New(os.Stderr, prefix, 0)
|
||||
return NewLogstreamer(logger, "", false)
|
||||
}
|
||||
|
||||
func NewLogstreamer(logger *log.Logger, prefix string, record bool) *Logstreamer {
|
||||
streamer := &Logstreamer{
|
||||
Logger: logger,
|
||||
buf: bytes.NewBuffer([]byte("")),
|
||||
prefix: prefix,
|
||||
record: record,
|
||||
persist: "",
|
||||
colorOkay: "",
|
||||
colorFail: "",
|
||||
colorReset: "",
|
||||
}
|
||||
|
||||
if strings.HasPrefix(os.Getenv("TERM"), "xterm") {
|
||||
streamer.colorOkay = "\x1b[32m"
|
||||
streamer.colorFail = "\x1b[31m"
|
||||
streamer.colorReset = "\x1b[0m"
|
||||
}
|
||||
|
||||
return streamer
|
||||
}
|
||||
|
||||
func (l *Logstreamer) Write(p []byte) (n int, err error) {
|
||||
if n, err = l.buf.Write(p); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = l.OutputLines()
|
||||
return
|
||||
}
|
||||
|
||||
func (l *Logstreamer) Close() error {
|
||||
if err := l.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
l.buf = bytes.NewBuffer([]byte(""))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Logstreamer) Flush() error {
|
||||
var p []byte
|
||||
if _, err := l.buf.Read(p); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
l.out(string(p))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Logstreamer) OutputLines() error {
|
||||
for {
|
||||
line, err := l.buf.ReadString('\n')
|
||||
|
||||
if len(line) > 0 {
|
||||
if strings.HasSuffix(line, "\n") {
|
||||
l.out(line)
|
||||
} else {
|
||||
// put back into buffer, it's not a complete line yet
|
||||
// Close() or Flush() have to be used to flush out
|
||||
// the last remaining line if it does not end with a newline
|
||||
if _, err := l.buf.WriteString(line); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Logstreamer) FlushRecord() string {
|
||||
buffer := l.persist
|
||||
l.persist = ""
|
||||
return buffer
|
||||
}
|
||||
|
||||
func (l *Logstreamer) out(str string) {
|
||||
if len(str) < 1 {
|
||||
return
|
||||
}
|
||||
|
||||
if l.record == true {
|
||||
l.persist = l.persist + str
|
||||
}
|
||||
|
||||
if l.prefix == "stdout" {
|
||||
str = l.colorOkay + l.prefix + l.colorReset + " " + str
|
||||
} else if l.prefix == "stderr" {
|
||||
str = l.colorFail + l.prefix + l.colorReset + " " + str
|
||||
} else {
|
||||
str = l.prefix + str
|
||||
}
|
||||
|
||||
l.Logger.Print(str)
|
||||
}
|
||||
Reference in New Issue
Block a user