diff --git a/logger/context.go b/logger/context.go
new file mode 100644
index 0000000000000000000000000000000000000000..9f709dd5dec4f850d6834e3fb3d8a8a6376155e3
--- /dev/null
+++ b/logger/context.go
@@ -0,0 +1,92 @@
+package logger
+
+import (
+	"fmt"
+)
+
+func GetContextLogger() Logger {
+	return logger
+}
+
+// func ClearInfo() {
+// 	InitLogs(nil, nil)
+// }
+
+// func InitLogs(requestID *string, request *events.APIGatewayProxyRequest) {
+// 	logger = Logger{
+// 		currentRequestID: requestID,
+// 		apiRequest:       request,
+// 		level:            LevelInfo,
+// 		writer:           os.Stderr,
+// 		data: map[string]interface{}{
+// 			"environment": getEnvironment(),
+// 		},
+// 	}
+// 	if requestID != nil {
+// 		logger.data["request_id"] = *requestID
+// 	}
+
+// 	if val, exists := os.LookupEnv("DEBUGGING"); exists && strings.ToLower(val) == "true" {
+// 		logger.level = LevelDebug
+// 	}
+// }
+
+// func getEnvironment() string {
+// 	environment := os.Getenv("ENVIRONMENT")
+// 	if environment == "" {
+// 		environment = "prod"
+// 	}
+// 	return environment
+// }
+
+func LogMessageWithFields(fields map[string]interface{}, message interface{}) {
+	logger.WithFields(fields).log(LevelInfo, 1, fmt.Sprintf("%v", message))
+}
+
+func LogMessage(format string, a ...interface{}) {
+	logger.log(LevelInfo, 1, fmt.Sprintf(format, a...))
+}
+
+func LogError(fields map[string]interface{}, err error) {
+	// sendRaygunError(fields, err)
+	logger.WithFields(fields).log(LevelError, 1, fmt.Sprintf("%+v", err))
+}
+
+func LogErrorMessage(message interface{}, err error) {
+	if err != nil || message != nil {
+		logger.WithFields(map[string]interface{}{
+			"error": fmt.Sprintf("%+v", err),
+		}).log(LevelError, 1, fmt.Sprintf("%v", message))
+	}
+}
+
+func LogWarningMessage(format string, a ...interface{}) {
+	logger.log(LevelWarn, 1, fmt.Sprintf(format, a...))
+}
+
+func LogWarning(fields map[string]interface{}, err error) {
+	logger.WithFields(fields).log(LevelWarn, 1, fmt.Sprintf("%+v", err))
+}
+
+func SQLDebugInfo(sql string) {
+	logger.WithFields(map[string]interface{}{
+		"sql": sql,
+	}).log(LevelInfo, 1, "SQL")
+}
+
+// func LogRequestInfo(req events.APIGatewayProxyRequest) {
+// 	fields := map[string]interface{}{
+// 		"http_method":                req.HTTPMethod,
+// 		"path":                       req.Path,
+// 		"api_gateway_request_id":     req.RequestContext.RequestID,
+// 		"user_cognito_auth_provider": req.RequestContext.Identity.CognitoAuthenticationProvider,
+// 		"user_arn":                   req.RequestContext.Identity.UserArn,
+// 	}
+// 	logger.WithFields(fields).log(LevelInfo, 1, "Request Info start")
+// }
+
+// func LogSQSEvent(event events.SQSEvent) {
+// 	logger.WithFields(map[string]interface{}{
+// 		"records": event.Records,
+// 	}).log(LevelInfo, 1, "SQS event start")
+// }
diff --git a/logger/db.go b/logger/db.go
new file mode 100644
index 0000000000000000000000000000000000000000..c684855c087543acb668af1fa4e399a1de4b33f4
--- /dev/null
+++ b/logger/db.go
@@ -0,0 +1,24 @@
+package logger
+
+import (
+	"context"
+	"os"
+
+	"github.com/go-pg/pg/v10"
+)
+
+type DbLogger struct{}
+
+func (d DbLogger) BeforeQuery(c context.Context, q *pg.QueryEvent) (context.Context, error) {
+	return c, nil
+}
+
+func (d DbLogger) AfterQuery(c context.Context, q *pg.QueryEvent) error {
+	sql, _ := q.FormattedQuery()
+	if os.Getenv("DEBUGGING") == "true" {
+		Debugf(string(sql))
+	} else {
+		SQLDebugInfo(string(sql))
+	}
+	return nil
+}
diff --git a/logger/fields.go b/logger/fields.go
new file mode 100644
index 0000000000000000000000000000000000000000..4133d9ac1ea372e296f9e7b47ff8e185e055cc40
--- /dev/null
+++ b/logger/fields.go
@@ -0,0 +1,3 @@
+package logger
+
+type Fields map[string]interface{}
diff --git a/logger/global.go b/logger/global.go
new file mode 100644
index 0000000000000000000000000000000000000000..ec746ab32c4ce6d58a0ce8e8f3a8e4f781c70fcf
--- /dev/null
+++ b/logger/global.go
@@ -0,0 +1,68 @@
+package logger
+
+import (
+	"fmt"
+	"os"
+
+	"github.com/uafrica/go-utils/errors"
+)
+
+var logger Logger
+
+func init() {
+	logger = Logger{
+		level:  LevelDebug,
+		writer: os.Stderr,
+		data:   map[string]interface{}{},
+	}
+	//	InitLogs(nil, nil)
+}
+
+func New() Logger {
+	return logger.WithFields(nil)
+}
+
+//shortcut functions to use current logger
+//this should only be used outside of a request context
+//or anywhere if you have a single threaded process
+func Fatalf(format string, args ...interface{}) {
+	logger.WithFields(map[string]interface{}{"call_stack": errors.Stack(3)}).log(LevelFatal, 1, fmt.Sprintf(format, args...))
+	os.Exit(1)
+}
+
+func Fatal(args ...interface{}) {
+	logger.WithFields(map[string]interface{}{"call_stack": errors.Stack(3)}).log(LevelFatal, 1, fmt.Sprint(args...))
+	os.Exit(1)
+}
+
+func Errorf(format string, args ...interface{}) {
+	logger.WithFields(map[string]interface{}{"call_stack": errors.Stack(3)}).log(LevelError, 1, fmt.Sprintf(format, args...))
+}
+
+func Error(args ...interface{}) {
+	logger.WithFields(map[string]interface{}{"call_stack": errors.Stack(3)}).log(LevelError, 1, fmt.Sprint(args...))
+}
+
+func Warnf(format string, args ...interface{}) {
+	logger.log(LevelError, 1, fmt.Sprintf(format, args...))
+}
+
+func Warn(args ...interface{}) {
+	logger.log(LevelError, 1, fmt.Sprint(args...))
+}
+
+func Infof(format string, args ...interface{}) {
+	logger.log(LevelError, 1, fmt.Sprintf(format, args...))
+}
+
+func Info(args ...interface{}) {
+	logger.log(LevelError, 1, fmt.Sprint(args...))
+}
+
+func Debugf(format string, args ...interface{}) {
+	logger.log(LevelError, 1, fmt.Sprintf(format, args...))
+}
+
+func Debug(args ...interface{}) {
+	logger.log(LevelError, 1, fmt.Sprint(args...))
+}
diff --git a/logger/level.go b/logger/level.go
new file mode 100644
index 0000000000000000000000000000000000000000..3014994e7f9fa39006368bf8ad965ffb44e3ced9
--- /dev/null
+++ b/logger/level.go
@@ -0,0 +1,33 @@
+package logger
+
+import "fmt"
+
+type Level int
+
+func (level Level) String() string {
+	switch level {
+	case LevelFatal:
+		return "fatal"
+	case LevelError:
+		return "error"
+	case LevelWarn:
+		return "warn"
+	case LevelInfo:
+		return "info"
+	case LevelDebug:
+		return "debug"
+	}
+	return fmt.Sprintf("Level(%d)", level)
+}
+
+func (level Level) MarshalJSON() ([]byte, error) {
+	return []byte("\"" + level.String() + "\""), nil
+}
+
+const (
+	LevelFatal Level = iota
+	LevelError
+	LevelWarn
+	LevelInfo
+	LevelDebug
+)
diff --git a/logger/logger.go b/logger/logger.go
new file mode 100644
index 0000000000000000000000000000000000000000..0a6419137ef080b30145af491f314cdc8213c6af
--- /dev/null
+++ b/logger/logger.go
@@ -0,0 +1,210 @@
+package logger
+
+import (
+	"encoding/json"
+	"fmt"
+	"io"
+	"strings"
+	"time"
+
+	"github.com/uafrica/go-utils/errors"
+)
+
+type ILogger interface {
+	Fatalf(format string, args ...interface{})
+	Fatal(args ...interface{})
+	Errorf(format string, args ...interface{})
+	Error(args ...interface{})
+	Warnf(format string, args ...interface{})
+	Warn(args ...interface{})
+	Infof(format string, args ...interface{})
+	Info(args ...interface{})
+	Debugf(format string, args ...interface{})
+	Debug(args ...interface{})
+}
+
+type Logger struct {
+	//apiRequest       *events.APIGatewayProxyRequest
+	//currentRequestID *string
+	level  Level
+	writer io.Writer
+	data   map[string]interface{}
+}
+
+func (l Logger) WithFields(data map[string]interface{}) Logger {
+	newLogger := Logger{
+		level:  l.level,
+		writer: l.writer,
+		data:   map[string]interface{}{},
+	}
+	for n, v := range l.data {
+		newLogger.data[n] = v
+	}
+	for n, v := range data {
+		newLogger.data[n] = v
+	}
+	return newLogger
+}
+
+func (l Logger) Fatalf(format string, args ...interface{}) {
+	l.WithFields(map[string]interface{}{"call_stack": errors.Stack(3)}).log(LevelFatal, 1, fmt.Sprintf(format, args...))
+}
+
+func (l Logger) Fatal(args ...interface{}) {
+	l.WithFields(map[string]interface{}{"call_stack": errors.Stack(3)}).log(LevelFatal, 1, fmt.Sprint(args...))
+}
+
+func (l Logger) Errorf(format string, args ...interface{}) {
+	l.WithFields(map[string]interface{}{"call_stack": errors.Stack(3)}).log(LevelError, 1, fmt.Sprintf(format, args...))
+}
+
+func (l Logger) Error(args ...interface{}) {
+	l.WithFields(map[string]interface{}{"call_stack": errors.Stack(3)}).log(LevelError, 1, fmt.Sprint(args...))
+}
+
+func (l Logger) Warnf(format string, args ...interface{}) {
+	l.log(LevelError, 1, fmt.Sprintf(format, args...))
+}
+
+func (l Logger) Warn(args ...interface{}) {
+	l.log(LevelError, 1, fmt.Sprint(args...))
+}
+
+func (l Logger) Infof(format string, args ...interface{}) {
+	l.log(LevelInfo, 1, fmt.Sprintf(format, args...))
+}
+
+func (l Logger) Info(args ...interface{}) {
+	l.log(LevelInfo, 1, fmt.Sprint(args...))
+}
+
+func (l Logger) Debugf(format string, args ...interface{}) {
+	l.log(LevelDebug, 1, fmt.Sprintf(format, args...))
+}
+
+func (l Logger) Debug(args ...interface{}) {
+	l.log(LevelDebug, 1, fmt.Sprint(args...))
+}
+
+func (l Logger) log(level Level, skip int, msg string) {
+	if level <= l.level && l.writer != nil {
+		entry := Entry{
+			Timestamp: time.Now(),
+			Level:     level,
+			Caller:    errors.GetCaller(skip + 2).Info(),
+			Data:      l.data,
+			Message:   strings.ReplaceAll(msg, "\n", ";"),
+		}
+
+		// jsonEntry, err := json.Marshal(entry)
+		// if err != nil {
+		// 	l.writer.Write([]byte(fmt.Sprintf("failed to marshal entry: %v: %+v\n", err, entry)))
+		// }
+		// l.writer.Write(append(jsonEntry, []byte("\n")...))
+
+		source := fmt.Sprintf("%s(%d)", entry.Caller.File, entry.Caller.Line)
+		if len(source) > 25 {
+			source = source[len(source)-25:]
+		}
+		textEntry := fmt.Sprintf("%s %5.5s %-25.25s %s",
+			entry.Timestamp.Format("2006-01-02 15:04:05"),
+			entry.Level,
+			source,
+			entry.Message)
+		if len(entry.Data) > 0 {
+			jsonData, _ := json.Marshal(entry.Data)
+			textEntry += " " + string(jsonData)
+		}
+		l.writer.Write(append([]byte(textEntry), []byte("\n")...))
+	}
+}
+
+type Entry struct {
+	Timestamp time.Time              `json:"time"`
+	Level     Level                  `json:"level"`
+	Caller    errors.CallerInfo      `json:"caller"`
+	Data      map[string]interface{} `json:"data"`
+	Message   string                 `json:"message"`
+}
+
+// func sendRaygunError(fields map[string]interface{}, errToSend error) {
+// 	if os.Getenv("DEBUGGING") == "true" {
+// 		// Don't log raygun errors on debug
+// 		return
+// 	}
+
+// 	raygun, err := RaygunReporter()
+// 	if err != nil || raygun == nil {
+// 		logger.Errorf("Unable to create Raygun client: " + err.Error())
+// 		return
+// 	}
+
+// 	env := getEnvironment()
+// 	tags := []string{env}
+// 	//todo: raygun.Version(globals.BuildVersion)
+// 	//todo: tags = append(tags, globals.BuildVersion)
+
+// 	if logger.apiRequest != nil {
+// 		methodAndPath := logger.apiRequest.HTTPMethod + ": " + logger.apiRequest.Path
+// 		tags = append(tags, methodAndPath)
+// 		fields["body"] = logger.apiRequest.Body
+// 		fields["query"] = logger.apiRequest.QueryStringParameters
+// 		fields["identity"] = logger.apiRequest.RequestContext.Identity
+// 	}
+
+// 	raygun.Tags(tags)
+// 	if logger.currentRequestID != nil {
+// 		fields["request_id"] = logger.currentRequestID
+// 	}
+
+// 	fields["env"] = env
+// 	raygun.CustomData(fields)
+// 	raygun.Request(fakeHttpRequest())
+
+// 	if errToSend == nil {
+// 		errToSend = errors.New("")
+// 	}
+// 	err = raygun.SendError(errToSend)
+// 	if err != nil {
+// 		logger.Errorf("Failed to send raygun error: " + err.Error())
+// 	}
+// }
+
+// func fakeHttpRequest() *http.Request {
+// 	if logger.apiRequest == nil {
+// 		return nil
+// 	}
+
+// 	requestURL := url.URL{
+// 		Path: logger.apiRequest.Path,
+// 		Host: logger.apiRequest.Headers["Host"],
+// 	}
+// 	request := http.Request{
+// 		Method: logger.apiRequest.HTTPMethod,
+// 		URL:    &requestURL,
+// 		Header: logger.apiRequest.MultiValueHeaders,
+// 	}
+// 	return &request
+// }
+
+// func RaygunReporter() (*raygun4go.Client, error) {
+// 	key := "AwdpHhwOF1lTT6AppyEHA"
+
+// 	// TODO Raygun per environment
+// 	//env := getEnvironment()
+// 	//if env == "dev" {
+// 	//	key = "q98iTyE5CcF7raZsIiLA"
+// 	//} else if env == "stage" {
+// 	//	key = "TA54mzcv9cBWBLmfwIQMDg"
+// 	//} else if env == "prod" {
+// 	//	key = "BXdraqiPKBXImqP4siK1w"
+// 	//}
+
+// 	raygun, err := raygun4go.New("uAfrica V3", key)
+// 	if err != nil || raygun == nil {
+// 		logger.Errorf("Unable to create Raygun client:" + err.Error())
+// 		return nil, err
+// 	}
+
+// 	return raygun, nil
+// }
diff --git a/logger/logs_test.go b/logger/logs_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..f27ff0af7d74078320cfc6a5d13845e19aa5dc74
--- /dev/null
+++ b/logger/logs_test.go
@@ -0,0 +1,36 @@
+package logger_test
+
+import (
+	"os"
+	"testing"
+
+	"github.com/uafrica/go-utils/errors"
+	"github.com/uafrica/go-utils/logger"
+)
+
+func TestLogs(t *testing.T) {
+	//requestID := t.Name()
+	//event := events.APIGatewayProxyRequest{}
+	os.Setenv("DEBUGGING", "true")
+	//logger.InitLogs(&requestID, &event)
+
+	// formatter := log.TextFormatter{}
+	// log.SetFormatter(&formatter)
+
+	logger.LogMessageWithFields(map[string]interface{}{"a": 1, "b": 2}, "MyLogMessage1")
+	logger.LogMessage("MyLogMessage2=%d,%d,%d", 1, 2, 3)
+	logger.LogError(map[string]interface{}{"a": 4, "b": 5}, errors.Errorf("simple mistake"))
+	logger.LogErrorMessage("Error Message", errors.Errorf("another simple mistake"))
+	logger.LogWarningMessage("Warning about a=%s,%s,%s", "a", "b", "c")
+	logger.LogWarning(map[string]interface{}{"a": 4, "b": 5}, errors.Errorf("Cant believe it failed"))
+	logger.SQLDebugInfo("SELECT * from user")
+	//logger.LogRequestInfo(event)
+	//logs.LogSQSEvent(sqsEvent)
+
+	ctx := logger.GetContextLogger()
+	ctx.Debugf("Debugging %d!", 456)
+	ctx.Infof("Info %d", 789)
+
+	//logs.Errorf("Debugging %d!", 456)
+	//logs.Error("Info")
+}