git.iko.soy/typeable/oauth2-proxy
on master help sign in
Allow Logging to stdout with separate Error Log Channel (#718)

* Add dedicated error logging writer * Document new errors to stdout flag * Update changelog * Thread-safe the log buffer * Address feedback * Remove duplication by adding log level * Clean up error formatting * Apply suggestions from code review Co-authored-by: Joel Speed <Joel.speed@hotmail.co.uk>

commitd69fd6af22751974364e35cc697f9eaaaef160d3 authorPhil Taprogge <signup@lightweaver.info> dateMon 10 Aug 2020, 11:44 (6 years ago) parent33e04cc
27 files changed+144−73
CHANGELOG.md+1 docs/configuration/configuration.md+1 http.go+1−1 main.go+3−3 oauthproxy.go+13−13 pkg/apis/options/legacy_options.go+1−1 pkg/apis/options/logging.go+3 pkg/authentication/basic/htpasswd.go+1−1 pkg/cookies/cookies.go+2−2 pkg/logger/logger.go+87−22 pkg/middleware/basic_session.go+1−1 pkg/middleware/jwt_session.go+1−1 pkg/middleware/stored_session.go+2−2 pkg/sessions/cookie/session_store.go+1−1 pkg/sessions/redis/redis_store.go+2−2 pkg/upstream/proxy.go+1−1 pkg/validation/logging.go+2−1 pkg/validation/options.go+1−1 providers/azure.go+2−2 providers/bitbucket.go+5−5 providers/google.go+3−3 providers/internal_util.go+5−5 providers/keycloak.go+1−1 providers/provider_data.go+1−1 validator.go+1−1 watcher.go+1−1 watcher_unsupported.go+1−1
CHANGELOG.md+1
@@ -11,6 +11,7 @@
1111
1212## Changes since v6.0.0
1313
14+- [#718](https://github.com/oauth2-proxy/oauth2-proxy/pull/718) Allow Logging to stdout with separate Error Log Channel
1415- [#690](https://github.com/oauth2-proxy/oauth2-proxy/pull/690) Address GoSec security findings & remediate (@NickMeves)
1516- [#689](https://github.com/oauth2-proxy/oauth2-proxy/pull/689) Fix finicky logging_handler_test from time drift (@NickMeves)
1617- [#699](https://github.com/oauth2-proxy/oauth2-proxy/pull/699) Align persistence ginkgo tests with conventions (@NickMeves)
docs/configuration/configuration.md+1
@@ -45,6 +45,7 @@ An example [oauth2-proxy.cfg]({{ site.gitweb }}/contrib/oauth2-proxy.cfg.example
4545| `--custom-templates-dir` | string | path to custom html templates | |
4646| `--display-htpasswd-form` | bool | display username / password login form if an htpasswd file is provided | true |
4747| `--email-domain` | string \| list | authenticate emails with the specified domain (may be given multiple times). Use `*` to authenticate any email | |
48+| `--errors-to-info-log` | bool | redirects error-level logging to default log channel instead of stderr | |
4849| `--extra-jwt-issuers` | string | if `--skip-jwt-bearer-tokens` is set, a list of extra JWT `issuer=audience` pairs (where the issuer URL has a `.well-known/openid-configuration` or a `.well-known/jwks.json`) | |
4950| `--exclude-logging-paths` | string | comma separated list of paths to exclude from logging, e.g. `"/ping,/path2"` |`""` (no paths excluded) |
5051| `--flush-interval` | duration | period between flushing response buffers when streaming responses | `"1s"` |
http.go+1−1
@@ -106,7 +106,7 @@ func (s *Server) serve(listener net.Listener) {
106106
107107 err := srv.Serve(listener)
108108 if err != nil && !errors.Is(err, http.ErrServerClosed) {
109− logger.Printf("ERROR: http.Serve() - %s", err)
109+ logger.Errorf("ERROR: http.Serve() - %s", err)
110110 }
111111 <-idleConnsClosed
112112}
main.go+3−3
@@ -38,13 +38,13 @@ func main() {
3838 legacyOpts := options.NewLegacyOptions()
3939 err = options.Load(*config, flagSet, legacyOpts)
4040 if err != nil {
41− logger.Printf("ERROR: Failed to load config: %v", err)
41+ logger.Errorf("ERROR: Failed to load config: %v", err)
4242 os.Exit(1)
4343 }
4444
4545 opts, err := legacyOpts.ToOptions()
4646 if err != nil {
47− logger.Printf("ERROR: Failed to convert config: %v", err)
47+ logger.Errorf("ERROR: Failed to convert config: %v", err)
4848 os.Exit(1)
4949 }
5050
@@ -57,7 +57,7 @@ func main() {
5757 validator := NewValidator(opts.EmailDomains, opts.AuthenticatedEmailsFile)
5858 oauthproxy, err := NewOAuthProxy(opts, validator)
5959 if err != nil {
60− logger.Printf("ERROR: Failed to initialise OAuth2 Proxy: %v", err)
60+ logger.Errorf("ERROR: Failed to initialise OAuth2 Proxy: %v", err)
6161 os.Exit(1)
6262 }
6363
oauthproxy.go+13−13
@@ -337,7 +337,7 @@ func (p *OAuthProxy) makeCookie(req *http.Request, name string, value string, ex
337337 domain = h
338338 }
339339 if !strings.HasSuffix(domain, cookieDomain) {
340− logger.Printf("Warning: request host is %q but using configured cookie domain of %q", domain, cookieDomain)
340+ logger.Errorf("Warning: request host is %q but using configured cookie domain of %q", domain, cookieDomain)
341341 }
342342 }
343343
@@ -423,7 +423,7 @@ func (p *OAuthProxy) SignInPage(rw http.ResponseWriter, req *http.Request, code
423423
424424 redirectURL, err := p.GetRedirect(req)
425425 if err != nil {
426− logger.Printf("Error obtaining redirect: %v", err)
426+ logger.Errorf("Error obtaining redirect: %v", err)
427427 p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error())
428428 return
429429 }
@@ -623,7 +623,7 @@ func (p *OAuthProxy) IsTrustedIP(req *http.Request) bool {
623623
624624 remoteAddr, err := ip.GetClientIP(p.realClientIPParser, req)
625625 if err != nil {
626− logger.Printf("Error obtaining real IP for trusted IP list: %v", err)
626+ logger.Errorf("Error obtaining real IP for trusted IP list: %v", err)
627627 // Possibly spoofed X-Real-IP header
628628 return false
629629 }
@@ -666,7 +666,7 @@ func (p *OAuthProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
666666func (p *OAuthProxy) SignIn(rw http.ResponseWriter, req *http.Request) {
667667 redirect, err := p.GetRedirect(req)
668668 if err != nil {
669− logger.Printf("Error obtaining redirect: %v", err)
669+ logger.Errorf("Error obtaining redirect: %v", err)
670670 p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error())
671671 return
672672 }
@@ -718,13 +718,13 @@ func (p *OAuthProxy) UserInfo(rw http.ResponseWriter, req *http.Request) {
718718func (p *OAuthProxy) SignOut(rw http.ResponseWriter, req *http.Request) {
719719 redirect, err := p.GetRedirect(req)
720720 if err != nil {
721− logger.Printf("Error obtaining redirect: %v", err)
721+ logger.Errorf("Error obtaining redirect: %v", err)
722722 p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error())
723723 return
724724 }
725725 err = p.ClearSessionCookie(rw, req)
726726 if err != nil {
727− logger.Printf("Error clearing session cookie: %v", err)
727+ logger.Errorf("Error clearing session cookie: %v", err)
728728 p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error())
729729 return
730730 }
@@ -736,14 +736,14 @@ func (p *OAuthProxy) OAuthStart(rw http.ResponseWriter, req *http.Request) {
736736 prepareNoCache(rw)
737737 nonce, err := encryption.Nonce()
738738 if err != nil {
739− logger.Printf("Error obtaining nonce: %v", err)
739+ logger.Errorf("Error obtaining nonce: %v", err)
740740 p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error())
741741 return
742742 }
743743 p.SetCSRFCookie(rw, req, nonce)
744744 redirect, err := p.GetRedirect(req)
745745 if err != nil {
746− logger.Printf("Error obtaining redirect: %v", err)
746+ logger.Errorf("Error obtaining redirect: %v", err)
747747 p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error())
748748 return
749749 }
@@ -759,27 +759,27 @@ func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) {
759759 // finish the oauth cycle
760760 err := req.ParseForm()
761761 if err != nil {
762− logger.Printf("Error while parsing OAuth2 callback: %v", err)
762+ logger.Errorf("Error while parsing OAuth2 callback: %v", err)
763763 p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error())
764764 return
765765 }
766766 errorString := req.Form.Get("error")
767767 if errorString != "" {
768− logger.Printf("Error while parsing OAuth2 callback: %s", errorString)
768+ logger.Errorf("Error while parsing OAuth2 callback: %s", errorString)
769769 p.ErrorPage(rw, http.StatusForbidden, "Permission Denied", errorString)
770770 return
771771 }
772772
773773 session, err := p.redeemCode(req.Context(), req.Host, req.Form.Get("code"))
774774 if err != nil {
775− logger.Printf("Error redeeming code during OAuth2 callback: %v", err)
775+ logger.Errorf("Error redeeming code during OAuth2 callback: %v", err)
776776 p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", "Internal Error")
777777 return
778778 }
779779
780780 s := strings.SplitN(req.Form.Get("state"), ":", 2)
781781 if len(s) != 2 {
782− logger.Printf("Error while parsing OAuth2 state: invalid length")
782+ logger.Error("Error while parsing OAuth2 state: invalid length")
783783 p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", "Invalid State")
784784 return
785785 }
@@ -865,7 +865,7 @@ func (p *OAuthProxy) Proxy(rw http.ResponseWriter, req *http.Request) {
865865
866866 default:
867867 // unknown error
868− logger.Printf("Unexpected internal error: %s", err)
868+ logger.Errorf("Unexpected internal error: %v", err)
869869 p.ErrorPage(rw, http.StatusInternalServerError,
870870 "Internal Error", "Internal Error")
871871 }
pkg/apis/options/legacy_options.go+1−1
@@ -91,7 +91,7 @@ func (l *LegacyUpstreams) convert() (Upstreams, error) {
9191 case "static":
9292 responseCode, err := strconv.Atoi(u.Host)
9393 if err != nil {
94− logger.Printf("unable to convert %q to int, use default \"200\"", u.Host)
94+ logger.Errorf("unable to convert %q to int, use default \"200\"", u.Host)
9595 responseCode = 200
9696 }
9797 upstream.Static = true
pkg/apis/options/logging.go+3
@@ -13,6 +13,7 @@ type Logging struct {
1313 RequestFormat string `flag:"request-logging-format" cfg:"request_logging_format"`
1414 StandardEnabled bool `flag:"standard-logging" cfg:"standard_logging"`
1515 StandardFormat string `flag:"standard-logging-format" cfg:"standard_logging_format"`
16+ ErrToInfo bool `flag:"errors-to-info-log" cfg:"errors_to_info_log"`
1617 ExcludePaths []string `flag:"exclude-logging-path" cfg:"exclude_logging_paths"`
1718 LocalTime bool `flag:"logging-local-time" cfg:"logging_local_time"`
1819 SilencePing bool `flag:"silence-ping-logging" cfg:"silence_ping_logging"`
@@ -37,6 +38,7 @@ func loggingFlagSet() *pflag.FlagSet {
3738 flagSet.String("standard-logging-format", logger.DefaultStandardLoggingFormat, "Template for standard log lines")
3839 flagSet.Bool("request-logging", true, "Log HTTP requests")
3940 flagSet.String("request-logging-format", logger.DefaultRequestLoggingFormat, "Template for HTTP request log lines")
41+ flagSet.Bool("errors-to-info-log", false, "Log errors to the standard logging channel instead of stderr")
4042
4143 flagSet.StringSlice("exclude-logging-path", []string{}, "Exclude logging requests to paths (eg: '/path1,/path2,/path3')")
4244 flagSet.Bool("logging-local-time", true, "If the time in log files and backup filenames are local or UTC time")
@@ -63,6 +65,7 @@ func loggingDefaults() Logging {
6365 RequestFormat: logger.DefaultRequestLoggingFormat,
6466 StandardEnabled: true,
6567 StandardFormat: logger.DefaultStandardLoggingFormat,
68+ ErrToInfo: false,
6669 File: LogFileOptions{
6770 Filename: "",
6871 MaxSize: 100,
pkg/authentication/basic/htpasswd.go+1−1
@@ -78,7 +78,7 @@ func createHtpasswdMap(records [][]string) (*htpasswdMap, error) {
7878
7979 // Password is neither sha1 or bcrypt
8080 // TODO(JoelSpeed): In the next breaking release, make this return an error.
81− logger.Printf("Invalid htpasswd entry for %s. Must be a SHA or bcrypt entry.", user)
81+ logger.Errorf("Invalid htpasswd entry for %s. Must be a SHA or bcrypt entry.", user)
8282 }
8383 return h, nil
8484}
pkg/cookies/cookies.go+2−2
@@ -20,7 +20,7 @@ func MakeCookie(req *http.Request, name string, value string, path string, domai
2020 host = h
2121 }
2222 if !strings.HasSuffix(host, domain) {
23− logger.Printf("Warning: request host is %q but using configured cookie domain of %q", host, domain)
23+ logger.Errorf("Warning: request host is %q but using configured cookie domain of %q", host, domain)
2424 }
2525 }
2626
@@ -45,7 +45,7 @@ func MakeCookieFromOptions(req *http.Request, name string, value string, cookieO
4545 return MakeCookie(req, name, value, cookieOpts.Path, domain, cookieOpts.HTTPOnly, cookieOpts.Secure, expiration, now, ParseSameSite(cookieOpts.SameSite))
4646 }
4747 // If nothing matches, create the cookie with the shortest domain
48− logger.Printf("Warning: request host %q did not match any of the specific cookie domains of %q", GetRequestHost(req), strings.Join(cookieOpts.Domains, ","))
48+ logger.Errorf("Warning: request host %q did not match any of the specific cookie domains of %q", GetRequestHost(req), strings.Join(cookieOpts.Domains, ","))
4949 defaultDomain := ""
5050 if len(cookieOpts.Domains) > 0 {
5151 defaultDomain = cookieOpts.Domains[len(cookieOpts.Domains)-1]
pkg/logger/logger.go+87−22
@@ -1,6 +1,7 @@
11package logger
22
33import (
4+ "bytes"
45 "fmt"
56 "io"
67 "net/http"
@@ -15,6 +16,9 @@ import (
1516// AuthStatus defines the different types of auth logging that occur
1617type AuthStatus string
1718
19+// Level indicates the log level for log messages
20+type Level int
21+
1822const (
1923 // DefaultStandardLoggingFormat defines the default standard log format
2024 DefaultStandardLoggingFormat = "[{{.Timestamp}}] [{{.File}}] {{.Message}}"
@@ -38,6 +42,11 @@ const (
3842 LUTC
3943 // LstdFlags flag for initial values for the logger
4044 LstdFlags = Lshortfile
45+
46+ // DEFAULT is the default log level (effectively INFO)
47+ DEFAULT Level = iota
48+ // ERROR is for error-level logging
49+ ERROR
4150)
4251
4352// These are the containers for all values that are available as variables in the logging formats.
@@ -87,6 +96,7 @@ type Logger struct {
8796 mu sync.Mutex
8897 flag int
8998 writer io.Writer
99+ errWriter io.Writer
90100 stdEnabled bool
91101 authEnabled bool
92102 reqEnabled bool
@@ -100,7 +110,8 @@ type Logger struct {
100110// New creates a new Standarderr Logger.
101111func New(flag int) *Logger {
102112 return &Logger{
103− writer: os.Stderr,
113+ writer: os.Stdout,
114+ errWriter: os.Stderr,
104115 flag: flag,
105116 stdEnabled: true,
106117 authEnabled: true,
@@ -115,13 +126,7 @@ func New(flag int) *Logger {
115126
116127var std = New(LstdFlags)
117128
118−// Output a standard log template with a simple message.
119−// Write a final newline at the end of every message.
120−func (l *Logger) Output(calldepth int, message string) {
121− if !l.stdEnabled {
122− return
123− }
124−
129+func (l *Logger) formatLogMessage(calldepth int, message string) []byte {
125130 now := time.Now()
126131 file := "???:0"
127132
@@ -129,10 +134,8 @@ func (l *Logger) Output(calldepth int, message string) {
129134 file = l.GetFileLineString(calldepth + 1)
130135 }
131136
132− l.mu.Lock()
133− defer l.mu.Unlock()
134−
135− err := l.stdLogTemplate.Execute(l.writer, stdLogMessageData{
137+ var logBuff = new(bytes.Buffer)
138+ err := l.stdLogTemplate.Execute(logBuff, stdLogMessageData{
136139 Timestamp: FormatTimestamp(now),
137140 File: file,
138141 Message: message,
@@ -145,6 +148,26 @@ func (l *Logger) Output(calldepth int, message string) {
145148 if err != nil {
146149 panic(err)
147150 }
151+ logBuff.Write([]byte("\n"))
152+
153+ return logBuff.Bytes()
154+}
155+
156+// Output a standard log template with a simple message to default output channel.
157+// Write a final newline at the end of every message.
158+func (l *Logger) Output(lvl Level, calldepth int, message string) {
159+ l.mu.Lock()
160+ defer l.mu.Unlock()
161+ if !l.stdEnabled {
162+ return
163+ }
164+ msg := l.formatLogMessage(calldepth, message)
165+ switch lvl {
166+ case ERROR:
167+ l.errWriter.Write(msg)
168+ default:
169+ l.writer.Write(msg)
170+ }
148171}
149172
150173// PrintAuthf writes auth info to the logger. Requires an http.Request to
@@ -302,6 +325,17 @@ func (l *Logger) SetStandardEnabled(e bool) {
302325 l.stdEnabled = e
303326}
304327
328+// SetErrToInfo enables or disables error logging to error writer instead of the default.
329+func (l *Logger) SetErrToInfo(e bool) {
330+ l.mu.Lock()
331+ defer l.mu.Unlock()
332+ if e {
333+ l.errWriter = l.writer
334+ } else {
335+ l.errWriter = os.Stderr
336+ }
337+}
338+
305339// SetAuthEnabled enables or disables auth logging.
306340func (l *Logger) SetAuthEnabled(e bool) {
307341 l.mu.Lock()
@@ -371,19 +405,32 @@ func SetFlags(flag int) {
371405 std.SetFlags(flag)
372406}
373407
374−// SetOutput sets the output destination for the standard logger.
408+// SetOutput sets the output destination for the standard logger's default channel.
375409func SetOutput(w io.Writer) {
376410 std.mu.Lock()
377411 defer std.mu.Unlock()
378412 std.writer = w
379413}
380414
415+// SetErrOutput sets the output destination for the standard logger's error channel.
416+func SetErrOutput(w io.Writer) {
417+ std.mu.Lock()
418+ defer std.mu.Unlock()
419+ std.errWriter = w
420+}
421+
381422// SetStandardEnabled enables or disables standard logging for the
382423// standard logger.
383424func SetStandardEnabled(e bool) {
384425 std.SetStandardEnabled(e)
385426}
386427
428+// SetErrToInfo enables or disables error logging to output writer instead of
429+// error writer.
430+func SetErrToInfo(e bool) {
431+ std.SetErrToInfo(e)
432+}
433+
387434// SetAuthEnabled enables or disables auth logging for the standard
388435// logger.
389436func SetAuthEnabled(e bool) {
@@ -428,57 +475,75 @@ func SetReqTemplate(t string) {
428475// Print calls Output to print to the standard logger.
429476// Arguments are handled in the manner of fmt.Print.
430477func Print(v ...interface{}) {
431− std.Output(2, fmt.Sprint(v...))
478+ std.Output(DEFAULT, 2, fmt.Sprint(v...))
432479}
433480
434481// Printf calls Output to print to the standard logger.
435482// Arguments are handled in the manner of fmt.Printf.
436483func Printf(format string, v ...interface{}) {
437− std.Output(2, fmt.Sprintf(format, v...))
484+ std.Output(DEFAULT, 2, fmt.Sprintf(format, v...))
438485}
439486
440487// Println calls Output to print to the standard logger.
441488// Arguments are handled in the manner of fmt.Println.
442489func Println(v ...interface{}) {
443− std.Output(2, fmt.Sprintln(v...))
490+ std.Output(DEFAULT, 2, fmt.Sprintln(v...))
491+}
492+
493+// Error calls OutputErr to print to the standard logger's error channel.
494+// Arguments are handled in the manner of fmt.Print.
495+func Error(v ...interface{}) {
496+ std.Output(ERROR, 2, fmt.Sprint(v...))
497+}
498+
499+// Errorf calls OutputErr to print to the standard logger's error channel.
500+// Arguments are handled in the manner of fmt.Printf.
501+func Errorf(format string, v ...interface{}) {
502+ std.Output(ERROR, 2, fmt.Sprintf(format, v...))
503+}
504+
505+// Errorln calls OutputErr to print to the standard logger's error channel.
506+// Arguments are handled in the manner of fmt.Println.
507+func Errorln(v ...interface{}) {
508+ std.Output(ERROR, 2, fmt.Sprintln(v...))
444509}
445510
446511// Fatal is equivalent to Print() followed by a call to os.Exit(1).
447512func Fatal(v ...interface{}) {
448− std.Output(2, fmt.Sprint(v...))
513+ std.Output(ERROR, 2, fmt.Sprint(v...))
449514 os.Exit(1)
450515}
451516
452517// Fatalf is equivalent to Printf() followed by a call to os.Exit(1).
453518func Fatalf(format string, v ...interface{}) {
454− std.Output(2, fmt.Sprintf(format, v...))
519+ std.Output(ERROR, 2, fmt.Sprintf(format, v...))
455520 os.Exit(1)
456521}
457522
458523// Fatalln is equivalent to Println() followed by a call to os.Exit(1).
459524func Fatalln(v ...interface{}) {
460− std.Output(2, fmt.Sprintln(v...))
525+ std.Output(ERROR, 2, fmt.Sprintln(v...))
461526 os.Exit(1)
462527}
463528
464529// Panic is equivalent to Print() followed by a call to panic().
465530func Panic(v ...interface{}) {
466531 s := fmt.Sprint(v...)
467− std.Output(2, s)
532+ std.Output(ERROR, 2, s)
468533 panic(s)
469534}
470535
471536// Panicf is equivalent to Printf() followed by a call to panic().
472537func Panicf(format string, v ...interface{}) {
473538 s := fmt.Sprintf(format, v...)
474− std.Output(2, s)
539+ std.Output(ERROR, 2, s)
475540 panic(s)
476541}
477542
478543// Panicln is equivalent to Println() followed by a call to panic().
479544func Panicln(v ...interface{}) {
480545 s := fmt.Sprintln(v...)
481− std.Output(2, s)
546+ std.Output(ERROR, 2, s)
482547 panic(s)
483548}
484549
pkg/middleware/basic_session.go+1−1
@@ -34,7 +34,7 @@ func loadBasicAuthSession(validator basic.Validator, next http.Handler) http.Han
3434
3535 session, err := getBasicSession(validator, req)
3636 if err != nil {
37− logger.Printf("Error retrieving session from token in Authorization header: %v", err)
37+ logger.Errorf("Error retrieving session from token in Authorization header: %v", err)
3838 }
3939
4040 // Add the session to the scope if it was found
pkg/middleware/jwt_session.go+1−1
@@ -57,7 +57,7 @@ func (j *jwtSessionLoader) loadSession(next http.Handler) http.Handler {
5757
5858 session, err := j.getJwtSession(req)
5959 if err != nil {
60− logger.Printf("Error retrieving session from token in Authorization header: %v", err)
60+ logger.Errorf("Error retrieving session from token in Authorization header: %v", err)
6161 }
6262
6363 // Add the session to the scope if it was found
pkg/middleware/stored_session.go+2−2
@@ -72,10 +72,10 @@ func (s *storedSessionLoader) loadSession(next http.Handler) http.Handler {
7272 if err != nil {
7373 // In the case when there was an error loading the session,
7474 // we should clear the session
75− logger.Printf("Error loading cookied session: %v, removing session", err)
75+ logger.Errorf("Error loading cookied session: %v, removing session", err)
7676 err = s.store.Clear(rw, req)
7777 if err != nil {
78− logger.Printf("Error removing session: %v", err)
78+ logger.Errorf("Error removing session: %v", err)
7979 }
8080 }
8181
pkg/sessions/cookie/session_store.go+1−1
@@ -176,7 +176,7 @@ func splitCookie(c *http.Cookie) []*http.Cookie {
176176 return []*http.Cookie{c}
177177 }
178178
179− logger.Printf("WARNING: Multiple cookies are required for this session as it exceeds the 4kb cookie limit. Please use server side session storage (eg. Redis) instead.")
179+ logger.Errorf("WARNING: Multiple cookies are required for this session as it exceeds the 4kb cookie limit. Please use server side session storage (eg. Redis) instead.")
180180
181181 cookies := []*http.Cookie{}
182182 valueBytes := []byte(c.Value)
pkg/sessions/redis/redis_store.go+2−2
@@ -121,7 +121,7 @@ func buildStandaloneClient(opts options.RedisStoreOptions) (Client, error) {
121121 if opts.CAPath != "" {
122122 rootCAs, err := x509.SystemCertPool()
123123 if err != nil {
124− logger.Printf("failed to load system cert pool for redis connection, falling back to empty cert pool")
124+ logger.Errorf("failed to load system cert pool for redis connection, falling back to empty cert pool")
125125 }
126126 if rootCAs == nil {
127127 rootCAs = x509.NewCertPool()
@@ -133,7 +133,7 @@ func buildStandaloneClient(opts options.RedisStoreOptions) (Client, error) {
133133
134134 // Append our cert to the system pool
135135 if ok := rootCAs.AppendCertsFromPEM(certs); !ok {
136− logger.Printf("no certs appended, using system certs only")
136+ logger.Errorf("no certs appended, using system certs only")
137137 }
138138
139139 opt.TLSConfig.RootCAs = rootCAs
pkg/upstream/proxy.go+1−1
@@ -74,7 +74,7 @@ func (m *multiUpstreamProxy) registerHTTPUpstreamProxy(upstream options.Upstream
7474// NewProxyErrorHandler creates a ProxyErrorHandler using the template given.
7575func NewProxyErrorHandler(errorTemplate *template.Template, proxyPrefix string) ProxyErrorHandler {
7676 return func(rw http.ResponseWriter, req *http.Request, proxyErr error) {
77− logger.Printf("Error proxying to upstream server: %v", proxyErr)
77+ logger.Errorf("Error proxying to upstream server: %v", proxyErr)
7878 rw.WriteHeader(http.StatusBadGateway)
7979 data := struct {
8080 Title string
pkg/validation/logging.go+2−1
@@ -40,11 +40,12 @@ func configureLogger(o options.Logging, msgs []string) []string {
4040
4141 // Supply a sanity warning to the logger if all logging is disabled
4242 if !o.StandardEnabled && !o.AuthEnabled && !o.RequestEnabled {
43− logger.Print("Warning: Logging disabled. No further logs will be shown.")
43+ logger.Error("Warning: Logging disabled. No further logs will be shown.")
4444 }
4545
4646 // Pass configuration values to the standard logger
4747 logger.SetStandardEnabled(o.StandardEnabled)
48+ logger.SetErrToInfo(o.ErrToInfo)
4849 logger.SetAuthEnabled(o.AuthEnabled)
4950 logger.SetReqEnabled(o.RequestEnabled)
5051 logger.SetStandardTemplate(o.StandardFormat)
pkg/validation/options.go+1−1
@@ -92,7 +92,7 @@ func Validate(o *options.Options) error {
9292 Do().
9393 UnmarshalJSON()
9494 if err != nil {
95− logger.Printf("error: failed to discover OIDC configuration: %v", err)
95+ logger.Errorf("error: failed to discover OIDC configuration: %v", err)
9696 } else {
9797 // Prefer manually configured URLs. It's a bit unclear
9898 // why you'd be doing discovery and also providing the URLs
providers/azure.go+2−2
@@ -202,12 +202,12 @@ func (p *AzureProvider) GetEmailAddress(ctx context.Context, s *sessions.Session
202202
203203 email, err = json.Get("userPrincipalName").String()
204204 if err != nil {
205− logger.Printf("failed making request %s", err)
205+ logger.Errorf("failed making request %s", err)
206206 return "", err
207207 }
208208
209209 if email == "" {
210− logger.Printf("failed to get email address")
210+ logger.Errorf("failed to get email address")
211211 return "", err
212212 }
213213
providers/bitbucket.go+5−5
@@ -107,7 +107,7 @@ func (p *BitbucketProvider) GetEmailAddress(ctx context.Context, s *sessions.Ses
107107 Do().
108108 UnmarshalInto(&emails)
109109 if err != nil {
110− logger.Printf("failed making request: %v", err)
110+ logger.Errorf("failed making request: %v", err)
111111 return "", err
112112 }
113113
@@ -123,7 +123,7 @@ func (p *BitbucketProvider) GetEmailAddress(ctx context.Context, s *sessions.Ses
123123 Do().
124124 UnmarshalInto(&teams)
125125 if err != nil {
126− logger.Printf("failed requesting teams membership: %v", err)
126+ logger.Errorf("failed requesting teams membership: %v", err)
127127 return "", err
128128 }
129129 var found = false
@@ -134,7 +134,7 @@ func (p *BitbucketProvider) GetEmailAddress(ctx context.Context, s *sessions.Ses
134134 }
135135 }
136136 if !found {
137− logger.Print("team membership test failed, access denied")
137+ logger.Error("team membership test failed, access denied")
138138 return "", nil
139139 }
140140 }
@@ -153,7 +153,7 @@ func (p *BitbucketProvider) GetEmailAddress(ctx context.Context, s *sessions.Ses
153153 Do().
154154 UnmarshalInto(&repositories)
155155 if err != nil {
156− logger.Printf("failed checking repository access: %v", err)
156+ logger.Errorf("failed checking repository access: %v", err)
157157 return "", err
158158 }
159159
@@ -165,7 +165,7 @@ func (p *BitbucketProvider) GetEmailAddress(ctx context.Context, s *sessions.Ses
165165 }
166166 }
167167 if !found {
168− logger.Print("repository access test failed, access denied")
168+ logger.Error("repository access test failed, access denied")
169169 return "", nil
170170 }
171171 }
providers/google.go+3−3
@@ -212,7 +212,7 @@ func userInGroup(service *admin.Service, groups []string, email string) bool {
212212 gerr, ok := err.(*googleapi.Error)
213213 switch {
214214 case ok && gerr.Code == 404:
215− logger.Printf("error checking membership in group %s: group does not exist", group)
215+ logger.Errorf("error checking membership in group %s: group does not exist", group)
216216 case ok && gerr.Code == 400:
217217 // It is possible for Members.HasMember to return false even if the email is a group member.
218218 // One case that can cause this is if the user email is from a different domain than the group,
@@ -222,7 +222,7 @@ func userInGroup(service *admin.Service, groups []string, email string) bool {
222222 r, err := req.Do()
223223
224224 if err != nil {
225− logger.Printf("error using get API to check member %s of google group %s: user not in the group", email, group)
225+ logger.Errorf("error using get API to check member %s of google group %s: user not in the group", email, group)
226226 continue
227227 }
228228
@@ -232,7 +232,7 @@ func userInGroup(service *admin.Service, groups []string, email string) bool {
232232 return true
233233 }
234234 default:
235− logger.Printf("error checking group membership: %v", err)
235+ logger.Errorf("error checking group membership: %v", err)
236236 }
237237 continue
238238 }
providers/internal_util.go+5−5
@@ -24,14 +24,14 @@ func stripToken(endpoint string) string {
2424func stripParam(param, endpoint string) string {
2525 u, err := url.Parse(endpoint)
2626 if err != nil {
27− logger.Printf("error attempting to strip %s: %s", param, err)
27+ logger.Errorf("error attempting to strip %s: %s", param, err)
2828 return endpoint
2929 }
3030
3131 if u.RawQuery != "" {
3232 values, err := url.ParseQuery(u.RawQuery)
3333 if err != nil {
34− logger.Printf("error attempting to strip %s: %s", param, err)
34+ logger.Errorf("error attempting to strip %s: %s", param, err)
3535 return u.String()
3636 }
3737
@@ -61,8 +61,8 @@ func validateToken(ctx context.Context, p Provider, accessToken string, header h
6161 WithHeaders(header).
6262 Do()
6363 if result.Error() != nil {
64− logger.Printf("GET %s", stripToken(endpoint))
65− logger.Printf("token validation request failed: %s", result.Error())
64+ logger.Errorf("GET %s", stripToken(endpoint))
65+ logger.Errorf("token validation request failed: %s", result.Error())
6666 return false
6767 }
6868
@@ -71,6 +71,6 @@ func validateToken(ctx context.Context, p Provider, accessToken string, header h
7171 if result.StatusCode() == 200 {
7272 return true
7373 }
74− logger.Printf("token validation request failed: status %d - %s", result.StatusCode(), result.Body())
74+ logger.Errorf("token validation request failed: status %d - %s", result.StatusCode(), result.Body())
7575 return false
7676}
providers/keycloak.go+1−1
@@ -70,7 +70,7 @@ func (p *KeycloakProvider) GetEmailAddress(ctx context.Context, s *sessions.Sess
7070 Do().
7171 UnmarshalJSON()
7272 if err != nil {
73− logger.Printf("failed making request %s", err)
73+ logger.Errorf("failed making request %s", err)
7474 return "", err
7575 }
7676
providers/provider_data.go+1−1
@@ -39,7 +39,7 @@ func (p *ProviderData) GetClientSecret() (clientSecret string, err error) {
3939 // Getting ClientSecret can fail in runtime so we need to report it without returning the file name to the user
4040 fileClientSecret, err := ioutil.ReadFile(p.ClientSecretFile)
4141 if err != nil {
42− logger.Printf("error reading client secret file %s: %s", p.ClientSecretFile, err)
42+ logger.Errorf("error reading client secret file %s: %s", p.ClientSecretFile, err)
4343 return "", errors.New("could not read client secret file")
4444 }
4545 return string(fileClientSecret), nil
validator.go+1−1
@@ -62,7 +62,7 @@ func (um *UserMap) LoadAuthenticatedEmailsFile() {
6262 csvReader.TrimLeadingSpace = true
6363 records, err := csvReader.ReadAll()
6464 if err != nil {
65− logger.Printf("error reading authenticated-emails-file=%q, %s", um.usersFile, err)
65+ logger.Errorf("error reading authenticated-emails-file=%q, %s", um.usersFile, err)
6666 return
6767 }
6868 updated := make(map[string]bool)
watcher.go+1−1
@@ -69,7 +69,7 @@ func WatchForUpdates(filename string, done <-chan bool, action func()) {
6969 logger.Printf("reloading after event: %s", event)
7070 action()
7171 case err = <-watcher.Errors:
72− logger.Printf("error watching %s: %s", filename, err)
72+ logger.Errorf("error watching %s: %s", filename, err)
7373 }
7474 }
7575 }()
watcher_unsupported.go+1−1
@@ -5,6 +5,6 @@ package main
55import "github.com/oauth2-proxy/oauth2-proxy/pkg/logger"
66
77func WatchForUpdates(filename string, done <-chan bool, action func()) {
8− logger.Printf("file watching not implemented on this platform")
8+ logger.Errorf("file watching not implemented on this platform")
99 go func() { <-done }()
1010}