diff --git a/controller/log.go b/controller/log.go
index 9377b338..87f681ce 100644
--- a/controller/log.go
+++ b/controller/log.go
@@ -20,7 +20,8 @@ func GetAllLogs(c *gin.Context) {
tokenName := c.Query("token_name")
modelName := c.Query("model_name")
channel, _ := strconv.Atoi(c.Query("channel"))
- logs, err := model.GetAllLogs(logType, startTimestamp, endTimestamp, modelName, username, tokenName, p*config.ItemsPerPage, config.ItemsPerPage, channel)
+ clientIP := c.Query("client_ip")
+ logs, err := model.GetAllLogs(logType, startTimestamp, endTimestamp, modelName, username, tokenName, p*config.ItemsPerPage, config.ItemsPerPage, channel, clientIP)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"success": false,
diff --git a/model/log.go b/model/log.go
index 9615c237..80eaf256 100644
--- a/model/log.go
+++ b/model/log.go
@@ -3,6 +3,7 @@ package model
import (
"context"
"fmt"
+
"github.com/songquanpeng/one-api/common"
"github.com/songquanpeng/one-api/common/config"
"github.com/songquanpeng/one-api/common/helper"
@@ -24,6 +25,7 @@ type Log struct {
PromptTokens int `json:"prompt_tokens" gorm:"default:0"`
CompletionTokens int `json:"completion_tokens" gorm:"default:0"`
ChannelId int `json:"channel" gorm:"index"`
+ ClientIP string `json:"client_ip" gorm:"index;default:''"`
}
const (
@@ -51,7 +53,7 @@ func RecordLog(userId int, logType int, content string) {
}
}
-func RecordConsumeLog(ctx context.Context, userId int, channelId int, promptTokens int, completionTokens int, modelName string, tokenName string, quota int, content string) {
+func RecordConsumeLog(ctx context.Context, userId int, channelId int, promptTokens int, completionTokens int, modelName string, tokenName string, quota int, content string, clientIP string) {
logger.Info(ctx, fmt.Sprintf("record consume log: userId=%d, channelId=%d, promptTokens=%d, completionTokens=%d, modelName=%s, tokenName=%s, quota=%d, content=%s", userId, channelId, promptTokens, completionTokens, modelName, tokenName, quota, content))
if !config.LogConsumeEnabled {
return
@@ -68,6 +70,7 @@ func RecordConsumeLog(ctx context.Context, userId int, channelId int, promptToke
ModelName: modelName,
Quota: quota,
ChannelId: channelId,
+ ClientIP: clientIP,
}
err := DB.Create(log).Error
if err != nil {
@@ -75,7 +78,7 @@ func RecordConsumeLog(ctx context.Context, userId int, channelId int, promptToke
}
}
-func GetAllLogs(logType int, startTimestamp int64, endTimestamp int64, modelName string, username string, tokenName string, startIdx int, num int, channel int) (logs []*Log, err error) {
+func GetAllLogs(logType int, startTimestamp int64, endTimestamp int64, modelName string, username string, tokenName string, startIdx int, num int, channel int, clientIP string) (logs []*Log, err error) {
var tx *gorm.DB
if logType == LogTypeUnknown {
tx = DB
@@ -100,6 +103,10 @@ func GetAllLogs(logType int, startTimestamp int64, endTimestamp int64, modelName
if channel != 0 {
tx = tx.Where("channel_id = ?", channel)
}
+ if clientIP != "" {
+ tx = tx.Where("client_ip = ?", clientIP)
+ }
+
err = tx.Order("id desc").Limit(num).Offset(startIdx).Find(&logs).Error
return logs, err
}
diff --git a/relay/controller/audio.go b/relay/controller/audio.go
index ee8771c9..58f441db 100644
--- a/relay/controller/audio.go
+++ b/relay/controller/audio.go
@@ -7,6 +7,10 @@ import (
"encoding/json"
"errors"
"fmt"
+ "io"
+ "net/http"
+ "strings"
+
"github.com/gin-gonic/gin"
"github.com/songquanpeng/one-api/common"
"github.com/songquanpeng/one-api/common/config"
@@ -16,9 +20,6 @@ import (
"github.com/songquanpeng/one-api/relay/constant"
relaymodel "github.com/songquanpeng/one-api/relay/model"
"github.com/songquanpeng/one-api/relay/util"
- "io"
- "net/http"
- "strings"
)
func RelayAudioHelper(c *gin.Context, relayMode int) *relaymodel.ErrorWithStatusCode {
@@ -98,6 +99,9 @@ func RelayAudioHelper(c *gin.Context, relayMode int) *relaymodel.ErrorWithStatus
baseURL := common.ChannelBaseURLs[channelType]
requestURL := c.Request.URL.String()
+ clientIP := c.ClientIP()
+ fmt.Println(clientIP)
+
if c.GetString("base_url") != "" {
baseURL = c.GetString("base_url")
}
@@ -203,7 +207,7 @@ func RelayAudioHelper(c *gin.Context, relayMode int) *relaymodel.ErrorWithStatus
}
quotaDelta := quota - preConsumedQuota
defer func(ctx context.Context) {
- go util.PostConsumeQuota(ctx, tokenId, quotaDelta, quota, userId, channelId, modelRatio, groupRatio, audioModel, tokenName)
+ go util.PostConsumeQuota(ctx, tokenId, quotaDelta, quota, userId, channelId, modelRatio, groupRatio, audioModel, tokenName, clientIP)
}(c.Request.Context())
for k, v := range resp.Header {
diff --git a/relay/controller/helper.go b/relay/controller/helper.go
index 89fc69ce..364a4a8a 100644
--- a/relay/controller/helper.go
+++ b/relay/controller/helper.go
@@ -4,6 +4,9 @@ import (
"context"
"errors"
"fmt"
+ "math"
+ "net/http"
+
"github.com/gin-gonic/gin"
"github.com/songquanpeng/one-api/common"
"github.com/songquanpeng/one-api/common/config"
@@ -13,8 +16,6 @@ import (
"github.com/songquanpeng/one-api/relay/constant"
relaymodel "github.com/songquanpeng/one-api/relay/model"
"github.com/songquanpeng/one-api/relay/util"
- "math"
- "net/http"
)
func getAndValidateTextRequest(c *gin.Context, relayMode int) (*relaymodel.GeneralOpenAIRequest, error) {
@@ -144,7 +145,7 @@ func preConsumeQuota(ctx context.Context, textRequest *relaymodel.GeneralOpenAIR
return preConsumedQuota, nil
}
-func postConsumeQuota(ctx context.Context, usage *relaymodel.Usage, meta *util.RelayMeta, textRequest *relaymodel.GeneralOpenAIRequest, ratio float64, preConsumedQuota int, modelRatio float64, groupRatio float64) {
+func postConsumeQuota(ctx context.Context, usage *relaymodel.Usage, meta *util.RelayMeta, textRequest *relaymodel.GeneralOpenAIRequest, ratio float64, preConsumedQuota int, modelRatio float64, groupRatio float64, clientIP string) {
if usage == nil {
logger.Error(ctx, "usage is nil, which is unexpected")
return
@@ -173,7 +174,7 @@ func postConsumeQuota(ctx context.Context, usage *relaymodel.Usage, meta *util.R
logger.Error(ctx, "error update user quota cache: "+err.Error())
}
logContent := fmt.Sprintf("模型倍率 %.2f,分组倍率 %.2f,补全倍率 %.2f", modelRatio, groupRatio, completionRatio)
- model.RecordConsumeLog(ctx, meta.UserId, meta.ChannelId, promptTokens, completionTokens, textRequest.Model, meta.TokenName, quota, logContent)
+ model.RecordConsumeLog(ctx, meta.UserId, meta.ChannelId, promptTokens, completionTokens, textRequest.Model, meta.TokenName, quota, logContent, clientIP)
model.UpdateUserUsedQuotaAndRequestCount(meta.UserId, quota)
model.UpdateChannelUsedQuota(meta.ChannelId, quota)
}
diff --git a/relay/controller/image.go b/relay/controller/image.go
index 3ce3809b..7c82a2f7 100644
--- a/relay/controller/image.go
+++ b/relay/controller/image.go
@@ -6,6 +6,10 @@ import (
"encoding/json"
"errors"
"fmt"
+ "io"
+ "net/http"
+ "strings"
+
"github.com/songquanpeng/one-api/common"
"github.com/songquanpeng/one-api/common/logger"
"github.com/songquanpeng/one-api/model"
@@ -13,9 +17,6 @@ import (
"github.com/songquanpeng/one-api/relay/constant"
relaymodel "github.com/songquanpeng/one-api/relay/model"
"github.com/songquanpeng/one-api/relay/util"
- "io"
- "net/http"
- "strings"
"github.com/gin-gonic/gin"
)
@@ -132,7 +133,7 @@ func RelayImageHelper(c *gin.Context, relayMode int) *relaymodel.ErrorWithStatus
if quota != 0 {
tokenName := c.GetString("token_name")
logContent := fmt.Sprintf("模型倍率 %.2f,分组倍率 %.2f", modelRatio, groupRatio)
- model.RecordConsumeLog(ctx, meta.UserId, meta.ChannelId, 0, 0, imageRequest.Model, tokenName, quota, logContent)
+ model.RecordConsumeLog(ctx, meta.UserId, meta.ChannelId, 0, 0, imageRequest.Model, tokenName, quota, logContent, c.ClientIP())
model.UpdateUserUsedQuotaAndRequestCount(meta.UserId, quota)
channelId := c.GetInt("channel_id")
model.UpdateChannelUsedQuota(channelId, quota)
diff --git a/relay/controller/text.go b/relay/controller/text.go
index 781170f4..5511cbc7 100644
--- a/relay/controller/text.go
+++ b/relay/controller/text.go
@@ -4,6 +4,10 @@ import (
"bytes"
"encoding/json"
"fmt"
+ "io"
+ "net/http"
+ "strings"
+
"github.com/gin-gonic/gin"
"github.com/songquanpeng/one-api/common"
"github.com/songquanpeng/one-api/common/logger"
@@ -12,9 +16,6 @@ import (
"github.com/songquanpeng/one-api/relay/helper"
"github.com/songquanpeng/one-api/relay/model"
"github.com/songquanpeng/one-api/relay/util"
- "io"
- "net/http"
- "strings"
)
func RelayTextHelper(c *gin.Context) *model.ErrorWithStatusCode {
@@ -98,6 +99,6 @@ func RelayTextHelper(c *gin.Context) *model.ErrorWithStatusCode {
return respErr
}
// post-consume quota
- go postConsumeQuota(ctx, usage, meta, textRequest, ratio, preConsumedQuota, modelRatio, groupRatio)
+ go postConsumeQuota(ctx, usage, meta, textRequest, ratio, preConsumedQuota, modelRatio, groupRatio, c.ClientIP())
return nil
}
diff --git a/relay/util/common.go b/relay/util/common.go
index 20257488..c5e2c889 100644
--- a/relay/util/common.go
+++ b/relay/util/common.go
@@ -4,15 +4,16 @@ import (
"context"
"encoding/json"
"fmt"
+ "io"
+ "net/http"
+ "strconv"
+ "strings"
+
"github.com/songquanpeng/one-api/common"
"github.com/songquanpeng/one-api/common/config"
"github.com/songquanpeng/one-api/common/logger"
"github.com/songquanpeng/one-api/model"
relaymodel "github.com/songquanpeng/one-api/relay/model"
- "io"
- "net/http"
- "strconv"
- "strings"
"github.com/gin-gonic/gin"
)
@@ -148,7 +149,7 @@ func GetFullRequestURL(baseURL string, requestURL string, channelType int) strin
return fullRequestURL
}
-func PostConsumeQuota(ctx context.Context, tokenId int, quotaDelta int, totalQuota int, userId int, channelId int, modelRatio float64, groupRatio float64, modelName string, tokenName string) {
+func PostConsumeQuota(ctx context.Context, tokenId int, quotaDelta int, totalQuota int, userId int, channelId int, modelRatio float64, groupRatio float64, modelName string, tokenName string, clientIP string) {
// quotaDelta is remaining quota to be consumed
err := model.PostConsumeTokenQuota(tokenId, quotaDelta)
if err != nil {
@@ -161,7 +162,7 @@ func PostConsumeQuota(ctx context.Context, tokenId int, quotaDelta int, totalQuo
// totalQuota is total quota consumed
if totalQuota != 0 {
logContent := fmt.Sprintf("模型倍率 %.2f,分组倍率 %.2f", modelRatio, groupRatio)
- model.RecordConsumeLog(ctx, userId, channelId, totalQuota, 0, modelName, tokenName, totalQuota, logContent)
+ model.RecordConsumeLog(ctx, userId, channelId, totalQuota, 0, modelName, tokenName, totalQuota, logContent, clientIP)
model.UpdateUserUsedQuotaAndRequestCount(userId, totalQuota)
model.UpdateChannelUsedQuota(channelId, totalQuota)
}
diff --git a/web/default/src/components/LogsTable.js b/web/default/src/components/LogsTable.js
index e266d79a..96daa8a6 100644
--- a/web/default/src/components/LogsTable.js
+++ b/web/default/src/components/LogsTable.js
@@ -1,21 +1,26 @@
import React, { useEffect, useState } from 'react';
-import { Button, Form, Header, Label, Pagination, Segment, Select, Table } from 'semantic-ui-react';
+import {
+ Button,
+ Form,
+ Header,
+ Label,
+ Pagination,
+ Segment,
+ Select,
+ Table,
+} from 'semantic-ui-react';
import { API, isAdmin, showError, timestamp2string } from '../helpers';
import { ITEMS_PER_PAGE } from '../constants';
import { renderQuota } from '../helpers/render';
function renderTimestamp(timestamp) {
- return (
- <>
- {timestamp2string(timestamp)}
- >
- );
+ return <>{timestamp2string(timestamp)}>;
}
const MODE_OPTIONS = [
{ key: 'all', text: '全部用户', value: 'all' },
- { key: 'self', text: '当前用户', value: 'self' }
+ { key: 'self', text: '当前用户', value: 'self' },
];
const LOG_OPTIONS = [
@@ -23,21 +28,46 @@ const LOG_OPTIONS = [
{ key: '1', text: '充值', value: 1 },
{ key: '2', text: '消费', value: 2 },
{ key: '3', text: '管理', value: 3 },
- { key: '4', text: '系统', value: 4 }
+ { key: '4', text: '系统', value: 4 },
];
function renderType(type) {
switch (type) {
case 1:
- return ;
+ return (
+
+ );
case 2:
- return ;
+ return (
+
+ );
case 3:
- return ;
+ return (
+
+ );
case 4:
- return ;
+ return (
+
+ );
default:
- return ;
+ return (
+
+ );
}
}
@@ -57,13 +87,22 @@ const LogsTable = () => {
model_name: '',
start_timestamp: timestamp2string(0),
end_timestamp: timestamp2string(now.getTime() / 1000 + 3600),
- channel: ''
+ channel: '',
+ client_ip: '',
});
- const { username, token_name, model_name, start_timestamp, end_timestamp, channel } = inputs;
+ const {
+ username,
+ token_name,
+ model_name,
+ start_timestamp,
+ end_timestamp,
+ channel,
+ client_ip,
+ } = inputs;
const [stat, setStat] = useState({
quota: 0,
- token: 0
+ token: 0,
});
const handleInputChange = (e, { name, value }) => {
@@ -73,7 +112,9 @@ const LogsTable = () => {
const getLogSelfStat = async () => {
let localStartTimestamp = Date.parse(start_timestamp) / 1000;
let localEndTimestamp = Date.parse(end_timestamp) / 1000;
- let res = await API.get(`/api/log/self/stat?type=${logType}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`);
+ let res = await API.get(
+ `/api/log/self/stat?type=${logType}&token_name=${token_name}&model_name=${model_name}&client_ip=${client_ip}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`
+ );
const { success, message, data } = res.data;
if (success) {
setStat(data);
@@ -85,7 +126,9 @@ const LogsTable = () => {
const getLogStat = async () => {
let localStartTimestamp = Date.parse(start_timestamp) / 1000;
let localEndTimestamp = Date.parse(end_timestamp) / 1000;
- let res = await API.get(`/api/log/stat?type=${logType}&username=${username}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}&channel=${channel}`);
+ let res = await API.get(
+ `/api/log/stat?type=${logType}&username=${username}&token_name=${token_name}&model_name=${model_name}&client_ip=${client_ip}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}&channel=${channel}`
+ );
const { success, message, data } = res.data;
if (success) {
setStat(data);
@@ -110,9 +153,9 @@ const LogsTable = () => {
let localStartTimestamp = Date.parse(start_timestamp) / 1000;
let localEndTimestamp = Date.parse(end_timestamp) / 1000;
if (isAdminUser) {
- url = `/api/log/?p=${startIdx}&type=${logType}&username=${username}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}&channel=${channel}`;
+ url = `/api/log/?p=${startIdx}&type=${logType}&username=${username}&token_name=${token_name}&model_name=${model_name}&client_ip=${client_ip}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}&channel=${channel}`;
} else {
- url = `/api/log/self/?p=${startIdx}&type=${logType}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`;
+ url = `/api/log/self/?p=${startIdx}&type=${logType}&token_name=${token_name}&model_name=${model_name}&client_ip=${client_ip}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`;
}
const res = await API.get(url);
const { success, message, data } = res.data;
@@ -201,37 +244,91 @@ const LogsTable = () => {