agent client
React Native
Une application n’a pas de span côté serveur auquel se comparer : un appel refusé ou une réponse vide n’est donc visible que depuis l’appareil. C’est l’agent qui les rend visibles.
- paquet
@sixty-sh/react-nativesur npm- tourne sur
- iOS et Android. Environ 2,6 µs de coût bloquant par requête observée.
- source
- sixty-sh/sixty-react-native
L’installer
iOS and Android. API shape and latency, errors and crashes, render loops — with a public key.
L’installation est écrite comme un prompt pour l’agent de code que vous avez déjà ouvert, pas comme une liste de tâches pour vous. C’est délibéré : elle nomme ce qui doit être vrai une fois l’installation terminée plutôt que les fichiers à modifier, parce que l’endroit où va le code dépend du framework et que le mettre au mauvais endroit échoue silencieusement. Un agent peut lire votre dépôt et le déduire ; un paragraphe sur une page de documentation, non.
Le même texte est ce que renvoie install_sixty via le serveur MCP et ce que le collecteur sert à /v1/setup?kind=react-native. Il n’en existe qu’une seule copie.
l’installation React Native, en entier
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.Il lui faut une clé publique — épinglée à vos origines, parce qu’une application sans serveur à elle n’a nulle part où garder un secret.
Ce qu’il mesure
| signal | unité | ce que cela veut dire |
|---|---|---|
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 |
Où il s’accroche
- React Navigation — Branché au navigateur d’écrans, pour que les mesures soient classées sous un écran. Sauter cette étape range tous les chiffres sous une seule route.
- Vos propres composants — Un plugin Babel. Metro met en cache les modules transformés et ne remarque pas une nouvelle liste de plugins : il faut donc vider le cache, sinon le build ne fait silencieusement rien.
Ce que seul celui-ci fait
- Des signaux réseau depuis l’appareil — Refus d’authentification et réponses réussies mais vides, qui sur un téléphone sont le seul endroit où l’un et l’autre sont observables.
- Des versions conscientes de l’OTA — L’identifiant de la mise à jour over-the-air fait partie de l’identifiant de version. Sans lui, chaque bundle poussé par les airs est la même version et la comparaison n’a rien à comparer.
Ce qu’il ne peut pas faire
- Une clé publique, parce qu’une application n’a pas d’origine par laquelle relayer ni d’endroit où un secret puisse vivre. Contrairement au niveau navigateur, aucun meilleur mode n’est abandonné ici.
- dead_interaction est lié à button, a et form, et ne connaît pas encore Pressable ni onPress. effect_loop et missing_cleanup sont des règles React et s’appliquent telles quelles à ce code.
Configuration
Chaque agent lit les mêmes quatre variables, et DRIFT_* répond toujours partout où SIXTY_* répond — le produit a été renommé, et ce nom ne nous appartient pas au point de le retirer des déploiements des autres.
SIXTY_API_KEY | Sans elle l’agent reste inerte et le dit. Il ne devine jamais, ne réessaie jamais contre un point d’entrée inconnu, et ne lève jamais d’exception. |
|---|---|
SIXTY_SERVICE | Comment appeler ce service. Par défaut le nom du projet là où il est lisible. |
SIXTY_RELEASE | La plus importante. Récupérée automatiquement sur Vercel, Render, Railway, Fly, Heroku et GitHub Actions ; partout ailleurs, mettez-y le SHA du commit. Sans elle, chaque mesure atterrit dans un unique seau sans nom et aucune comparaison n’est jamais possible. |
SIXTY_ENDPOINT | Où remonter. Par défaut http://localhost:4319, ce qui est juste sur un portable et faux dès l’instant où l’application est servie à quelqu’un d’autre. |
Le reste — intervalle d’envoi, taux d’échantillonnage, quoi instrumenter — est dans le README du paquet lui-même, là où il peut rester vrai à mesure que l’agent change.