// Copyright 2016 Palantir Technologies // // 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 stacktrace import ( "fmt" "strings" ) /* DefaultFormat defines the behavior of err.Error() when called on a stacktrace, as well as the default behavior of the "%v", "%s" and "%q" formatting specifiers. By default, all of these produce a full stacktrace including line number information. To have them produce a condensed single-line output, set this value to stacktrace.FormatBrief. The formatting specifier "%+s" can be used to force a full stacktrace regardless of the value of DefaultFormat. Similarly, the formatting specifier "%#s" can be used to force a brief output. */ var DefaultFormat = FormatFull // Format is the type of the two possible values of stacktrace.DefaultFormat. type Format int const ( // FormatFull means format as a full stacktrace including line number information. FormatFull Format = iota // FormatBrief means Format on a single line without line number information. FormatBrief ) var _ fmt.Formatter = (*stacktrace)(nil) func (st *stacktrace) Format(f fmt.State, c rune) { var text string if f.Flag('+') && !f.Flag('#') && c == 's' { // "%+s" text = formatFull(st) } else if f.Flag('#') && !f.Flag('+') && c == 's' { // "%#s" text = formatBrief(st) } else { text = map[Format]func(*stacktrace) string{ FormatFull: formatFull, FormatBrief: formatBrief, }[DefaultFormat](st) } formatString := "%" // keep the flags recognized by fmt package for _, flag := range "-+# 0" { if f.Flag(int(flag)) { formatString += string(flag) } } if width, has := f.Width(); has { formatString += fmt.Sprint(width) } if precision, has := f.Precision(); has { formatString += "." formatString += fmt.Sprint(precision) } formatString += string(c) fmt.Fprintf(f, formatString, text) } func formatFull(st *stacktrace) string { var str string newline := func() { if str != "" && !strings.HasSuffix(str, "\n") { str += "\n" } } for curr, ok := st, true; ok; curr, ok = curr.cause.(*stacktrace) { str += curr.message if curr.file != "" { newline() if curr.function == "" { str += fmt.Sprintf(" --- at %v:%v ---", curr.file, curr.line) } else { str += fmt.Sprintf(" --- at %v:%v (%v) ---", curr.file, curr.line, curr.function) } } if curr.cause != nil { newline() if cause, ok := curr.cause.(*stacktrace); !ok { str += "Caused by: " str += curr.cause.Error() } else if cause.message != "" { str += "Caused by: " } } } return str } func formatBrief(st *stacktrace) string { var str string concat := func(msg string) { if str != "" && msg != "" { str += ": " } str += msg } curr := st for { concat(curr.message) if cause, ok := curr.cause.(*stacktrace); ok { curr = cause } else { break } } if curr.cause != nil { concat(curr.cause.Error()) } return str }