github.com/coreos/mantle@v0.13.0/network/journal/format.go (about)

     1  // Copyright 2017 CoreOS, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package journal
    16  
    17  import (
    18  	"bytes"
    19  	"fmt"
    20  	"io"
    21  	"time"
    22  	"unicode"
    23  	"unicode/utf8"
    24  )
    25  
    26  type Formatter interface {
    27  	SetTimezone(tz *time.Location)
    28  	WriteEntry(entry Entry) error
    29  }
    30  
    31  type shortWriter struct {
    32  	w      io.Writer
    33  	tz     *time.Location
    34  	bootid string
    35  }
    36  
    37  // ShortWriter writes journal entries in a format similar to journalctl's
    38  // "short-precise" format, excluding hostname for conciseness.
    39  func ShortWriter(w io.Writer) Formatter {
    40  	return &shortWriter{
    41  		w:  w,
    42  		tz: time.Local,
    43  	}
    44  }
    45  
    46  // SetTimezone updates the time location. The default is local time.
    47  func (s *shortWriter) SetTimezone(tz *time.Location) {
    48  	s.tz = tz
    49  }
    50  
    51  func (s *shortWriter) WriteEntry(entry Entry) error {
    52  	realtime := entry.Realtime()
    53  	message, ok := entry[FIELD_MESSAGE]
    54  	if realtime.IsZero() || !ok {
    55  		// Simply skip entries that are woefully incomplete.
    56  		return nil
    57  	}
    58  
    59  	if s.isReboot(entry) {
    60  		io.WriteString(s.w, "-- Reboot --\n")
    61  	}
    62  
    63  	var buf bytes.Buffer
    64  	buf.WriteString(realtime.In(s.tz).Format(time.StampMicro))
    65  
    66  	if identifier, ok := entry[FIELD_SYSLOG_IDENTIFIER]; ok {
    67  		buf.WriteByte(' ')
    68  		buf.Write(identifier)
    69  	} else {
    70  		buf.WriteString(" unknown")
    71  	}
    72  
    73  	if pid, ok := entry[FIELD_PID]; ok {
    74  		buf.WriteByte('[')
    75  		buf.Write(pid)
    76  		buf.WriteByte(']')
    77  	} else if pid, ok := entry[FIELD_SYSLOG_PID]; ok {
    78  		buf.WriteByte('[')
    79  		buf.Write(pid)
    80  		buf.WriteByte(']')
    81  	}
    82  
    83  	buf.WriteString(": ")
    84  	indent := buf.Len()
    85  	lines := bytes.Split(message, []byte{'\n'})
    86  	writeEscaped(&buf, lines[0])
    87  	for _, line := range lines[1:] {
    88  		buf.WriteByte('\n')
    89  		buf.Write(bytes.Repeat([]byte{' '}, indent))
    90  		writeEscaped(&buf, line)
    91  	}
    92  
    93  	buf.WriteByte('\n')
    94  
    95  	_, err := buf.WriteTo(s.w)
    96  	return err
    97  }
    98  
    99  func (s *shortWriter) isReboot(entry Entry) bool {
   100  	bootid, ok := entry[FIELD_BOOT_ID]
   101  	if !ok || len(bootid) == 0 {
   102  		return false
   103  	}
   104  
   105  	newid := string(bootid)
   106  	if s.bootid == "" {
   107  		s.bootid = newid
   108  		return false
   109  	} else if s.bootid != newid {
   110  		s.bootid = newid
   111  		return true
   112  	}
   113  	return false
   114  }
   115  
   116  func writeEscaped(buf *bytes.Buffer, line []byte) {
   117  	const tab = "        " // 8 spaces
   118  	for len(line) > 0 {
   119  		r, n := utf8.DecodeRune(line)
   120  		switch r {
   121  		case utf8.RuneError:
   122  			fmt.Fprintf(buf, "\\x%02x", line[0])
   123  		case '\t':
   124  			buf.WriteString(tab)
   125  		default:
   126  			if unicode.IsPrint(r) {
   127  				buf.Write(line[:n])
   128  			} else {
   129  				fmt.Fprintf(buf, "\\u%04x", r)
   130  			}
   131  		}
   132  		line = line[n:]
   133  	}
   134  }