chore(runtime): Add logrus logging framework with structured logging

Add logrus as the primary logging framework with dependency injection
pattern. All errors now use WithError() for context, and structured
logging uses camelCase field names. Tag runtime service with service
field for better log organization.

- Add logrus dependency
- Update Runtime to accept logger via dependency injection
- Add WithLogger() option for logger configuration
- Log errors with WithError() for context
- Log async function failures with service context
- Document logging practices in AGENTS.md
This commit is contained in:
2026-01-29 20:28:19 -05:00
parent 23730868b8
commit f308970531
7 changed files with 88 additions and 6 deletions

View File

@@ -106,6 +106,62 @@ functions.RegisterAsyncFunction[FetchArgs, *FetchResult]("fetch", Fetch)
- `github.com/evanw/esbuild/pkg/api` - TypeScript transpilation
- `github.com/fastschema/qjs` - JavaScript execution (CGO-free QuickJS runtime)
- `github.com/stretchr/testify/assert` - Test assertions
- `github.com/sirupsen/logrus` - Logging framework
## Logging Practices
### Primary Logger
Use `github.com/sirupsen/logrus` as the primary logging framework throughout the codebase.
### Error Handling
- Use `WithError(err)` when logging errors to include the error context
- Every error should eventually be logged somewhere appropriate in the call chain
- Don't log on every error condition - find the appropriate upstream logging location
- Use structured logging with context fields when relevant for debugging
### Structured Logging
- Use `WithField(key, value)` for contextual information
- Use `WithFields(fields)` when adding multiple fields
- Field names must be in camelCase (e.g., `WithField("filePath", path)`)
- Only use WithField(s) when the field is relevant and helpful for debugging issues
- Tag services with their name: `logger.WithField("service", "runtime")`
### Example Usage
```go
import "github.com/sirupsen/logrus"
func New(ctx context.Context) (*Runtime, error) {
logger := logrus.New()
r := &Runtime{logger: logger.WithField("service", "runtime")}
rt, err := qjs.New(r.opts)
if err != nil {
logger.WithError(err).Error("Failed to create QuickJS context")
return nil, err
}
return r, nil
}
func (r *Runtime) ExecuteFile(filePath string) error {
data, err := os.ReadFile(filePath)
if err != nil {
return fmt.Errorf("read file %s: %w", filePath, err)
}
result, err := r.ctx.Eval("code.ts", qjs.Code(string(data)))
if err != nil {
r.logger.WithField("filePath", filePath).WithError(err).Error("Execution failed")
return err
}
return nil
}
```
## Code Conventions