@@ -27,10 +27,13 @@ import (
2727 "net"
2828 "net/http"
2929 "net/url"
30+ "os"
31+ "path/filepath"
3032 "reflect"
3133 "regexp"
3234 "runtime"
3335 "runtime/debug"
36+ "runtime/pprof"
3437 "slices"
3538 "sort"
3639 "strconv"
@@ -1831,3 +1834,70 @@ func IsRevTreeID(s string) bool {
18311834 }
18321835 return false
18331836}
1837+
1838+ // GetStackTrace will return goroutine stack traces for all goroutines in Sync Gateway.
1839+ func GetStackTrace () (bytes.Buffer , error ) {
1840+ profBuf := bytes.Buffer {}
1841+ err := pprof .Lookup ("goroutine" ).WriteTo (& profBuf , 2 )
1842+ return profBuf , err
1843+ }
1844+
1845+ // RotateFilenamesIfNeeded will remove old files if there are more than
1846+ // 10 matching the given filename pattern.
1847+ func RotateFilenamesIfNeeded (filename string ) error {
1848+ existingFiles , err := filepath .Glob (filename )
1849+ if err != nil {
1850+ return fmt .Errorf ("Error listing existing profiles in %q: %w" , filename , err )
1851+ }
1852+ if len (existingFiles ) <= 10 {
1853+ return nil
1854+ }
1855+ slices .Reverse (existingFiles )
1856+ var multiErr * MultiError
1857+ for _ , profile := range existingFiles [10 :] {
1858+ err = os .Remove (profile )
1859+ if err != nil {
1860+ multiErr = multiErr .Append (fmt .Errorf ("Error removing old profile %q: %w" , profile , err ))
1861+ }
1862+ }
1863+ return multiErr .ErrorOrNil ()
1864+ }
1865+
1866+ func LogStackTraces (ctx context.Context , logDirectory string , stackTrace bytes.Buffer , timestamp string ) {
1867+
1868+ // log to console
1869+ _ , _ = fmt .Fprintf (os .Stderr , "Stack trace:\n %s\n " , stackTrace .String ())
1870+
1871+ err := writeStackTraceFile (ctx , logDirectory , timestamp , stackTrace )
1872+ if err != nil {
1873+ return
1874+ }
1875+
1876+ rotatePath := filepath .Join (logDirectory , StackFilePrefix + "*.log" )
1877+ err = RotateFilenamesIfNeeded (rotatePath )
1878+ if err != nil {
1879+ WarnfCtx (ctx , "Error rotating stack trace files in path %s: %v" , rotatePath , err )
1880+ }
1881+ }
1882+
1883+ func writeStackTraceFile (ctx context.Context , logDirectory , timestamp string , stackTrace bytes.Buffer ) error {
1884+ filename := filepath .Join (logDirectory , StackFilePrefix + timestamp + ".log" )
1885+ file , err := os .Create (filename )
1886+ defer func () {
1887+ closeErr := file .Close ()
1888+ if closeErr != nil {
1889+ WarnfCtx (ctx , "Error closing stack trace file %s: %v" , filename , closeErr )
1890+ }
1891+ }()
1892+ if err != nil {
1893+ WarnfCtx (ctx , "Error opening stack trace file %s: %v" , filename , err )
1894+ return err
1895+ }
1896+
1897+ _ , err = file .WriteString (fmt .Sprintf ("Stack trace:\n %s\n " , stackTrace .String ()))
1898+ if err != nil {
1899+ WarnfCtx (ctx , "Error writing stack trace to file %s: %v" , filename , err )
1900+ return err
1901+ }
1902+ return nil
1903+ }
0 commit comments