Skip to content

Commit

Permalink
util/log: MVP of customizable tz support for the datetime field
Browse files Browse the repository at this point in the history
Release note (cli change): The `json` log output format now recognizes
the extra format option `datetime-timezone` which selects which timezone
to use when formatting the `datetime` field.
`datetime-timezone` must be combined with `datetime-format` because
the default value for the latter option is `none`, i.e. `datetime`
is not produced by default.

For example:

```yaml
sinks:
  fluent-groups:
     custom-json:
        format: json
        format-options: {datetime-format: rfc3339, datetime-timezone: America/New_York}
```
  • Loading branch information
knz committed Jun 13, 2023
1 parent 56da2cb commit 24e577a
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 13 deletions.
4 changes: 4 additions & 0 deletions docs/generated/logformats.md
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ Additional options recognized via `format-options`:
| Option | Description |
|--------|-------------|
| `datetime-format` | The format to use for the `datetime` field. The value can be one of `none`, `iso8601`/`rfc3339` (synonyms), or `rfc1123`. Default is `none`. |
| `datetime-timezone` | The timezone to use for the `datetime` field. The value can be any timezone name recognized by the Go standard library. Default is `UTC` |



Expand Down Expand Up @@ -439,6 +440,7 @@ Additional options recognized via `format-options`:
| Option | Description |
|--------|-------------|
| `datetime-format` | The format to use for the `datetime` field. The value can be one of `none`, `iso8601`/`rfc3339` (synonyms), or `rfc1123`. Default is `none`. |
| `datetime-timezone` | The timezone to use for the `datetime` field. The value can be any timezone name recognized by the Go standard library. Default is `UTC` |



Expand Down Expand Up @@ -504,6 +506,7 @@ Additional options recognized via `format-options`:
| Option | Description |
|--------|-------------|
| `datetime-format` | The format to use for the `datetime` field. The value can be one of `none`, `iso8601`/`rfc3339` (synonyms), or `rfc1123`. Default is `none`. |
| `datetime-timezone` | The timezone to use for the `datetime` field. The value can be any timezone name recognized by the Go standard library. Default is `UTC` |



Expand Down Expand Up @@ -569,6 +572,7 @@ Additional options recognized via `format-options`:
| Option | Description |
|--------|-------------|
| `datetime-format` | The format to use for the `datetime` field. The value can be one of `none`, `iso8601`/`rfc3339` (synonyms), or `rfc1123`. Default is `none`. |
| `datetime-timezone` | The timezone to use for the `datetime` field. The value can be any timezone name recognized by the Go standard library. Default is `UTC` |



25 changes: 20 additions & 5 deletions pkg/util/log/format_json.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func (formatFluentJSONCompact) formatterName() string { return "json-fluent-comp
func (formatFluentJSONCompact) doc() string { return formatJSONDoc(true /* fluent */, tagCompact) }

func (f formatFluentJSONCompact) formatEntry(entry logEntry) *buffer {
return formatJSON(entry, true /* fluent */, tagCompact, "")
return formatJSON(entry, true /* fluent */, tagCompact, "", nil)
}

func (formatFluentJSONCompact) contentType() string { return "application/json" }
Expand All @@ -53,7 +53,7 @@ func (formatFluentJSONFull) setOption(k string, _ string) error {
func (formatFluentJSONFull) formatterName() string { return "json-fluent" }

func (f formatFluentJSONFull) formatEntry(entry logEntry) *buffer {
return formatJSON(entry, true /* fluent */, tagVerbose, "")
return formatJSON(entry, true /* fluent */, tagVerbose, "", nil)
}

func (formatFluentJSONFull) doc() string { return formatJSONDoc(true /* fluent */, tagVerbose) }
Expand All @@ -69,7 +69,7 @@ func (formatJSONCompact) setOption(k string, _ string) error {
func (formatJSONCompact) formatterName() string { return "json-compact" }

func (f formatJSONCompact) formatEntry(entry logEntry) *buffer {
return formatJSON(entry, false /* fluent */, tagCompact, "")
return formatJSON(entry, false /* fluent */, tagCompact, "", nil)
}

func (formatJSONCompact) doc() string { return formatJSONDoc(false /* fluent */, tagCompact) }
Expand All @@ -78,10 +78,19 @@ func (formatJSONCompact) contentType() string { return "application/json" }

type formatJSONFull struct {
datetimeFormat string
loc *time.Location
}

func (f *formatJSONFull) setOption(k string, v string) error {
switch k {
case "datetime-timezone":
l, err := timeutil.LoadLocation(v)
if err != nil {
return errors.Wrapf(err, "invalid timezone: %q", v)
}
f.loc = l
return nil

case "datetime-format":
switch v {
case "none":
Expand All @@ -106,7 +115,7 @@ func (f *formatJSONFull) setOption(k string, v string) error {
func (formatJSONFull) formatterName() string { return "json" }

func (f formatJSONFull) formatEntry(entry logEntry) *buffer {
return formatJSON(entry, false /* fluent */, tagVerbose, f.datetimeFormat)
return formatJSON(entry, false /* fluent */, tagVerbose, f.datetimeFormat, f.loc)
}

func (formatJSONFull) doc() string { return formatJSONDoc(false /* fluent */, tagVerbose) }
Expand Down Expand Up @@ -195,6 +204,7 @@ Additional options recognized via ` + "`format-options`" + `:
| Option | Description |
|--------|-------------|
| ` + "`datetime-format`" + ` | The format to use for the ` + "`datetime`" + ` field. The value can be one of ` + "`none`" + `, ` + "`iso8601`/`rfc3339` (synonyms)" + `, or ` + "`rfc1123`" + `. Default is ` + "`none`" + `. |
| ` + "`datetime-timezone`" + ` | The timezone to use for the ` + "`datetime`" + ` field. The value can be any timezone name recognized by the Go standard library. Default is ` + "`UTC`" + ` |
`)

Expand Down Expand Up @@ -259,7 +269,9 @@ var channelNamesLowercase = func() map[Channel]string {
return lnames
}()

func formatJSON(entry logEntry, forFluent bool, tags tagChoice, datetimeFormat string) *buffer {
func formatJSON(
entry logEntry, forFluent bool, tags tagChoice, datetimeFormat string, loc *time.Location,
) *buffer {
jtags := jsonTags
buf := getBuffer()
buf.WriteByte('{')
Expand Down Expand Up @@ -328,6 +340,9 @@ func formatJSON(entry logEntry, forFluent bool, tags tagChoice, datetimeFormat s
// Extra "datetime" field if requested.
if len(datetimeFormat) > 0 {
t := timeutil.FromUnixNanos(entry.ts)
if loc != nil {
t = t.In(loc)
}
buf.WriteString(`,"`)
buf.WriteString(jtags['d'].tags[tags])
buf.WriteString(`":"`)
Expand Down
7 changes: 6 additions & 1 deletion pkg/util/log/format_json_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/cockroachdb/cockroach/pkg/util/log/channel"
"github.com/cockroachdb/cockroach/pkg/util/log/logpb"
"github.com/cockroachdb/cockroach/pkg/util/log/severity"
"github.com/cockroachdb/cockroach/pkg/util/timeutil"
"github.com/cockroachdb/datadriven"
"github.com/cockroachdb/logtags"
"github.com/kr/pretty"
Expand Down Expand Up @@ -69,12 +70,16 @@ func TestJSONFormats(t *testing.T) {
makeUnstructuredEntry(ctx, severity.ERROR, channel.HEALTH, 0, true, "hello %s", "world"),
}

l, err := timeutil.LoadLocation("America/New_York")
if err != nil {
t.Fatal(err)
}
formats := []logFormatter{
formatFluentJSONCompact{},
formatFluentJSONFull{},
formatJSONCompact{},
&formatJSONFull{},
&formatJSONFull{datetimeFormat: "2006-01-02 xx 15:04:05"},
&formatJSONFull{datetimeFormat: "2006-01-02 xx 15:04:05+07", loc: l},
}

// We only use the datadriven framework for the ability to rewrite the output.
Expand Down
14 changes: 7 additions & 7 deletions pkg/util/log/testdata/json
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,40 @@ json-fluent-compact: {"tag":"logtest.unknown","header":1,"t":"1136214245.6543210
json-fluent: {"tag":"logtest.unknown","header":1,"timestamp":"1136214245.654321000","tenant_id":1,"version":"v999.0.0","goroutine":11,"file":"util/log/format_json_test.go","line":123,"redactable":1,"tags":{"noval":"","s":"‹1›","long":"‹2›"},"message":"hello ‹world›"}
json-compact: {"header":1,"t":"1136214245.654321000","T":1,"v":"v999.0.0","g":11,"f":"util/log/format_json_test.go","l":123,"r":1,"tags":{"noval":"","s":"‹1›","long":"‹2›"},"message":"hello ‹world›"}
json: {"header":1,"timestamp":"1136214245.654321000","tenant_id":1,"version":"v999.0.0","goroutine":11,"file":"util/log/format_json_test.go","line":123,"redactable":1,"tags":{"noval":"","s":"‹1›","long":"‹2›"},"message":"hello ‹world›"}
json: {"header":1,"timestamp":"1136214245.654321000","datetime":"2006-01-02 xx 15:04:05","tenant_id":1,"version":"v999.0.0","goroutine":11,"file":"util/log/format_json_test.go","line":123,"redactable":1,"tags":{"noval":"","s":"‹1›","long":"‹2›"},"message":"hello ‹world›"}
json: {"header":1,"timestamp":"1136214245.654321000","datetime":"2006-01-02 xx 10:04:05+07","tenant_id":1,"version":"v999.0.0","goroutine":11,"file":"util/log/format_json_test.go","line":123,"redactable":1,"tags":{"noval":"","s":"‹1›","long":"‹2›"},"message":"hello ‹world›"}
#
json-fluent-compact: {"tag":"logtest.dev","c":0,"t":"1136214245.654321000","s":0,"g":11,"f":"","l":123,"n":0,"r":0,"message":""}
json-fluent: {"tag":"logtest.dev","channel_numeric":0,"channel":"DEV","timestamp":"1136214245.654321000","severity_numeric":0,"severity":"UNKNOWN","goroutine":11,"file":"","line":123,"entry_counter":0,"redactable":0,"message":""}
json-compact: {"c":0,"t":"1136214245.654321000","s":0,"g":11,"f":"","l":123,"n":0,"r":0,"message":""}
json: {"channel_numeric":0,"channel":"DEV","timestamp":"1136214245.654321000","severity_numeric":0,"severity":"UNKNOWN","goroutine":11,"file":"","line":123,"entry_counter":0,"redactable":0,"message":""}
json: {"channel_numeric":0,"channel":"DEV","timestamp":"1136214245.654321000","datetime":"2006-01-02 xx 15:04:05","severity_numeric":0,"severity":"UNKNOWN","goroutine":11,"file":"","line":123,"entry_counter":0,"redactable":0,"message":""}
json: {"channel_numeric":0,"channel":"DEV","timestamp":"1136214245.654321000","datetime":"2006-01-02 xx 10:04:05+07","severity_numeric":0,"severity":"UNKNOWN","goroutine":11,"file":"","line":123,"entry_counter":0,"redactable":0,"message":""}
#
json-fluent-compact: {"tag":"logtest.dev","c":0,"t":"1136214245.654321000","x":"abc","N":123,"T":1,"s":0,"g":11,"f":"","l":123,"n":0,"r":0,"message":""}
json-fluent: {"tag":"logtest.dev","channel_numeric":0,"channel":"DEV","timestamp":"1136214245.654321000","cluster_id":"abc","node_id":123,"tenant_id":1,"severity_numeric":0,"severity":"UNKNOWN","goroutine":11,"file":"","line":123,"entry_counter":0,"redactable":0,"message":""}
json-compact: {"c":0,"t":"1136214245.654321000","x":"abc","N":123,"T":1,"s":0,"g":11,"f":"","l":123,"n":0,"r":0,"message":""}
json: {"channel_numeric":0,"channel":"DEV","timestamp":"1136214245.654321000","cluster_id":"abc","node_id":123,"tenant_id":1,"severity_numeric":0,"severity":"UNKNOWN","goroutine":11,"file":"","line":123,"entry_counter":0,"redactable":0,"message":""}
json: {"channel_numeric":0,"channel":"DEV","timestamp":"1136214245.654321000","datetime":"2006-01-02 xx 15:04:05","cluster_id":"abc","node_id":123,"tenant_id":1,"severity_numeric":0,"severity":"UNKNOWN","goroutine":11,"file":"","line":123,"entry_counter":0,"redactable":0,"message":""}
json: {"channel_numeric":0,"channel":"DEV","timestamp":"1136214245.654321000","datetime":"2006-01-02 xx 10:04:05+07","cluster_id":"abc","node_id":123,"tenant_id":1,"severity_numeric":0,"severity":"UNKNOWN","goroutine":11,"file":"","line":123,"entry_counter":0,"redactable":0,"message":""}
#
json-fluent-compact: {"tag":"logtest.dev","c":0,"t":"1136214245.654321000","T":456,"q":123,"s":0,"g":11,"f":"","l":123,"n":0,"r":0,"message":""}
json-fluent: {"tag":"logtest.dev","channel_numeric":0,"channel":"DEV","timestamp":"1136214245.654321000","tenant_id":456,"instance_id":123,"severity_numeric":0,"severity":"UNKNOWN","goroutine":11,"file":"","line":123,"entry_counter":0,"redactable":0,"message":""}
json-compact: {"c":0,"t":"1136214245.654321000","T":456,"q":123,"s":0,"g":11,"f":"","l":123,"n":0,"r":0,"message":""}
json: {"channel_numeric":0,"channel":"DEV","timestamp":"1136214245.654321000","tenant_id":456,"instance_id":123,"severity_numeric":0,"severity":"UNKNOWN","goroutine":11,"file":"","line":123,"entry_counter":0,"redactable":0,"message":""}
json: {"channel_numeric":0,"channel":"DEV","timestamp":"1136214245.654321000","datetime":"2006-01-02 xx 15:04:05","tenant_id":456,"instance_id":123,"severity_numeric":0,"severity":"UNKNOWN","goroutine":11,"file":"","line":123,"entry_counter":0,"redactable":0,"message":""}
json: {"channel_numeric":0,"channel":"DEV","timestamp":"1136214245.654321000","datetime":"2006-01-02 xx 10:04:05+07","tenant_id":456,"instance_id":123,"severity_numeric":0,"severity":"UNKNOWN","goroutine":11,"file":"","line":123,"entry_counter":0,"redactable":0,"message":""}
#
json-fluent-compact: {"tag":"logtest.dev","c":0,"t":"1136214245.654321000","T":1,"v":"v999.0.0","s":1,"sev":"I","g":11,"f":"util/log/format_json_test.go","l":123,"n":0,"r":1,"tags":{"noval":"","s":"‹1›","long":"‹2›"},"event":{"Timestamp":123,"EventType":"rename_database","Event":"‹rename from `hello` to `world`›"}}
json-fluent: {"tag":"logtest.dev","channel_numeric":0,"channel":"DEV","timestamp":"1136214245.654321000","tenant_id":1,"version":"v999.0.0","severity_numeric":1,"severity":"INFO","goroutine":11,"file":"util/log/format_json_test.go","line":123,"entry_counter":0,"redactable":1,"tags":{"noval":"","s":"‹1›","long":"‹2›"},"event":{"Timestamp":123,"EventType":"rename_database","Event":"‹rename from `hello` to `world`›"}}
json-compact: {"c":0,"t":"1136214245.654321000","T":1,"v":"v999.0.0","s":1,"sev":"I","g":11,"f":"util/log/format_json_test.go","l":123,"n":0,"r":1,"tags":{"noval":"","s":"‹1›","long":"‹2›"},"event":{"Timestamp":123,"EventType":"rename_database","Event":"‹rename from `hello` to `world`›"}}
json: {"channel_numeric":0,"channel":"DEV","timestamp":"1136214245.654321000","tenant_id":1,"version":"v999.0.0","severity_numeric":1,"severity":"INFO","goroutine":11,"file":"util/log/format_json_test.go","line":123,"entry_counter":0,"redactable":1,"tags":{"noval":"","s":"‹1›","long":"‹2›"},"event":{"Timestamp":123,"EventType":"rename_database","Event":"‹rename from `hello` to `world`›"}}
json: {"channel_numeric":0,"channel":"DEV","timestamp":"1136214245.654321000","datetime":"2006-01-02 xx 15:04:05","tenant_id":1,"version":"v999.0.0","severity_numeric":1,"severity":"INFO","goroutine":11,"file":"util/log/format_json_test.go","line":123,"entry_counter":0,"redactable":1,"tags":{"noval":"","s":"‹1›","long":"‹2›"},"event":{"Timestamp":123,"EventType":"rename_database","Event":"‹rename from `hello` to `world`›"}}
json: {"channel_numeric":0,"channel":"DEV","timestamp":"1136214245.654321000","datetime":"2006-01-02 xx 10:04:05+07","tenant_id":1,"version":"v999.0.0","severity_numeric":1,"severity":"INFO","goroutine":11,"file":"util/log/format_json_test.go","line":123,"entry_counter":0,"redactable":1,"tags":{"noval":"","s":"‹1›","long":"‹2›"},"event":{"Timestamp":123,"EventType":"rename_database","Event":"‹rename from `hello` to `world`›"}}
#
json-fluent-compact: {"tag":"logtest.ops","c":1,"t":"1136214245.654321000","T":1,"v":"v999.0.0","s":2,"sev":"W","g":11,"f":"util/log/format_json_test.go","l":123,"n":0,"r":0,"tags":{"noval":"","s":"1","long":"2"},"message":"hello world"}
json-fluent: {"tag":"logtest.ops","channel_numeric":1,"channel":"OPS","timestamp":"1136214245.654321000","tenant_id":1,"version":"v999.0.0","severity_numeric":2,"severity":"WARNING","goroutine":11,"file":"util/log/format_json_test.go","line":123,"entry_counter":0,"redactable":0,"tags":{"noval":"","s":"1","long":"2"},"message":"hello world"}
json-compact: {"c":1,"t":"1136214245.654321000","T":1,"v":"v999.0.0","s":2,"sev":"W","g":11,"f":"util/log/format_json_test.go","l":123,"n":0,"r":0,"tags":{"noval":"","s":"1","long":"2"},"message":"hello world"}
json: {"channel_numeric":1,"channel":"OPS","timestamp":"1136214245.654321000","tenant_id":1,"version":"v999.0.0","severity_numeric":2,"severity":"WARNING","goroutine":11,"file":"util/log/format_json_test.go","line":123,"entry_counter":0,"redactable":0,"tags":{"noval":"","s":"1","long":"2"},"message":"hello world"}
json: {"channel_numeric":1,"channel":"OPS","timestamp":"1136214245.654321000","datetime":"2006-01-02 xx 15:04:05","tenant_id":1,"version":"v999.0.0","severity_numeric":2,"severity":"WARNING","goroutine":11,"file":"util/log/format_json_test.go","line":123,"entry_counter":0,"redactable":0,"tags":{"noval":"","s":"1","long":"2"},"message":"hello world"}
json: {"channel_numeric":1,"channel":"OPS","timestamp":"1136214245.654321000","datetime":"2006-01-02 xx 10:04:05+07","tenant_id":1,"version":"v999.0.0","severity_numeric":2,"severity":"WARNING","goroutine":11,"file":"util/log/format_json_test.go","line":123,"entry_counter":0,"redactable":0,"tags":{"noval":"","s":"1","long":"2"},"message":"hello world"}
#
json-fluent-compact: {"tag":"logtest.health","c":2,"t":"1136214245.654321000","T":1,"v":"v999.0.0","s":3,"sev":"E","g":11,"f":"util/log/format_json_test.go","l":123,"n":0,"r":1,"tags":{"noval":"","s":"‹1›","long":"‹2›"},"message":"hello ‹world›"}
json-fluent: {"tag":"logtest.health","channel_numeric":2,"channel":"HEALTH","timestamp":"1136214245.654321000","tenant_id":1,"version":"v999.0.0","severity_numeric":3,"severity":"ERROR","goroutine":11,"file":"util/log/format_json_test.go","line":123,"entry_counter":0,"redactable":1,"tags":{"noval":"","s":"‹1›","long":"‹2›"},"message":"hello ‹world›"}
json-compact: {"c":2,"t":"1136214245.654321000","T":1,"v":"v999.0.0","s":3,"sev":"E","g":11,"f":"util/log/format_json_test.go","l":123,"n":0,"r":1,"tags":{"noval":"","s":"‹1›","long":"‹2›"},"message":"hello ‹world›"}
json: {"channel_numeric":2,"channel":"HEALTH","timestamp":"1136214245.654321000","tenant_id":1,"version":"v999.0.0","severity_numeric":3,"severity":"ERROR","goroutine":11,"file":"util/log/format_json_test.go","line":123,"entry_counter":0,"redactable":1,"tags":{"noval":"","s":"‹1›","long":"‹2›"},"message":"hello ‹world›"}
json: {"channel_numeric":2,"channel":"HEALTH","timestamp":"1136214245.654321000","datetime":"2006-01-02 xx 15:04:05","tenant_id":1,"version":"v999.0.0","severity_numeric":3,"severity":"ERROR","goroutine":11,"file":"util/log/format_json_test.go","line":123,"entry_counter":0,"redactable":1,"tags":{"noval":"","s":"‹1›","long":"‹2›"},"message":"hello ‹world›"}
json: {"channel_numeric":2,"channel":"HEALTH","timestamp":"1136214245.654321000","datetime":"2006-01-02 xx 10:04:05+07","tenant_id":1,"version":"v999.0.0","severity_numeric":3,"severity":"ERROR","goroutine":11,"file":"util/log/format_json_test.go","line":123,"entry_counter":0,"redactable":1,"tags":{"noval":"","s":"‹1›","long":"‹2›"},"message":"hello ‹world›"}

0 comments on commit 24e577a

Please sign in to comment.