Client-Agent
React Native
Eine App hat keinen serverseitigen Span, mit dem sie sich vergleichen könnte — ein abgewiesener Aufruf oder eine leere Antwort ist also nur vom Gerät aus sichtbar. Das ist der Agent, der sie sichtbar macht.
- Paket
@sixty-sh/react-nativeauf npm- läuft auf
- iOS und Android. Etwa 2,6 µs blockierende Kosten pro beobachteter Anfrage.
- Quellcode
- sixty-sh/sixty-react-native
Installation
iOS and Android. API shape and latency, errors and crashes, render loops — with a public key.
Die Installation ist als Prompt für den Coding-Agenten geschrieben, den du ohnehin offen hast, nicht als Checkliste für dich. Das ist Absicht: sie benennt, was am Ende wahr sein muss, statt welche Dateien zu bearbeiten sind — denn wohin der Code gehört, hängt vom Framework ab, und ihn an die falsche Stelle zu setzen scheitert lautlos. Ein Agent kann dein Repository lesen und das herausfinden; ein Absatz auf einer Dokumentationsseite kann es nicht.
Denselben Text liefert install_sixty über den MCP-Server zurück, und den serviert der Collector unter /v1/setup?kind=react-native. Es gibt genau eine Kopie davon.
die vollständige React Native-Installation
Add sixty.sh monitoring to this React Native app. It measures API call shape
and latency, JavaScript errors and crashes, and runaway re-render loops, and
reports anonymously.
Before editing, ask: "Do you want mobile incident replay?" Enable replay
only if the user says yes. It retains a bounded screen/request/error timeline
around failures, never screenshots, screen text, bodies, headers or ids.
1. Install @sixty-sh/react-native. Also install
@react-native-async-storage/async-storage if it is not already present —
without it, telemetry buffered when the user backgrounds the app is lost,
and that is how most sessions end.
2. Call init() once, at the top of the entry file (index.js or App.tsx),
before anything else imports:
import { init, composeRelease } from "@sixty-sh/react-native"
init({
key: "sixty_pk_YOUR_KEY",
service: "my-app",
endpoint: "https://ingest.sixty.sh",
release: composeRelease({ version: APP_VERSION, build: BUILD_NUMBER }),
// replay: true, // uncomment only with explicit consent
})
Work out where the version and build number actually come from in THIS
project — expo-constants, react-native-device-info, a generated file, or a
constant somebody maintains by hand. Do not add a dependency just for this;
if none exists, hardcode the current version and say so in your summary.
IF this app ships over-the-air updates (expo-updates or CodePush), the
release MUST also include the update id:
composeRelease({ version, build, updateId: Updates.updateId })
Without it every OTA bundle reports as the same release, and since OTA is
how most changes ship, almost nothing can ever be compared. Nothing errors;
the feed just stays empty.
3. Add the Babel plugin to babel.config.js:
plugins: ["@sixty-sh/react-native/babel"]
Then start Metro with --reset-cache once. Metro caches transformed modules
and will not notice the plugin list changed, so without this the build
succeeds and the transform is silently not applied.
4. Wire the navigator, or every measurement in the app is filed under one
route and no per-screen finding is possible. Find the NavigationContainer:
import { createNavigationTracker } from "@sixty-sh/react-native/navigation"
const tracker = createNavigationTracker()
<NavigationContainer onReady={tracker.onReady} onStateChange={tracker.onStateChange}>
If this app does not use React Navigation, call setRoute("ScreenName") from
wherever it does track the current screen instead.
5. If the app has React error boundaries, call reportError(error) from
componentDidCatch. React calls the boundary instead of any global handler,
so errors caught there are invisible to the agent otherwise — and those are
the ones that leave a user looking at a fallback screen.
Constraints — correctness requirements, not style preferences:
- Do NOT wrap or replace fetch. React Native's fetch is implemented on top of
XMLHttpRequest, which the agent already instruments; adding a fetch wrapper
makes every request count twice and nothing will look wrong.
- Do NOT change any existing screen, hook, or network call. This is
instrumentation only; no behaviour should change.
- The key above is PUBLIC by design: write-only and pinned to one service.
Paste it literally. Do not move it into an environment variable.
- Do NOT add any analytics library, user id, session id, or device id. This
agent is deliberately anonymous and must stay that way.
When you are done, tell me where you found the version number, whether this
app ships OTA updates, and whether you cleared the Metro cache. Findings need
two released builds with traffic under each before anything can be compared.Er braucht einen öffentlichen Schlüssel — an deine Origins gebunden, weil eine Anwendung ohne eigenen Server keinen Ort hat, an dem ein Geheimnis leben könnte.
Was er misst
| Signal | Einheit | was es bedeutet |
|---|---|---|
client_error | of views | this error is being thrown in real users’ browsers |
auth_failures | — | The server is turning these away on permission grounds rather than failing. People see an empty page or a save that quietly does nothing. |
silent_empty | — | The query runs and succeeds, and returns no rows where it used to return plenty. Nothing reports an error, so the page just renders blank — this is what a broken permission rule looks like from the outside. |
latency | ms per call | this operation takes longer end to end than it used to |
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 |
Wo er sich einhängt
- React Navigation — An den Navigator gehängt, damit Messungen unter einem Screen abgelegt werden. Das auszulassen legt jede Zahl unter eine einzige Route.
- Deine eigenen Komponenten — Ein Babel-Plugin. Metro cacht transformierte Module und bemerkt eine neue Plugin-Liste nicht, der Cache muss also geleert werden, sonst tut der Build stillschweigend nichts.
Was nur dieser kann
- Netzwerksignale vom Gerät — Abgewiesene Anmeldungen und erfolgreiche, aber leere Antworten — auf einem Telefon der einzige Ort, an dem beides beobachtbar ist.
- OTA-bewusste Releases — Die Id des Over-the-Air-Updates ist Teil des Release-Bezeichners. Ohne sie ist jedes über die Luft ausgelieferte Bundle dasselbe Release, und der Vergleich hat nichts zu vergleichen.
Was er nicht kann
- Ein öffentlicher Schlüssel, weil eine App kein Origin hat, über das sie proxen könnte, und keinen Ort, an dem ein Geheimnis leben kann. Anders als bei der Browser-Stufe wird hier kein besserer Modus aufgegeben.
- dead_interaction ist an button, a und form gebunden und kennt Pressable und onPress noch nicht. effect_loop und missing_cleanup sind React-Regeln und greifen auf diesem Quellcode unverändert.
Konfiguration
Jeder Agent liest dieselben vier Variablen, und DRIFT_* antwortet weiterhin überall dort, wo SIXTY_* es tut — das Produkt wurde umbenannt, und dieser Name ist nicht unserer, um ihn aus fremden Deployments zu entfernen.
SIXTY_API_KEY | Ohne sie bleibt der Agent untätig und sagt das auch. Er rät nie, versucht es nie erneut gegen einen unbekannten Endpunkt, und wirft nie. |
|---|---|
SIXTY_SERVICE | Wie dieser Dienst heißen soll. Standardmäßig der Projektname, wo einer lesbar ist. |
SIXTY_RELEASE | Die wichtigste. Wird auf Vercel, Render, Railway, Fly, Heroku und GitHub Actions automatisch abgeholt; überall sonst setze sie auf den Commit-SHA. Ohne sie landet jede Messung in einem einzigen namenlosen Eimer, und kein Vergleich ist je möglich. |
SIXTY_ENDPOINT | Wohin gemeldet wird. Standardmäßig http://localhost:4319, was auf einem Laptop richtig ist und in dem Moment falsch, in dem die Anwendung jemand anderem ausgeliefert wird. |
Der Rest — Flush-Intervall, Sample-Rate, was instrumentiert wird — steht im README des Pakets selbst, wo es wahr bleiben kann, während sich der Agent verändert.