mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-30 22:22:25 -05:00 
			
		
		
		
	[performance] improved logrus output switching performance (#544)
* improved logrus output switching performance Signed-off-by: kim <grufwub@gmail.com> * add license to test file Signed-off-by: kim <grufwub@gmail.com>
This commit is contained in:
		
					parent
					
						
							
								a561ef3541
							
						
					
				
			
			
				commit
				
					
						08eb271a4c
					
				
			
		
					 2 changed files with 101 additions and 13 deletions
				
			
		|  | @ -20,9 +20,9 @@ package log | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"bytes" | 	"bytes" | ||||||
| 	"os" | 	"io" | ||||||
| 
 |  | ||||||
| 	"log/syslog" | 	"log/syslog" | ||||||
|  | 	"os" | ||||||
| 
 | 
 | ||||||
| 	"github.com/sirupsen/logrus" | 	"github.com/sirupsen/logrus" | ||||||
| 	lSyslog "github.com/sirupsen/logrus/hooks/syslog" | 	lSyslog "github.com/sirupsen/logrus/hooks/syslog" | ||||||
|  | @ -34,12 +34,13 @@ import ( | ||||||
| // log level from the viper store, or using a default if the level | // log level from the viper store, or using a default if the level | ||||||
| // has not been set in viper. | // has not been set in viper. | ||||||
| // | // | ||||||
| // It also sets the output to log.outputSplitter, | // It also sets the output to log.SplitErrOutputs(...) | ||||||
| // so you get error logs on stderr and normal logs on stdout. | // so you get error logs on stderr and normal logs on stdout. | ||||||
| // | // | ||||||
| // If syslog settings are also in viper, then Syslog will be initialized as well. | // If syslog settings are also in viper, then Syslog will be initialized as well. | ||||||
| func Initialize() error { | func Initialize() error { | ||||||
| 	logrus.SetOutput(&outputSplitter{}) | 	out := SplitErrOutputs(os.Stdout, os.Stderr) | ||||||
|  | 	logrus.SetOutput(out) | ||||||
| 
 | 
 | ||||||
| 	logrus.SetFormatter(&logrus.TextFormatter{ | 	logrus.SetFormatter(&logrus.TextFormatter{ | ||||||
| 		DisableColors: true, | 		DisableColors: true, | ||||||
|  | @ -79,14 +80,32 @@ func Initialize() error { | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // outputSplitter implements the io.Writer interface for use with Logrus, and simply | // SplitErrOutputs returns an OutputSplitFunc that splits output to either one of | ||||||
| // splits logs between stdout and stderr depending on their severity. | // two given outputs depending on whether the level is "error","fatal","panic". | ||||||
| // See: https://github.com/sirupsen/logrus/issues/403#issuecomment-346437512 | func SplitErrOutputs(out, err io.Writer) OutputSplitFunc { | ||||||
| type outputSplitter struct{} | 	return func(lvl []byte) io.Writer { | ||||||
|  | 		switch string(lvl) /* convert to str for compare is no-alloc */ { | ||||||
|  | 		case "error", "fatal", "panic": | ||||||
|  | 			return err | ||||||
|  | 		default: | ||||||
|  | 			return out | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| func (splitter *outputSplitter) Write(p []byte) (n int, err error) { | // OutputSplitFunc implements the io.Writer interface for use with Logrus, and simply | ||||||
| 	if bytes.Contains(p, []byte("level=error")) { | // splits logs between stdout and stderr depending on their severity. | ||||||
| 		return os.Stderr.Write(p) | type OutputSplitFunc func(lvl []byte) io.Writer | ||||||
|  | 
 | ||||||
|  | var levelBytes = []byte("level=") | ||||||
|  | 
 | ||||||
|  | func (fn OutputSplitFunc) Write(b []byte) (int, error) { | ||||||
|  | 	var lvl []byte | ||||||
|  | 	if i := bytes.Index(b, levelBytes); i >= 0 { | ||||||
|  | 		blvl := b[i+len(levelBytes):] | ||||||
|  | 		if i := bytes.IndexByte(blvl, ' '); i >= 0 { | ||||||
|  | 			lvl = blvl[:i] | ||||||
| 		} | 		} | ||||||
| 	return os.Stdout.Write(p) | 	} | ||||||
|  | 	return fn(lvl).Write(b) | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										69
									
								
								internal/log/log_test.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								internal/log/log_test.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,69 @@ | ||||||
|  | /* | ||||||
|  |    GoToSocial | ||||||
|  |    Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org | ||||||
|  | 
 | ||||||
|  |    This program is free software: you can redistribute it and/or modify | ||||||
|  |    it under the terms of the GNU Affero 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 Affero General Public License for more details. | ||||||
|  | 
 | ||||||
|  |    You should have received a copy of the GNU Affero General Public License | ||||||
|  |    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | package log_test | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"github.com/sirupsen/logrus" | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/log" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func TestOutputSplitFunc(t *testing.T) { | ||||||
|  | 	var outbuf, errbuf bytes.Buffer | ||||||
|  | 
 | ||||||
|  | 	out := log.SplitErrOutputs(&outbuf, &errbuf) | ||||||
|  | 
 | ||||||
|  | 	log := logrus.New() | ||||||
|  | 	log.SetOutput(out) | ||||||
|  | 	log.SetLevel(logrus.TraceLevel) | ||||||
|  | 
 | ||||||
|  | 	for _, lvl := range logrus.AllLevels { | ||||||
|  | 		func() { | ||||||
|  | 			defer func() { recover() }() | ||||||
|  | 			log.Log(lvl, "hello world") | ||||||
|  | 		}() | ||||||
|  | 
 | ||||||
|  | 		t.Logf("outbuf=%q errbuf=%q", outbuf.String(), errbuf.String()) | ||||||
|  | 
 | ||||||
|  | 		switch lvl { | ||||||
|  | 		case logrus.PanicLevel: | ||||||
|  | 			if outbuf.Len() > 0 || errbuf.Len() == 0 { | ||||||
|  | 				t.Error("expected panic to log to OutputSplitter.Err") | ||||||
|  | 			} | ||||||
|  | 		case logrus.FatalLevel: | ||||||
|  | 			if outbuf.Len() > 0 || errbuf.Len() == 0 { | ||||||
|  | 				t.Error("expected fatal to log to OutputSplitter.Err") | ||||||
|  | 			} | ||||||
|  | 		case logrus.ErrorLevel: | ||||||
|  | 			if outbuf.Len() > 0 || errbuf.Len() == 0 { | ||||||
|  | 				t.Error("expected error to log to OutputSplitter.Err") | ||||||
|  | 			} | ||||||
|  | 		default: | ||||||
|  | 			if outbuf.Len() == 0 || errbuf.Len() > 0 { | ||||||
|  | 				t.Errorf("expected %s to log to OutputSplitter.Out", lvl) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// Reset buffers | ||||||
|  | 		outbuf.Reset() | ||||||
|  | 		errbuf.Reset() | ||||||
|  | 	} | ||||||
|  | } | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue