If a log is uploaded with a sensible filename, preserve it (#18)
- the mobile guys want to be able to distinguish between crash.log and other logs.
This commit is contained in:
parent
f630c6a78a
commit
63d6917dfe
3 changed files with 29 additions and 8 deletions
|
@ -57,11 +57,16 @@ logs.)
|
||||||
* `log`: a log file, with lines separated by newline characters. Multiple log
|
* `log`: a log file, with lines separated by newline characters. Multiple log
|
||||||
files can be included by including several `log` parts.
|
files can be included by including several `log` parts.
|
||||||
|
|
||||||
|
If the log is uploaded with a filename `name.ext`, where `name` contains only
|
||||||
|
alphanumerics, `.`, `-` or `_`, and `ext` is one of `log` or `txt`, then the
|
||||||
|
file saved to disk is based on that. Otherwise, a suitable name is
|
||||||
|
constructed.
|
||||||
|
|
||||||
If using the JSON upload encoding, the request object should instead include
|
If using the JSON upload encoding, the request object should instead include
|
||||||
a single `logs` field, which is an array of objects with the following
|
a single `logs` field, which is an array of objects with the following
|
||||||
fields:
|
fields:
|
||||||
|
|
||||||
* `id`: textual identifier for the logs. Currently ignored.
|
* `id`: textual identifier for the logs. Used as the filename, as above.
|
||||||
* `lines`: log data. Newlines should be encoded as `\n`, as normal in JSON).
|
* `lines`: log data. Newlines should be encoded as `\n`, as normal in JSON).
|
||||||
|
|
||||||
* `compressed-log`: a gzipped logfile. Decompressed and then treated the same as
|
* `compressed-log`: a gzipped logfile. Decompressed and then treated the same as
|
||||||
|
|
|
@ -189,7 +189,7 @@ func parseJSONRequest(w http.ResponseWriter, req *http.Request, reportDir string
|
||||||
|
|
||||||
for i, log := range p.Logs {
|
for i, log := range p.Logs {
|
||||||
buf := bytes.NewBufferString(log.Lines)
|
buf := bytes.NewBufferString(log.Lines)
|
||||||
leafName, err := saveLogPart(i, buf, reportDir)
|
leafName, err := saveLogPart(i, log.ID, buf, reportDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -286,7 +286,7 @@ func parseFormPart(part *multipart.Part, p *parsedPayload, reportDir string) err
|
||||||
}
|
}
|
||||||
|
|
||||||
if field == "log" || field == "compressed-log" {
|
if field == "log" || field == "compressed-log" {
|
||||||
leafName, err := saveLogPart(len(p.Logs), partReader, reportDir)
|
leafName, err := saveLogPart(len(p.Logs), part.FileName(), partReader, reportDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -359,11 +359,27 @@ func saveFormPart(leafName string, reader io.Reader, reportDir string) (string,
|
||||||
return leafName, nil
|
return leafName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we require a sensible extension, and don't allow the filename to start with
|
||||||
|
// '.'
|
||||||
|
var logRegexp = regexp.MustCompile(`^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*\.(log|txt)$`)
|
||||||
|
|
||||||
// saveLogPart saves a log upload to the report directory.
|
// saveLogPart saves a log upload to the report directory.
|
||||||
//
|
//
|
||||||
// Returns the leafname of the saved file.
|
// Returns the leafname of the saved file.
|
||||||
func saveLogPart(logNum int, reader io.Reader, reportDir string) (string, error) {
|
func saveLogPart(logNum int, filename string, reader io.Reader, reportDir string) (string, error) {
|
||||||
leafName := fmt.Sprintf("logs-%04d.log.gz", logNum)
|
// pick a name to save the log file with.
|
||||||
|
//
|
||||||
|
// some clients use sensible names (foo.N.log), which we preserve. For
|
||||||
|
// others, we just make up a filename.
|
||||||
|
//
|
||||||
|
// Either way, we need to append .gz, because we're compressing it.
|
||||||
|
var leafName string
|
||||||
|
if logRegexp.MatchString(filename) {
|
||||||
|
leafName = filename + ".gz"
|
||||||
|
} else {
|
||||||
|
leafName = fmt.Sprintf("logs-%04d.log.gz", logNum)
|
||||||
|
}
|
||||||
|
|
||||||
fullname := filepath.Join(reportDir, leafName)
|
fullname := filepath.Join(reportDir, leafName)
|
||||||
|
|
||||||
f, err := os.Create(fullname)
|
f, err := os.Create(fullname)
|
||||||
|
|
|
@ -112,7 +112,7 @@ func TestMultipartUpload(t *testing.T) {
|
||||||
|
|
||||||
// check logs uploaded correctly
|
// check logs uploaded correctly
|
||||||
checkUploadedFile(t, reportDir, "logs-0000.log.gz", true, "log\nlog\nlog")
|
checkUploadedFile(t, reportDir, "logs-0000.log.gz", true, "log\nlog\nlog")
|
||||||
checkUploadedFile(t, reportDir, "logs-0001.log.gz", true, "log")
|
checkUploadedFile(t, reportDir, "console.0.log.gz", true, "log")
|
||||||
checkUploadedFile(t, reportDir, "logs-0002.log.gz", true, "test\n")
|
checkUploadedFile(t, reportDir, "logs-0002.log.gz", true, "test\n")
|
||||||
|
|
||||||
// check file uploaded correctly
|
// check file uploaded correctly
|
||||||
|
@ -156,7 +156,7 @@ log
|
||||||
log
|
log
|
||||||
log
|
log
|
||||||
------WebKitFormBoundarySsdgl8Nq9voFyhdO
|
------WebKitFormBoundarySsdgl8Nq9voFyhdO
|
||||||
Content-Disposition: form-data; name="log"; filename="instance-0.067644760733513781492004890379"
|
Content-Disposition: form-data; name="log"; filename="console.0.log"
|
||||||
Content-Type: text/plain
|
Content-Type: text/plain
|
||||||
|
|
||||||
log
|
log
|
||||||
|
@ -207,7 +207,7 @@ func checkParsedMultipartUpload(t *testing.T, p *parsedPayload) {
|
||||||
if p.Logs[0] != wanted {
|
if p.Logs[0] != wanted {
|
||||||
t.Errorf("Log 0: got %s, want %s", p.Logs[0], wanted)
|
t.Errorf("Log 0: got %s, want %s", p.Logs[0], wanted)
|
||||||
}
|
}
|
||||||
wanted = "logs-0001.log.gz"
|
wanted = "console.0.log.gz"
|
||||||
if p.Logs[1] != wanted {
|
if p.Logs[1] != wanted {
|
||||||
t.Errorf("Log 1: got %s, want %s", p.Logs[1], wanted)
|
t.Errorf("Log 1: got %s, want %s", p.Logs[1], wanted)
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue