diff --git a/go.mod b/go.mod
index 36b138158c4344ab392ea8faf149aa561c54fa32_Z28ubW9k..e3263107bd445741ceea3577706c98b6bcb60f01_Z28ubW9k 100644
--- a/go.mod
+++ b/go.mod
@@ -4,6 +4,8 @@
 
 require (
 	github.com/getsentry/sentry-go v0.7.0
+	github.com/json-iterator/go v1.1.6
+	github.com/orus-io/go-flags v1.4.0
 	github.com/rs/zerolog v1.19.0
 	github.com/stretchr/testify v1.6.1
 	golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
diff --git a/go.sum b/go.sum
index 36b138158c4344ab392ea8faf149aa561c54fa32_Z28uc3Vt..e3263107bd445741ceea3577706c98b6bcb60f01_Z28uc3Vt 100644
--- a/go.sum
+++ b/go.sum
@@ -55,6 +55,7 @@
 github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0=
 github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI=
 github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw=
+github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
 github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
@@ -84,4 +85,5 @@
 github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
 github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -87,4 +89,5 @@
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
 github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM=
@@ -93,6 +96,8 @@
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
+github.com/orus-io/go-flags v1.4.0 h1:eDbrM1iRPkT68HUJibvtli/xhXSst5FOj7EGTEOeeCA=
+github.com/orus-io/go-flags v1.4.0/go.mod h1:DZNFUSWKy2JbxIQeDVY6onal2VQbdL9ug5ODZfawylw=
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
 github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
diff --git a/logging.go b/logging.go
index 36b138158c4344ab392ea8faf149aa561c54fa32_bG9nZ2luZy5nbw==..e3263107bd445741ceea3577706c98b6bcb60f01_bG9nZ2luZy5nbw== 100644
--- a/logging.go
+++ b/logging.go
@@ -39,6 +39,12 @@
 	*o.log = o.log.Output(out)
 }
 
+// AddLogWrapper adds a log wrapper to the stack
+func (o *LoggingOptions) AddLogWrapper(wrapper func(io.Writer) io.Writer) {
+	o.logWrappers = append(o.logWrappers, wrapper)
+	o.resetOutput()
+}
+
 // SetMinLoggingLevel makes sure the logging level is not under a given value
 func (o *LoggingOptions) SetMinLoggingLevel(level zerolog.Level) {
 	if level < o.log.GetLevel() {
diff --git a/sentry.go b/sentry.go
new file mode 100644
index 0000000000000000000000000000000000000000..e3263107bd445741ceea3577706c98b6bcb60f01_c2VudHJ5Lmdv
--- /dev/null
+++ b/sentry.go
@@ -0,0 +1,147 @@
+package orusapi
+
+import (
+	"bytes"
+	"io"
+	"time"
+
+	"github.com/getsentry/sentry-go"
+	jsoniter "github.com/json-iterator/go"
+	"github.com/rs/zerolog/log"
+)
+
+// SentryOptions ...
+type SentryOptions struct {
+	SentryDSN func(string) `long:"dsn" env:"DSN" ini-name:"dsn" description:"Sentry DSN"`
+
+	client *sentry.Client
+	hub    *sentry.Hub
+}
+
+// GetClient returns the sentry client
+func (o SentryOptions) GetClient() *sentry.Client {
+	return o.client
+}
+
+// GetHub returns the sentry hub
+func (o SentryOptions) GetHub() *sentry.Hub {
+	return o.hub
+}
+
+// SentryLogger ...
+type SentryLogger struct {
+	hub  *sentry.Hub
+	next io.Writer
+}
+
+var (
+	levelMarker       = []byte(`"level":"`)
+	levelMarkerLen    = len(levelMarker)
+	levelsFirstLetter = []byte(`wefp`)
+)
+
+func buildEventIfLevelGtWarn(p []byte) *sentry.Event {
+	if i := bytes.Index(p, levelMarker); i != -1 {
+		first := p[i+levelMarkerLen]
+		if bytes.IndexByte(levelsFirstLetter, first) != -1 {
+			return buildEvent(p)
+		}
+	}
+	return nil
+}
+
+func buildEvent(p []byte) *sentry.Event {
+	iter := jsoniter.ConfigFastest.BorrowIterator(p)
+	defer jsoniter.ConfigFastest.ReturnIterator(iter)
+
+	if iter.WhatIsNext() != jsoniter.ObjectValue {
+		return nil
+	}
+	event := sentry.NewEvent()
+
+	if !iter.ReadObjectCB(func(iter *jsoniter.Iterator, field string) bool {
+		switch field {
+		case "message":
+			event.Message = iter.ReadString()
+		case "error":
+			errMsg := iter.ReadString()
+			if event.Message == "" {
+				event.Message = errMsg
+			}
+			event.Extra["error"] = errMsg
+		case "level":
+			level := iter.ReadString()
+			if level == "warn" {
+				event.Level = sentry.LevelWarning
+			} else {
+				event.Level = sentry.Level(level)
+			}
+		case "request":
+			iter.ReadVal(&event.Request)
+		case "user":
+			if !iter.ReadObjectCB(func(iter *jsoniter.Iterator, field string) bool {
+				switch field {
+				case "id":
+					event.User.ID = iter.ReadString()
+				default:
+					event.Extra["user."+field] = iter.Read()
+				}
+				return true
+			}) {
+				return false
+			}
+		case "exception":
+			var e sentry.Exception
+			iter.ReadVal(&e)
+			event.Exception = []sentry.Exception{e}
+		default:
+			event.Extra[field] = iter.Read()
+		}
+		return true
+	}) {
+		return nil
+	}
+	if op, ok := event.Extra["api-operation-id"]; ok {
+		tag, _ := event.Extra["api-tag"].(string)
+		event.Message = tag + "/" + op.(string) + ": " + event.Message
+	} else if event.Request != nil && event.Request.Method != "" {
+		event.Message = event.Request.Method + " " + event.Request.URL + ": " + event.Message
+	}
+	return event
+}
+
+// Write ...
+func (l SentryLogger) Write(p []byte) (n int, err error) {
+	if event := buildEventIfLevelGtWarn(p); event != nil {
+		l.hub.CaptureEvent(event)
+	}
+	return l.next.Write(p)
+}
+
+// Setup ...
+func (o *SentryOptions) Setup(loggingOptions *LoggingOptions, environment *string) {
+	o.SentryDSN = func(dsn string) {
+		client, err := sentry.NewClient(sentry.ClientOptions{
+			Environment: *environment,
+			Dsn:         dsn,
+		})
+		if err != nil {
+			log.Err(err).Msg("Could not initialize sentry")
+			return
+		}
+		o.client = client
+		o.hub = sentry.NewHub(client, sentry.NewScope())
+		loggingOptions.AddLogWrapper(func(next io.Writer) io.Writer {
+			return SentryLogger{o.hub, next}
+		})
+	}
+}
+
+// Shutdown ...
+func (o SentryOptions) Shutdown() {
+	if o.hub != nil {
+		o.hub.Flush(time.Second)
+		o.hub = nil
+		o.client = nil
+	}
+}