log.go 9.7 KB

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