-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
util/log: new JSON output formats, and experimental integration with …
…Fluentd Release note (cli change): It is now possible to redirect logging to [Fluentd](https://www.fluentd.org)-compatible network collectors. See the documentation for details. This is an alpha-quality feature. Release note (cli change): It is now possible to set the `format` parameter of any log sink, including file sinks, to `json-fluent` or `json-fluent-compact` to write entries as structured JSON.
- Loading branch information
Showing
15 changed files
with
982 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
// Copyright 2020 The Cockroach Authors. | ||
// | ||
// Use of this software is governed by the Business Source License | ||
// included in the file licenses/BSL.txt. | ||
// | ||
// As of the Change Date specified in that file, in accordance with | ||
// the Business Source License, use of this software will be governed | ||
// by the Apache License, Version 2.0, included in the file | ||
// licenses/APL.txt. | ||
|
||
package log | ||
|
||
import ( | ||
"fmt" | ||
"net" | ||
|
||
"github.com/cockroachdb/cockroach/pkg/cli/exit" | ||
"github.com/cockroachdb/errors" | ||
) | ||
|
||
// fluentSink represents a Fluentd-compatible network collector. | ||
type fluentSink struct { | ||
// The network address of the fluentd collector. | ||
network string | ||
addr string | ||
|
||
// good indicates that the connection can be used. | ||
good bool | ||
conn net.Conn | ||
} | ||
|
||
func newFluentSink(network, addr string) *fluentSink { | ||
f := &fluentSink{ | ||
addr: addr, | ||
network: network, | ||
} | ||
return f | ||
} | ||
|
||
func (l *fluentSink) String() string { | ||
return fmt.Sprintf("fluent:%s://%s", l.network, l.addr) | ||
} | ||
|
||
// activeAtSeverity implements the logSink interface. | ||
func (l *fluentSink) active() bool { return true } | ||
|
||
// attachHints implements the logSink interface. | ||
func (l *fluentSink) attachHints(stacks []byte) []byte { | ||
return stacks | ||
} | ||
|
||
// exitCode implements the logSink interface. | ||
func (l *fluentSink) exitCode() exit.Code { | ||
return exit.LoggingNetCollectorUnavailable() | ||
} | ||
|
||
// output implements the logSink interface. | ||
func (l *fluentSink) output(extraSync bool, b []byte) error { | ||
// Try to write and reconnect immediately if the first write fails. | ||
// | ||
// TODO(knz): Add some net socket write deadlines here. | ||
_ = l.tryWrite(b) | ||
if l.good { | ||
return nil | ||
} | ||
|
||
if err := l.ensureConn(b); err != nil { | ||
return err | ||
} | ||
return l.tryWrite(b) | ||
} | ||
|
||
// emergencyOutput implements the logSink interface. | ||
func (l *fluentSink) emergencyOutput(b []byte) { | ||
// TODO(knz): Add some net socket write deadlines here. | ||
_ = l.tryWrite(b) | ||
if !l.good { | ||
_ = l.ensureConn(b) | ||
_ = l.tryWrite(b) | ||
} | ||
} | ||
|
||
func (l *fluentSink) close() { | ||
l.good = false | ||
if l.conn != nil { | ||
if err := l.conn.Close(); err != nil { | ||
fmt.Fprintf(OrigStderr, "error closing network logger: %v\n", err) | ||
} | ||
l.conn = nil | ||
} | ||
} | ||
|
||
func (l *fluentSink) ensureConn(b []byte) error { | ||
if l.good { | ||
return nil | ||
} | ||
l.close() | ||
var err error | ||
l.conn, err = net.Dial(l.network, l.addr) | ||
if err != nil { | ||
fmt.Fprintf(OrigStderr, "%s: error dialing network logger: %v\n%s", l, err, b) | ||
return err | ||
} | ||
fmt.Fprintf(OrigStderr, "%s: connection to network logger resumed\n", l) | ||
l.good = true | ||
return nil | ||
} | ||
|
||
var errNoConn = errors.New("no connection opened") | ||
|
||
func (l *fluentSink) tryWrite(b []byte) error { | ||
if !l.good { | ||
return errNoConn | ||
} | ||
n, err := l.conn.Write(b) | ||
if err != nil || n < len(b) { | ||
fmt.Fprintf(OrigStderr, "%s: logging error: %v or short write (%d/%d)\n%s", | ||
l, err, n, len(b), b) | ||
l.good = false | ||
} | ||
return err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// Copyright 2020 The Cockroach Authors. | ||
// | ||
// Use of this software is governed by the Business Source License | ||
// included in the file licenses/BSL.txt. | ||
// | ||
// As of the Change Date specified in that file, in accordance with | ||
// the Business Source License, use of this software will be governed | ||
// by the Apache License, Version 2.0, included in the file | ||
// licenses/APL.txt. | ||
|
||
package log | ||
|
||
import ( | ||
"strings" | ||
"testing" | ||
"time" | ||
|
||
"github.com/cockroachdb/cockroach/pkg/util/log/logpb" | ||
"github.com/cockroachdb/cockroach/pkg/util/log/severity" | ||
) | ||
|
||
func TestFluentClientEncodeEntry(t *testing.T) { | ||
tm, err := time.Parse(MessageTimeFormat, "060102 15:04:05.654321") | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
testCases := []struct { | ||
entry logpb.Entry | ||
expected string | ||
}{ | ||
{logpb.Entry{}, ` | ||
{"tag":"log_test.dev","c":0,"t":"0.000000000","s":0,"g":0,"f":"","l":0,"n":0,"r":0,"message":""} | ||
`}, | ||
{logpb.Entry{Time: tm.UnixNano(), Severity: severity.INFO, File: "hello", Goroutine: 876, Line: 432, Counter: 543, Redactable: true}, ` | ||
{"tag":"log_test.dev","c":0,"t":"1136214245.654321000","s":1,"sev":"I","g":876,"f":"hello","l":432,"n":543,"r":1,"message":""} | ||
`}, | ||
// Check escaping of newlines and other special characters. | ||
// However generally unicode characters can be preserved. | ||
{logpb.Entry{Tags: "hai", Message: "hello\nsnowman " + `"☃"`}, ` | ||
{"tag":"log_test.dev","c":0,"t":"0.000000000","s":0,"g":0,"f":"","l":0,"n":0,"r":0,"tags":"hai","message":"hello\nsnowman \"☃\""} | ||
`}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
var f formatFluentJSONCompact | ||
buf := f.formatEntry(tc.entry, nil) | ||
exp := strings.TrimPrefix(tc.expected, "\n") | ||
actual := buf.String() | ||
putBuffer(buf) | ||
if exp != actual { | ||
t.Errorf("expected:\n%s\ngot:\n%s", exp, actual) | ||
} | ||
} | ||
} |
Oops, something went wrong.