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  }