feat(platform): expose vehicle service runtime health
This commit is contained in:
@@ -37,7 +37,9 @@ func NewServer(cfg config.Config) http.Handler {
|
||||
log.Printf("production mysql store enabled")
|
||||
}
|
||||
}
|
||||
api := platform.NewHandler(platform.NewService(store))
|
||||
api := platform.NewHandler(platform.NewServiceWithRuntime(store, platform.RuntimeInfo{
|
||||
RequestTimeoutMs: int(cfg.RequestTimeout / time.Millisecond),
|
||||
}))
|
||||
return withRequestTimeout(static.Handler(cfg.StaticDir, api), cfg.RequestTimeout)
|
||||
}
|
||||
|
||||
|
||||
@@ -195,6 +195,25 @@ func TestHandlerHistoryMileageQualityOps(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandlerOpsHealthIncludesVehicleServiceRuntime(t *testing.T) {
|
||||
handler := NewHandler(NewServiceWithRuntime(NewMockStore(), RuntimeInfo{RequestTimeoutMs: 1500}))
|
||||
rec := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/ops/health", nil)
|
||||
handler.ServeHTTP(rec, req)
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("status = %d body=%s", rec.Code, rec.Body.String())
|
||||
}
|
||||
var body struct {
|
||||
Data OpsHealth `json:"data"`
|
||||
}
|
||||
if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil {
|
||||
t.Fatalf("response JSON should decode: %v body=%s", err, rec.Body.String())
|
||||
}
|
||||
if body.Data.Runtime.RequestTimeoutMs != 1500 {
|
||||
t.Fatalf("ops health should include request timeout runtime, got %+v body=%s", body.Data.Runtime, rec.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandlerVehicleDataAPIsResolveVehicleKeyword(t *testing.T) {
|
||||
handler := NewHandler(NewService(NewMockStore()))
|
||||
cases := []struct {
|
||||
|
||||
@@ -184,4 +184,9 @@ type OpsHealth struct {
|
||||
RedisOnlineKeys *int `json:"redisOnlineKeys"`
|
||||
TDengineWritable bool `json:"tdengineWritable"`
|
||||
MySQLWritable bool `json:"mysqlWritable"`
|
||||
Runtime RuntimeInfo `json:"runtime"`
|
||||
}
|
||||
|
||||
type RuntimeInfo struct {
|
||||
RequestTimeoutMs int `json:"requestTimeoutMs"`
|
||||
}
|
||||
|
||||
@@ -36,13 +36,18 @@ type RawFrameQuery struct {
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
store Store
|
||||
store Store
|
||||
runtime RuntimeInfo
|
||||
}
|
||||
|
||||
func NewService(store Store) *Service {
|
||||
return &Service{store: store}
|
||||
}
|
||||
|
||||
func NewServiceWithRuntime(store Store, runtime RuntimeInfo) *Service {
|
||||
return &Service{store: store, runtime: runtime}
|
||||
}
|
||||
|
||||
func (s *Service) DashboardSummary(ctx context.Context) (DashboardSummary, error) {
|
||||
return s.store.DashboardSummary(ctx)
|
||||
}
|
||||
@@ -282,7 +287,12 @@ func (s *Service) QualityIssues(ctx context.Context, query url.Values) (Page[Qua
|
||||
}
|
||||
|
||||
func (s *Service) OpsHealth(ctx context.Context) (OpsHealth, error) {
|
||||
return s.store.OpsHealth(ctx)
|
||||
health, err := s.store.OpsHealth(ctx)
|
||||
if err != nil {
|
||||
return OpsHealth{}, err
|
||||
}
|
||||
health.Runtime = s.runtime
|
||||
return health, nil
|
||||
}
|
||||
|
||||
func (s *Service) resolveVehicleQuery(ctx context.Context, query url.Values) (url.Values, error) {
|
||||
|
||||
@@ -174,6 +174,11 @@ export interface OpsHealth {
|
||||
redisOnlineKeys: number | null;
|
||||
tdengineWritable: boolean;
|
||||
mysqlWritable: boolean;
|
||||
runtime: RuntimeInfo;
|
||||
}
|
||||
|
||||
export interface RuntimeInfo {
|
||||
requestTimeoutMs: number;
|
||||
}
|
||||
|
||||
export interface Page<T> {
|
||||
|
||||
@@ -20,6 +20,11 @@ function storageReadStatus(health: OpsHealth | null) {
|
||||
return health?.tdengineWritable && health.mysqlWritable ? 'ok' : 'error';
|
||||
}
|
||||
|
||||
function formatRequestTimeout(health: OpsHealth | null) {
|
||||
const value = health?.runtime?.requestTimeoutMs;
|
||||
return value == null || value <= 0 ? '未限制' : `${value.toLocaleString()} ms`;
|
||||
}
|
||||
|
||||
const emptySummary: QualitySummary = {
|
||||
issueVehicleCount: 0,
|
||||
issueRecordCount: 0,
|
||||
@@ -125,9 +130,10 @@ export function Quality({
|
||||
))}
|
||||
</div>
|
||||
<Row gutter={16}>
|
||||
<Col span={8}><Card bordered title="Kafka Lag">{formatLag(health?.kafkaLag)}</Card></Col>
|
||||
<Col span={8}><Card bordered title="Redis 在线 Key">{formatLag(health?.redisOnlineKeys)}</Card></Col>
|
||||
<Col span={8}>
|
||||
<Col span={6}><Card bordered title="Kafka Lag">{formatLag(health?.kafkaLag)}</Card></Col>
|
||||
<Col span={6}><Card bordered title="Redis 在线 Key">{formatLag(health?.redisOnlineKeys)}</Card></Col>
|
||||
<Col span={6}><Card bordered title="BFF 请求超时">{formatRequestTimeout(health)}</Card></Col>
|
||||
<Col span={6}>
|
||||
<Card bordered title="存储读取">
|
||||
<Tag color={statusColor[storageReadStatus(health)]}>{storageReadStatus(health) === 'ok' ? '正常' : '异常'}</Tag>
|
||||
</Card>
|
||||
|
||||
Reference in New Issue
Block a user