mirror of
https://github.com/coreos/ignition.git
synced 2026-02-07 03:47:55 +01:00
Closing the logger is usually the last operation that will be run and at this point, we don't have a good way to report errors, and it's also too late to act on them. Thus ignore those errors. Fixes lint: ``` Error return value of `l.ops.Close` is not checked (errcheck) ```
221 lines
6.3 KiB
Go
221 lines
6.3 KiB
Go
// Copyright 2015 CoreOS, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package log
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"log/syslog"
|
|
"os/exec"
|
|
"strings"
|
|
|
|
"github.com/coreos/vcontext/report"
|
|
)
|
|
|
|
type LoggerOps interface {
|
|
Emerg(string) error
|
|
Alert(string) error
|
|
Crit(string) error
|
|
Err(string) error
|
|
Warning(string) error
|
|
Notice(string) error
|
|
Info(string) error
|
|
Debug(string) error
|
|
Close() error
|
|
}
|
|
|
|
// Logger implements a variadic flavor of log/syslog.Writer
|
|
type Logger struct {
|
|
ops LoggerOps
|
|
prefixStack []string
|
|
opSequenceNum int
|
|
}
|
|
|
|
// New creates a new logger.
|
|
// If logToStdout is true, syslog is tried first. If syslog fails or logToStdout
|
|
// is false Stdout is used.
|
|
func New(logToStdout bool) Logger {
|
|
logger := Logger{}
|
|
if !logToStdout {
|
|
var err error
|
|
logger.ops, err = syslog.New(syslog.LOG_DEBUG, "ignition")
|
|
if err != nil {
|
|
logger.ops = Stdout{}
|
|
logger.Err("unable to open syslog: %v", err)
|
|
}
|
|
return logger
|
|
}
|
|
logger.ops = Stdout{}
|
|
return logger
|
|
}
|
|
|
|
// Close closes the logger. Ignore errors.
|
|
func (l Logger) Close() {
|
|
_ = l.ops.Close()
|
|
}
|
|
|
|
// Emerg logs a message at emergency priority.
|
|
func (l Logger) Emerg(format string, a ...interface{}) {
|
|
l.log(l.ops.Emerg, format, a...)
|
|
}
|
|
|
|
// Alert logs a message at alert priority.
|
|
func (l Logger) Alert(format string, a ...interface{}) {
|
|
l.log(l.ops.Alert, format, a...)
|
|
}
|
|
|
|
// Crit logs a message at critical priority.
|
|
func (l Logger) Crit(format string, a ...interface{}) {
|
|
l.log(l.ops.Crit, format, a...)
|
|
}
|
|
|
|
// Err logs a message at error priority.
|
|
func (l Logger) Err(format string, a ...interface{}) {
|
|
l.log(l.ops.Err, format, a...)
|
|
}
|
|
|
|
// Warning logs a message at warning priority.
|
|
func (l Logger) Warning(format string, a ...interface{}) {
|
|
l.log(l.ops.Warning, format, a...)
|
|
}
|
|
|
|
// Notice logs a message at notice priority.
|
|
func (l Logger) Notice(format string, a ...interface{}) {
|
|
l.log(l.ops.Notice, format, a...)
|
|
}
|
|
|
|
// Info logs a message at info priority.
|
|
func (l Logger) Info(format string, a ...interface{}) {
|
|
l.log(l.ops.Info, format, a...)
|
|
}
|
|
|
|
// Debug logs a message at debug priority.
|
|
func (l Logger) Debug(format string, a ...interface{}) {
|
|
l.log(l.ops.Debug, format, a...)
|
|
}
|
|
|
|
// PushPrefix pushes the supplied message onto the Logger's prefix stack.
|
|
// The prefix stack is concatenated in FIFO order and prefixed to the start of every message logged via Logger.
|
|
func (l *Logger) PushPrefix(format string, a ...interface{}) {
|
|
l.prefixStack = append(l.prefixStack, fmt.Sprintf(format, a...))
|
|
}
|
|
|
|
// PopPrefix pops the top entry from the Logger's prefix stack.
|
|
// The prefix stack is concatenated in FIFO order and prefixed to the start of every message logged via Logger.
|
|
func (l *Logger) PopPrefix() {
|
|
if len(l.prefixStack) == 0 {
|
|
l.Debug("popped from empty stack")
|
|
return
|
|
}
|
|
l.prefixStack = l.prefixStack[:len(l.prefixStack)-1]
|
|
}
|
|
|
|
// QuotedCmd returns a concatenated, quoted form of cmd's cmdline
|
|
func QuotedCmd(cmd *exec.Cmd) string {
|
|
if len(cmd.Args) == 0 {
|
|
return fmt.Sprintf("%q", cmd.Path)
|
|
}
|
|
|
|
var q []string
|
|
for _, s := range cmd.Args {
|
|
q = append(q, fmt.Sprintf("%q", s))
|
|
}
|
|
|
|
return strings.Join(q, ` `)
|
|
}
|
|
|
|
// LogCmd runs and logs the supplied cmd as an operation with distinct start/finish/fail log messages uniformly combined with the supplied format string.
|
|
// The exact command path and arguments being executed are also logged for debugging assistance.
|
|
func (l *Logger) LogCmd(cmd *exec.Cmd, format string, a ...interface{}) (int, error) {
|
|
code := -1
|
|
f := func() error {
|
|
cmdLine := QuotedCmd(cmd)
|
|
l.Debug("executing: %s", cmdLine)
|
|
|
|
stdout := &bytes.Buffer{}
|
|
stderr := &bytes.Buffer{}
|
|
cmd.Stdout = stdout
|
|
cmd.Stderr = stderr
|
|
if err := cmd.Run(); err != nil {
|
|
if exitErr, ok := err.(*exec.ExitError); ok {
|
|
code = exitErr.ExitCode()
|
|
}
|
|
return fmt.Errorf("%v: Cmd: %s Stdout: %q Stderr: %q", err, cmdLine, stdout.Bytes(), stderr.Bytes())
|
|
}
|
|
return nil
|
|
}
|
|
err := l.LogOp(f, format, a...)
|
|
return code, err
|
|
}
|
|
|
|
// LogOp calls and logs the supplied function as an operation with distinct start/finish/fail log messages uniformly combined with the supplied format string.
|
|
func (l *Logger) LogOp(op func() error, format string, a ...interface{}) error {
|
|
l.opSequenceNum++
|
|
l.PushPrefix("op(%x)", l.opSequenceNum)
|
|
defer l.PopPrefix()
|
|
|
|
l.logStart(format, a...)
|
|
if err := op(); err != nil {
|
|
l.logFail("%s: %v", fmt.Sprintf(format, a...), err)
|
|
return err
|
|
}
|
|
l.logFinish(format, a...)
|
|
return nil
|
|
}
|
|
|
|
// LogReport logs entries from the report at appropriate levels.
|
|
func (l Logger) LogReport(r report.Report) {
|
|
for _, entry := range r.Entries {
|
|
switch entry.Kind {
|
|
case report.Error:
|
|
l.Crit("%v", entry)
|
|
case report.Warn:
|
|
l.Warning("%v", entry)
|
|
case report.Info:
|
|
l.Info("%v", entry)
|
|
}
|
|
}
|
|
}
|
|
|
|
// logStart logs the start of a multi-step/substantial/time-consuming operation.
|
|
func (l Logger) logStart(format string, a ...interface{}) {
|
|
l.Info(fmt.Sprintf("[started] %s", format), a...)
|
|
}
|
|
|
|
// logFail logs the failure of a multi-step/substantial/time-consuming operation.
|
|
func (l Logger) logFail(format string, a ...interface{}) {
|
|
l.Crit(fmt.Sprintf("[failed] %s", format), a...)
|
|
}
|
|
|
|
// logFinish logs the completion of a multi-step/substantial/time-consuming operation.
|
|
func (l Logger) logFinish(format string, a ...interface{}) {
|
|
l.Info(fmt.Sprintf("[finished] %s", format), a...)
|
|
}
|
|
|
|
// log logs a formatted message using the supplied logFunc.
|
|
func (l Logger) log(logFunc func(string) error, format string, a ...interface{}) {
|
|
_ = logFunc(l.sprintf(format, a...))
|
|
}
|
|
|
|
// sprintf returns the current prefix stack, if any, concatenated with the supplied format string and args in expanded form.
|
|
func (l Logger) sprintf(format string, a ...interface{}) string {
|
|
m := []string{}
|
|
for _, pfx := range l.prefixStack {
|
|
m = append(m, fmt.Sprintf("%s:", pfx))
|
|
}
|
|
m = append(m, fmt.Sprintf(format, a...))
|
|
return strings.Join(m, " ")
|
|
}
|