log.go 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. package model
  2. import (
  3. "context"
  4. "fmt"
  5. "one-api/common"
  6. "os"
  7. "strings"
  8. "time"
  9. "github.com/bytedance/gopkg/util/gopool"
  10. "gorm.io/gorm"
  11. )
  12. var groupCol string
  13. func init() {
  14. if common.UsingPostgreSQL {
  15. groupCol = `"group"`
  16. } else {
  17. groupCol = "`group`"
  18. }
  19. }
  20. type Log struct {
  21. Id int `json:"id" gorm:"index:idx_created_at_id,priority:1"`
  22. UserId int `json:"user_id" gorm:"index"`
  23. CreatedAt int64 `json:"created_at" gorm:"bigint;index:idx_created_at_id,priority:2;index:idx_created_at_type"`
  24. Type int `json:"type" gorm:"index:idx_created_at_type"`
  25. Content string `json:"content"`
  26. Username string `json:"username" gorm:"index:index_username_model_name,priority:2;default:''"`
  27. TokenName string `json:"token_name" gorm:"index;default:''"`
  28. ModelName string `json:"model_name" gorm:"index;index:index_username_model_name,priority:1;default:''"`
  29. Quota int `json:"quota" gorm:"default:0"`
  30. PromptTokens int `json:"prompt_tokens" gorm:"default:0"`
  31. CompletionTokens int `json:"completion_tokens" gorm:"default:0"`
  32. UseTime int `json:"use_time" gorm:"default:0"`
  33. IsStream bool `json:"is_stream" gorm:"default:false"`
  34. ChannelId int `json:"channel" gorm:"index"`
  35. TokenId int `json:"token_id" gorm:"default:0;index"`
  36. Group string `json:"group" gorm:"index"`
  37. Other string `json:"other"`
  38. }
  39. const (
  40. LogTypeUnknown = iota
  41. LogTypeTopup
  42. LogTypeConsume
  43. LogTypeManage
  44. LogTypeSystem
  45. )
  46. func GetLogByKey(key string) (logs []*Log, err error) {
  47. if os.Getenv("LOG_SQL_DSN") != "" {
  48. var tk Token
  49. if err = DB.Model(&Token{}).Where("`key`=?", strings.TrimPrefix(key, "sk-")).First(&tk).Error; err != nil {
  50. return nil, err
  51. }
  52. err = LOG_DB.Model(&Log{}).Where("token_id=?", tk.Id).Find(&logs).Error
  53. } else {
  54. err = LOG_DB.Joins("left join tokens on tokens.id = logs.token_id").Where("tokens.key = ?", strings.TrimPrefix(key, "sk-")).Find(&logs).Error
  55. }
  56. return logs, err
  57. }
  58. func RecordLog(userId int, logType int, content string) {
  59. if logType == LogTypeConsume && !common.LogConsumeEnabled {
  60. return
  61. }
  62. username, _ := CacheGetUsername(userId)
  63. log := &Log{
  64. UserId: userId,
  65. Username: username,
  66. CreatedAt: common.GetTimestamp(),
  67. Type: logType,
  68. Content: content,
  69. }
  70. err := LOG_DB.Create(log).Error
  71. if err != nil {
  72. common.SysError("failed to record log: " + err.Error())
  73. }
  74. }
  75. func RecordConsumeLog(ctx context.Context, userId int, channelId int, promptTokens int, completionTokens int,
  76. modelName string, tokenName string, quota int, content string, tokenId int, userQuota int, useTimeSeconds int,
  77. isStream bool, group string, other map[string]interface{}) {
  78. common.LogInfo(ctx, fmt.Sprintf("record consume log: userId=%d, 用户调用前余额=%d, channelId=%d, promptTokens=%d, completionTokens=%d, modelName=%s, tokenName=%s, quota=%d, content=%s", userId, userQuota, channelId, promptTokens, completionTokens, modelName, tokenName, quota, content))
  79. if !common.LogConsumeEnabled {
  80. return
  81. }
  82. username, _ := CacheGetUsername(userId)
  83. otherStr := common.MapToJsonStr(other)
  84. log := &Log{
  85. UserId: userId,
  86. Username: username,
  87. CreatedAt: common.GetTimestamp(),
  88. Type: LogTypeConsume,
  89. Content: content,
  90. PromptTokens: promptTokens,
  91. CompletionTokens: completionTokens,
  92. TokenName: tokenName,
  93. ModelName: modelName,
  94. Quota: quota,
  95. ChannelId: channelId,
  96. TokenId: tokenId,
  97. UseTime: useTimeSeconds,
  98. IsStream: isStream,
  99. Group: group,
  100. Other: otherStr,
  101. }
  102. err := LOG_DB.Create(log).Error
  103. if err != nil {
  104. common.LogError(ctx, "failed to record log: "+err.Error())
  105. }
  106. if common.DataExportEnabled {
  107. gopool.Go(func() {
  108. LogQuotaData(userId, username, modelName, quota, common.GetTimestamp(), promptTokens+completionTokens)
  109. })
  110. }
  111. }
  112. func GetAllLogs(logType int, startTimestamp int64, endTimestamp int64, modelName string, username string, tokenName string, startIdx int, num int, channel int, group string) (logs []*Log, total int64, err error) {
  113. var tx *gorm.DB
  114. if logType == LogTypeUnknown {
  115. tx = LOG_DB
  116. } else {
  117. tx = LOG_DB.Where("type = ?", logType)
  118. }
  119. if modelName != "" {
  120. tx = tx.Where("model_name like ?", modelName)
  121. }
  122. if username != "" {
  123. tx = tx.Where("username = ?", username)
  124. }
  125. if tokenName != "" {
  126. tx = tx.Where("token_name = ?", tokenName)
  127. }
  128. if startTimestamp != 0 {
  129. tx = tx.Where("created_at >= ?", startTimestamp)
  130. }
  131. if endTimestamp != 0 {
  132. tx = tx.Where("created_at <= ?", endTimestamp)
  133. }
  134. if channel != 0 {
  135. tx = tx.Where("channel_id = ?", channel)
  136. }
  137. if group != "" {
  138. tx = tx.Where(groupCol+" = ?", group)
  139. }
  140. err = tx.Model(&Log{}).Count(&total).Error
  141. if err != nil {
  142. return nil, 0, err
  143. }
  144. err = tx.Order("id desc").Limit(num).Offset(startIdx).Find(&logs).Error
  145. if err != nil {
  146. return nil, 0, err
  147. }
  148. return logs, total, err
  149. }
  150. func GetUserLogs(userId int, logType int, startTimestamp int64, endTimestamp int64, modelName string, tokenName string, startIdx int, num int, group string) (logs []*Log, total int64, err error) {
  151. var tx *gorm.DB
  152. if logType == LogTypeUnknown {
  153. tx = LOG_DB.Where("user_id = ?", userId)
  154. } else {
  155. tx = LOG_DB.Where("user_id = ? and type = ?", userId, logType)
  156. }
  157. if modelName != "" {
  158. tx = tx.Where("model_name like ?", modelName)
  159. }
  160. if tokenName != "" {
  161. tx = tx.Where("token_name = ?", tokenName)
  162. }
  163. if startTimestamp != 0 {
  164. tx = tx.Where("created_at >= ?", startTimestamp)
  165. }
  166. if endTimestamp != 0 {
  167. tx = tx.Where("created_at <= ?", endTimestamp)
  168. }
  169. if group != "" {
  170. tx = tx.Where(groupCol+" = ?", group)
  171. }
  172. err = tx.Model(&Log{}).Count(&total).Error
  173. if err != nil {
  174. return nil, 0, err
  175. }
  176. err = tx.Order("id desc").Limit(num).Offset(startIdx).Find(&logs).Error
  177. for i := range logs {
  178. var otherMap map[string]interface{}
  179. otherMap = common.StrToMap(logs[i].Other)
  180. if otherMap != nil {
  181. // delete admin
  182. delete(otherMap, "admin_info")
  183. }
  184. logs[i].Other = common.MapToJsonStr(otherMap)
  185. logs[i].Id = logs[i].Id % 1024
  186. }
  187. return logs, total, err
  188. }
  189. func SearchAllLogs(keyword string) (logs []*Log, err error) {
  190. err = LOG_DB.Where("type = ? or content LIKE ?", keyword, keyword+"%").Order("id desc").Limit(common.MaxRecentItems).Find(&logs).Error
  191. return logs, err
  192. }
  193. func SearchUserLogs(userId int, keyword string) (logs []*Log, err error) {
  194. err = LOG_DB.Where("user_id = ? and type = ?", userId, keyword).Order("id desc").Limit(common.MaxRecentItems).Find(&logs).Error
  195. for i := range logs {
  196. var otherMap map[string]interface{}
  197. otherMap = common.StrToMap(logs[i].Other)
  198. if otherMap != nil {
  199. // delete admin
  200. delete(otherMap, "admin_info")
  201. }
  202. logs[i].Other = common.MapToJsonStr(otherMap)
  203. logs[i].Id = logs[i].Id % 1024
  204. }
  205. return logs, err
  206. }
  207. type Stat struct {
  208. Quota int `json:"quota"`
  209. Rpm int `json:"rpm"`
  210. Tpm int `json:"tpm"`
  211. }
  212. func SumUsedQuota(logType int, startTimestamp int64, endTimestamp int64, modelName string, username string, tokenName string, channel int, group string) (stat Stat) {
  213. tx := LOG_DB.Table("logs").Select("sum(quota) quota")
  214. // 为rpm和tpm创建单独的查询
  215. rpmTpmQuery := LOG_DB.Table("logs").Select("count(*) rpm, sum(prompt_tokens) + sum(completion_tokens) tpm")
  216. if username != "" {
  217. tx = tx.Where("username = ?", username)
  218. rpmTpmQuery = rpmTpmQuery.Where("username = ?", username)
  219. }
  220. if tokenName != "" {
  221. tx = tx.Where("token_name = ?", tokenName)
  222. rpmTpmQuery = rpmTpmQuery.Where("token_name = ?", tokenName)
  223. }
  224. if startTimestamp != 0 {
  225. tx = tx.Where("created_at >= ?", startTimestamp)
  226. }
  227. if endTimestamp != 0 {
  228. tx = tx.Where("created_at <= ?", endTimestamp)
  229. }
  230. if modelName != "" {
  231. tx = tx.Where("model_name like ?", modelName)
  232. rpmTpmQuery = rpmTpmQuery.Where("model_name like ?", modelName)
  233. }
  234. if channel != 0 {
  235. tx = tx.Where("channel_id = ?", channel)
  236. rpmTpmQuery = rpmTpmQuery.Where("channel_id = ?", channel)
  237. }
  238. if group != "" {
  239. tx = tx.Where(groupCol+" = ?", group)
  240. rpmTpmQuery = rpmTpmQuery.Where(groupCol+" = ?", group)
  241. }
  242. tx = tx.Where("type = ?", LogTypeConsume)
  243. rpmTpmQuery = rpmTpmQuery.Where("type = ?", LogTypeConsume)
  244. // 只统计最近60秒的rpm和tpm
  245. rpmTpmQuery = rpmTpmQuery.Where("created_at >= ?", time.Now().Add(-60*time.Second).Unix())
  246. // 执行查询
  247. tx.Scan(&stat)
  248. rpmTpmQuery.Scan(&stat)
  249. return stat
  250. }
  251. func SumUsedToken(logType int, startTimestamp int64, endTimestamp int64, modelName string, username string, tokenName string) (token int) {
  252. tx := LOG_DB.Table("logs").Select("ifnull(sum(prompt_tokens),0) + ifnull(sum(completion_tokens),0)")
  253. if username != "" {
  254. tx = tx.Where("username = ?", username)
  255. }
  256. if tokenName != "" {
  257. tx = tx.Where("token_name = ?", tokenName)
  258. }
  259. if startTimestamp != 0 {
  260. tx = tx.Where("created_at >= ?", startTimestamp)
  261. }
  262. if endTimestamp != 0 {
  263. tx = tx.Where("created_at <= ?", endTimestamp)
  264. }
  265. if modelName != "" {
  266. tx = tx.Where("model_name = ?", modelName)
  267. }
  268. tx.Where("type = ?", LogTypeConsume).Scan(&token)
  269. return token
  270. }
  271. func DeleteOldLog(targetTimestamp int64) (int64, error) {
  272. result := LOG_DB.Where("created_at < ?", targetTimestamp).Delete(&Log{})
  273. return result.RowsAffected, result.Error
  274. }