mirror of
https://github.com/certimate-go/certimate.git
synced 2026-09-24 23:10:13 +08:00
refactor: make log level to use int32
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
package domain
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"log/slog"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const CollectionNameWorkflowLog = "workflow_logs"
|
||||
|
||||
@@ -11,7 +14,7 @@ type WorkflowLog struct {
|
||||
NodeId string `json:"nodeId" db:"nodeId"`
|
||||
NodeName string `json:"nodeName" db:"nodeName"`
|
||||
Timestamp int64 `json:"timestamp" db:"timestamp"` // 毫秒级时间戳
|
||||
Level string `json:"level" db:"level"`
|
||||
Level int32 `json:"level" db:"level"`
|
||||
Message string `json:"message" db:"message"`
|
||||
Data map[string]any `json:"data" db:"data"`
|
||||
}
|
||||
@@ -21,7 +24,7 @@ type WorkflowLogs []WorkflowLog
|
||||
func (r WorkflowLogs) ErrorString() string {
|
||||
var builder strings.Builder
|
||||
for _, log := range r {
|
||||
if log.Level == "ERROR" {
|
||||
if log.Level >= int32(slog.LevelError) {
|
||||
builder.WriteString(log.Message)
|
||||
builder.WriteString("\n")
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ func (r *WorkflowLogRepository) castRecordToModel(record *core.Record) (*domain.
|
||||
NodeId: record.GetString("nodeId"),
|
||||
NodeName: record.GetString("nodeName"),
|
||||
Timestamp: int64(record.GetInt("timestamp")),
|
||||
Level: record.GetString("level"),
|
||||
Level: int32(record.GetInt("level")),
|
||||
Message: record.GetString("message"),
|
||||
Data: logdata,
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ func (w *workflowInvoker) processNode(ctx context.Context, node *domain.Workflow
|
||||
log.NodeId = current.Id
|
||||
log.NodeName = current.Name
|
||||
log.Timestamp = record.Time.UnixMilli()
|
||||
log.Level = record.Level.String()
|
||||
log.Level = int32(record.Level)
|
||||
log.Message = record.Message
|
||||
log.Data = record.Data
|
||||
log.CreatedAt = record.Time
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
m "github.com/pocketbase/pocketbase/migrations"
|
||||
)
|
||||
|
||||
func init() {
|
||||
m.Register(func(app core.App) error {
|
||||
tracer := NewTracer("v0.4.0")
|
||||
tracer.Printf("go ...")
|
||||
|
||||
// update collection `workflow_logs`
|
||||
{
|
||||
collection, err := app.FindCollectionByNameOrId("pbc_1682296116")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
field := collection.Fields.GetByName("level")
|
||||
if field != nil && field.Type() == "text" {
|
||||
// add temp field `levelTmp`
|
||||
if err := collection.Fields.AddMarshaledJSONAt(7, []byte(`{
|
||||
"hidden": false,
|
||||
"id": "number760395071",
|
||||
"max": null,
|
||||
"min": null,
|
||||
"name": "levelTmp",
|
||||
"onlyInt": false,
|
||||
"presentable": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "number"
|
||||
}`)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := app.Save(collection); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// copy `level` to `levelTmp`
|
||||
if _, err := app.DB().NewQuery("UPDATE workflow_logs SET levelTmp = -4 WHERE level = 'DEBUG'").Execute(); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := app.DB().NewQuery("UPDATE workflow_logs SET levelTmp = 4 WHERE level = 'WARN'").Execute(); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := app.DB().NewQuery("UPDATE workflow_logs SET levelTmp = 8 WHERE level = 'ERROR'").Execute(); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := app.DB().NewQuery("UPDATE workflow_logs SET levelTmp = 0 WHERE levelTmp IS NULL").Execute(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// remove old field `level`
|
||||
collection.Fields.RemoveById(field.GetId())
|
||||
if err := app.Save(collection); err != nil {
|
||||
println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
// rename field `levelTmp` to `level`
|
||||
if err := collection.Fields.AddMarshaledJSONAt(6, []byte(`{
|
||||
"hidden": false,
|
||||
"id": "number760395071",
|
||||
"max": null,
|
||||
"min": null,
|
||||
"name": "level",
|
||||
"onlyInt": false,
|
||||
"presentable": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "number"
|
||||
}`)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := app.Save(collection); err != nil {
|
||||
println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
tracer.Printf("collection '%s' updated", collection.Name)
|
||||
}
|
||||
}
|
||||
|
||||
tracer.Printf("done")
|
||||
return nil
|
||||
}, func(app core.App) error {
|
||||
return nil
|
||||
})
|
||||
}
|
||||
+1
-14
@@ -2,7 +2,6 @@ package logging
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"sync"
|
||||
|
||||
@@ -114,22 +113,10 @@ func (h *HookHandler) Handle(ctx context.Context, r slog.Record) error {
|
||||
|
||||
log := &Record{
|
||||
Time: r.Time,
|
||||
Level: r.Level,
|
||||
Message: r.Message,
|
||||
Data: types.JSONMap[any](data),
|
||||
}
|
||||
switch r.Level {
|
||||
case slog.LevelDebug:
|
||||
log.Level = LevelDebug
|
||||
case slog.LevelInfo:
|
||||
log.Level = LevelInfo
|
||||
case slog.LevelWarn:
|
||||
log.Level = LevelWarn
|
||||
case slog.LevelError:
|
||||
log.Level = LevelError
|
||||
default:
|
||||
log.Level = Level(fmt.Sprintf("LV(%d)", r.Level))
|
||||
}
|
||||
|
||||
if err := h.writeRecord(ctx, log); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
package logging
|
||||
|
||||
import "log/slog"
|
||||
|
||||
type Level string
|
||||
|
||||
const (
|
||||
LevelDebug Level = "DEBUG"
|
||||
LevelInfo Level = "INFO"
|
||||
LevelWarn Level = "WARN"
|
||||
LevelError Level = "ERROR"
|
||||
)
|
||||
|
||||
func (l Level) String() string {
|
||||
return string(l)
|
||||
}
|
||||
|
||||
func (l Level) Level() slog.Level {
|
||||
switch l {
|
||||
case LevelDebug:
|
||||
return slog.LevelDebug
|
||||
case LevelInfo:
|
||||
return slog.LevelInfo
|
||||
case LevelWarn:
|
||||
return slog.LevelWarn
|
||||
case LevelError:
|
||||
return slog.LevelError
|
||||
default:
|
||||
return slog.Level(-1)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
types "github.com/pocketbase/pocketbase/tools/types"
|
||||
@@ -8,7 +9,7 @@ import (
|
||||
|
||||
type Record struct {
|
||||
Time time.Time
|
||||
Level Level
|
||||
Level slog.Level
|
||||
Message string
|
||||
Data types.JSONMap[any]
|
||||
}
|
||||
|
||||
@@ -93,6 +93,7 @@ const RootApp = () => {
|
||||
Layout: {
|
||||
...antdTheme?.components?.Layout,
|
||||
bodyBg: "transparent",
|
||||
headerBg: "transparent",
|
||||
siderBg: "transparent",
|
||||
},
|
||||
Dropdown: {
|
||||
|
||||
@@ -9,7 +9,7 @@ import { ClientResponseError } from "pocketbase";
|
||||
import CertificateDetailDrawer from "@/components/certificate/CertificateDetailDrawer";
|
||||
import Show from "@/components/Show";
|
||||
import { type CertificateModel } from "@/domain/certificate";
|
||||
import { type WorkflowLogModel } from "@/domain/workflowLog";
|
||||
import { WorkflowLogLevel, type WorkflowLogModel } from "@/domain/workflowLog";
|
||||
import { WORKFLOW_RUN_STATUSES, type WorkflowRunModel } from "@/domain/workflowRun";
|
||||
import { useBrowserTheme } from "@/hooks";
|
||||
import { listByWorkflowRunId as listCertificatesByWorkflowRunId } from "@/repository/certificate";
|
||||
@@ -138,10 +138,14 @@ const WorkflowRunLogs = ({ runId, runStatus }: { runId: string; runStatus: strin
|
||||
<div
|
||||
className={mergeCls(
|
||||
"font-mono",
|
||||
record.level === "DEBUG" ? "text-stone-400" : "",
|
||||
record.level === "WARN" ? "text-yellow-600" : "",
|
||||
record.level === "ERROR" ? "text-red-600" : "",
|
||||
!showWhitespace ? "whitespace-pre-line" : ""
|
||||
record.level < WorkflowLogLevel.Info
|
||||
? "text-stone-400"
|
||||
: record.level < WorkflowLogLevel.Warn
|
||||
? ""
|
||||
: record.level < WorkflowLogLevel.Error
|
||||
? "text-warning"
|
||||
: "text-error",
|
||||
{ ["whitespace-pre-line"]: !showWhitespace }
|
||||
)}
|
||||
>
|
||||
{message}
|
||||
@@ -259,94 +263,6 @@ const WorkflowRunLogs = ({ runId, runStatus }: { runId: string; runStatus: strin
|
||||
);
|
||||
};
|
||||
|
||||
const WorkflowRunArtifacts = ({ runId }: { runId: string }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { notification } = App.useApp();
|
||||
|
||||
const tableColumns: TableProps<CertificateModel>["columns"] = [
|
||||
{
|
||||
key: "$index",
|
||||
align: "center",
|
||||
fixed: "left",
|
||||
width: 50,
|
||||
render: (_, __, index) => index + 1,
|
||||
},
|
||||
{
|
||||
key: "type",
|
||||
title: t("workflow_run_artifact.props.type"),
|
||||
render: () => t("workflow_run_artifact.props.type.certificate"),
|
||||
},
|
||||
{
|
||||
key: "name",
|
||||
title: t("workflow_run_artifact.props.name"),
|
||||
render: (_, record) => {
|
||||
return (
|
||||
<div className="max-w-full truncate">
|
||||
<Typography.Text delete={!!record.deleted} ellipsis>
|
||||
{record.subjectAltNames}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "$action",
|
||||
align: "end",
|
||||
width: 120,
|
||||
render: (_, record) => (
|
||||
<div className="flex items-center justify-end">
|
||||
<CertificateDetailDrawer
|
||||
data={record}
|
||||
trigger={
|
||||
<Tooltip title={t("common.button.view")}>
|
||||
<Button color="primary" disabled={!!record.deleted} icon={<IconBrowserShare size="1.25em" />} variant="text" />
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
];
|
||||
const [tableData, setTableData] = useState<CertificateModel[]>([]);
|
||||
const { loading: tableLoading } = useRequest(
|
||||
() => {
|
||||
return listCertificatesByWorkflowRunId(runId);
|
||||
},
|
||||
{
|
||||
refreshDeps: [runId],
|
||||
onSuccess: (res) => {
|
||||
setTableData(res.items);
|
||||
},
|
||||
onError: (err) => {
|
||||
if (err instanceof ClientResponseError && err.isAbort) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.error(err);
|
||||
notification.error({ message: t("common.text.request_error"), description: getErrMsg(err) });
|
||||
|
||||
throw err;
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Typography.Title level={5}>{t("workflow_run.artifacts")}</Typography.Title>
|
||||
<Table<CertificateModel>
|
||||
columns={tableColumns}
|
||||
dataSource={tableData}
|
||||
loading={tableLoading}
|
||||
locale={{
|
||||
emptyText: <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />,
|
||||
}}
|
||||
pagination={false}
|
||||
rowKey={(record) => record.id}
|
||||
size="small"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
const WorkflowRunArtifacts = ({ runId }: { runId: string }) => {};
|
||||
|
||||
export default WorkflowRunDetail;
|
||||
|
||||
@@ -2,7 +2,14 @@ export interface WorkflowLogModel extends Omit<BaseModel, "updated"> {
|
||||
nodeId: string;
|
||||
nodeName: string;
|
||||
timestamp: ReturnType<typeof Date.prototype.getTime>;
|
||||
level: "DEBUG" | "INFO" | "WARN" | "ERROR";
|
||||
level: number;
|
||||
message: string;
|
||||
data: Record<string, any>;
|
||||
}
|
||||
|
||||
export enum WorkflowLogLevel {
|
||||
Debug = -4,
|
||||
Info = 0,
|
||||
Warn = 4,
|
||||
Error = 8,
|
||||
}
|
||||
|
||||
@@ -12,10 +12,10 @@ import {
|
||||
IconMenu2,
|
||||
IconSettings,
|
||||
} from "@tabler/icons-react";
|
||||
import { Alert, Button, Drawer, Layout, Menu, type MenuProps, theme } from "antd";
|
||||
import { Alert, Button, Divider, Drawer, Layout, Menu, type MenuProps, theme } from "antd";
|
||||
|
||||
import AppLocale, { useAppLocaleMenuItems } from "@/components/AppLocale";
|
||||
import AppTheme, { useAppThemeMenuItems } from "@/components/AppTheme";
|
||||
import AppLocale from "@/components/AppLocale";
|
||||
import AppTheme from "@/components/AppTheme";
|
||||
import AppVersion from "@/components/AppVersion";
|
||||
import Show from "@/components/Show";
|
||||
import { APP_DOCUMENT_URL, APP_REPO_URL } from "@/domain/app";
|
||||
@@ -30,9 +30,6 @@ const ConsoleLayout = () => {
|
||||
|
||||
const { token: themeToken } = theme.useToken();
|
||||
|
||||
const localeMenuItems = useAppLocaleMenuItems();
|
||||
const themeMenuItems = useAppThemeMenuItems();
|
||||
|
||||
const handleLogoutClick = () => {
|
||||
auth.clear();
|
||||
navigate("/login");
|
||||
@@ -76,26 +73,6 @@ const ConsoleLayout = () => {
|
||||
<Menu
|
||||
style={{ background: "transparent", borderInlineEnd: "none" }}
|
||||
items={[
|
||||
{
|
||||
key: "theme",
|
||||
icon: (
|
||||
<span className="anticon scale-125" role="img">
|
||||
<AppTheme.Icon size="1em" />
|
||||
</span>
|
||||
),
|
||||
label: t("common.menu.theme"),
|
||||
children: themeMenuItems,
|
||||
},
|
||||
{
|
||||
key: "locale",
|
||||
icon: (
|
||||
<span className="anticon scale-115" role="img">
|
||||
<AppLocale.Icon size="1em" />
|
||||
</span>
|
||||
),
|
||||
label: t("common.menu.locale"),
|
||||
children: localeMenuItems,
|
||||
},
|
||||
{
|
||||
key: "document",
|
||||
icon: (
|
||||
@@ -126,7 +103,7 @@ const ConsoleLayout = () => {
|
||||
</Layout.Sider>
|
||||
|
||||
<Layout className="flex flex-col overflow-hidden">
|
||||
<Layout.Header className="shadow-xs md:hidden" style={{ padding: 0 }}>
|
||||
<Layout.Header className="border-b shadow-sm md:hidden" style={{ padding: 0, borderBottomColor: themeToken.colorBorderSecondary }}>
|
||||
<div className="flex size-full items-center justify-between overflow-hidden px-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<SiderMenuDrawer trigger={<Button icon={<IconMenu2 size="1.25em" stroke="1.25" />} />} />
|
||||
|
||||
@@ -52,7 +52,7 @@ const SettingsAppearanceTheme = ({ className, style }: { className?: string; sty
|
||||
<div className="flex w-full items-center gap-4 max-md:flex-wrap">
|
||||
{themeItems.map((item) => (
|
||||
<div className="relative max-w-44 flex-1/3 max-md:flex-1/2 max-sm:flex-1" key={item.key}>
|
||||
<div className="overflow-hidden rounded-lg border border-solid" style={{ borderColor: themeToken.colorBorderSecondary }}>
|
||||
<div className="overflow-hidden rounded-lg border border-solid" style={{ borderColor: themeToken.colorBorder }}>
|
||||
<img className="mb-2 w-full" src={`/imgs/themes/${item.key}.png`} />
|
||||
<div className="mb-2 px-2">
|
||||
<Radio value={item.key}>{item.label}</Radio>
|
||||
|
||||
Reference in New Issue
Block a user