zap_journald.go 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. // Copyright 2018 The etcd Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package logutil
  15. import (
  16. "bytes"
  17. "encoding/json"
  18. "fmt"
  19. "io"
  20. "os"
  21. "path/filepath"
  22. "github.com/coreos/go-systemd/journal"
  23. "go.uber.org/zap/zapcore"
  24. )
  25. // NewJournaldWriter wraps "io.Writer" to redirect log output
  26. // to the local systemd journal. If journald send fails, it fails
  27. // back to writing to the original writer.
  28. // The decode overhead is only <30µs per write.
  29. // Reference: https://github.com/coreos/pkg/blob/master/capnslog/journald_formatter.go
  30. func NewJournaldWriter(wr io.Writer) io.Writer {
  31. return &journaldWriter{Writer: wr}
  32. }
  33. type journaldWriter struct {
  34. io.Writer
  35. }
  36. type logLine struct {
  37. Level string `json:"level"`
  38. Caller string `json:"caller"`
  39. }
  40. func (w *journaldWriter) Write(p []byte) (int, error) {
  41. line := &logLine{}
  42. if err := json.NewDecoder(bytes.NewReader(p)).Decode(line); err != nil {
  43. return 0, err
  44. }
  45. var pri journal.Priority
  46. switch line.Level {
  47. case zapcore.DebugLevel.String():
  48. pri = journal.PriDebug
  49. case zapcore.InfoLevel.String():
  50. pri = journal.PriInfo
  51. case zapcore.WarnLevel.String():
  52. pri = journal.PriWarning
  53. case zapcore.ErrorLevel.String():
  54. pri = journal.PriErr
  55. case zapcore.DPanicLevel.String():
  56. pri = journal.PriCrit
  57. case zapcore.PanicLevel.String():
  58. pri = journal.PriCrit
  59. case zapcore.FatalLevel.String():
  60. pri = journal.PriCrit
  61. default:
  62. panic(fmt.Errorf("unknown log level: %q", line.Level))
  63. }
  64. err := journal.Send(string(p), pri, map[string]string{
  65. "PACKAGE": filepath.Dir(line.Caller),
  66. "SYSLOG_IDENTIFIER": filepath.Base(os.Args[0]),
  67. })
  68. if err != nil {
  69. fmt.Println("FAILED TO WRITE TO JOURNALD", err, string(p))
  70. return w.Writer.Write(p)
  71. }
  72. return 0, nil
  73. }