added poseidon with aws to k8s changes
This commit is contained in:
41
pkg/logging/context_hook.go
Normal file
41
pkg/logging/context_hook.go
Normal file
@ -0,0 +1,41 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"github.com/openHPI/poseidon/pkg/dto"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// ContextHook logs the values referenced by the of dto.LoggedContextKeys.
|
||||
// By default Logrus does not log the values stored in the passed context.
|
||||
type ContextHook struct{}
|
||||
|
||||
// Fire is triggered on new log entries.
|
||||
func (hook *ContextHook) Fire(entry *logrus.Entry) error {
|
||||
if entry.Context != nil {
|
||||
injectContextValuesIntoData(entry)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func injectContextValuesIntoData(entry *logrus.Entry) {
|
||||
for _, key := range dto.LoggedContextKeys {
|
||||
value := entry.Context.Value(key)
|
||||
_, valueExisting := entry.Data[string(key)]
|
||||
if !valueExisting && value != nil {
|
||||
entry.Data[string(key)] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Levels returns all levels this hook should be registered to.
|
||||
func (hook *ContextHook) Levels() []logrus.Level {
|
||||
return []logrus.Level{
|
||||
logrus.PanicLevel,
|
||||
logrus.FatalLevel,
|
||||
logrus.ErrorLevel,
|
||||
logrus.WarnLevel,
|
||||
logrus.InfoLevel,
|
||||
logrus.DebugLevel,
|
||||
logrus.TraceLevel,
|
||||
}
|
||||
}
|
109
pkg/logging/logging.go
Normal file
109
pkg/logging/logging.go
Normal file
@ -0,0 +1,109 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/openHPI/poseidon/pkg/dto"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const TimestampFormat = "2006-01-02T15:04:05.000000Z"
|
||||
|
||||
var log = &logrus.Logger{
|
||||
Out: os.Stderr,
|
||||
Formatter: &logrus.TextFormatter{
|
||||
TimestampFormat: TimestampFormat,
|
||||
DisableColors: true,
|
||||
FullTimestamp: true,
|
||||
},
|
||||
Hooks: make(logrus.LevelHooks),
|
||||
Level: logrus.InfoLevel,
|
||||
}
|
||||
|
||||
const GracefulSentryShutdown = 5 * time.Second
|
||||
|
||||
func InitializeLogging(logLevel string, formatter dto.Formatter) {
|
||||
level, err := logrus.ParseLevel(logLevel)
|
||||
if err != nil {
|
||||
log.WithError(err).Fatal("Error parsing loglevel")
|
||||
return
|
||||
}
|
||||
log.SetLevel(level)
|
||||
if formatter == dto.FormatterJSON {
|
||||
log.Formatter = &logrus.JSONFormatter{
|
||||
TimestampFormat: TimestampFormat,
|
||||
}
|
||||
}
|
||||
log.AddHook(&ContextHook{})
|
||||
log.AddHook(&SentryHook{})
|
||||
log.ExitFunc = func(i int) {
|
||||
sentry.Flush(GracefulSentryShutdown)
|
||||
os.Exit(i)
|
||||
}
|
||||
}
|
||||
|
||||
func GetLogger(pkg string) *logrus.Entry {
|
||||
return log.WithField("package", pkg)
|
||||
}
|
||||
|
||||
// ResponseWriter wraps the default http.ResponseWriter and catches the status code
|
||||
// that is written.
|
||||
type ResponseWriter struct {
|
||||
http.ResponseWriter
|
||||
StatusCode int
|
||||
}
|
||||
|
||||
func NewLoggingResponseWriter(w http.ResponseWriter) *ResponseWriter {
|
||||
return &ResponseWriter{w, http.StatusOK}
|
||||
}
|
||||
|
||||
func (writer *ResponseWriter) WriteHeader(code int) {
|
||||
writer.StatusCode = code
|
||||
writer.ResponseWriter.WriteHeader(code)
|
||||
}
|
||||
|
||||
func (writer *ResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
conn, rw, err := writer.ResponseWriter.(http.Hijacker).Hijack()
|
||||
if err != nil {
|
||||
return conn, nil, fmt.Errorf("hijacking connection failed: %w", err)
|
||||
}
|
||||
return conn, rw, nil
|
||||
}
|
||||
|
||||
// HTTPLoggingMiddleware returns a http.Handler that logs different information about every request.
|
||||
func HTTPLoggingMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
start := time.Now().UTC()
|
||||
path := RemoveNewlineSymbol(r.URL.Path)
|
||||
|
||||
lrw := NewLoggingResponseWriter(w)
|
||||
next.ServeHTTP(lrw, r)
|
||||
|
||||
latency := time.Now().UTC().Sub(start)
|
||||
logEntry := log.WithContext(r.Context()).WithFields(logrus.Fields{
|
||||
"code": lrw.StatusCode,
|
||||
"method": r.Method,
|
||||
"path": path,
|
||||
"duration": latency,
|
||||
"user_agent": RemoveNewlineSymbol(r.UserAgent()),
|
||||
})
|
||||
if r.UserAgent() == dto.UserAgentFiltered {
|
||||
logEntry.Trace()
|
||||
} else {
|
||||
logEntry.Debug()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// RemoveNewlineSymbol GOOD: remove newlines from user controlled input before logging.
|
||||
func RemoveNewlineSymbol(data string) string {
|
||||
data = strings.ReplaceAll(data, "\r", "")
|
||||
data = strings.ReplaceAll(data, "\n", "")
|
||||
return data
|
||||
}
|
67
pkg/logging/sentry_hook.go
Normal file
67
pkg/logging/sentry_hook.go
Normal file
@ -0,0 +1,67 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/openHPI/poseidon/pkg/dto"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// SentryHook is a simple adapter that converts logrus entries into Sentry events.
|
||||
// Consider replacing this with a more feature rich, additional dependency: https://github.com/evalphobia/logrus_sentry
|
||||
type SentryHook struct{}
|
||||
|
||||
var ErrorHubInvalid = errors.New("the hub is invalid")
|
||||
|
||||
// Fire is triggered on new log entries.
|
||||
func (hook *SentryHook) Fire(entry *logrus.Entry) error {
|
||||
var hub *sentry.Hub
|
||||
if entry.Context != nil {
|
||||
hub = sentry.GetHubFromContext(entry.Context)
|
||||
injectContextValuesIntoData(entry)
|
||||
}
|
||||
if hub == nil {
|
||||
hub = sentry.CurrentHub()
|
||||
}
|
||||
client, scope := hub.Client(), hub.Scope()
|
||||
if client == nil || scope == nil {
|
||||
return ErrorHubInvalid
|
||||
}
|
||||
|
||||
scope.SetContext("Poseidon Details", entry.Data)
|
||||
if runnerID, ok := entry.Data[dto.KeyRunnerID].(string); ok {
|
||||
scope.SetTag(dto.KeyRunnerID, runnerID)
|
||||
}
|
||||
if environmentID, ok := entry.Data[dto.KeyEnvironmentID].(string); ok {
|
||||
scope.SetTag(dto.KeyEnvironmentID, environmentID)
|
||||
}
|
||||
|
||||
event := client.EventFromMessage(entry.Message, sentry.Level(entry.Level.String()))
|
||||
event.Timestamp = entry.Time
|
||||
if data, ok := entry.Data["error"]; ok {
|
||||
err, ok := data.(error)
|
||||
if ok {
|
||||
entry.Data["error"] = err.Error()
|
||||
}
|
||||
}
|
||||
hub.CaptureEvent(event)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Levels returns all levels this hook should be registered to.
|
||||
func (hook *SentryHook) Levels() []logrus.Level {
|
||||
return []logrus.Level{
|
||||
logrus.PanicLevel,
|
||||
logrus.FatalLevel,
|
||||
logrus.ErrorLevel,
|
||||
logrus.WarnLevel,
|
||||
}
|
||||
}
|
||||
|
||||
func StartSpan(op, description string, ctx context.Context, callback func(context.Context)) {
|
||||
span := sentry.StartSpan(ctx, op)
|
||||
span.Description = description
|
||||
defer span.Finish()
|
||||
callback(span.Context())
|
||||
}
|
Reference in New Issue
Block a user