logger.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. package logging
  2. import (
  3. "fmt"
  4. "log/slog"
  5. "os"
  6. // "path/filepath"
  7. "encoding/json"
  8. "runtime"
  9. "runtime/debug"
  10. "sync"
  11. "time"
  12. )
  13. func getCaller() string {
  14. var caller string
  15. if _, file, line, ok := runtime.Caller(2); ok {
  16. // caller = fmt.Sprintf("%s:%d", filepath.Base(file), line)
  17. caller = fmt.Sprintf("%s:%d", file, line)
  18. } else {
  19. caller = "unknown"
  20. }
  21. return caller
  22. }
  23. func Info(msg string, args ...any) {
  24. source := getCaller()
  25. slog.Info(msg, append([]any{"source", source}, args...)...)
  26. }
  27. func Debug(msg string, args ...any) {
  28. // slog.Debug(msg, args...)
  29. source := getCaller()
  30. slog.Debug(msg, append([]any{"source", source}, args...)...)
  31. }
  32. func Warn(msg string, args ...any) {
  33. slog.Warn(msg, args...)
  34. }
  35. func Error(msg string, args ...any) {
  36. slog.Error(msg, args...)
  37. }
  38. func InfoPersist(msg string, args ...any) {
  39. args = append(args, persistKeyArg, true)
  40. slog.Info(msg, args...)
  41. }
  42. func DebugPersist(msg string, args ...any) {
  43. args = append(args, persistKeyArg, true)
  44. slog.Debug(msg, args...)
  45. }
  46. func WarnPersist(msg string, args ...any) {
  47. args = append(args, persistKeyArg, true)
  48. slog.Warn(msg, args...)
  49. }
  50. func ErrorPersist(msg string, args ...any) {
  51. args = append(args, persistKeyArg, true)
  52. slog.Error(msg, args...)
  53. }
  54. // RecoverPanic is a common function to handle panics gracefully.
  55. // It logs the error, creates a panic log file with stack trace,
  56. // and executes an optional cleanup function before returning.
  57. func RecoverPanic(name string, cleanup func()) {
  58. if r := recover(); r != nil {
  59. // Log the panic
  60. ErrorPersist(fmt.Sprintf("Panic in %s: %v", name, r))
  61. // Create a timestamped panic log file
  62. timestamp := time.Now().Format("20060102-150405")
  63. filename := fmt.Sprintf("crush-panic-%s-%s.log", name, timestamp)
  64. file, err := os.Create(filename)
  65. if err != nil {
  66. ErrorPersist(fmt.Sprintf("Failed to create panic log: %v", err))
  67. } else {
  68. defer file.Close()
  69. // Write panic information and stack trace
  70. fmt.Fprintf(file, "Panic in %s: %v\n\n", name, r)
  71. fmt.Fprintf(file, "Time: %s\n\n", time.Now().Format(time.RFC3339))
  72. fmt.Fprintf(file, "Stack Trace:\n%s\n", debug.Stack())
  73. InfoPersist(fmt.Sprintf("Panic details written to %s", filename))
  74. }
  75. // Execute cleanup function if provided
  76. if cleanup != nil {
  77. cleanup()
  78. }
  79. }
  80. }
  81. // Message Logging for Debug
  82. var MessageDir string
  83. func GetSessionPrefix(sessionId string) string {
  84. return sessionId[:8]
  85. }
  86. var sessionLogMutex sync.Mutex
  87. func AppendToSessionLogFile(sessionId string, filename string, content string) string {
  88. if MessageDir == "" || sessionId == "" {
  89. return ""
  90. }
  91. sessionPrefix := GetSessionPrefix(sessionId)
  92. sessionLogMutex.Lock()
  93. defer sessionLogMutex.Unlock()
  94. sessionPath := fmt.Sprintf("%s/%s", MessageDir, sessionPrefix)
  95. if _, err := os.Stat(sessionPath); os.IsNotExist(err) {
  96. if err := os.MkdirAll(sessionPath, 0o766); err != nil {
  97. Error("Failed to create session directory", "dirpath", sessionPath, "error", err)
  98. return ""
  99. }
  100. }
  101. filePath := fmt.Sprintf("%s/%s", sessionPath, filename)
  102. f, err := os.OpenFile(filePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
  103. if err != nil {
  104. Error("Failed to open session log file", "filepath", filePath, "error", err)
  105. return ""
  106. }
  107. defer f.Close()
  108. // Append chunk to file
  109. _, err = f.WriteString(content)
  110. if err != nil {
  111. Error("Failed to write chunk to session log file", "filepath", filePath, "error", err)
  112. return ""
  113. }
  114. return filePath
  115. }
  116. func WriteRequestMessageJson(sessionId string, requestSeqId int, message any) string {
  117. if MessageDir == "" || sessionId == "" || requestSeqId <= 0 {
  118. return ""
  119. }
  120. msgJson, err := json.Marshal(message)
  121. if err != nil {
  122. Error("Failed to marshal message", "session_id", sessionId, "request_seq_id", requestSeqId, "error", err)
  123. return ""
  124. }
  125. return WriteRequestMessage(sessionId, requestSeqId, string(msgJson))
  126. }
  127. func WriteRequestMessage(sessionId string, requestSeqId int, message string) string {
  128. if MessageDir == "" || sessionId == "" || requestSeqId <= 0 {
  129. return ""
  130. }
  131. filename := fmt.Sprintf("%d_request.json", requestSeqId)
  132. return AppendToSessionLogFile(sessionId, filename, message)
  133. }
  134. func AppendToStreamSessionLogJson(sessionId string, requestSeqId int, jsonableChunk any) string {
  135. if MessageDir == "" || sessionId == "" || requestSeqId <= 0 {
  136. return ""
  137. }
  138. chunkJson, err := json.Marshal(jsonableChunk)
  139. if err != nil {
  140. Error("Failed to marshal message", "session_id", sessionId, "request_seq_id", requestSeqId, "error", err)
  141. return ""
  142. }
  143. return AppendToStreamSessionLog(sessionId, requestSeqId, string(chunkJson))
  144. }
  145. func AppendToStreamSessionLog(sessionId string, requestSeqId int, chunk string) string {
  146. if MessageDir == "" || sessionId == "" || requestSeqId <= 0 {
  147. return ""
  148. }
  149. filename := fmt.Sprintf("%d_response_stream.log", requestSeqId)
  150. return AppendToSessionLogFile(sessionId, filename, chunk)
  151. }
  152. func WriteChatResponseJson(sessionId string, requestSeqId int, response any) string {
  153. if MessageDir == "" || sessionId == "" || requestSeqId <= 0 {
  154. return ""
  155. }
  156. responseJson, err := json.Marshal(response)
  157. if err != nil {
  158. Error("Failed to marshal response", "session_id", sessionId, "request_seq_id", requestSeqId, "error", err)
  159. return ""
  160. }
  161. filename := fmt.Sprintf("%d_response.json", requestSeqId)
  162. return AppendToSessionLogFile(sessionId, filename, string(responseJson))
  163. }
  164. func WriteToolResultsJson(sessionId string, requestSeqId int, toolResults any) string {
  165. if MessageDir == "" || sessionId == "" || requestSeqId <= 0 {
  166. return ""
  167. }
  168. toolResultsJson, err := json.Marshal(toolResults)
  169. if err != nil {
  170. Error("Failed to marshal tool results", "session_id", sessionId, "request_seq_id", requestSeqId, "error", err)
  171. return ""
  172. }
  173. filename := fmt.Sprintf("%d_tool_results.json", requestSeqId)
  174. return AppendToSessionLogFile(sessionId, filename, string(toolResultsJson))
  175. }