Skip to content

Conversation

@alisdair
Copy link
Contributor

ConnLogger can be used to log executed statements for a connection. The interface captures events for Begin, Exec, Commit, and Rollback calls.

Updates tailscale/corp#33577

sqlite.go Outdated
Comment on lines 1097 to 1117
// ConnLogger is implemented by the caller to support statement-level logging for
// write transactions. Only Exec calls are logged, not Query calls, as this is
// intended as a mechanism to replay failed transactions.
type ConnLogger interface {
// Begin is called when a writable transaction is opened.
Begin()

// Statement is called with evaluated SQL when a statement is executed.
Statement(sql string)

// Commit is called when a transaction successfully commits.
Commit()

// Rollback is called when a transaction is rolled back.
Rollback()
}
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 not sure this gives us enough information for what we'd want to log.

I want to see separate log events for beginning to commit vs commit done and its result, possibly including the WAL growth. Well, we track that per-Tx WAL growth already in cfgdb, so I guess we have enough to stich that in, but I think we'd at least need here for the Commit method above to take an error parameter for what we returned to the caller, and then update the docs to say it's not just about successful commits. Or Rollback needs an error perhaps, where a nil error means it was an explicit Rollback from user code, as opposed to a failed commit resulting in a rollback?

Other misc thought: maybe Begin should return some comparable TxID that the other 3 methods also receive, for lining stuff up. (but maybe not needed if we in practice serialize all writes... but maybe we're not?)

Copy link
Contributor

Choose a reason for hiding this comment

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

I am not against having more debugging data, but I don't think I understand why this API is insufficient for logging all committed write transactions? If a logger resets its state on Begin and only logs accumulated statements upon receiving Commit, do we care about unsuccessful commits or rollbacks at all? For the task at hand, I am not sure we even need the Rollback callback.

Same thoughts about TxID - I don't mind more details, but if a single conn can only have one transaction at a time, the logger itself can assign an id when Begin is called, and attribute all subsequent statements to that ID, 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've added an error parameter to Commit.

// ConnLogger is implemented by the caller to support statement-level logging for
// write transactions. Only Exec calls are logged, not Query calls, as this is
// intended as a mechanism to replay failed transactions.
type ConnLogger interface {
Copy link
Contributor

Choose a reason for hiding this comment

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

We probably want some kind of Close() method to ensure the file is flushed to disk when we exit.

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 don't think we want Close here, although of course you're right that we need to flush the logs to disk on exit. In the corresponding PR, I've added a Close handler which is attached to the database shutdown process. Thanks for spotting it!

sqlite.go Outdated
Comment on lines 1097 to 1102
// ConnLogger is implemented by the caller to support statement-level logging for
// write transactions. Only Exec calls are logged, not Query calls, as this is
Copy link
Contributor

Choose a reason for hiding this comment

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

As implemented, I think this would log read transactions too? I took a stab at skipping the logger for readonly transactions: 112a07d

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks! Merged your commit into mine.

sqlite.go Outdated
Comment on lines 1097 to 1117
// ConnLogger is implemented by the caller to support statement-level logging for
// write transactions. Only Exec calls are logged, not Query calls, as this is
// intended as a mechanism to replay failed transactions.
type ConnLogger interface {
// Begin is called when a writable transaction is opened.
Begin()

// Statement is called with evaluated SQL when a statement is executed.
Statement(sql string)

// Commit is called when a transaction successfully commits.
Commit()

// Rollback is called when a transaction is rolled back.
Rollback()
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I am not against having more debugging data, but I don't think I understand why this API is insufficient for logging all committed write transactions? If a logger resets its state on Begin and only logs accumulated statements upon receiving Commit, do we care about unsuccessful commits or rollbacks at all? For the task at hand, I am not sure we even need the Rollback callback.

Same thoughts about TxID - I don't mind more details, but if a single conn can only have one transaction at a time, the logger itself can assign an id when Begin is called, and attribute all subsequent statements to that ID, right?

@alisdair alisdair force-pushed the alisdair/connlogger branch 2 times, most recently from 2ebf665 to b07a594 Compare October 23, 2025 23:00
sqlite.go Outdated
type connector struct {
name string
tracer sqliteh.Tracer
makeLogger func() ConnLogger
Copy link
Member

Choose a reason for hiding this comment

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

// or nil

Comment on lines 400 to 405
if tx.conn.tracer != nil {
tx.conn.tracer.Commit(tx.conn.id, err)
}
Copy link
Member

Choose a reason for hiding this comment

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

This Tracer interface is what I was remembering with its type TraceConnID it plumbs through.

It's kinda weird having both Tracer and Logger that are such overlaps. Oh well.

I guess Tracer has the SQL with placeholders and Logger has it without? Might be worth documenting on both the relationship between the two.

@alisdair alisdair force-pushed the alisdair/connlogger branch from b07a594 to 94eb142 Compare October 24, 2025 16:28
}
}

func ConnectorWithLogger(sqliteURI string, connInitFunc ConnInitFunc, tracer sqliteh.Tracer, makeLogger func() ConnLogger) driver.Connector {
Copy link
Member

Choose a reason for hiding this comment

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

Document when makeLogger is called (i.e., "when Connect is called")?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

ConnLogger can be used to log executed statements for a connection. The
interface captures events for Begin, Exec, Commit, and Rollback calls.

Updates tailscale/corp#33577

Co-authored-by: Anton Tolchanov <[email protected]>
@alisdair alisdair force-pushed the alisdair/connlogger branch from 94eb142 to 731f626 Compare October 27, 2025 13:19
@alisdair alisdair merged commit 35d6745 into main Oct 27, 2025
2 checks passed
@alisdair alisdair deleted the alisdair/connlogger branch October 27, 2025 13:35
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.

5 participants