@ai-content-space/loopx 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +422 -57
- package/README.zh-CN.md +485 -0
- package/assets/logo.svg +89 -0
- package/package.json +5 -1
- package/plugins/loopx/.codex-plugin/plugin.json +1 -1
- package/plugins/loopx/scripts/plugin-install.test.mjs +14 -0
- package/plugins/loopx/skills/archive/SKILL.md +49 -0
- package/plugins/loopx/skills/build/SKILL.md +111 -9
- package/plugins/loopx/skills/clarify/SKILL.md +129 -8
- package/plugins/loopx/skills/debug/SKILL.md +296 -0
- package/plugins/loopx/skills/debug/condition-based-waiting.md +115 -0
- package/plugins/loopx/skills/debug/defense-in-depth.md +122 -0
- package/plugins/loopx/skills/debug/find-polluter.sh +63 -0
- package/plugins/loopx/skills/debug/root-cause-tracing.md +169 -0
- package/plugins/loopx/skills/go-style/SKILL.md +71 -0
- package/plugins/loopx/skills/kratos/SKILL.md +74 -0
- package/plugins/loopx/skills/kratos/references/advanced-features.md +314 -0
- package/plugins/loopx/skills/kratos/references/architecture.md +488 -0
- package/plugins/loopx/skills/kratos/references/configuration.md +399 -0
- package/plugins/loopx/skills/kratos/references/http-customization.md +512 -0
- package/plugins/loopx/skills/kratos/references/middleware-logging.md +400 -0
- package/plugins/loopx/skills/kratos/references/proto-api-design.md +432 -0
- package/plugins/loopx/skills/kratos/references/security-auth.md +411 -0
- package/plugins/loopx/skills/kratos/references/troubleshooting.md +385 -0
- package/plugins/loopx/skills/plan/SKILL.md +24 -3
- package/plugins/loopx/skills/review/SKILL.md +98 -1
- package/plugins/loopx/skills/tdd/SKILL.md +371 -0
- package/plugins/loopx/skills/tdd/testing-anti-patterns.md +299 -0
- package/plugins/loopx/skills/verify/SKILL.md +139 -0
- package/scripts/codex-stop-hook.mjs +71 -0
- package/scripts/codex-workflow-hook.mjs +248 -0
- package/skills/archive/SKILL.md +49 -0
- package/skills/build/SKILL.md +111 -9
- package/skills/clarify/SKILL.md +129 -8
- package/skills/debug/SKILL.md +296 -0
- package/skills/debug/condition-based-waiting.md +115 -0
- package/skills/debug/defense-in-depth.md +122 -0
- package/skills/debug/find-polluter.sh +63 -0
- package/skills/debug/root-cause-tracing.md +169 -0
- package/skills/go-style/SKILL.md +71 -0
- package/skills/kratos/SKILL.md +74 -0
- package/skills/kratos/references/advanced-features.md +314 -0
- package/skills/kratos/references/architecture.md +488 -0
- package/skills/kratos/references/configuration.md +399 -0
- package/skills/kratos/references/http-customization.md +512 -0
- package/skills/kratos/references/middleware-logging.md +400 -0
- package/skills/kratos/references/proto-api-design.md +432 -0
- package/skills/kratos/references/security-auth.md +411 -0
- package/skills/kratos/references/troubleshooting.md +385 -0
- package/skills/plan/SKILL.md +20 -3
- package/skills/review/SKILL.md +98 -1
- package/skills/tdd/SKILL.md +371 -0
- package/skills/tdd/testing-anti-patterns.md +299 -0
- package/skills/verify/SKILL.md +139 -0
- package/src/build-runtime.mjs +311 -26
- package/src/build-stop-gate.mjs +94 -0
- package/src/cli.mjs +57 -5
- package/src/codex-exec-runtime.mjs +105 -5
- package/src/context-manifest.mjs +172 -0
- package/src/html-views.mjs +316 -0
- package/src/install-discovery.mjs +352 -5
- package/src/next-skill.mjs +57 -5
- package/src/plan-runtime.mjs +102 -122
- package/src/review-runtime.mjs +558 -0
- package/src/runtime-maintenance.mjs +429 -14
- package/src/template-governance.mjs +223 -0
- package/src/workflow.mjs +2341 -120
- package/src/workspace-context.mjs +166 -0
- package/src/workspace-memory.mjs +69 -0
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
# Middleware & Logging
|
|
2
|
+
|
|
3
|
+
Guide for middleware development and log management in Kratos.
|
|
4
|
+
|
|
5
|
+
## When to Use
|
|
6
|
+
|
|
7
|
+
- Creating custom middleware
|
|
8
|
+
- Extracting request context
|
|
9
|
+
- Setting up log filtering
|
|
10
|
+
- Configuring middleware chains
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Middleware Skeleton
|
|
15
|
+
|
|
16
|
+
### Basic Structure
|
|
17
|
+
|
|
18
|
+
```go
|
|
19
|
+
import (
|
|
20
|
+
"context"
|
|
21
|
+
"github.com/go-kratos/kratos/v2/middleware"
|
|
22
|
+
"github.com/go-kratos/kratos/v2/transport"
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
func MyMiddleware() middleware.Middleware {
|
|
26
|
+
return func(handler middleware.Handler) middleware.Handler {
|
|
27
|
+
return func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
28
|
+
// === PRE-PROCESSING ===
|
|
29
|
+
// Extract info, validate, modify request
|
|
30
|
+
|
|
31
|
+
// === CALL HANDLER ===
|
|
32
|
+
reply, err := handler(ctx, req)
|
|
33
|
+
|
|
34
|
+
// === POST-PROCESSING ===
|
|
35
|
+
// Modify response, handle errors, log
|
|
36
|
+
|
|
37
|
+
return reply, err
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Registration
|
|
44
|
+
|
|
45
|
+
```go
|
|
46
|
+
import (
|
|
47
|
+
"github.com/go-kratos/kratos/v2/middleware/recovery"
|
|
48
|
+
"github.com/go-kratos/kratos/v2/middleware/validate"
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
srv := http.NewServer(
|
|
52
|
+
http.Address(":8000"),
|
|
53
|
+
http.Middleware(
|
|
54
|
+
recovery.Recovery(),
|
|
55
|
+
validate.Validator(),
|
|
56
|
+
MyMiddleware(),
|
|
57
|
+
),
|
|
58
|
+
)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Transport Extraction
|
|
64
|
+
|
|
65
|
+
### Get Request Info
|
|
66
|
+
|
|
67
|
+
```go
|
|
68
|
+
func MyMiddleware() middleware.Middleware {
|
|
69
|
+
return func(handler middleware.Handler) middleware.Handler {
|
|
70
|
+
return func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
71
|
+
if tr, ok := transport.FromServerContext(ctx); ok {
|
|
72
|
+
// Generic transport info
|
|
73
|
+
operation := tr.Operation()
|
|
74
|
+
kind := tr.Kind() // transport.KindHTTP or KindGRPC
|
|
75
|
+
|
|
76
|
+
// HTTP-specific info
|
|
77
|
+
if hr, ok := tr.(*http.Transport); ok {
|
|
78
|
+
method := hr.Request().Method
|
|
79
|
+
url := hr.Request().URL.String()
|
|
80
|
+
path := hr.Request().URL.Path
|
|
81
|
+
|
|
82
|
+
// Headers
|
|
83
|
+
userAgent := hr.RequestHeader().Get("User-Agent")
|
|
84
|
+
auth := hr.RequestHeader().Get("Authorization")
|
|
85
|
+
contentType := hr.RequestHeader().Get("Content-Type")
|
|
86
|
+
|
|
87
|
+
// Set response header
|
|
88
|
+
hr.ResponseHeader().Set("X-Response-Time", "100ms")
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return handler(ctx, req)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Set Response Header in Service
|
|
99
|
+
|
|
100
|
+
```go
|
|
101
|
+
func (s *UserService) GetUser(ctx context.Context, req *v1.GetUserRequest) (*v1.GetUserResponse, error) {
|
|
102
|
+
if httpCtx, ok := ctx.(http.Context); ok {
|
|
103
|
+
httpCtx.Response().Header().Set("X-Custom", "value")
|
|
104
|
+
}
|
|
105
|
+
// Business logic...
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Log Filtering
|
|
112
|
+
|
|
113
|
+
Kratos provides log filtering for sensitive data:
|
|
114
|
+
|
|
115
|
+
### Filter by Level
|
|
116
|
+
|
|
117
|
+
```go
|
|
118
|
+
import "github.com/go-kratos/kratos/v2/log"
|
|
119
|
+
|
|
120
|
+
h := log.NewHelper(
|
|
121
|
+
log.NewFilter(logger,
|
|
122
|
+
log.FilterLevel(log.LevelError), // Only log errors
|
|
123
|
+
),
|
|
124
|
+
)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Filter by Key
|
|
128
|
+
|
|
129
|
+
```go
|
|
130
|
+
h := log.NewHelper(
|
|
131
|
+
log.NewFilter(logger,
|
|
132
|
+
log.FilterKey("password"), // Hide password field
|
|
133
|
+
),
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
h.Info("password", "secret123") // Output: password "***"
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Filter by Value
|
|
140
|
+
|
|
141
|
+
```go
|
|
142
|
+
h := log.NewHelper(
|
|
143
|
+
log.NewFilter(logger,
|
|
144
|
+
log.FilterValue("secret"), // Hide exact value "secret"
|
|
145
|
+
),
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
h.Info("token", "secret") // Output: token "***"
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Custom Filter Function
|
|
152
|
+
|
|
153
|
+
```go
|
|
154
|
+
h := log.NewHelper(
|
|
155
|
+
log.NewFilter(logger,
|
|
156
|
+
log.FilterFunc(func(level log.Level, keyvals ...interface{}) bool {
|
|
157
|
+
// keyvals is alternating: key, value, key, value...
|
|
158
|
+
for i := 0; i < len(keyvals); i += 2 {
|
|
159
|
+
key := keyvals[i]
|
|
160
|
+
value := keyvals[i+1]
|
|
161
|
+
|
|
162
|
+
if key == "token" {
|
|
163
|
+
keyvals[i+1] = "***MASKED***"
|
|
164
|
+
}
|
|
165
|
+
if key == "password" {
|
|
166
|
+
keyvals[i+1] = "***"
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return false // Return false to continue logging
|
|
170
|
+
}),
|
|
171
|
+
),
|
|
172
|
+
)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Recovery Middleware
|
|
178
|
+
|
|
179
|
+
Catch panics and return proper errors:
|
|
180
|
+
|
|
181
|
+
```go
|
|
182
|
+
import "github.com/go-kratos/kratos/v2/middleware/recovery"
|
|
183
|
+
|
|
184
|
+
srv := http.NewServer(
|
|
185
|
+
http.Address(":8000"),
|
|
186
|
+
http.Middleware(
|
|
187
|
+
recovery.Recovery(), // Always first in chain
|
|
188
|
+
),
|
|
189
|
+
)
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
With custom recovery handler:
|
|
193
|
+
|
|
194
|
+
```go
|
|
195
|
+
srv := http.NewServer(
|
|
196
|
+
http.Middleware(
|
|
197
|
+
recovery.Recovery(
|
|
198
|
+
recovery.WithHandler(func(ctx context.Context, req, err interface{}) error {
|
|
199
|
+
log.Errorf("panic recovered: %v", err)
|
|
200
|
+
return errors.New(500, "INTERNAL_ERROR", "internal server error")
|
|
201
|
+
}),
|
|
202
|
+
),
|
|
203
|
+
),
|
|
204
|
+
)
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Validate Middleware
|
|
210
|
+
|
|
211
|
+
Kratos provides two approaches for protovalidate integration:
|
|
212
|
+
|
|
213
|
+
### Approach 1: Middleware-level Validation
|
|
214
|
+
|
|
215
|
+
Use `validate.Validator()` middleware for automatic request validation:
|
|
216
|
+
|
|
217
|
+
```go
|
|
218
|
+
import (
|
|
219
|
+
"buf.build/go/protovalidate"
|
|
220
|
+
"github.com/go-kratos/kratos/v2/middleware/validate"
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
func provideValidator() (protovalidate.Validator, error) {
|
|
224
|
+
v, err := protovalidate.New()
|
|
225
|
+
if err != nil {
|
|
226
|
+
return nil, err
|
|
227
|
+
}
|
|
228
|
+
return v, nil
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
srv := http.NewServer(
|
|
232
|
+
http.Middleware(
|
|
233
|
+
validate.Validator(provideValidator()), // Validates all proto requests
|
|
234
|
+
),
|
|
235
|
+
)
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Approach 2: Service-level Validation
|
|
239
|
+
|
|
240
|
+
For selective validation or custom error handling, validate in service layer:
|
|
241
|
+
|
|
242
|
+
```go
|
|
243
|
+
type MyService struct {
|
|
244
|
+
v1.UnimplementedMyServiceServer
|
|
245
|
+
validator protovalidate.Validator
|
|
246
|
+
uc *biz.MyUseCase
|
|
247
|
+
log *log.Helper
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
func NewMyService(uc *biz.MyUseCase, logger log.Logger, validator protovalidate.Validator) *MyService {
|
|
251
|
+
return &MyService{
|
|
252
|
+
uc: uc,
|
|
253
|
+
validator: validator,
|
|
254
|
+
log: log.NewHelper(logger),
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
func (s *MyService) GetUser(ctx context.Context, req *v1.GetUserRequest) (*v1.GetUserResponse, error) {
|
|
259
|
+
// Validate request before business logic
|
|
260
|
+
if err := s.validator.Validate(req); err != nil {
|
|
261
|
+
return nil, v1.ErrorInvalidArgument("validation failed: %v", err)
|
|
262
|
+
}
|
|
263
|
+
return s.uc.GetUser(ctx, req.UserId)
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
**When to use each approach:**
|
|
268
|
+
- **Middleware-level**: All requests need validation, consistent error format
|
|
269
|
+
- **Service-level**: Selective validation, partial updates, custom error messages
|
|
270
|
+
|
|
271
|
+
**Note:** For partial updates (PATCH), you may need to skip validation on optional fields. Use service-level validation for this case.
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## Middleware Chain Ordering
|
|
276
|
+
|
|
277
|
+
Recommended order for HTTP server:
|
|
278
|
+
|
|
279
|
+
```go
|
|
280
|
+
srv := http.NewServer(
|
|
281
|
+
http.Middleware(
|
|
282
|
+
// 1. Recovery - catch panics first
|
|
283
|
+
recovery.Recovery(),
|
|
284
|
+
|
|
285
|
+
// 2. Tracing - set trace context
|
|
286
|
+
tracing.Server(),
|
|
287
|
+
|
|
288
|
+
// 3. Logging - log requests
|
|
289
|
+
logging.Server(logger),
|
|
290
|
+
|
|
291
|
+
// 4. Authentication - verify identity
|
|
292
|
+
jwt.Server(signingKey),
|
|
293
|
+
|
|
294
|
+
// 5. Authorization - check permissions
|
|
295
|
+
casbinM.Server(enforcer),
|
|
296
|
+
|
|
297
|
+
// 6. Validate - check request format
|
|
298
|
+
validate.Validator(),
|
|
299
|
+
|
|
300
|
+
// 7. Rate limiting - prevent abuse
|
|
301
|
+
ratelimit.Server(),
|
|
302
|
+
|
|
303
|
+
// 8. Business middleware
|
|
304
|
+
MyBusinessMiddleware(),
|
|
305
|
+
),
|
|
306
|
+
)
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
**Order rationale:**
|
|
310
|
+
- Recovery first: catch all panics
|
|
311
|
+
- Tracing/logging early: capture full request
|
|
312
|
+
- Auth before validate: don't validate unauthorized requests
|
|
313
|
+
- Validate before business: don't process invalid requests
|
|
314
|
+
- Rate limit last before business: allow legitimate requests through
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## Logging Helper Pattern
|
|
319
|
+
|
|
320
|
+
```go
|
|
321
|
+
import "github.com/go-kratos/kratos/v2/log"
|
|
322
|
+
|
|
323
|
+
type MyUseCase struct {
|
|
324
|
+
log *log.Helper
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
func NewMyUseCase(logger log.Logger) *MyUseCase {
|
|
328
|
+
return &MyUseCase{
|
|
329
|
+
log: log.NewHelper(
|
|
330
|
+
log.With(logger,
|
|
331
|
+
"module", "biz/myUseCase",
|
|
332
|
+
"service", "myService",
|
|
333
|
+
),
|
|
334
|
+
),
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
func (uc *MyUseCase) DoSomething(ctx context.Context) {
|
|
339
|
+
// Log with context (includes trace info)
|
|
340
|
+
uc.log.WithContext(ctx).Infof("doing something")
|
|
341
|
+
|
|
342
|
+
// Log error
|
|
343
|
+
uc.log.WithContext(ctx).Errorf("error occurred: %v", err)
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
349
|
+
## Request/Response Logging
|
|
350
|
+
|
|
351
|
+
```go
|
|
352
|
+
func LoggingMiddleware(logger log.Logger) middleware.Middleware {
|
|
353
|
+
h := log.NewHelper(logger)
|
|
354
|
+
|
|
355
|
+
return func(handler middleware.Handler) middleware.Handler {
|
|
356
|
+
return func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
357
|
+
// Log request
|
|
358
|
+
if tr, ok := transport.FromServerContext(ctx); ok {
|
|
359
|
+
h.WithContext(ctx).Infof(
|
|
360
|
+
"request: operation=%s kind=%s",
|
|
361
|
+
tr.Operation(),
|
|
362
|
+
tr.Kind(),
|
|
363
|
+
)
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Call handler
|
|
367
|
+
start := time.Now()
|
|
368
|
+
reply, err := handler(ctx, req)
|
|
369
|
+
duration := time.Since(start)
|
|
370
|
+
|
|
371
|
+
// Log response
|
|
372
|
+
h.WithContext(ctx).Infof(
|
|
373
|
+
"response: duration=%dms error=%v",
|
|
374
|
+
duration.Milliseconds(),
|
|
375
|
+
err,
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
return reply, err
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
---
|
|
385
|
+
|
|
386
|
+
## Custom Operation for Non-Proto Routes
|
|
387
|
+
|
|
388
|
+
```go
|
|
389
|
+
func (s *UploadService) uploadFile(ctx http.Context) error {
|
|
390
|
+
// Set operation for middleware to identify
|
|
391
|
+
http.SetOperation(ctx, "/upload.v1.UploadService/Upload")
|
|
392
|
+
|
|
393
|
+
// Now middleware can use tr.Operation() = "/upload.v1.UploadService/Upload"
|
|
394
|
+
h := ctx.Middleware(func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
395
|
+
return s.uc.Upload(ctx, req.(*UploadRequest))
|
|
396
|
+
})
|
|
397
|
+
|
|
398
|
+
resp, err := h(ctx, &req)
|
|
399
|
+
return ctx.JSON(200, resp)
|
|
400
|
+
}
|