Repository: caddyserver/caddy (language: go)

Issue:
Customise the field names for logs.
Hey,

I was looking at configuring the logging in caddy to reflect https://www.elastic.co/guide/en/ecs/current/ecs-field-reference.html. For example taking request>remote_ip and using `client.ip` instead of `remote_ip` to store our logs in a  more consistent format.

However although we can customise what appears in the logs, we don't appear to be able to change the field names.

Is this a feature I'm not seeing or something that would need to be implemented?

For example:
```json
"request": {
    "remote_ip": "client.ip",
    "remote_port": "52783",
}
```

Becomes
```json
"request": {
    "client.ip": "client.ip",
    "client.port": "52783",
}
```

Thanks,
Liam


Discussion / hints:
I wonder if this can be done with the [transform encoder](https://github.com/caddyserver/transform-encoder) plugin?

In general though, JSON log consumers should meet the JSON, rather than the other way around (the JSON logs meeting the consumer). So that does seem a bit odd to need this capability.
You can use this encoder: https://github.com/leodido/caddy-jsonselect-encoder

It _might_ be possible to add support for renaming fields with the `filter` encoder, but I think you'd probably be better served by that `jsonselect` plugin.
Woah, I don't remember seeing that one. Cool

Fix patch:
diff --git a/modules/logging/filters.go b/modules/logging/filters.go
index af64cc4b36f..aa96e5ef9bf 100644
--- a/modules/logging/filters.go
+++ b/modules/logging/filters.go
@@ -31,12 +31,13 @@ import (
 
 func init() {
 	caddy.RegisterModule(DeleteFilter{})
+	caddy.RegisterModule(HashFilter{})
 	caddy.RegisterModule(ReplaceFilter{})
 	caddy.RegisterModule(IPMaskFilter{})
 	caddy.RegisterModule(QueryFilter{})
 	caddy.RegisterModule(CookieFilter{})
 	caddy.RegisterModule(RegexpFilter{})
-	caddy.RegisterModule(HashFilter{})
+	caddy.RegisterModule(RenameFilter{})
 }
 
 // LogFieldFilter can filter (or manipulate)
@@ -542,21 +543,56 @@ func (f *RegexpFilter) Filter(in zapcore.Field) zapcore.Field {
 	return in
 }
 
+// RenameFilter is a Caddy log field filter that
+// renames the field's key with the indicated name.
+type RenameFilter struct {
+	Name string `json:"name,omitempty"`
+}
+
+// CaddyModule returns the Caddy module information.
+func (RenameFilter) CaddyModule() caddy.ModuleInfo {
+	return caddy.ModuleInfo{
+		ID:  "caddy.logging.encoders.filter.rename",
+		New: func() caddy.Module { return new(RenameFilter) },
+	}
+}
+
+// UnmarshalCaddyfile sets up the module from Caddyfile tokens.
+func (f *RenameFilter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
+	for d.Next() {
+		if d.NextArg() {
+			f.Name = d.Val()
+		}
+	}
+	return nil
+}
+
+// Filter renames the input field with the replacement name.
+func (f *RenameFilter) Filter(in zapcore.Field) zapcore.Field {
+	in.Type = zapcore.StringType
+	in.Key = f.Name
+	return in
+}
+
 // Interface guards
 var (
 	_ LogFieldFilter = (*DeleteFilter)(nil)
+	_ LogFieldFilter = (*HashFilter)(nil)
 	_ LogFieldFilter = (*ReplaceFilter)(nil)
 	_ LogFieldFilter = (*IPMaskFilter)(nil)
 	_ LogFieldFilter = (*QueryFilter)(nil)
 	_ LogFieldFilter = (*CookieFilter)(nil)
 	_ LogFieldFilter = (*RegexpFilter)(nil)
+	_ LogFieldFilter = (*RenameFilter)(nil)
 
 	_ caddyfile.Unmarshaler = (*DeleteFilter)(nil)
+	_ caddyfile.Unmarshaler = (*HashFilter)(nil)
 	_ caddyfile.Unmarshaler = (*ReplaceFilter)(nil)
 	_ caddyfile.Unmarshaler = (*IPMaskFilter)(nil)
 	_ caddyfile.Unmarshaler = (*QueryFilter)(nil)
 	_ caddyfile.Unmarshaler = (*CookieFilter)(nil)
 	_ caddyfile.Unmarshaler = (*RegexpFilter)(nil)
+	_ caddyfile.Unmarshaler = (*RenameFilter)(nil)
 
 	_ caddy.Provisioner = (*IPMaskFilter)(nil)
 	_ caddy.Provisioner = (*RegexpFilter)(nil)


Test patch: