minio/pkg/iodine/iodine.go

209 lines
4.9 KiB
Go
Raw Normal View History

2015-03-25 02:54:43 -04:00
/*
2015-03-25 16:23:07 -04:00
* Iodine, (C) 2015 Minio, Inc.
2015-03-25 02:54:43 -04:00
*
* 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 iodine
import (
"bytes"
"encoding/json"
"fmt"
"os"
2015-03-25 16:23:07 -04:00
"path"
"reflect"
2015-03-25 02:54:43 -04:00
"runtime"
"strconv"
2015-03-25 16:23:07 -04:00
"strings"
2015-03-25 13:37:01 -04:00
"sync"
"github.com/dustin/go-humanize"
2015-03-25 02:54:43 -04:00
)
2015-04-08 21:02:03 -04:00
// Error is the iodine error which contains a pointer to the original error
2015-03-25 03:17:50 -04:00
// and stack traces.
2015-03-26 18:55:06 -04:00
type Error struct {
2015-03-25 02:54:43 -04:00
EmbeddedError error `json:"-"`
ErrorMessage string
ErrorType string
2015-03-25 02:54:43 -04:00
Stack []StackEntry
}
2015-03-25 03:17:50 -04:00
// StackEntry contains the entry in the stack trace
2015-03-25 02:54:43 -04:00
type StackEntry struct {
Host string
File string
Func string
2015-03-25 02:54:43 -04:00
Line int
Data map[string]string
}
2015-03-25 16:23:07 -04:00
var gopath string
2015-03-25 13:37:01 -04:00
var globalState = struct {
sync.RWMutex
m map[string]string
}{m: make(map[string]string)}
2015-03-25 16:23:07 -04:00
// SetGlobalState - set global state
2015-03-25 13:37:01 -04:00
func SetGlobalState(key, value string) {
globalState.Lock()
globalState.m[key] = value
globalState.Unlock()
}
2015-03-25 16:23:07 -04:00
// ClearGlobalState - clear info in globalState struct
2015-03-25 13:37:01 -04:00
func ClearGlobalState() {
globalState.Lock()
2015-03-25 16:23:07 -04:00
for k := range globalState.m {
2015-03-25 13:37:01 -04:00
delete(globalState.m, k)
}
globalState.Unlock()
}
2015-03-25 16:23:07 -04:00
// GetGlobalState - get map from globalState struct
2015-03-25 13:37:01 -04:00
func GetGlobalState() map[string]string {
result := make(map[string]string)
globalState.RLock()
for k, v := range globalState.m {
result[k] = v
}
globalState.RUnlock()
return result
}
2015-03-25 16:23:07 -04:00
// GetGlobalStateKey - get value for key from globalState struct
func GetGlobalStateKey(k string) string {
result, ok := globalState.m[k]
if !ok {
return ""
}
return result
}
2015-04-08 21:02:03 -04:00
// New - instantiate an error, turning it into an iodine error.
2015-03-25 03:17:50 -04:00
// Adds an initial stack trace.
2015-03-26 18:55:06 -04:00
func New(err error, data map[string]string) error {
2015-03-25 20:32:25 -04:00
if err != nil {
entry := createStackEntry()
2015-03-26 18:55:06 -04:00
var newErr Error
// check if error is wrapped
switch typedError := err.(type) {
2015-03-26 18:55:06 -04:00
case Error:
{
newErr = typedError
}
default:
{
2015-03-26 18:55:06 -04:00
newErr = Error{
EmbeddedError: err,
ErrorMessage: err.Error(),
ErrorType: reflect.TypeOf(err).String(),
Stack: []StackEntry{},
}
}
}
2015-03-25 20:32:25 -04:00
for k, v := range data {
entry.Data[k] = v
}
newErr.Stack = append(newErr.Stack, entry)
return newErr
2015-03-25 02:54:43 -04:00
}
2015-03-25 20:32:25 -04:00
return nil
2015-03-25 02:54:43 -04:00
}
2015-03-25 16:23:07 -04:00
// createStackEntry - create stack entries
2015-03-25 02:54:43 -04:00
func createStackEntry() StackEntry {
host, _ := os.Hostname()
pc, file, line, _ := runtime.Caller(2)
function := runtime.FuncForPC(pc).Name()
_, function = path.Split(function)
2015-03-25 16:23:07 -04:00
file = strings.TrimPrefix(file, gopath) // trim gopath from file
data := GetGlobalState()
for k, v := range getSystemData() {
data[k] = v
}
2015-03-25 02:54:43 -04:00
entry := StackEntry{
Host: host,
File: file,
Func: function,
2015-03-25 02:54:43 -04:00
Line: line,
2015-03-25 16:23:07 -04:00
Data: data,
2015-03-25 02:54:43 -04:00
}
return entry
}
2015-03-25 16:23:07 -04:00
func getSystemData() map[string]string {
host, err := os.Hostname()
if err != nil {
host = ""
}
memstats := &runtime.MemStats{}
runtime.ReadMemStats(memstats)
return map[string]string{
"sys.host": host,
"sys.os": runtime.GOOS,
"sys.arch": runtime.GOARCH,
"sys.go": runtime.Version(),
"sys.cpus": strconv.Itoa(runtime.NumCPU()),
"sys.mem.used": humanize.Bytes(memstats.Alloc),
"sys.mem.allocated": humanize.Bytes(memstats.TotalAlloc),
"sys.mem.heap.used": humanize.Bytes(memstats.HeapAlloc),
"sys.mem.heap.allocated": humanize.Bytes(memstats.HeapSys),
2015-03-25 16:23:07 -04:00
}
}
2015-03-25 03:17:50 -04:00
// Annotate an error with a stack entry and returns itself
//func (err *WrappedError) Annotate(info map[string]string) *WrappedError {
// entry := createStackEntry()
// for k, v := range info {
// entry.Data[k] = v
// }
// err.Stack = append(err.Stack, entry)
// return err
//}
2015-03-25 02:54:43 -04:00
2015-03-25 03:17:50 -04:00
// EmitJSON writes JSON output for the error
2015-03-26 18:55:06 -04:00
func (err Error) EmitJSON() ([]byte, error) {
2015-03-25 02:54:43 -04:00
return json.Marshal(err)
}
2015-03-25 03:17:50 -04:00
// EmitHumanReadable returns a human readable error message
2015-03-26 18:55:06 -04:00
func (err Error) EmitHumanReadable() string {
2015-03-25 02:54:43 -04:00
var errorBuffer bytes.Buffer
fmt.Fprintln(&errorBuffer, err.ErrorMessage)
2015-03-25 02:54:43 -04:00
for i, entry := range err.Stack {
prettyData, _ := json.Marshal(entry.Data)
fmt.Fprintln(&errorBuffer, "-", i, entry.Host+":"+entry.File+":"+strconv.Itoa(entry.Line)+" "+entry.Func+"():", string(prettyData))
2015-03-25 02:54:43 -04:00
}
return string(errorBuffer.Bytes())
}
2015-03-25 03:17:50 -04:00
// Emits the original error message
2015-03-26 18:55:06 -04:00
func (err Error) Error() string {
return err.EmitHumanReadable()
2015-03-25 02:54:43 -04:00
}
2015-03-25 16:23:07 -04:00
func init() {
_, iodineFile, _, _ := runtime.Caller(0)
iodineFile = path.Dir(iodineFile) // trim iodine.go
iodineFile = path.Dir(iodineFile) // trim iodine
iodineFile = path.Dir(iodineFile) // trim minio-io
gopath = path.Dir(iodineFile) + "/" // trim github.com
}