github.com/oarkflow/log@v1.0.78/gelf/message.go (about) 1 package gelf 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "time" 8 ) 9 10 // Message represents the contents of the GELF message. It is gzipped 11 // before sending. 12 type Message struct { 13 Version string `json:"version"` 14 Host string `json:"host"` 15 Short string `json:"short_message"` 16 Full string `json:"full_message,omitempty"` 17 TimeUnix float64 `json:"timestamp"` 18 Level int32 `json:"level,omitempty"` 19 Facility string `json:"facility,omitempty"` 20 Extra map[string]interface{} `json:"-"` 21 RawExtra json.RawMessage `json:"-"` 22 } 23 24 // Syslog severity levels 25 const ( 26 LOG_EMERG = 0 27 LOG_ALERT = 1 28 LOG_CRIT = 2 29 LOG_ERR = 3 30 LOG_WARNING = 4 31 LOG_NOTICE = 5 32 LOG_INFO = 6 33 LOG_DEBUG = 7 34 ) 35 36 func (m *Message) MarshalJSONBuf(buf *bytes.Buffer) error { 37 b, err := json.Marshal(m) 38 if err != nil { 39 return err 40 } 41 // write up until the final } 42 if _, err = buf.Write(b[:len(b)-1]); err != nil { 43 return err 44 } 45 if len(m.Extra) > 0 { 46 eb, err := json.Marshal(m.Extra) 47 if err != nil { 48 return err 49 } 50 // merge serialized message + serialized extra map 51 if err = buf.WriteByte(','); err != nil { 52 return err 53 } 54 // write serialized extra bytes, without enclosing quotes 55 if _, err = buf.Write(eb[1 : len(eb)-1]); err != nil { 56 return err 57 } 58 } 59 60 if len(m.RawExtra) > 0 { 61 if err := buf.WriteByte(','); err != nil { 62 return err 63 } 64 65 // write serialized extra bytes, without enclosing quotes 66 if _, err = buf.Write(m.RawExtra[1 : len(m.RawExtra)-1]); err != nil { 67 return err 68 } 69 } 70 71 // write final closing quotes 72 return buf.WriteByte('}') 73 } 74 75 func (m *Message) UnmarshalJSON(data []byte) error { 76 i := make(map[string]interface{}, 16) 77 if err := json.Unmarshal(data, &i); err != nil { 78 return err 79 } 80 for k, v := range i { 81 if k[0] == '_' { 82 if m.Extra == nil { 83 m.Extra = make(map[string]interface{}, 1) 84 } 85 m.Extra[k] = v 86 continue 87 } 88 89 ok := true 90 switch k { 91 case "version": 92 m.Version, ok = v.(string) 93 case "host": 94 m.Host, ok = v.(string) 95 case "short_message": 96 m.Short, ok = v.(string) 97 case "full_message": 98 m.Full, ok = v.(string) 99 case "timestamp": 100 m.TimeUnix, ok = v.(float64) 101 case "level": 102 var level float64 103 level, ok = v.(float64) 104 m.Level = int32(level) 105 case "facility": 106 m.Facility, ok = v.(string) 107 } 108 109 if !ok { 110 return fmt.Errorf("invalid type for field %s", k) 111 } 112 } 113 return nil 114 } 115 116 func (m *Message) toBytes(buf *bytes.Buffer) (messageBytes []byte, err error) { 117 if err = m.MarshalJSONBuf(buf); err != nil { 118 return nil, err 119 } 120 messageBytes = buf.Bytes() 121 return messageBytes, nil 122 } 123 124 func constructMessage(p []byte, hostname string, facility string, file string, line int) (m *Message) { 125 data := ByteToMap(p) 126 level := "info" 127 // If there are newlines in the message, use the first line 128 // for the short message and set the full message to the 129 // original input. If the input has no newlines, stick the 130 // whole thing in Short. 131 short := []byte("Success") 132 if val, ok := data["message"]; ok { 133 short = []byte(val.(string)) 134 } 135 if val, ok := data["level"]; ok { 136 level = val.(string) 137 delete(data, "level") 138 } 139 full := []byte("") 140 if i := bytes.IndexRune(short, '\n'); i > 0 { 141 short = short[:i] 142 full = short 143 } 144 data["_file"] = file 145 data["_line"] = line 146 m = &Message{ 147 Version: "1.1", 148 Host: hostname, 149 Short: string(short), 150 Full: string(full), 151 TimeUnix: float64(time.Now().UnixNano()) / float64(time.Second), 152 Facility: facility, 153 Extra: data, 154 } 155 switch level { 156 case "emergency": 157 m.Level = 0 158 case "alert": 159 m.Level = 1 160 case "critical": 161 m.Level = 2 162 case "error": 163 m.Level = 3 164 case "warn": 165 m.Level = 4 166 case "info": 167 m.Level = 6 168 case "debug": 169 m.Level = 7 170 } 171 return m 172 }