Skip to content

Commit

Permalink
Merge pull request #16 from MWM-io/feat/wip_log_and_err
Browse files Browse the repository at this point in the history
Improve errors & improve log
  • Loading branch information
danysousa committed Jul 6, 2023
2 parents 2183079 + 964dbc3 commit d9ffcbb
Show file tree
Hide file tree
Showing 36 changed files with 456 additions and 180 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ You can see examples implementations in the examples folder:

- a Hello-world example showing you the
basics ([examples/hello-world](https://github.com/MWM-io/gapi/tree/main/examples/hello-world))
- a Google Cloud Platform Logger example showing you how to setup a cloud-logging
- a Google Cloud Platform Logger example showing you how to set up a cloud-logging
logger ([examples/gcp-logger](https://github.com/MWM-io/gapi/tree/main/examples/gcp-logger))
- a CRUD example showing you how to make a CRUD with this
library ([examples/crud](https://github.com/MWM-io/gapi/tree/main/examples/crud))
18 changes: 18 additions & 0 deletions config/env.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package config

import (
"os"
)

// PORT is the port used by the server to listen
// default value is 8080
var PORT string

// IS_LOCAL is a flag to indicate if the server is running locally
var IS_LOCAL = os.Getenv("IS_LOCAL") == "true"

func init() {
if PORT = os.Getenv("PORT"); PORT == "" {
PORT = "8080"
}
}
79 changes: 79 additions & 0 deletions errors/callstack.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package errors

import (
"fmt"
"runtime"
"strings"
)

// GetCallers return the caller of the function and the call stack
func GetCallers() (callerName, caller string, callStack []string) {
// Ask runtime.Callers for up to 10 pcs
pc := make([]uintptr, 10)
n := runtime.Callers(1, pc)
if n == 0 {
// No pcs available. Stop now.
// This can happen if the first argument to runtime.Callers are large.
caller = "unknown"
return
}

pc = pc[:n] // pass only valid pcs to runtime.CallersFrames
frames := runtime.CallersFrames(pc)

firstFrame := true
// Loop to get frames.
// A fixed number of pcs can expand to an indefinite number of Frames.
for {
frame, more := frames.Next()
if !more {
break
}

// Stop call stack when we reach the handler caller
// every call after this is not relevant
if !firstFrame && strings.Contains(frame.File, "github.com/mwm-io/gapi") && strings.Contains(frame.File, "/handler/") {
break
}

// Ignore errors package from call trace because all errors was created from this package
if strings.Contains(frame.File, "github.com/mwm-io/gapi") && strings.Contains(frame.File, "/errors/") {
continue
}

if firstFrame {
caller = formatFrame(frame)
callerName = frame.Func.Name()
firstFrame = false
} else {
callStack = append(callStack, formatFrame(frame))
}
}

return
}

func formatFrame(frame runtime.Frame) string {
file := frame.File
line := frame.Line
function := frame.Function

return fmt.Sprintf("%s:%s -> %s", file, itoa(line, -1), function)
}

// Cheap integer to fixed-width decimal ASCII. Give a negative width to avoid zero-padding.
func itoa(i int, wid int) string {
// Assemble decimal in reverse order.
var b [20]byte
bp := len(b) - 1
for i >= 10 || wid > 1 {
wid--
q := i / 10
b[bp] = byte('0' + i - q*10)
bp--
i = q
}
// i < 10
b[bp] = byte('0' + i)
return string(b[bp:])
}
50 changes: 45 additions & 5 deletions errors/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ type Error interface {
Kind() string
StatusCode() int
Timestamp() time.Time
CallerName() string
Caller() string
Callstack() []string

WithMessage(format string, args ...interface{}) Error
WithKind(string) Error
Expand All @@ -34,6 +37,9 @@ type FullError struct {
status int
timestamp time.Time
sourceErr error
callerName string
caller string
callstack []string
}

// Wrap will wrap the given error and return a new Error.
Expand All @@ -42,34 +48,48 @@ func Wrap(err error) Error {
return nil
}

if castedErr, ok := err.(Error); ok {
return castedErr
}

for _, builder := range errorBuilders {
if gErr := builder(err); gErr != nil {
return gErr
}
}

callerName, caller, callstack := GetCallers()

newErr := &FullError{
userMessage: err.Error(),
kind: "",
kind: "internal_error",
errorMessage: err.Error(),
timestamp: time.Now(),
status: http.StatusInternalServerError,
timestamp: time.Now(),
sourceErr: err,
callerName: callerName,
caller: caller,
callstack: callstack,
}

return newErr
}

// Err creates a new Error.
func Err(format string, args ...interface{}) Error {
func Err(kind, format string, args ...interface{}) Error {
message := fmt.Sprintf(format, args...)

callerName, caller, callstack := GetCallers()

return &FullError{
userMessage: message,
kind: "",
kind: kind,
errorMessage: message,
timestamp: time.Now(),
status: http.StatusInternalServerError,
callerName: callerName,
caller: caller,
callstack: callstack,
}
}

Expand Down Expand Up @@ -130,11 +150,31 @@ func (e *FullError) WithKind(kind string) Error {
// WithError wrap source error.
func (e *FullError) WithError(err error) Error {
e.sourceErr = err
e.errorMessage = err.Error()
if e.errorMessage == "" {
e.errorMessage = err.Error()
}

return e
}

// CallerName implements the error interface.
// It will return the name of the function that created the error
func (e *FullError) CallerName() string {
return e.callerName
}

// Caller implements the error interface.
// It will return the name of the function that created the error
func (e *FullError) Caller() string {
return e.callerName
}

// Callstack implements the error interface.
// It will return the complete callstack of the error creation
func (e *FullError) Callstack() []string {
return e.callstack
}

// HttpError is used to json.Marshal or xml.Marshal FullError.
// You can use it to decode an incoming error.
type HttpError struct {
Expand Down
4 changes: 1 addition & 3 deletions errors/error_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,7 @@ func TestErr(t *testing.T) {
expectedMessage := "this is my error"
expectedStatusCode := http.StatusInternalServerError

err := Err("this is my error").
WithMessage(expectedMessage).
WithKind(expectedKind).
err := Err(expectedKind, expectedMessage).
WithStatus(expectedStatusCode)

assert.Equal(t, expectedKind, err.Kind())
Expand Down
Loading

0 comments on commit d9ffcbb

Please sign in to comment.