You've already forked agentic-coding-workflow
fix(logger): expose AddSource option; FromEnv enables caller in non-dev (spec §8.2)
This commit is contained in:
@@ -28,12 +28,14 @@ const (
|
|||||||
LevelError = slog.LevelError
|
LevelError = slog.LevelError
|
||||||
)
|
)
|
||||||
|
|
||||||
// Options 控制日志器的输出格式、最低级别与目标 Writer。
|
// Options 控制日志器的输出格式、最低级别、目标 Writer 与是否记录调用位置。
|
||||||
// Writer 留空时默认写入 os.Stdout。
|
// Writer 留空时默认写入 os.Stdout。
|
||||||
|
// AddSource 为 true 时日志附带调用文件:行号;零值 false(开发环境噪声大,按需开启)。
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Format Format
|
Format Format
|
||||||
Level Level
|
Level Level
|
||||||
Writer io.Writer
|
Writer io.Writer
|
||||||
|
AddSource bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// New 按 Options 构造一个 *slog.Logger。Format 非 FormatText 时一律使用 JSON handler。
|
// New 按 Options 构造一个 *slog.Logger。Format 非 FormatText 时一律使用 JSON handler。
|
||||||
@@ -42,7 +44,7 @@ func New(opt Options) *slog.Logger {
|
|||||||
if w == nil {
|
if w == nil {
|
||||||
w = os.Stdout
|
w = os.Stdout
|
||||||
}
|
}
|
||||||
hopts := &slog.HandlerOptions{Level: opt.Level, AddSource: false}
|
hopts := &slog.HandlerOptions{Level: opt.Level, AddSource: opt.AddSource}
|
||||||
var h slog.Handler
|
var h slog.Handler
|
||||||
switch opt.Format {
|
switch opt.Format {
|
||||||
case FormatText:
|
case FormatText:
|
||||||
@@ -54,10 +56,11 @@ func New(opt Options) *slog.Logger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FromEnv 根据环境名返回合适默认:
|
// FromEnv 根据环境名返回合适默认:
|
||||||
// development → text + debug;其他 → json + info。
|
// - development → text + debug,不含 source(开发噪声大)
|
||||||
|
// - 其他(production/staging/test)→ json + info,含 source(spec §8.2 caller 必填)
|
||||||
func FromEnv(env string, w io.Writer) *slog.Logger {
|
func FromEnv(env string, w io.Writer) *slog.Logger {
|
||||||
if env == "development" {
|
if env == "development" {
|
||||||
return New(Options{Format: FormatText, Level: LevelDebug, Writer: w})
|
return New(Options{Format: FormatText, Level: LevelDebug, Writer: w})
|
||||||
}
|
}
|
||||||
return New(Options{Format: FormatJSON, Level: LevelInfo, Writer: w})
|
return New(Options{Format: FormatJSON, Level: LevelInfo, Writer: w, AddSource: true})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,3 +57,33 @@ func TestFromEnv_NonDevelopmentUsesJSON(t *testing.T) {
|
|||||||
require.NoError(t, json.Unmarshal(buf.Bytes(), &got))
|
require.NoError(t, json.Unmarshal(buf.Bytes(), &got))
|
||||||
require.Equal(t, "prod", got["msg"])
|
require.Equal(t, "prod", got["msg"])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNew_AddSourceTrue_IncludesSourceField(t *testing.T) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
log := New(Options{Format: FormatJSON, Level: LevelInfo, Writer: &buf, AddSource: true})
|
||||||
|
log.Info("with source")
|
||||||
|
|
||||||
|
var got map[string]any
|
||||||
|
require.NoError(t, json.Unmarshal(buf.Bytes(), &got))
|
||||||
|
require.Contains(t, got, "source", "AddSource:true 时应有 source 字段")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNew_AddSourceFalse_OmitsSourceField(t *testing.T) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
log := New(Options{Format: FormatJSON, Level: LevelInfo, Writer: &buf, AddSource: false})
|
||||||
|
log.Info("without source")
|
||||||
|
|
||||||
|
var got map[string]any
|
||||||
|
require.NoError(t, json.Unmarshal(buf.Bytes(), &got))
|
||||||
|
require.NotContains(t, got, "source")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromEnv_NonDevelopmentEnablesSource(t *testing.T) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
log := FromEnv("production", &buf)
|
||||||
|
log.Info("prod log")
|
||||||
|
|
||||||
|
var got map[string]any
|
||||||
|
require.NoError(t, json.Unmarshal(buf.Bytes(), &got))
|
||||||
|
require.Contains(t, got, "source", "production 应自动开 AddSource (spec §8.2)")
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user