Files
loginsrv/logging/logger.go
T
2017-06-20 16:23:02 +02:00

268 lines
6.1 KiB
Go

package logging
import (
"encoding/json"
"fmt"
"github.com/tarent/logrus"
"net/http"
"os"
"strings"
"time"
)
var Logger *logrus.Logger
// The of cookies which should not be logged
var AccessLogCookiesBlacklist = []string{}
var LifecycleEnvVars = []string{"BUILD_NUMBER", "BUILD_HASH", "BUILD_DATE"}
func init() {
Set("info", false)
}
// Set creates a new Logger with the matching specification
func Set(level string, textLogging bool) error {
l, err := logrus.ParseLevel(level)
if err != nil {
return err
}
logger := logrus.New()
if textLogging {
logger.Formatter = &logrus.TextFormatter{}
} else {
logger.Formatter = &LogstashFormatter{TimestampFormat: time.RFC3339Nano}
}
logger.Level = l
Logger = logger
return nil
}
// Access logs an access entry with call duration and status code
func Access(r *http.Request, start time.Time, statusCode int) {
e := access(r, start, statusCode, nil)
var msg string
if len(r.URL.RawQuery) == 0 {
msg = fmt.Sprintf("%v ->%v %v", statusCode, r.Method, r.URL.Path)
} else {
msg = fmt.Sprintf("%v ->%v %v?...", statusCode, r.Method, r.URL.Path)
}
if statusCode >= 200 && statusCode <= 399 {
e.Info(msg)
} else if statusCode >= 400 && statusCode <= 499 {
e.Warn(msg)
} else {
e.Error(msg)
}
}
// AccessError logs an error while accessing
func AccessError(r *http.Request, start time.Time, err error) {
e := access(r, start, 0, err)
e.Errorf("ERROR ->%v %v", r.Method, r.URL.Path)
}
func access(r *http.Request, start time.Time, statusCode int, err error) *logrus.Entry {
url := r.URL.Path
if r.URL.RawQuery != "" {
url += "?" + r.URL.RawQuery
}
fields := logrus.Fields{
"type": "access",
"@timestamp": start,
"remote_ip": getRemoteIp(r),
"host": r.Host,
"url": url,
"method": r.Method,
"proto": r.Proto,
"duration": time.Since(start).Nanoseconds() / 1000000,
"User_Agent": r.Header.Get("User-Agent"),
}
if statusCode != 0 {
fields["response_status"] = statusCode
}
if err != nil {
fields[logrus.ErrorKey] = err.Error()
}
setCorrelationIds(fields, r.Header)
cookies := map[string]string{}
for _, c := range r.Cookies() {
if !contains(AccessLogCookiesBlacklist, c.Name) {
cookies[c.Name] = c.Value
}
}
if len(cookies) > 0 {
fields["cookies"] = cookies
}
return Logger.WithFields(fields)
}
// Call logs the result of an outgoing call
func Call(r *http.Request, resp *http.Response, start time.Time, err error) {
url := r.URL.Path
if r.URL.RawQuery != "" {
url += "?" + r.URL.RawQuery
}
fields := logrus.Fields{
"type": "call",
"@timestamp": start,
"host": r.Host,
"url": url,
"full_url": r.URL.String(),
"method": r.Method,
"duration": time.Since(start).Nanoseconds() / 1000000,
}
setCorrelationIds(fields, r.Header)
if err != nil {
fields[logrus.ErrorKey] = err.Error()
Logger.WithFields(fields).Error(err)
return
}
if resp != nil {
fields["response_status"] = resp.StatusCode
fields["content_type"] = resp.Header.Get("Content-Type")
e := Logger.WithFields(fields)
msg := fmt.Sprintf("%v %v-> %v", resp.StatusCode, r.Method, r.URL.String())
if resp.StatusCode >= 200 && resp.StatusCode <= 399 {
e.Info(msg)
} else if resp.StatusCode >= 400 && resp.StatusCode <= 499 {
e.Warn(msg)
} else {
e.Error(msg)
}
return
}
Logger.WithFields(fields).Warn("call, but no response given")
}
// Cacheinfo logs the hit information a accessing a ressource
func Cacheinfo(url string, hit bool) {
var msg string
if hit {
msg = fmt.Sprintf("cache hit: %v", url)
} else {
msg = fmt.Sprintf("cache miss: %v", url)
}
Logger.WithFields(
logrus.Fields{
"type": "cacheinfo",
"url": url,
"hit": hit,
}).
Debug(msg)
}
// Return a log entry for application logs,
// prefilled with the correlation ids out of the supplied request.
func Application(h http.Header) *logrus.Entry {
fields := logrus.Fields{
"type": "application",
}
setCorrelationIds(fields, h)
return Logger.WithFields(fields)
}
// LifecycleStart logs the start of an application
// with the configuration struct or map as paramter.
func LifecycleStart(appName string, args interface{}) {
fields := logrus.Fields{}
jsonString, err := json.Marshal(args)
if err == nil {
err := json.Unmarshal(jsonString, &fields)
if err != nil {
fields["parse_error"] = err.Error()
}
}
fields["type"] = "lifecycle"
fields["event"] = "start"
for _, env := range LifecycleEnvVars {
if os.Getenv(env) != "" {
fields[strings.ToLower(env)] = os.Getenv(env)
}
}
Logger.WithFields(fields).Infof("starting application: %v", appName)
}
// LifecycleStop logs the stop of an application
func LifecycleStop(appName string, signal os.Signal, err error) {
fields := logrus.Fields{
"type": "lifecycle",
"event": "stop",
}
if signal != nil {
fields["signal"] = signal.String()
}
if os.Getenv("BUILD_NUMBER") != "" {
fields["build_number"] = os.Getenv("BUILD_NUMBER")
}
if err != nil {
Logger.WithFields(fields).
WithError(err).
Errorf("stopping application: %v (%v)", appName, err)
} else {
Logger.WithFields(fields).Infof("stopping application: %v (%v)", appName, signal)
}
}
// LifecycleStop logs the stop of an application
func ServerClosed(appName string) {
fields := logrus.Fields{
"type": "application",
"event": "stop",
}
if os.Getenv("BUILD_NUMBER") != "" {
fields["build_number"] = os.Getenv("BUILD_NUMBER")
}
Logger.WithFields(fields).Infof("http server was closed: %v", appName)
}
func getRemoteIp(r *http.Request) string {
if r.Header.Get("X-Cluster-Client-Ip") != "" {
return r.Header.Get("X-Cluster-Client-Ip")
}
if r.Header.Get("X-Real-Ip") != "" {
return r.Header.Get("X-Real-Ip")
}
return strings.Split(r.RemoteAddr, ":")[0]
}
func setCorrelationIds(fields logrus.Fields, h http.Header) {
correlationId := GetCorrelationId(h)
if correlationId != "" {
fields["correlation_id"] = correlationId
}
userCorrelationId := GetUserCorrelationId(h)
if userCorrelationId != "" {
fields["user_correlation_id"] = userCorrelationId
}
}
func contains(s []string, e string) bool {
for _, a := range s {
if a == e {
return true
}
}
return false
}