| // Copyright 2020 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package logs |
| |
| import ( |
| "compress/gzip" |
| "context" |
| "fmt" |
| "io" |
| "os/exec" |
| "regexp" |
| ) |
| |
| // croslogCursorRegexp parses output from a `croslog --show-cursor` command. Example output: |
| // -- cursor: s=bfd03b7e4c464cc09b43e1843880f9ee;i=a5eab;b=cdd933be... |
| var croslogCursorRegexp = regexp.MustCompile(`^-- cursor:\s+(\S+)`) |
| |
| // GetUnifiedLogCursor returns a log cursor to the current tip of the unified system logs. |
| // The return cursor can later be passed to WriteLogs. |
| func GetUnifiedLogCursor(ctx context.Context) (string, error) { |
| cmd := exec.CommandContext(ctx, "croslog", "--lines=0", "--show-cursor", "--quiet", "--no-pager") |
| out, err := cmd.Output() |
| if err != nil { |
| return "", err |
| } |
| m := croslogCursorRegexp.FindSubmatch(out) |
| if m == nil { |
| return "", fmt.Errorf("failed to find %q in %q", croslogCursorRegexp, out) |
| } |
| return string(m[1]), nil |
| } |
| |
| // UnifiedLogFormat is a format for syslog entries. |
| type UnifiedLogFormat int |
| |
| const ( |
| // CompactLogFormat corresponds to human-readable single-line log entries. |
| CompactLogFormat UnifiedLogFormat = iota |
| // GzippedExportLogFormat corresponds to croslog's "--output=export" multiline format that preserves metadata. |
| // The data is additionally gzip-compressed. |
| GzippedExportLogFormat |
| ) |
| |
| // ExportUnifiedLogs writes unified system log entries generated after cursor to w using fm. |
| // cursor should have been generated by an earlier call to GetUnifiedLogCursor. |
| func ExportUnifiedLogs(ctx context.Context, w io.Writer, cursor string, fm UnifiedLogFormat) error { |
| args := []string{"--after-cursor=" + cursor} |
| if fm == GzippedExportLogFormat { |
| args = append(args, "--output=export") |
| } |
| cmd := exec.CommandContext(ctx, "croslog", args...) |
| |
| var ww io.WriteCloser // wraps w; may be nil |
| if fm == GzippedExportLogFormat { |
| ww = gzip.NewWriter(w) |
| cmd.Stdout = ww |
| } else { |
| cmd.Stdout = w |
| } |
| |
| err := cmd.Run() |
| |
| // If the writer was wrapped, close the wrapper to flush data. |
| if ww != nil { |
| if cerr := ww.Close(); cerr != nil && err == nil { |
| err = cerr |
| } |
| } |
| |
| return err |
| } |