mirror of
https://github.com/dokku/dokku.git
synced 2025-12-16 20:17:44 +01:00
250 lines
6.0 KiB
Go
250 lines
6.0 KiB
Go
package common
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/hashicorp/go-multierror"
|
|
)
|
|
|
|
// ErrWithExitCode wraps error and exposes an ExitCode method
|
|
type ErrWithExitCode interface {
|
|
ExitCode() int
|
|
}
|
|
|
|
type writer struct {
|
|
mu *sync.Mutex
|
|
source string
|
|
}
|
|
|
|
// Write prints the data to either stdout or stderr using the log helper functions
|
|
func (w *writer) Write(bytes []byte) (int, error) {
|
|
w.mu.Lock()
|
|
defer w.mu.Unlock()
|
|
|
|
if w.source == "stdout" {
|
|
for _, line := range strings.Split(string(bytes), "\n") {
|
|
if line == "" {
|
|
continue
|
|
}
|
|
|
|
LogVerboseQuiet(line)
|
|
}
|
|
} else {
|
|
for _, line := range strings.Split(string(bytes), "\n") {
|
|
if line == "" {
|
|
continue
|
|
}
|
|
|
|
LogVerboseStderrQuiet(line)
|
|
}
|
|
}
|
|
|
|
return len(bytes), nil
|
|
}
|
|
|
|
// PrefixingWriter is a writer that prefixes all writes with a given prefix
|
|
type PrefixingWriter struct {
|
|
Prefix []byte
|
|
Writer io.Writer
|
|
}
|
|
|
|
// Write writes the given bytes to the writer with the prefix
|
|
func (pw *PrefixingWriter) Write(p []byte) (int, error) {
|
|
if len(p) == 0 {
|
|
return 0, nil
|
|
}
|
|
|
|
// Perform an "atomic" write of a prefix and p to make sure that it doesn't interleave
|
|
// sub-line when used concurrently with io.PipeWrite.
|
|
n, err := pw.Writer.Write(append(pw.Prefix, p...))
|
|
if n > len(p) {
|
|
// To comply with the io.Writer interface requirements we must
|
|
// return a number of bytes written from p (0 <= n <= len(p)),
|
|
// so we are ignoring the length of the prefix here.
|
|
return len(p), err
|
|
}
|
|
return n, err
|
|
}
|
|
|
|
// LogFail is the failure log formatter
|
|
// prints text to stderr and exits with status 1
|
|
func LogFail(text string) {
|
|
fmt.Fprintln(os.Stderr, fmt.Sprintf(" ! %s", text))
|
|
os.Exit(1)
|
|
}
|
|
|
|
// LogFailWithError is the failure log formatter
|
|
// prints text to stderr and exits with the specified exit code
|
|
func LogFailWithError(err error) {
|
|
if err == nil {
|
|
return
|
|
}
|
|
|
|
if merr, ok := err.(*multierror.Error); ok {
|
|
for _, e := range merr.Errors {
|
|
fmt.Fprintf(os.Stderr, " ! %s\n", e.Error())
|
|
}
|
|
} else {
|
|
if err.Error() != "" {
|
|
fmt.Fprintf(os.Stderr, " ! %s\n", err.Error())
|
|
}
|
|
}
|
|
if errExit, ok := err.(ErrWithExitCode); ok {
|
|
os.Exit(errExit.ExitCode())
|
|
}
|
|
os.Exit(1)
|
|
}
|
|
|
|
// LogFailWithErrorQuiet is the failure log formatter (with quiet option)
|
|
// prints text to stderr and exits with the specified exit code
|
|
// The error message is not printed if DOKKU_QUIET_OUTPUT has any value
|
|
func LogFailWithErrorQuiet(err error) {
|
|
if os.Getenv("DOKKU_QUIET_OUTPUT") == "" {
|
|
fmt.Fprintln(os.Stderr, fmt.Sprintf(" ! %s", err.Error()))
|
|
}
|
|
if errExit, ok := err.(ErrWithExitCode); ok {
|
|
os.Exit(errExit.ExitCode())
|
|
}
|
|
os.Exit(1)
|
|
}
|
|
|
|
// LogFailQuiet is the failure log formatter (with quiet option)
|
|
// prints text to stderr and exits with status 1
|
|
func LogFailQuiet(text string) {
|
|
if os.Getenv("DOKKU_QUIET_OUTPUT") == "" {
|
|
fmt.Fprintln(os.Stderr, fmt.Sprintf(" ! %s", text))
|
|
}
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Log is the log formatter
|
|
func Log(text string) {
|
|
fmt.Println(text)
|
|
}
|
|
|
|
// LogQuiet is the log formatter (with quiet option)
|
|
func LogQuiet(text string) {
|
|
if os.Getenv("DOKKU_QUIET_OUTPUT") == "" {
|
|
fmt.Println(text)
|
|
}
|
|
}
|
|
|
|
// LogInfo1 is the info1 header formatter
|
|
func LogInfo1(text string) {
|
|
fmt.Println(fmt.Sprintf("-----> %s", text))
|
|
}
|
|
|
|
// LogInfo1Quiet is the info1 header formatter (with quiet option)
|
|
func LogInfo1Quiet(text string) {
|
|
if os.Getenv("DOKKU_QUIET_OUTPUT") == "" {
|
|
LogInfo1(text)
|
|
}
|
|
}
|
|
|
|
// LogInfo2 is the info2 header formatter
|
|
func LogInfo2(text string) {
|
|
fmt.Println(fmt.Sprintf("=====> %s", text))
|
|
}
|
|
|
|
// LogInfo2Quiet is the info2 header formatter (with quiet option)
|
|
func LogInfo2Quiet(text string) {
|
|
if os.Getenv("DOKKU_QUIET_OUTPUT") == "" {
|
|
LogInfo2(text)
|
|
}
|
|
}
|
|
|
|
// LogVerbose is the verbose log formatter
|
|
// prints indented text to stdout
|
|
func LogVerbose(text string) {
|
|
fmt.Println(fmt.Sprintf(" %s", text))
|
|
}
|
|
|
|
// LogVerboseStderr is the verbose log formatter
|
|
// prints indented text to stderr
|
|
func LogVerboseStderr(text string) {
|
|
fmt.Fprintln(os.Stderr, fmt.Sprintf(" ! %s", text))
|
|
}
|
|
|
|
// LogVerboseQuiet is the verbose log formatter
|
|
// prints indented text to stdout (with quiet option)
|
|
func LogVerboseQuiet(text string) {
|
|
if os.Getenv("DOKKU_QUIET_OUTPUT") == "" {
|
|
LogVerbose(text)
|
|
}
|
|
}
|
|
|
|
// LogVerboseStderrQuiet is the verbose log formatter
|
|
// prints indented text to stderr (with quiet option)
|
|
func LogVerboseStderrQuiet(text string) {
|
|
if os.Getenv("DOKKU_QUIET_OUTPUT") == "" {
|
|
LogVerboseStderr(text)
|
|
}
|
|
}
|
|
|
|
// LogVerboseQuietContainerLogs is the verbose log formatter for container logs
|
|
func LogVerboseQuietContainerLogs(containerID string) {
|
|
LogVerboseQuietContainerLogsTail(containerID, 0, false)
|
|
}
|
|
|
|
// LogVerboseQuietContainerLogsTail is the verbose log formatter for container logs with tail mode enabled
|
|
func LogVerboseQuietContainerLogsTail(containerID string, lines int, tail bool) {
|
|
args := []string{"container", "logs", containerID}
|
|
if lines > 0 {
|
|
args = append(args, "--tail", strconv.Itoa(lines))
|
|
}
|
|
if tail {
|
|
args = append(args, "--follow")
|
|
}
|
|
|
|
var mu sync.Mutex
|
|
result, err := CallExecCommand(ExecCommandInput{
|
|
Command: DockerBin(),
|
|
Args: args,
|
|
DisableStdioBuffer: true,
|
|
StdoutWriter: &writer{
|
|
mu: &mu,
|
|
source: "stdout",
|
|
},
|
|
StderrWriter: &writer{
|
|
mu: &mu,
|
|
source: "stderr",
|
|
},
|
|
})
|
|
|
|
if err != nil {
|
|
LogExclaim(fmt.Sprintf("Failed to fetch container logs: %s", containerID))
|
|
return
|
|
}
|
|
|
|
if !tail && result.ExitCode != 0 {
|
|
LogExclaim(fmt.Sprintf("Failed to fetch container logs: %s", containerID))
|
|
}
|
|
}
|
|
|
|
// LogWarn is the warning log formatter
|
|
func LogWarn(text string) {
|
|
fmt.Fprintln(os.Stderr, fmt.Sprintf(" ! %s", text))
|
|
}
|
|
|
|
// LogExclaim is the log exclaim formatter
|
|
func LogExclaim(text string) {
|
|
fmt.Println(fmt.Sprintf(" ! %s", text))
|
|
}
|
|
|
|
// LogStderr is the stderr log formatter
|
|
func LogStderr(text string) {
|
|
fmt.Fprintln(os.Stderr, text)
|
|
}
|
|
|
|
// LogDebug is the debug log formatter
|
|
func LogDebug(text string) {
|
|
if os.Getenv("DOKKU_TRACE") == "1" {
|
|
fmt.Fprintln(os.Stderr, fmt.Sprintf(" ? %s", strings.TrimPrefix(text, " ? ")))
|
|
}
|
|
}
|