forked from casdoor/casdoor
feat: add "/api/v1/traces" API to receive OpenClaw's OpenTelemetry metric (#5349)
This commit is contained in:
@@ -85,6 +85,7 @@ p, *, *, POST, /api/send-verification-code, *, *
|
||||
p, *, *, GET, /api/get-captcha, *, *
|
||||
p, *, *, POST, /api/verify-captcha, *, *
|
||||
p, *, *, POST, /api/verify-code, *, *
|
||||
p, *, *, POST, /api/v1/traces, *, *
|
||||
p, *, *, POST, /api/reset-email-or-phone, *, *
|
||||
p, *, *, POST, /api/upload-resource, *, *
|
||||
p, *, *, GET, /.well-known/openid-configuration, *, *
|
||||
|
||||
77
controllers/entry_opentelemetry.go
Normal file
77
controllers/entry_opentelemetry.go
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright 2026 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/casdoor/casdoor/object"
|
||||
coltracepb "go.opentelemetry.io/proto/otlp/collector/trace/v1"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// AddOtlpEntry
|
||||
// @Title AddTrace
|
||||
// @Tag OTLP API
|
||||
// @Description receive otlp trace protobuf
|
||||
// @Success 200 {object} string
|
||||
// @router /api/v1/traces [post]
|
||||
func (c *ApiController) AddTrace() {
|
||||
if !strings.HasPrefix(c.Ctx.Input.Header("Content-Type"), "application/x-protobuf") {
|
||||
c.Ctx.Output.SetStatus(415)
|
||||
c.Ctx.Output.Body([]byte("unsupported content type"))
|
||||
return
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(c.Ctx.Request.Body)
|
||||
if err != nil {
|
||||
c.Ctx.Output.SetStatus(400)
|
||||
c.Ctx.Output.Body([]byte("read body failed"))
|
||||
return
|
||||
}
|
||||
|
||||
var req coltracepb.ExportTraceServiceRequest
|
||||
|
||||
if err := proto.Unmarshal(body, &req); err != nil {
|
||||
c.Ctx.Output.SetStatus(400)
|
||||
c.Ctx.Output.Body([]byte(fmt.Sprintf("bad protobuf: %v", err)))
|
||||
return
|
||||
}
|
||||
|
||||
message, err := protojson.Marshal(&req)
|
||||
if err != nil {
|
||||
c.Ctx.Output.SetStatus(500)
|
||||
c.Ctx.Output.Body([]byte(fmt.Sprintf("marshal trace failed: %v", err)))
|
||||
return
|
||||
}
|
||||
|
||||
entry := object.NewTraceEntry(message)
|
||||
|
||||
if _, err := object.AddEntry(entry); err != nil {
|
||||
c.Ctx.Output.SetStatus(500)
|
||||
c.Ctx.Output.Body([]byte(fmt.Sprintf("save trace failed: %v", err)))
|
||||
return
|
||||
}
|
||||
|
||||
resp := &coltracepb.ExportTraceServiceResponse{}
|
||||
respBytes, _ := proto.Marshal(resp)
|
||||
|
||||
c.Ctx.Output.Header("Content-Type", "application/x-protobuf")
|
||||
c.Ctx.Output.SetStatus(200)
|
||||
c.Ctx.Output.Body(respBytes)
|
||||
}
|
||||
2
go.mod
2
go.mod
@@ -80,6 +80,7 @@ require (
|
||||
github.com/xorm-io/builder v0.3.13
|
||||
github.com/xorm-io/core v0.7.4
|
||||
github.com/xorm-io/xorm v1.1.6
|
||||
go.opentelemetry.io/proto/otlp v1.7.1
|
||||
golang.org/x/crypto v0.47.0
|
||||
golang.org/x/net v0.49.0
|
||||
golang.org/x/oauth2 v0.34.0
|
||||
@@ -186,6 +187,7 @@ require (
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/gregdel/pushover v1.3.1 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||
|
||||
4
go.sum
4
go.sum
@@ -1280,6 +1280,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90=
|
||||
github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
|
||||
github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
@@ -1855,6 +1857,8 @@ go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTq
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
|
||||
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
|
||||
@@ -31,6 +31,21 @@ type Entry struct {
|
||||
Url string `xorm:"varchar(500)" json:"url"`
|
||||
Token string `xorm:"varchar(500)" json:"token"`
|
||||
Application string `xorm:"varchar(100)" json:"application"`
|
||||
Message string `xorm:"mediumtext" json:"message"`
|
||||
}
|
||||
|
||||
func NewTraceEntry(message []byte) *Entry {
|
||||
currentTime := util.GetCurrentTime()
|
||||
traceId := fmt.Sprintf("trace_%s_%s", util.GenerateSimpleTimeId(), util.GetRandomName())
|
||||
|
||||
return &Entry{
|
||||
Owner: CasdoorOrganization,
|
||||
Name: traceId,
|
||||
CreatedTime: currentTime,
|
||||
UpdatedTime: currentTime,
|
||||
DisplayName: traceId,
|
||||
Message: string(message),
|
||||
}
|
||||
}
|
||||
|
||||
func GetEntries(owner string) ([]*Entry, error) {
|
||||
|
||||
@@ -146,6 +146,8 @@ func InitAPI() {
|
||||
web.Router("/api/add-entry", &controllers.ApiController{}, "POST:AddEntry")
|
||||
web.Router("/api/delete-entry", &controllers.ApiController{}, "POST:DeleteEntry")
|
||||
|
||||
web.Router("/api/v1/traces", &controllers.ApiController{}, "POST:AddTrace")
|
||||
|
||||
web.Router("/api/get-global-sites", &controllers.ApiController{}, "GET:GetGlobalSites")
|
||||
web.Router("/api/get-sites", &controllers.ApiController{}, "GET:GetSites")
|
||||
web.Router("/api/get-site", &controllers.ApiController{}, "GET:GetSite")
|
||||
|
||||
@@ -22,6 +22,7 @@ import * as OrganizationBackend from "./backend/OrganizationBackend";
|
||||
import * as ApplicationBackend from "./backend/ApplicationBackend";
|
||||
|
||||
const {Option} = Select;
|
||||
const {TextArea} = Input;
|
||||
|
||||
class EntryEditPage extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -208,6 +209,16 @@ class EntryEditPage extends React.Component {
|
||||
</Select>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("payment:Message")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<TextArea autoSize={{minRows: 8, maxRows: 20}} value={this.state.entry.message} onChange={e => {
|
||||
this.updateEntryField("message", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ class EntryListPage extends BaseListPage {
|
||||
url: "",
|
||||
token: "",
|
||||
application: "",
|
||||
message: "",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user