Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to timestamp lines #430

Merged
merged 3 commits into from
Mar 7, 2017

Conversation

stefanmb
Copy link
Contributor

Background:
The issue in buildkite/feedback#80 has become a showstopper for me. I am supporting a Buildkite based container building service which allows users to provide their own complex build scripts. There are often performance problems that must be diagnosed. Since I do not control the individual build steps I cannot add group header to their build output in order to get timing information. I require an option to timestamp each line of output so we can profile any part of the build.

I've tried ad-hoc solutions involving ts and unbuffer but they are not drop-in replacements, this feature is needed in the agent itself.

Changes:
This PR adds a BUILDKITE_TIMESTAMP_LINES environment variable. If set to one of 1, t, T, TRUE, true, True, 0, f, F each line of output will be prefixed with an RFC3339 UTC timestamp.

Note that the header groups are not affected.

I've tried to avoid introducing perf hits (due to single-threaded regex parsing) if the option is not enabled. The code became a bit complex as the header API appears to only transmit a list of timestamps and their matching headers must be parsed a second time by the backend, so they cannot be prefixed. Unfortunately this approach does introduce more coupling between process.go and header_times_streamer.go.

I made this PR against 2-3-stable because that's what we're using. If you want me to port it to master please let me know.

Review:
Would greatly appreciate some comments from @keithpitt @toolmantim.
cc: @camilo @n1koo

@@ -73,18 +73,16 @@ func (h *HeaderTimesStreamer) Scan(line string) {
h.scanWaitGroup.Add(1)
defer h.scanWaitGroup.Done()

if h.lineIsHeader(line) {
Copy link
Member

Choose a reason for hiding this comment

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

I'm probably missing something, but I think this should still be there? Otherwise each line is probably considered a ---header right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I had to move that check out of header_times_streamer.go, it introduces some more coupling but I didn't want the check to happen twice. Unfortunately I have to know if something is a header before I can timestamp it, so it's done here now:

https://github.com/buildkite/agent/pull/430/files#diff-14ee62401fee1966227df2f4ab955c3cR192

@keithpitt
Copy link
Member

Hey @stefanmb - this is awesome! I'm just doing a code review now and we'll probably need to do some manual testing on our end. But I'm happy to add it to stable once we've done everything we need to!

@@ -59,13 +60,20 @@ func (r JobRunner) Create() (runner *JobRunner, err error) {
// the Buildkite Agent API
runner.logStreamer = LogStreamer{MaxChunkSizeBytes: r.Job.ChunksMaxSizeBytes, Callback: r.onUploadChunk}.New()

timestamp, err := strconv.ParseBool(os.Getenv("BUILDKITE_TIMESTAMP_LINES"))
Copy link
Member

Choose a reason for hiding this comment

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

Maybe we should check this in AgentConfiguration? Or are you thinking that BUILDKITE_TIMESTAMP_LINES could be turned on/off from the BK UI at runtime?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, I can move it there!

// otherwise we let the goroutines take the perf hit.

checkedForCallback := false
lineHasCallback := false
Copy link
Member

Choose a reason for hiding this comment

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

This bit here has me a little confused. lineHasCallback always seems to be false unless timestamping has been turned on?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, that variable is not strictly needed but I left it for clarity.

The idea was to use a short circuit evaluation to prevent doing the check twice in case the timestamping option is enabled: https://github.com/buildkite/agent/pull/430/files#diff-14ee62401fee1966227df2f4ab955c3cR207

I admit it's not super elegant.

PTY: r.AgentConfiguration.RunInPty,
Timestamp: timestamp,
StartCallback: r.onProcessStartCallback,
LineCallback: runner.headerTimesStreamer.Scan,
Copy link
Member

Choose a reason for hiding this comment

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

Ohhh right right, I see how this works now. Took a while for it to click :)

So essentially, LineCallback will only be called if the LineCallbackFilter function returns true for a particular line, is that right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's exactly the idea! The ugly bit of coupling is that lines are not timestamped if there is any callback handler. However, the only callback handler that exists right now is used for the header timestamps.

@stefanmb
Copy link
Contributor Author

@keithpitt I've moved the env variable to AgentConfiguration (and added a corresponding command line switch). Let me know if there is anything else I can do to help! Thanks!

@keithpitt
Copy link
Member

Rad, I've just done some local testing and it works great. Let's merge it!

@keithpitt keithpitt merged commit f2fb1e9 into buildkite:2-3-stable Mar 7, 2017
@toolmantim
Copy link
Contributor

I think we forgot to port this to master…

@mdub
Copy link

mdub commented May 30, 2017

Is this something that can be enabled per-pipeline? Or can it only be set at agent level?

@stefanmb
Copy link
Contributor Author

Agent level as I implemented it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants