You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The bStats errors chart is reporting real errors from live servers, but there is no way to tell what any of them were. The chart resolves a failure to one of four fixed strings and nothing else, so "we had N sql-write errors" is the end of the investigation.
Worse, the underlying exception is discarded, so the server owner sitting at their own console has nothing to send us either. There is currently no log to output anywhere.
What we have today
Errors.java:11-14 defines exactly four categories: sql-write, sql-read, db-connect, item-data. MetricsReporter.java:185 feeds those as the labels of the errors bar chart.
At four of the six call sites the exception is caught and never read:
DatabaseUtil.java:88, DatabaseUtil.java:109, DatabaseUtil.java:132, and ConnectionManager.java:37. No message, no SQLState, no stack trace. Only PersistentHome.java:45 and PersistentString.java:41 call printStackTrace().
bStats cannot close this gap on its own. It carries numbers and label strings on registered chart ids, and has no field for a message or a stack trace.
Part 1: stop swallowing the exception
Add a private helper in DatabaseUtil taking the operation name, the SQL, and the throwable. It logs one severe line carrying the message, getSQLState(), and getErrorCode(), plus the SQL template. The full stack trace prints only when debugLevel: info, so ordinary consoles stay clean while owners keep a switch.
Wire it into the four sites above, and route the two existing printStackTrace() calls through it as well.
Logging the SQL is safe. setParams binds values into a prepared statement, so the string we hold is the template and never contains player data.
Part 2: enrich the bStats labels
Give Errors.count an overload taking the throwable, deriving a suffix from a fixed allowlist of SQLite result codes: SQLITE_BUSY, SQLITE_LOCKED, SQLITE_READONLY, SQLITE_CORRUPT, SQLITE_FULL, SQLITE_CANTOPEN, SQLITE_CONSTRAINT, SQLITE_NOTADB, falling back to other. The label becomes sql-write/SQLITE_BUSY.
The allowlist is load bearing, not a nicety. Raw exception messages contain absolute paths such as /home/mcserver/plugins/SetHomesTwo/database/homes.db, which is exactly the server-specific data the Errors javadoc promises never leaves the server. An allowlist makes that structurally impossible, and it bounds cardinality at roughly 36 labels.
The errors chart id is already registered and bar chart labels are submitted by the plugin, so this should need no bStats dashboard change. Confirm that against the first day of real data rather than assuming it.
Explicitly not doing: remote error reporting
Recording the research so it does not get repeated.
Sentry's free Developer plan does allow SDK ingestion. There is no API gate on sending events; the DSN endpoint needs no auth token. The limits that bite are 5,000 events per month (shared between errors and transactions), one seat, and 30 day retention. Team is 26 USD per month for 50,000 events.
Datadog is a worse fit. Its free tier excludes Log Management and excludes Error Tracking, which requires buying Log Management or RUM. Free covers 5 hosts and 1 day metric retention. Log ingestion is 0.10 USD per ingested GB.
Three reasons to leave this parked:
The quota model does not match the situation. These are other people's servers, not our app. One server looping on a locked SQLite database can emit thousands of events in an hour and burn the whole month, blinding us to everyone else.
Jar size. The shaded jar is currently 665 KB, right after update pom to decrease generated jar file size #69 reduced it. The Sentry Java SDK and its transitive dependencies would be a large fraction of that again.
Consent posture. bStats is the accepted community norm. Shipping stack traces from an owner's server to a US third party is a different proposition, needs a real opt in and a CurseForge data collection disclosure, and tends to land badly with server owners.
Parts 1 and 2 will probably identify these errors without any of that. If they do not, the specific gap they leave should shape the remote design rather than guesswork.
Testing
MockBukkit, alongside the existing suite:
A forced SQLite failure produces the expected enriched label.
An unrecognized error code falls back to other.
The emitted label never contains the exception message.
Problem
The bStats
errorschart is reporting real errors from live servers, but there is no way to tell what any of them were. The chart resolves a failure to one of four fixed strings and nothing else, so "we had N sql-write errors" is the end of the investigation.Worse, the underlying exception is discarded, so the server owner sitting at their own console has nothing to send us either. There is currently no log to output anywhere.
What we have today
Errors.java:11-14defines exactly four categories:sql-write,sql-read,db-connect,item-data.MetricsReporter.java:185feeds those as the labels of theerrorsbar chart.At four of the six call sites the exception is caught and never read:
DatabaseUtil.java:88,DatabaseUtil.java:109,DatabaseUtil.java:132, andConnectionManager.java:37. No message, no SQLState, no stack trace. OnlyPersistentHome.java:45andPersistentString.java:41callprintStackTrace().bStats cannot close this gap on its own. It carries numbers and label strings on registered chart ids, and has no field for a message or a stack trace.
Part 1: stop swallowing the exception
Add a private helper in
DatabaseUtiltaking the operation name, the SQL, and the throwable. It logs onesevereline carrying the message,getSQLState(), andgetErrorCode(), plus the SQL template. The full stack trace prints only whendebugLevel: info, so ordinary consoles stay clean while owners keep a switch.Wire it into the four sites above, and route the two existing
printStackTrace()calls through it as well.Logging the SQL is safe.
setParamsbinds values into a prepared statement, so the string we hold is the template and never contains player data.Part 2: enrich the bStats labels
Give
Errors.countan overload taking the throwable, deriving a suffix from a fixed allowlist of SQLite result codes:SQLITE_BUSY,SQLITE_LOCKED,SQLITE_READONLY,SQLITE_CORRUPT,SQLITE_FULL,SQLITE_CANTOPEN,SQLITE_CONSTRAINT,SQLITE_NOTADB, falling back toother. The label becomessql-write/SQLITE_BUSY.The allowlist is load bearing, not a nicety. Raw exception messages contain absolute paths such as
/home/mcserver/plugins/SetHomesTwo/database/homes.db, which is exactly the server-specific data theErrorsjavadoc promises never leaves the server. An allowlist makes that structurally impossible, and it bounds cardinality at roughly 36 labels.The
errorschart id is already registered and bar chart labels are submitted by the plugin, so this should need no bStats dashboard change. Confirm that against the first day of real data rather than assuming it.Explicitly not doing: remote error reporting
Recording the research so it does not get repeated.
Sentry's free Developer plan does allow SDK ingestion. There is no API gate on sending events; the DSN endpoint needs no auth token. The limits that bite are 5,000 events per month (shared between errors and transactions), one seat, and 30 day retention. Team is 26 USD per month for 50,000 events.
Datadog is a worse fit. Its free tier excludes Log Management and excludes Error Tracking, which requires buying Log Management or RUM. Free covers 5 hosts and 1 day metric retention. Log ingestion is 0.10 USD per ingested GB.
Three reasons to leave this parked:
Parts 1 and 2 will probably identify these errors without any of that. If they do not, the specific gap they leave should shape the remote design rather than guesswork.
Testing
MockBukkit, alongside the existing suite:
other.