Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file.
- @gbolo added option to harden the TLS client
- @chopmann added option to bind the http server to an address
- @ibrokethecloud added ability to add custom key:value pairs as EXCLUDE_LABEL.
- @localghost added option to read original logs timestamps from containers

### Removed

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ If you use multiline logging with raw, it's recommended to json encode the Data
* `MULTILINE_PATTERN` - pattern for multiline logging, see: [MULTILINE_MATCH](#multiline_match) (default: `^\s`)
* `MULTILINE_FLUSH_AFTER` - maximum time between the first and last lines of a multiline log entry in milliseconds (default: 500)
* `MULTILINE_SEPARATOR` - separator between lines for output (default: `\n`)
* `ORIGINAL_TIMESTAMPS` - read original log timestamps from containers (default: generate timestamps as the logs are processed by logspout)

#### Raw Format

Expand Down
29 changes: 27 additions & 2 deletions router/pump.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ func getopt(name, dfault string) string {
return value
}

func originalTimestamps() bool {
return getopt("ORIGINAL_TIMESTAMPS", "false") == "true"
}

func debug(v ...interface{}) {
if os.Getenv("DEBUG") != "" {
log.Println(v...)
Expand Down Expand Up @@ -244,6 +248,7 @@ func (p *LogsPump) pumpLogs(event *docker.APIEvents, backlog bool, inactivityTim
Since: sinceTime.Unix(),
InactivityTimeout: inactivityTimeout,
RawTerminal: rawTerminal,
Timestamps: originalTimestamps(),
})
if err != nil {
debug("pump.pumpLogs():", id, "stopped with error:", err)
Expand Down Expand Up @@ -370,10 +375,11 @@ func newContainerPump(container *docker.Container, stdout, stderr io.Reader) *co
}
return
}
logMessage, logTime := parseLogLine(line, originalTimestamps())
cp.send(&Message{
Data: strings.TrimSuffix(line, "\n"),
Data: logMessage,
Container: container,
Time: time.Now(),
Time: logTime,
Source: source,
})
}
Expand Down Expand Up @@ -405,3 +411,22 @@ func (cp *containerPump) remove(logstream chan *Message) {
defer cp.Unlock()
delete(cp.logstreams, logstream)
}

func parseLogLine(line string, originalTimestamps bool) (string, time.Time) {
line = strings.TrimSuffix(line, "\n")

if ! originalTimestamps {
return line, time.Now()
}

logEntry := strings.SplitN(line, " ", 2)
logTime, err := time.Parse(time.RFC3339Nano, logEntry[0])
if err != nil {
return line, time.Now()
}

if len(logEntry) == 2 {
return logEntry[1], logTime
}
return "", logTime
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we want to return an empty log line? Shouldn't this also be: return line, time.Now()

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if line has no white spaces - if it had the function would finish at len(logEntry) == 2 - and logEntry[0] is a valid time string - otherwise the function would finish at err != nil then it means that an empty message was logged

}