chore: init bj_power monorepo (dashboard/mes/wms/wms_client/workstation), clear subproject git metadata and align naming to folder names

This commit is contained in:
SunYF
2026-08-27 10:09:54 +08:00
commit 1f0ee844a4
408 changed files with 102201 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
package log
type LogConf struct {
Level string `json:",default=INFO"` // 日志级别: DEBUG, INFO, WARN, ERROR
Format string `json:",default=text"` // 日志格式: text, json
TimeFormat string `json:",default=2006-01-02 15:04:05"` // 时间格式
Filename string `json:",default=spcm.log"` // 日志文件名
MaxSize int `json:",default=100"` // 单个日志文件最大大小(MB)
MaxBackups int `json:",default=10"` // 保留的旧日志文件数量
MaxAge int `json:",default=30"` // 日志文件保留天数
Compress bool `json:",default=true"` // 是否压缩旧日志文件
Console bool `json:",default=false"` // 是否同时输出到控制台
AddSource bool `json:",default=false"` // 是否在日志中添加文件名和行号
Stats bool `json:",default=false"`
}
+46
View File
@@ -0,0 +1,46 @@
package log
import (
"context"
"fmt"
"log/slog"
"runtime"
"strings"
"time"
)
type EntLogger struct {
*slog.Logger
}
func (l *EntLogger) DebugWithContext(ctx context.Context, v ...any) {
var pcs [1]uintptr
// skip [runtime.Callers, this function, this function's caller]
var pc uintptr
for i := 6; i < 20; i++ {
runtime.Callers(i, pcs[:])
frames := runtime.CallersFrames(pcs[:])
frame, _ := frames.Next()
//fmt.Println(i, frame.Function, frame.File, frame.Line)
if !strings.Contains(frame.File, "automatic_changer/ent") && !strings.Contains(frame.File, "entgo.io") {
pc = pcs[0]
break
}
}
r := slog.NewRecord(time.Now(), slog.LevelDebug, fmt.Sprint(v...), pc)
/*var attrs []slog.Attr
spanCtx := sdktrace.SpanContextFromContext(ctx)
if spanCtx.HasTraceID() {
attrs = append(attrs, slog.String("trace", spanCtx.TraceID().String()))
}
if spanCtx.HasSpanID() {
attrs = append(attrs, slog.String("span", spanCtx.SpanID().String()))
}
r.AddAttrs(attrs...)
if ctx == nil {
ctx = context.Background()
}*/
_ = l.Handler().Handle(ctx, r)
}
+67
View File
@@ -0,0 +1,67 @@
package log
import (
"context"
"io"
"log/slog"
"path/filepath"
"runtime"
"strings"
sdktrace "go.opentelemetry.io/otel/trace"
)
var projectRoot = getCompileTimeModuleRoot()
func getCompileTimeModuleRoot() string {
// 获取当前函数的调用信息
_, file, _, ok := runtime.Caller(0)
if !ok {
return ""
}
// file 就是编译时的绝对路径
return filepath.Dir(filepath.Dir(file))
}
func isProjectFile(file string) bool {
// 检查文件是否在编译时的模块根目录下
if projectRoot != "" {
if rel, err := filepath.Rel(projectRoot, file); err == nil {
if !strings.HasPrefix(rel, "..") {
return true
}
}
}
// 其他回退逻辑...
return false
}
func NewContextHandler(w io.Writer, encoding string, opts *slog.HandlerOptions) slog.Handler {
if strings.EqualFold(encoding, "json") {
return &ContextHandler{
Handler: slog.NewJSONHandler(w, opts),
}
}
return &ContextHandler{
Handler: slog.NewTextHandler(w, opts),
}
}
type ContextHandler struct {
slog.Handler
encoding string
}
func (h *ContextHandler) Handle(ctx context.Context, r slog.Record) error {
var attrs []slog.Attr
spanCtx := sdktrace.SpanContextFromContext(ctx)
if spanCtx.HasTraceID() {
attrs = append(attrs, slog.String("trace", spanCtx.TraceID().String()))
}
if spanCtx.HasSpanID() {
attrs = append(attrs, slog.String("span", spanCtx.SpanID().String()))
}
r.AddAttrs(attrs...)
return h.Handler.Handle(ctx, r)
}
+271
View File
@@ -0,0 +1,271 @@
package log
import (
"context"
"fmt"
"io"
"log/slog"
"os"
"path"
"path/filepath"
"runtime"
"strings"
"sync"
"time"
"github.com/zeromicro/go-zero/core/logx"
"go.opentelemetry.io/otel/trace"
"gopkg.in/natefinch/lumberjack.v2"
)
var workdir, _ = filepath.Abs(filepath.Dir(os.Args[0]))
type Logger struct {
*slog.Logger
}
func SetDefault(logger *Logger) {
slog.SetDefault(logger.Logger)
}
func NewLogger(conf LogConf) *Logger {
var log *slog.Logger
logLevel := slog.LevelInfo
switch strings.ToLower(conf.Level) {
case "error":
logLevel = slog.LevelError
case "warn":
logLevel = slog.LevelWarn
case "info":
logLevel = slog.LevelInfo
case "debug":
logLevel = slog.LevelDebug
}
if !path.IsAbs(conf.Filename) {
conf.Filename = filepath.Join(workdir, conf.Filename)
}
// 匧予𠧧嚗𡁏憭拐銝芣隞塚 bj_power_mes-YYYY-MM-DD.log
// 瘥誩予?lumberjack 匧之撠𧶏MaxSize嚗㗇靽萘 MaxBackups 銝芸隞?
dw := newDailyWriter(conf)
var writer io.Writer = dw
if conf.Console {
writer = io.MultiWriter(writer, os.Stdout)
}
log = slog.New(NewContextHandler(writer, conf.Format, &slog.HandlerOptions{
Level: logLevel,
AddSource: conf.AddSource,
ReplaceAttr: func(_ []string, attr slog.Attr) slog.Attr {
switch attr.Key {
case "caller":
return slog.Attr{}
case slog.SourceKey:
if source, ok := attr.Value.Any().(*slog.Source); ok {
if source.File == "" {
return slog.Attr{}
}
return slog.String(slog.SourceKey, " "+filepath.Base(source.File)+fmt.Sprintf(":%d", source.Line))
}
case slog.TimeKey:
return slog.String(slog.TimeKey, attr.Value.Time().Format(conf.TimeFormat))
}
return attr
},
}))
return &Logger{
Logger: log,
}
}
func (l *Logger) Alert(v any) {
l.Logger.Info("%v", v)
}
func (l *Logger) Close() error {
return nil
}
func (l *Logger) buildAttrs(fields []logx.LogField) []slog.Attr {
attrs := make([]slog.Attr, 0, len(fields))
for _, field := range fields {
attrs = append(attrs, slog.Attr{
Key: field.Key,
Value: slog.AnyValue(field.Value),
})
}
return attrs
}
func (l *Logger) DebugCtx(ctx context.Context, v ...any) {
if !l.Enabled(ctx, slog.LevelDebug) {
return
}
var pcs [1]uintptr
// skip [runtime.Callers, this function, this function's caller]
runtime.Callers(2, pcs[:])
pc := pcs[0]
r := slog.NewRecord(time.Now(), slog.LevelDebug, fmt.Sprint(v...), pc)
var attrs []slog.Attr
spanCtx := trace.SpanContextFromContext(ctx)
if spanCtx.HasTraceID() {
attrs = append(attrs, slog.String("trace", spanCtx.TraceID().String()))
}
if spanCtx.HasSpanID() {
attrs = append(attrs, slog.String("span", spanCtx.SpanID().String()))
}
r.AddAttrs(attrs...)
if ctx == nil {
ctx = context.Background()
}
_ = l.Handler().Handle(ctx, r)
}
func (l *Logger) log(ctx context.Context, level slog.Level, msg string, args ...any) {
if !l.Enabled(ctx, level) {
return
}
var pc uintptr
var pcs [1]uintptr
// skip [runtime.Callers, this function, this function's caller]
runtime.Callers(5, pcs[:])
pc = pcs[0]
r := slog.NewRecord(time.Now(), level, msg, pc)
r.Add(args...)
if ctx == nil {
ctx = context.Background()
}
_ = l.Handler().Handle(ctx, r)
}
func (l *Logger) logAttrs(ctx context.Context, level slog.Level, msg string, attrs ...slog.Attr) {
if !l.Enabled(ctx, level) {
return
}
var pcs [1]uintptr
// skip [runtime.Callers, this function, this function's caller]
runtime.Callers(5, pcs[:])
pc := pcs[0]
r := slog.NewRecord(time.Now(), level, msg, pc)
r.AddAttrs(attrs...)
if ctx == nil {
ctx = context.Background()
}
_ = l.Handler().Handle(ctx, r)
}
func (l *Logger) Debug(v any, fields ...logx.LogField) {
attrs := l.buildAttrs(fields)
l.logAttrs(context.Background(), slog.LevelDebug, fmt.Sprintf("%v", v), attrs...)
}
func (l *Logger) Error(v any, fields ...logx.LogField) {
attrs := l.buildAttrs(fields)
l.logAttrs(context.Background(), slog.LevelError, fmt.Sprintf("%v", v), attrs...)
}
// skipHttpPollLog 餈誘擃㗛頧株砭嚗𡁜蝡航蔭霂X𦻖𣂼 GET嚗㗇蝘埝㺭甈∪撅𧶏
// 靝遠潛凒乩腺撘雿頣POST/PUT/DELETE嚗劐?2xx 躰秤靽萘?
func skipHttpPollLog(v any) bool {
msg, ok := v.(string)
if !ok || !strings.HasPrefix(msg, "[HTTP]") {
return false
}
// : [HTTP] 200 - GET /api/v1/jobs - 127.0.0.1:49864 - UA
parts := strings.SplitN(msg, " - ", 3)
if len(parts) < 3 {
return false
}
code := strings.TrimSpace(strings.TrimPrefix(parts[0], "[HTTP]"))
if len(code) < 1 || code[0] != '2' {
return false
}
return strings.HasPrefix(parts[1], "GET ")
}
func (l *Logger) Info(v any, fields ...logx.LogField) {
if skipHttpPollLog(v) {
return
}
attrs := l.buildAttrs(fields)
l.logAttrs(context.Background(), slog.LevelInfo, fmt.Sprintf("%v", v), attrs...)
}
func (l *Logger) Severe(v any) {
l.log(context.Background(), slog.LevelError, fmt.Sprintf("%v", v))
}
func (l *Logger) Slow(v any, fields ...logx.LogField) {
if skipHttpPollLog(v) {
return
}
attrs := l.buildAttrs(fields)
l.logAttrs(context.Background(), slog.LevelWarn, fmt.Sprintf("%v", v), attrs...)
}
func (l *Logger) Stack(v any) {
l.log(context.Background(), slog.LevelError, fmt.Sprintf("%v", v))
}
func (l *Logger) Stat(v any, fields ...logx.LogField) {
attrs := l.buildAttrs(fields)
l.logAttrs(context.Background(), slog.LevelInfo, fmt.Sprintf("%v", v), attrs...)
}
// dailyWriter 匧予𠧧?
// 瘥𤩺活 Write 齿𠯫頝典予嗉䌊隞嗚?
// 瘥誩予眏 lumberjack 匧之撠𤩺靽萘 MaxBackups 銝芸隞賬?
type dailyWriter struct {
mu sync.Mutex
conf LogConf
baseDir string
baseName string // 銝滚鉄憒?"bj_power_mes"
ext string // 憒?".log"
currentDay string
lj *lumberjack.Logger
}
func newDailyWriter(conf LogConf) *dailyWriter {
dir := filepath.Dir(conf.Filename)
ext := filepath.Ext(conf.Filename)
base := strings.TrimSuffix(filepath.Base(conf.Filename), ext)
// 蝖桐摮睃銁
if err := os.MkdirAll(dir, 0755); err != nil {
// 𥕦遣憭梯揖啣虾嚗屸摨誩鍳典仃韐?
slog.Error("log: 𥕦遣憭梯揖嚗啣極雿𦦵𤌍敶?, "dir", dir, "error", err)
dir = workdir
}
return &dailyWriter{
conf: conf,
baseDir: dir,
baseName: base,
ext: ext,
}
}
func (w *dailyWriter) Write(p []byte) (n int, err error) {
w.mu.Lock()
defer w.mu.Unlock()
day := time.Now().Format("2006-01-02")
if day != w.currentDay {
// 頝典予嚗𡁜剜唂 writer嚗撱箸鰵
if w.lj != nil {
w.lj.Close()
}
filename := filepath.Join(w.baseDir, w.baseName+"-"+day+w.ext)
w.lj = &lumberjack.Logger{
Filename: filename,
MaxSize: w.conf.MaxSize,
MaxBackups: w.conf.MaxBackups,
MaxAge: w.conf.MaxAge,
Compress: w.conf.Compress,
}
w.currentDay = day
}
return w.lj.Write(p)
}