github.com/sujit-baniya/log@v1.0.73/journal.go (about) 1 //go:build linux 2 // +build linux 3 4 package log 5 6 import ( 7 "encoding/binary" 8 "fmt" 9 "io" 10 "net" 11 "os" 12 "strings" 13 "sync" 14 "syscall" 15 ) 16 17 // JournalWriter is an Writer that writes logs to journald. 18 type JournalWriter struct { 19 // JournalSocket specifies socket name, using `/run/systemd/journal/socket` if empty. 20 JournalSocket string 21 22 once sync.Once 23 addr *net.UnixAddr 24 conn *net.UnixConn 25 } 26 27 // Close implements io.Closer. 28 func (w *JournalWriter) Close() (err error) { 29 if w.conn != nil { 30 err = w.conn.Close() 31 } 32 return 33 } 34 35 // WriteEntry implements Writer. 36 func (w *JournalWriter) WriteEntry(e *Entry) (n int, err error) { 37 w.once.Do(func() { 38 // unix addr 39 w.addr = &net.UnixAddr{ 40 Net: "unixgram", 41 Name: w.JournalSocket, 42 } 43 if w.addr.Name == "" { 44 w.addr.Name = "/run/systemd/journal/socket" 45 } 46 // unix conn 47 var autobind *net.UnixAddr 48 autobind, err = net.ResolveUnixAddr("unixgram", "") 49 if err != nil { 50 return 51 } 52 w.conn, err = net.ListenUnixgram("unixgram", autobind) 53 }) 54 55 if err != nil { 56 return 57 } 58 59 b0 := bbpool.Get().(*bb) 60 b0.B = b0.B[:0] 61 defer bbpool.Put(b0) 62 b0.B = append(b0.B, e.buf...) 63 64 var args FormatterArgs 65 parseFormatterArgs(b0.B, &args) 66 if args.Time == "" { 67 return 68 } 69 70 // buffer 71 b := bbpool.Get().(*bb) 72 b.B = b.B[:0] 73 defer bbpool.Put(b) 74 75 print := func(w io.Writer, name, value string) { 76 if strings.ContainsRune(value, '\n') { 77 fmt.Fprintln(w, name) 78 _ = binary.Write(w, binary.LittleEndian, uint64(len(value))) 79 fmt.Fprintln(w, value) 80 } else { 81 fmt.Fprintf(w, "%s=%s\n", name, value) 82 } 83 } 84 85 // level 86 var priority string 87 switch e.Level { 88 case TraceLevel: 89 priority = "7" // Debug 90 case DebugLevel: 91 priority = "7" // Debug 92 case InfoLevel: 93 priority = "6" // Informational 94 case WarnLevel: 95 priority = "4" // Warning 96 case ErrorLevel: 97 priority = "3" // Error 98 case FatalLevel: 99 priority = "2" // Critical 100 case PanicLevel: 101 priority = "0" // Emergency 102 default: 103 priority = "5" // Notice 104 } 105 print(b, "PRIORITY", priority) 106 107 // message 108 print(b, "MESSAGE", args.Message) 109 110 // fields 111 for _, kv := range args.KeyValues { 112 print(b, strings.ToUpper(kv.Key), kv.Value) 113 } 114 115 print(b, "JSON", b2s(e.buf)) 116 117 // write 118 n, _, err = w.conn.WriteMsgUnix(b.B, nil, w.addr) 119 if err == nil { 120 return 121 } 122 123 opErr, ok := err.(*net.OpError) 124 if !ok || opErr == nil { 125 return 126 } 127 128 sysErr, ok := opErr.Err.(*os.SyscallError) 129 if !ok || sysErr == nil { 130 return 131 } 132 if sysErr.Err != syscall.EMSGSIZE && sysErr.Err != syscall.ENOBUFS { 133 return 134 } 135 136 // Large log entry, send it via tempfile and ancillary-fd. 137 var file *os.File 138 file, err = os.CreateTemp("/dev/shm/", "journal.XXXXX") 139 if err != nil { 140 return 141 } 142 err = syscall.Unlink(file.Name()) 143 if err != nil { 144 return 145 } 146 defer file.Close() 147 n, err = file.Write(b.B) 148 if err != nil { 149 return 150 } 151 rights := syscall.UnixRights(int(file.Fd())) 152 _, _, err = w.conn.WriteMsgUnix([]byte{}, rights, w.addr) 153 if err == nil { 154 n = len(e.buf) 155 } 156 157 return 158 } 159 160 var _ Writer = (*JournalWriter)(nil)