message.go 3.4 KB

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