github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/api/handlers/compat/containers_logs.go (about) 1 package compat 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 "io" 7 "net/http" 8 "strconv" 9 "strings" 10 "sync" 11 "time" 12 13 "github.com/hanks177/podman/v4/libpod" 14 "github.com/hanks177/podman/v4/libpod/logs" 15 "github.com/hanks177/podman/v4/pkg/api/handlers/utils" 16 api "github.com/hanks177/podman/v4/pkg/api/types" 17 "github.com/hanks177/podman/v4/pkg/util" 18 "github.com/gorilla/schema" 19 "github.com/pkg/errors" 20 log "github.com/sirupsen/logrus" 21 ) 22 23 func LogsFromContainer(w http.ResponseWriter, r *http.Request) { 24 decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) 25 runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) 26 27 query := struct { 28 Follow bool `schema:"follow"` 29 Stdout bool `schema:"stdout"` 30 Stderr bool `schema:"stderr"` 31 Since string `schema:"since"` 32 Until string `schema:"until"` 33 Timestamps bool `schema:"timestamps"` 34 Tail string `schema:"tail"` 35 }{ 36 Tail: "all", 37 } 38 if err := decoder.Decode(&query, r.URL.Query()); err != nil { 39 utils.Error(w, http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) 40 return 41 } 42 43 if !(query.Stdout || query.Stderr) { 44 msg := fmt.Sprintf("%s: you must choose at least one stream", http.StatusText(http.StatusBadRequest)) 45 utils.Error(w, http.StatusBadRequest, errors.Errorf("%s for %s", msg, r.URL.String())) 46 return 47 } 48 49 name := utils.GetName(r) 50 ctnr, err := runtime.LookupContainer(name) 51 if err != nil { 52 utils.ContainerNotFound(w, name, err) 53 return 54 } 55 56 var tail int64 = -1 57 if query.Tail != "all" { 58 tail, err = strconv.ParseInt(query.Tail, 0, 64) 59 if err != nil { 60 utils.BadRequest(w, "tail", query.Tail, err) 61 return 62 } 63 } 64 65 var since time.Time 66 if _, found := r.URL.Query()["since"]; found { 67 since, err = util.ParseInputTime(query.Since, true) 68 if err != nil { 69 utils.BadRequest(w, "since", query.Since, err) 70 return 71 } 72 } 73 74 var until time.Time 75 if _, found := r.URL.Query()["until"]; found { 76 if query.Until != "0" { 77 until, err = util.ParseInputTime(query.Until, false) 78 if err != nil { 79 utils.BadRequest(w, "until", query.Until, err) 80 return 81 } 82 } 83 } 84 85 options := &logs.LogOptions{ 86 Details: true, 87 Follow: query.Follow, 88 Since: since, 89 Until: until, 90 Tail: tail, 91 Timestamps: query.Timestamps, 92 } 93 94 var wg sync.WaitGroup 95 options.WaitGroup = &wg 96 97 logChannel := make(chan *logs.LogLine, tail+1) 98 if err := runtime.Log(r.Context(), []*libpod.Container{ctnr}, options, logChannel); err != nil { 99 utils.InternalServerError(w, errors.Wrapf(err, "failed to obtain logs for Container '%s'", name)) 100 return 101 } 102 go func() { 103 wg.Wait() 104 close(logChannel) 105 }() 106 107 w.WriteHeader(http.StatusOK) 108 109 var frame strings.Builder 110 header := make([]byte, 8) 111 112 writeHeader := true 113 // Docker does not write stream headers iff the container has a tty. 114 if !utils.IsLibpodRequest(r) { 115 inspectData, err := ctnr.Inspect(false) 116 if err != nil { 117 utils.InternalServerError(w, errors.Wrapf(err, "failed to obtain logs for Container '%s'", name)) 118 return 119 } 120 writeHeader = !inspectData.Config.Tty 121 } 122 123 for line := range logChannel { 124 if _, found := r.URL.Query()["until"]; found { 125 if line.Time.After(until) && !until.IsZero() { 126 break 127 } 128 } 129 130 // Reset buffer we're ready to loop again 131 frame.Reset() 132 switch line.Device { 133 case "stdout": 134 if !query.Stdout { 135 continue 136 } 137 header[0] = 1 138 case "stderr": 139 if !query.Stderr { 140 continue 141 } 142 header[0] = 2 143 default: 144 // Logging and moving on is the best we can do here. We may have already sent 145 // a Status and Content-Type to client therefore we can no longer report an error. 146 log.Infof("unknown Device type '%s' in log file from Container %s", line.Device, ctnr.ID()) 147 continue 148 } 149 150 if query.Timestamps { 151 frame.WriteString(line.Time.Format(time.RFC3339)) 152 frame.WriteString(" ") 153 } 154 155 frame.WriteString(line.Msg) 156 if !line.Partial() { 157 frame.WriteString("\n") 158 } 159 160 if writeHeader { 161 binary.BigEndian.PutUint32(header[4:], uint32(frame.Len())) 162 if _, err := w.Write(header[0:8]); err != nil { 163 log.Errorf("unable to write log output header: %q", err) 164 } 165 } 166 167 if _, err := io.WriteString(w, frame.String()); err != nil { 168 log.Errorf("unable to write frame string: %q", err) 169 } 170 if flusher, ok := w.(http.Flusher); ok { 171 flusher.Flush() 172 } 173 } 174 }