/* * Copyright (c) 2016, Psiphon Inc. * All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ package server import ( "io" "log/syslog" "os" "github.com/Psiphon-Inc/logrus" logrus_syslog "github.com/Psiphon-Inc/logrus/hooks/syslog" "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon" ) // ContextLogger adds context logging functionality to the // underlying logging packages. type ContextLogger struct { *logrus.Logger } // LogFields is an alias for the field struct in the // underlying logging package. type LogFields logrus.Fields // WithContext adds a "context" field containing the caller's // function name and source file line number. Use this function // when the log has no fields. func (logger *ContextLogger) WithContext() *logrus.Entry { return log.WithFields( logrus.Fields{ "context": psiphon.GetParentContext(), }) } // WithContextFields adds a "context" field containing the caller's // function name and source file line number. Use this function // when the log has fields. Note that any existing "context" field // will be renamed to "field.context". func (logger *ContextLogger) WithContextFields(fields LogFields) *logrus.Entry { _, ok := fields["context"] if ok { fields["fields.context"] = fields["context"] } fields["context"] = psiphon.GetParentContext() return log.WithFields(logrus.Fields(fields)) } // NewLogWriter returns an io.PipeWriter that can be used to write // to the global logger. Caller must Close() the writer. func NewLogWriter() *io.PipeWriter { return log.Writer() } var log *ContextLogger // InitLogging configures a logger according to the specified // config params. If not called, the default logger set by the // package init() is used. // When configured, InitLogging also establishes a local syslog // logger specifically for fail2ban integration. // Concurrenty note: should only be called from the main // goroutine. func InitLogging(config *Config) error { level, err := logrus.ParseLevel(config.LogLevel) if err != nil { return psiphon.ContextError(err) } hooks := make(logrus.LevelHooks) var syslogHook *logrus_syslog.SyslogHook if config.SyslogFacility != "" { syslogHook, err = logrus_syslog.NewSyslogHook( "", "", getSyslogPriority(config), config.SyslogTag) if err != nil { return psiphon.ContextError(err) } hooks.Add(syslogHook) } log = &ContextLogger{ &logrus.Logger{ Out: os.Stderr, Formatter: new(logrus.JSONFormatter), Hooks: hooks, Level: level, }, } return nil } // getSyslogPriority determines golang's syslog "priority" value // based on the provided config. func getSyslogPriority(config *Config) syslog.Priority { // TODO: assumes log.Level filter applies? severity := syslog.LOG_DEBUG facilityCodes := map[string]syslog.Priority{ "kern": syslog.LOG_KERN, "user": syslog.LOG_USER, "mail": syslog.LOG_MAIL, "daemon": syslog.LOG_DAEMON, "auth": syslog.LOG_AUTH, "syslog": syslog.LOG_SYSLOG, "lpr": syslog.LOG_LPR, "news": syslog.LOG_NEWS, "uucp": syslog.LOG_UUCP, "cron": syslog.LOG_CRON, "authpriv": syslog.LOG_AUTHPRIV, "ftp": syslog.LOG_FTP, "local0": syslog.LOG_LOCAL0, "local1": syslog.LOG_LOCAL1, "local2": syslog.LOG_LOCAL2, "local3": syslog.LOG_LOCAL3, "local4": syslog.LOG_LOCAL4, "local5": syslog.LOG_LOCAL5, "local6": syslog.LOG_LOCAL6, "local7": syslog.LOG_LOCAL7, } facility, ok := facilityCodes[config.SyslogFacility] if !ok { facility = syslog.LOG_USER } return severity | facility } func init() { log = &ContextLogger{ &logrus.Logger{ Out: os.Stderr, Formatter: new(logrus.JSONFormatter), Hooks: make(logrus.LevelHooks), Level: logrus.DebugLevel, }, } }