github.com/resin-io/docker@v1.13.1/pkg/jsonlog/jsonlogbytes.go (about)

     1  package jsonlog
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"unicode/utf8"
     7  )
     8  
     9  // JSONLogs is based on JSONLog.
    10  // It allows marshalling JSONLog from Log as []byte
    11  // and an already marshalled Created timestamp.
    12  type JSONLogs struct {
    13  	Log     []byte `json:"log,omitempty"`
    14  	Stream  string `json:"stream,omitempty"`
    15  	Created string `json:"time"`
    16  
    17  	// json-encoded bytes
    18  	RawAttrs json.RawMessage `json:"attrs,omitempty"`
    19  }
    20  
    21  // MarshalJSONBuf is based on the same method from JSONLog
    22  // It has been modified to take into account the necessary changes.
    23  func (mj *JSONLogs) MarshalJSONBuf(buf *bytes.Buffer) error {
    24  	var first = true
    25  
    26  	buf.WriteString(`{`)
    27  	if len(mj.Log) != 0 {
    28  		first = false
    29  		buf.WriteString(`"log":`)
    30  		ffjsonWriteJSONBytesAsString(buf, mj.Log)
    31  	}
    32  	if len(mj.Stream) != 0 {
    33  		if first == true {
    34  			first = false
    35  		} else {
    36  			buf.WriteString(`,`)
    37  		}
    38  		buf.WriteString(`"stream":`)
    39  		ffjsonWriteJSONString(buf, mj.Stream)
    40  	}
    41  	if len(mj.RawAttrs) > 0 {
    42  		if first {
    43  			first = false
    44  		} else {
    45  			buf.WriteString(`,`)
    46  		}
    47  		buf.WriteString(`"attrs":`)
    48  		buf.Write(mj.RawAttrs)
    49  	}
    50  	if !first {
    51  		buf.WriteString(`,`)
    52  	}
    53  	buf.WriteString(`"time":`)
    54  	buf.WriteString(mj.Created)
    55  	buf.WriteString(`}`)
    56  	return nil
    57  }
    58  
    59  // This is based on ffjsonWriteJSONBytesAsString. It has been changed
    60  // to accept a string passed as a slice of bytes.
    61  func ffjsonWriteJSONBytesAsString(buf *bytes.Buffer, s []byte) {
    62  	const hex = "0123456789abcdef"
    63  
    64  	buf.WriteByte('"')
    65  	start := 0
    66  	for i := 0; i < len(s); {
    67  		if b := s[i]; b < utf8.RuneSelf {
    68  			if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' {
    69  				i++
    70  				continue
    71  			}
    72  			if start < i {
    73  				buf.Write(s[start:i])
    74  			}
    75  			switch b {
    76  			case '\\', '"':
    77  				buf.WriteByte('\\')
    78  				buf.WriteByte(b)
    79  			case '\n':
    80  				buf.WriteByte('\\')
    81  				buf.WriteByte('n')
    82  			case '\r':
    83  				buf.WriteByte('\\')
    84  				buf.WriteByte('r')
    85  			default:
    86  
    87  				buf.WriteString(`\u00`)
    88  				buf.WriteByte(hex[b>>4])
    89  				buf.WriteByte(hex[b&0xF])
    90  			}
    91  			i++
    92  			start = i
    93  			continue
    94  		}
    95  		c, size := utf8.DecodeRune(s[i:])
    96  		if c == utf8.RuneError && size == 1 {
    97  			if start < i {
    98  				buf.Write(s[start:i])
    99  			}
   100  			buf.WriteString(`\ufffd`)
   101  			i += size
   102  			start = i
   103  			continue
   104  		}
   105  
   106  		if c == '\u2028' || c == '\u2029' {
   107  			if start < i {
   108  				buf.Write(s[start:i])
   109  			}
   110  			buf.WriteString(`\u202`)
   111  			buf.WriteByte(hex[c&0xF])
   112  			i += size
   113  			start = i
   114  			continue
   115  		}
   116  		i += size
   117  	}
   118  	if start < len(s) {
   119  		buf.Write(s[start:])
   120  	}
   121  	buf.WriteByte('"')
   122  }