サーバーエージェント
Go
導入は明示的です — ミドルウェア、ドライバのラップ、そして測る価値のある関数の先頭に2行。Go にはフックできるビルド段階がなく、渡してもらわない限り呼び出し側の context に手が届かないからです。
- パッケージ
github.com/sixty-sh/sixty-go/ Go modules- 動作環境
- Go 1.22 以降。依存なし。追加する go.sum にも何も入りません。
- ソース
- sixty-sh/sixty-go
導入方法
Any net/http server and any database/sql driver: functions, HTTP routes and SQL. Spans are threaded through context, so the install is explicit rather than automatic.
導入手順は、あなた向けのチェックリストではなく、すでに開いているコーディングエージェント向けのプロンプトとして書かれています。これは意図的です。どのファイルを編集するかではなく、導入が終わった時点で何が成り立っていなければならないかを述べています。コードをどこに置くかはフレームワーク次第で、置き場所を間違えると静かに失敗するからです。エージェントはリポジトリを読んでそれを判断できますが、ドキュメントの段落にはできません。
同じ文面は、 install_sixty が MCP サーバー 経由で返すものであり、コレクタが次の場所で配信しているものでもあります: /v1/setup?kind=go. 実体は1つだけです。
Go の導入手順(全文)
Install the sixty agent in this Go service so its functions, HTTP routes and
database queries report to sixty.
The package is github.com/sixty-sh/sixty-go, imported as "sixty". It
has no dependencies of its own.
Before editing, inspect whether this service makes LLM or agent calls. If it
does, ask the user: "Do you want LLM monitoring?" If yes, use StartAgent,
StartGeneration and StartTool, thread each returned context through the work,
and call generation.SetUsage with token/cost metadata before End. The API
does not accept prompts, outputs, tool arguments or tool results.
1. In main(), before the server starts:
defer sixty.Init(sixty.Config{}).Shutdown(context.Background())
The zero Config reads the environment. Shutdown sends the last window, so
a short-lived process still reports; on a long-lived server it runs at
exit. If this service already traps signals for graceful shutdown, call
Shutdown there instead of deferring.
2. Wrap the HTTP handler — sixty.Middleware(handler) — at the outermost level,
so the span covers the other middleware rather than sitting inside it.
Anything that speaks net/http works: chi, gorilla/mux, echo, the standard
library's own mux.
If this project uses a router that knows its route patterns, add one more
middleware AFTER routing that calls sixty.SetRoute(r.Context(), pattern) —
chi.RouteContext(r.Context()).RoutePattern(), or the equivalent. Without
it, /users/42/orders is templated to /users/:id/orders, which is close and
occasionally wrong.
3. Measure the database. Replace the sql.Open call with sixty.Open, passing
the same driver name and DSN:
db, err := sixty.Open("pgx", dsn)
If this project builds its pool some other way — a Connector, a pgx pool
through stdlib, a wrapper library — use sixty.WrapDriver(d) around the
driver it registers instead. Postgres is what the detector understands;
another database will report timings and nothing else.
4. Measure the functions worth measuring. This is the step that turns "this
endpoint got slow" into "this function started issuing 14 queries", and
skipping it leaves the feed with routes and queries and nothing between:
func (s *Store) GetUserOrders(ctx context.Context, id int) (_ []Order, err error) {
ctx, span := sixty.Start(ctx, "orders.GetUserOrders")
defer span.Capture(&err)
...
}
Put it on the service or repository layer — the code between the handler
and the database — not on handlers the middleware already covers. Name
operations package.Function, and keep the names stable: the name is the
identity, and renaming one starts its history over.
Do NOT put it on functions called hundreds of thousands of times a second.
A span costs a few hundred nanoseconds, which is nothing next to a request
and everything next to a tight loop.
5. Set these environment variables wherever the service is deployed:
SIXTY_API_KEY = a secret key starting sixty_sk_ — ask me for it. Do not
invent one, and do not commit it.
SIXTY_SERVICE = my-app
SIXTY_ENDPOINT = https://ingest.sixty.sh
The release identifier usually needs nothing: the Go toolchain stamps the
commit into the binary and the agent reads it back. If this builds with
-buildvcs=false, or from a source tarball with no repository, set
SIXTY_RELEASE to the commit SHA — without one, every measurement lands in
a single nameless bucket and no comparison can ever be made.
Constraints — correctness requirements, not style preferences:
- Do NOT change any application behaviour. This is instrumentation only: no
refactors, no reordering of business logic, no "while I was in here" fixes.
- Thread the context. sixty.Start returns a new ctx and the work must use it,
or the queries underneath are recorded as belonging to nobody. There is no
ambient-context version of this and you should not build one: no
goroutine-local storage, no //go:linkname, no global "current span".
- A goroutine started inside a measured function must be passed that ctx if
its work should count towards the operation. One that outlives the request
should NOT be — it would attribute background work to whoever happened to
start it.
- Do NOT add any analytics, user id, session id, or cookie to what is
reported. The agent is deliberately anonymous and must stay that way.
- Leave the sql.Rows handling alone. The agent counts rows as the caller
reads them and closes the span when the rows close, so a query whose rows
are never closed reports late — which is also a connection leak worth
fixing on its own terms.
When you are done, tell me which files you changed and what the deployed start
command now is, so I can confirm data is arriving.秘密鍵が必要です — sixty_sk_ で始まり、サーバー側に留まります。ログイン後、設定ページで発行してください。
何を測るか
| シグナル | 単位 | 意味 |
|---|---|---|
rows | rows per call | this query returns more rows than it used to |
fanout | queries per call | this operation now issues more database calls per invocation — an N+1 |
latency | ms per call | this operation takes longer end to end than it used to |
self_latency | ms per call | the time spent in this function itself got longer — its children did not |
payload | bytes per call | the serialized result of this operation got bigger |
errors | error rate | a larger fraction of calls are throwing |
runaway | calls per minute | this operation is being called far more often than anything triggers it |
repeated_query | times per request | the identical query runs several times within one request |
overfetch | rows per call | far more rows are fetched than the code appears to use |
unbounded | rows per call | this query has no upper bound on what it can return |
recursion | levels deep | this operation calls itself, deeper than it should |
new_error | occurrences | an error that did not occur in the previous release |
missing_tenancy | — | This reads a table of per-person data without saying whose rows it wants. Unless your database is filtering it for you, everyone gets everyone else's. |
collapse | — | This is handing back roughly half the data it used to, or less. If that was not deliberate, something is filtering out rows that somebody expects to see. |
vanished | — | It was being used steadily until this release and has not been used once since. Usually the link, button, or redirect that led here stopped working. |
traffic_drop | — | This is still being used, but a fraction as often, and its share of your traffic fell too — so it is not just a quiet period. |
どこに入り込むか
- net/http — sixty.Middleware はどんなハンドラも包みます。Go 1.22 のパターン mux、chi、gorilla、echo も含めて。
- ルート名 — sixty.SetRoute(r, "/orders/{id}") — ルータがパターンを知っていて、パスがそれを持たない場合に。
- あなた自身の関数 — ctx, done := sixty.Start(ctx, "orders.List"); defer done() — 2行、そして context を引き回す必要があります。
データベース
- database/sql — どのドライバでも。エージェントは接続ではなくドライバを包み、呼び出し側が反復するのに合わせて行数を数え、本物のドライバが実装する任意インターフェースをすべて写し取ります。
これだけができること
- 依存のないモジュール1つ — go.sum に何も足しません。これは他人の本番バイナリに読み込まれるものであり、依存を持つことは監視ツールが引き起こすバージョン衝突を意味します。
- 読まれるのに合わせて数える行数 — 返されたスライスからではありません — database/sql にはそれがないからです。スパンは rows が閉じたときに閉じるので、rows が一度も閉じられないクエリは報告が遅れます。
できないこと
- context は引き回す必要があります。ctx を渡されなかった goroutine はその操作の外側で仕事をします。バックグラウンド処理としては正しく、測るつもりだったファンアウトとしては誤りです。暗黙の context 版はありませんし、作るつもりもありません。
- クエリプランは取得しません。
- CPU と待ち時間は分けません。goroutine はスレッド間を移動するので、スレッド単位の時計はスパンを説明できません。
設定
どのエージェントも同じ4つの変数を読みます。そして SIXTY_* が答えるところでは DRIFT_* も引き続き答えます — 製品名は変わりましたが、その名前は他人のデプロイから引き上げてよい類のものではありません。
SIXTY_API_KEY | これがないとエージェントは何もせず、そのことを伝えます。推測もせず、未知のエンドポイントに再試行もせず、例外も投げません。 |
|---|---|
SIXTY_SERVICE | このサービスを何と呼ぶか。読み取れる場合はプロジェクト名が既定になります。 |
SIXTY_RELEASE | いちばん重要なもの。Vercel、Render、Railway、Fly、Heroku、GitHub Actions では自動で拾われます。それ以外ではコミットの SHA を設定してください。これがないとすべての計測が名前のない1つのバケツに入り、比較は永遠にできません。 |
SIXTY_ENDPOINT | どこへ報告するか。既定は http://localhost:4319で、ノートPCの上では正しく、そのアプリが他人に配信された瞬間に間違いになります。 |
残り — 送信間隔、サンプリング率、何を計測するか — はパッケージ自身の README にあります。エージェントが変わっても正しいままでいられる場所だからです。