Skip to content

Commit

Permalink
Move logging functions to internal/logger.
Browse files Browse the repository at this point in the history
  • Loading branch information
nicdumz committed Jan 13, 2025
1 parent 482cf6f commit b10e57a
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 85 deletions.
71 changes: 36 additions & 35 deletions cmd/age/age.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"filippo.io/age"
"filippo.io/age/agessh"
"filippo.io/age/armor"
"filippo.io/age/internal/logger"
"filippo.io/age/plugin"
"golang.org/x/term"
)
Expand Down Expand Up @@ -104,7 +105,7 @@ func main() {

if len(os.Args) == 1 {
flag.Usage()
exit(1)
logger.Global.Exit(1)
}

var (
Expand Down Expand Up @@ -180,47 +181,47 @@ func main() {
hints = append(hints, "only a single input file may be specified at a time")
}

errorWithHint("too many INPUT arguments: "+quotedArgs, hints...)
logger.Global.ErrorWithHint("too many INPUT arguments: "+quotedArgs, hints...)
}

switch {
case decryptFlag:
if encryptFlag {
errorf("-e/--encrypt can't be used with -d/--decrypt")
logger.Global.Errorf("-e/--encrypt can't be used with -d/--decrypt")
}
if armorFlag {
errorWithHint("-a/--armor can't be used with -d/--decrypt",
logger.Global.ErrorWithHint("-a/--armor can't be used with -d/--decrypt",
"note that armored files are detected automatically")
}
if passFlag {
errorWithHint("-p/--passphrase can't be used with -d/--decrypt",
logger.Global.ErrorWithHint("-p/--passphrase can't be used with -d/--decrypt",
"note that password protected files are detected automatically")
}
if len(recipientFlags) > 0 {
errorWithHint("-r/--recipient can't be used with -d/--decrypt",
logger.Global.ErrorWithHint("-r/--recipient can't be used with -d/--decrypt",
"did you mean to use -i/--identity to specify a private key?")
}
if len(recipientsFileFlags) > 0 {
errorWithHint("-R/--recipients-file can't be used with -d/--decrypt",
logger.Global.ErrorWithHint("-R/--recipients-file can't be used with -d/--decrypt",
"did you mean to use -i/--identity to specify a private key?")
}
default: // encrypt
if len(identityFlags) > 0 && !encryptFlag {
errorWithHint("-i/--identity and -j can't be used in encryption mode unless symmetric encryption is explicitly selected with -e/--encrypt",
logger.Global.ErrorWithHint("-i/--identity and -j can't be used in encryption mode unless symmetric encryption is explicitly selected with -e/--encrypt",
"did you forget to specify -d/--decrypt?")
}
if len(recipientFlags)+len(recipientsFileFlags)+len(identityFlags) == 0 && !passFlag {
errorWithHint("missing recipients",
logger.Global.ErrorWithHint("missing recipients",
"did you forget to specify -r/--recipient, -R/--recipients-file or -p/--passphrase?")
}
if len(recipientFlags) > 0 && passFlag {
errorf("-p/--passphrase can't be combined with -r/--recipient")
logger.Global.Errorf("-p/--passphrase can't be combined with -r/--recipient")
}
if len(recipientsFileFlags) > 0 && passFlag {
errorf("-p/--passphrase can't be combined with -R/--recipients-file")
logger.Global.Errorf("-p/--passphrase can't be combined with -R/--recipients-file")
}
if len(identityFlags) > 0 && passFlag {
errorf("-p/--passphrase can't be combined with -i/--identity and -j")
logger.Global.Errorf("-p/--passphrase can't be combined with -i/--identity and -j")
}
}

Expand All @@ -241,7 +242,7 @@ func main() {
inUseFiles = append(inUseFiles, absPath(name))
f, err := os.Open(name)
if err != nil {
errorf("failed to open input file %q: %v", name, err)
logger.Global.Errorf("failed to open input file %q: %v", name, err)
}
defer f.Close()
in = f
Expand All @@ -253,21 +254,21 @@ func main() {
// output don't get in the way of typing the input. See Issue 364.
buf, err := bufferTerminalInput(in)
if err != nil {
errorf("failed to buffer terminal input: %v", err)
logger.Global.Errorf("failed to buffer terminal input: %v", err)
}
in = buf
}
}
if name := outFlag; name != "" && name != "-" {
for _, f := range inUseFiles {
if f == absPath(name) {
errorf("input and output file are the same: %q", name)
logger.Global.Errorf("input and output file are the same: %q", name)
}
}
f := newLazyOpener(name)
defer func() {
if err := f.Close(); err != nil {
errorf("failed to close output file %q: %v", name, err)
logger.Global.Errorf("failed to close output file %q: %v", name, err)
}
}()
out = f
Expand All @@ -278,7 +279,7 @@ func main() {
} else if !armorFlag {
// If the output wouldn't be armored, refuse to send binary to
// the terminal unless explicitly requested with "-o -".
errorWithHint("refusing to output binary to the terminal",
logger.Global.ErrorWithHint("refusing to output binary to the terminal",
"did you mean to use -a/--armor?",
`force anyway with "-o -"`)
}
Expand Down Expand Up @@ -337,19 +338,19 @@ func encryptNotPass(recs, files []string, identities identityFlags, in io.Reader
for _, arg := range recs {
r, err := parseRecipient(arg)
if err, ok := err.(gitHubRecipientError); ok {
errorWithHint(err.Error(), "instead, use recipient files like",
logger.Global.ErrorWithHint(err.Error(), "instead, use recipient files like",
" curl -O https://github.com/"+err.username+".keys",
" age -R "+err.username+".keys")
}
if err != nil {
errorf("%v", err)
logger.Global.Errorf("%v", err)
}
recipients = append(recipients, r)
}
for _, name := range files {
recs, err := parseRecipientsFile(name)
if err != nil {
errorf("failed to parse recipient file %q: %v", name, err)
logger.Global.Errorf("failed to parse recipient file %q: %v", name, err)
}
recipients = append(recipients, recs...)
}
Expand All @@ -358,17 +359,17 @@ func encryptNotPass(recs, files []string, identities identityFlags, in io.Reader
case "i":
ids, err := parseIdentitiesFile(f.Value)
if err != nil {
errorf("reading %q: %v", f.Value, err)
logger.Global.Errorf("reading %q: %v", f.Value, err)
}
r, err := identitiesToRecipients(ids)
if err != nil {
errorf("internal error processing %q: %v", f.Value, err)
logger.Global.Errorf("internal error processing %q: %v", f.Value, err)
}
recipients = append(recipients, r...)
case "j":
id, err := plugin.NewIdentityWithoutData(f.Value, pluginTerminalUI)
if err != nil {
errorf("initializing %q: %v", f.Value, err)
logger.Global.Errorf("initializing %q: %v", f.Value, err)
}
recipients = append(recipients, id.Recipient())
}
Expand All @@ -379,12 +380,12 @@ func encryptNotPass(recs, files []string, identities identityFlags, in io.Reader
func encryptPass(in io.Reader, out io.Writer, armor bool) {
pass, err := passphrasePromptForEncryption()
if err != nil {
errorf("%v", err)
logger.Global.Errorf("%v", err)
}

r, err := age.NewScryptRecipient(pass)
if err != nil {
errorf("%v", err)
logger.Global.Errorf("%v", err)
}
testOnlyConfigureScryptIdentity(r)
encrypt([]age.Recipient{r}, in, out, armor)
Expand All @@ -397,20 +398,20 @@ func encrypt(recipients []age.Recipient, in io.Reader, out io.Writer, withArmor
a := armor.NewWriter(out)
defer func() {
if err := a.Close(); err != nil {
errorf("%v", err)
logger.Global.Errorf("%v", err)
}
}()
out = a
}
w, err := age.Encrypt(out, recipients...)
if err != nil {
errorf("%v", err)
logger.Global.Errorf("%v", err)
}
if _, err := io.Copy(w, in); err != nil {
errorf("%v", err)
logger.Global.Errorf("%v", err)
}
if err := w.Close(); err != nil {
errorf("%v", err)
logger.Global.Errorf("%v", err)
}
}

Expand All @@ -426,7 +427,7 @@ func (rejectScryptIdentity) Unwrap(stanzas []*age.Stanza) ([]byte, error) {
if len(stanzas) != 1 || stanzas[0].Type != "scrypt" {
return nil, age.ErrIncorrectIdentity
}
errorWithHint("file is passphrase-encrypted but identities were specified with -i/--identity or -j",
logger.Global.ErrorWithHint("file is passphrase-encrypted but identities were specified with -i/--identity or -j",
"remove all -i/--identity/-j flags to decrypt passphrase-encrypted files")
panic("unreachable")
}
Expand All @@ -439,13 +440,13 @@ func decryptNotPass(flags identityFlags, in io.Reader, out io.Writer) {
case "i":
ids, err := parseIdentitiesFile(f.Value)
if err != nil {
errorf("reading %q: %v", f.Value, err)
logger.Global.Errorf("reading %q: %v", f.Value, err)
}
identities = append(identities, ids...)
case "j":
id, err := plugin.NewIdentityWithoutData(f.Value, pluginTerminalUI)
if err != nil {
errorf("initializing %q: %v", f.Value, err)
logger.Global.Errorf("initializing %q: %v", f.Value, err)
}
identities = append(identities, id)
}
Expand All @@ -468,7 +469,7 @@ func decrypt(identities []age.Identity, in io.Reader, out io.Writer) {
rr := bufio.NewReader(in)
if intro, _ := rr.Peek(len(crlfMangledIntro)); string(intro) == crlfMangledIntro ||
string(intro) == utf16MangledIntro {
errorWithHint("invalid header intro",
logger.Global.ErrorWithHint("invalid header intro",
"it looks like this file was corrupted by PowerShell redirection",
"consider using -o or -a to encrypt files in PowerShell")
}
Expand All @@ -481,11 +482,11 @@ func decrypt(identities []age.Identity, in io.Reader, out io.Writer) {

r, err := age.Decrypt(in, identities...)
if err != nil {
errorf("%v", err)
logger.Global.Errorf("%v", err)
}
out.Write(nil) // trigger the lazyOpener even if r is empty
if _, err := io.Copy(out, r); err != nil {
errorf("%v", err)
logger.Global.Errorf("%v", err)
}
}

Expand Down
5 changes: 3 additions & 2 deletions cmd/age/age_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,16 @@ import (
"testing"

"filippo.io/age"
"filippo.io/age/internal/logger"
"github.com/rogpeppe/go-internal/testscript"
)

func TestMain(m *testing.M) {
os.Exit(testscript.RunMain(m, map[string]func() int{
"age": func() (exitCode int) {
testOnlyPanicInsteadOfExit = true
logger.Global.TestOnlyPanicInsteadOfExit = true
defer func() {
if testOnlyDidExit {
if logger.Global.TestOnlyDidExit {
exitCode = recover().(int)
}
}()
Expand Down
5 changes: 3 additions & 2 deletions cmd/age/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"filippo.io/age"
"filippo.io/age/agessh"
"filippo.io/age/armor"
"filippo.io/age/internal/logger"
"filippo.io/age/plugin"
"golang.org/x/crypto/cryptobyte"
"golang.org/x/crypto/ssh"
Expand Down Expand Up @@ -79,7 +80,7 @@ func parseRecipientsFile(name string) ([]age.Recipient, error) {
if err != nil {
if t, ok := sshKeyType(line); ok {
// Skip unsupported but valid SSH public keys with a warning.
warningf("recipients file %q: ignoring unsupported SSH key of type %q at line %d", name, t, n)
logger.Global.Warningf("recipients file %q: ignoring unsupported SSH key of type %q at line %d", name, t, n)
continue
}
// Hide the error since it might unintentionally leak the contents
Expand Down Expand Up @@ -169,7 +170,7 @@ func parseIdentitiesFile(name string) ([]age.Identity, error) {
return string(pass), nil
},
NoMatchWarning: func() {
warningf("encrypted identity file %q didn't match file's recipients", name)
logger.Global.Warningf("encrypted identity file %q didn't match file's recipients", name)
},
}}, nil

Expand Down
52 changes: 6 additions & 46 deletions cmd/age/tui.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,55 +18,15 @@ import (
"errors"
"fmt"
"io"
"log"
"os"
"runtime"

"filippo.io/age/armor"
"filippo.io/age/internal/logger"
"filippo.io/age/plugin"
"golang.org/x/term"
)

// l is a logger with no prefixes.
var l = log.New(os.Stderr, "", 0)

func printf(format string, v ...interface{}) {
l.Printf("age: "+format, v...)
}

func errorf(format string, v ...interface{}) {
l.Printf("age: error: "+format, v...)
l.Printf("age: report unexpected or unhelpful errors at https://filippo.io/age/report")
exit(1)
}

func warningf(format string, v ...interface{}) {
l.Printf("age: warning: "+format, v...)
}

func errorWithHint(error string, hints ...string) {
l.Printf("age: error: %s", error)
for _, hint := range hints {
l.Printf("age: hint: %s", hint)
}
l.Printf("age: report unexpected or unhelpful errors at https://filippo.io/age/report")
exit(1)
}

// If testOnlyPanicInsteadOfExit is true, exit will set testOnlyDidExit and
// panic instead of calling os.Exit. This way, the wrapper in TestMain can
// recover the panic and return the exit code only if it was originated in exit.
var testOnlyPanicInsteadOfExit bool
var testOnlyDidExit bool

func exit(code int) {
if testOnlyPanicInsteadOfExit {
testOnlyDidExit = true
panic(code)
}
os.Exit(code)
}

// clearLine clears the current line on the terminal, or opens a new line if
// terminal escape codes don't work.
func clearLine(out io.Writer) {
Expand Down Expand Up @@ -156,13 +116,13 @@ func readCharacter(prompt string) (c byte, err error) {

var pluginTerminalUI = &plugin.ClientUI{
DisplayMessage: func(name, message string) error {
printf("%s plugin: %s", name, message)
logger.Global.Printf("%s plugin: %s", name, message)
return nil
},
RequestValue: func(name, message string, _ bool) (s string, err error) {
defer func() {
if err != nil {
warningf("could not read value for age-plugin-%s: %v", name, err)
logger.Global.Warningf("could not read value for age-plugin-%s: %v", name, err)
}
}()
secret, err := readSecret(message)
Expand All @@ -174,7 +134,7 @@ var pluginTerminalUI = &plugin.ClientUI{
Confirm: func(name, message, yes, no string) (choseYes bool, err error) {
defer func() {
if err != nil {
warningf("could not read value for age-plugin-%s: %v", name, err)
logger.Global.Warningf("could not read value for age-plugin-%s: %v", name, err)
}
}()
if no == "" {
Expand All @@ -199,12 +159,12 @@ var pluginTerminalUI = &plugin.ClientUI{
case '\x03': // CTRL-C
return false, errors.New("user cancelled prompt")
default:
warningf("reading value for age-plugin-%s: invalid selection %q", name, selection)
logger.Global.Warningf("reading value for age-plugin-%s: invalid selection %q", name, selection)
}
}
},
WaitTimer: func(name string) {
printf("waiting on %s plugin...", name)
logger.Global.Printf("waiting on %s plugin...", name)
},
}

Expand Down
Loading

0 comments on commit b10e57a

Please sign in to comment.