fg-tracker-mcp 0.9.1 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -0
- package/README.md +22 -10
- package/dist/cli.js +12 -7
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +44 -4
- package/dist/client.js +80 -11
- package/dist/client.js.map +1 -1
- package/dist/database.js +1 -1
- package/dist/database.js.map +1 -1
- package/dist/diagnostics.d.ts +18 -0
- package/dist/diagnostics.js +467 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/docs.js +22 -22
- package/dist/docs.js.map +1 -1
- package/dist/format.d.ts +68 -3
- package/dist/format.js +113 -11
- package/dist/format.js.map +1 -1
- package/dist/guide.d.ts +1 -1
- package/dist/guide.js +27 -23
- package/dist/guide.js.map +1 -1
- package/dist/infra/handover.d.ts +1 -1
- package/dist/infra/handover.js +25 -23
- package/dist/infra/handover.js.map +1 -1
- package/dist/infra/iac.js +2 -2
- package/dist/infra/iac.js.map +1 -1
- package/dist/infra/instrument.js +5 -5
- package/dist/infra/instrument.js.map +1 -1
- package/dist/infra/tools.js +45 -32
- package/dist/infra/tools.js.map +1 -1
- package/dist/inputs.d.ts +40 -0
- package/dist/inputs.js +30 -0
- package/dist/inputs.js.map +1 -0
- package/dist/journeys.d.ts +71 -0
- package/dist/journeys.js +130 -0
- package/dist/journeys.js.map +1 -0
- package/dist/links.d.ts +87 -0
- package/dist/links.js +124 -0
- package/dist/links.js.map +1 -0
- package/dist/loadtest/scenario.js +1 -1
- package/dist/loadtest/scenario.js.map +1 -1
- package/dist/loadtest/tools.js +20 -11
- package/dist/loadtest/tools.js.map +1 -1
- package/dist/schemas.d.ts +621 -0
- package/dist/schemas.js +277 -0
- package/dist/schemas.js.map +1 -0
- package/dist/server.d.ts +21 -2
- package/dist/server.js +757 -147
- package/dist/server.js.map +1 -1
- package/package.json +3 -3
package/dist/docs.js
CHANGED
|
@@ -6,13 +6,13 @@ export const QUICKSTART_URI = 'fg-tracker://docs/quickstart';
|
|
|
6
6
|
/** @deprecated URI do `stread-track-mcp` ≤ 0.3; ainda registrada como alias, removida na 1.0. */
|
|
7
7
|
export const LEGACY_QUICKSTART_URI = 'stread-track://docs/quickstart';
|
|
8
8
|
export function quickstartMarkdown(endpoint) {
|
|
9
|
-
return `# FG
|
|
9
|
+
return `# FG Tracker — quickstart
|
|
10
10
|
|
|
11
11
|
APM leve para Node: traces (Express/NestJS/Next.js/Fastify), queries pg, chamadas HTTP de saída, logs e detecção automática de **N+1**, **loops**, **endpoints lentos** e **erros**. API em \`${endpoint}\` (Swagger em \`${endpoint}/docs\`).
|
|
12
12
|
|
|
13
13
|
## 1. Projeto e chave
|
|
14
|
-
- \`
|
|
15
|
-
- \`
|
|
14
|
+
- \`tracker_list_projects\` lista os projetos do usuário do token; \`tracker_create_project { name }\` cria um e devolve \`apiKey\` (\`stk_...\`).
|
|
15
|
+
- \`tracker_update_allowed_origins { projectId, origins }\` substitui os domínios permitidos (CORS do SDK de navegador); leia a lista atual em \`tracker_list_projects\` e confirme com o usuário antes.
|
|
16
16
|
- A chave vai no header \`x-fg-tracker-key\` da ingestão e em \`FG_TRACKER_KEY\` no app instrumentado.
|
|
17
17
|
|
|
18
18
|
## 2. SDK (\`fg-tracker-sdk\`)
|
|
@@ -26,7 +26,7 @@ const tracker = createTracker({
|
|
|
26
26
|
});
|
|
27
27
|
app.use(tracker.express()); // Express / NestJS — PRIMEIRO middleware (continua traceparent, lê req.user.id)
|
|
28
28
|
app.use(routes); app.use(tracker.expressErrorHandler()); // exceção real no span → issues por causa, não por status
|
|
29
|
-
tracker.instrumentPg(pool); // pg only; use
|
|
29
|
+
tracker.instrumentPg(pool); // pg only; use tracker_detect_databases for other drivers // pg.Pool → spans db.query
|
|
30
30
|
const fetchTracked = tracker.wrapFetch(); // fetch → spans http.client
|
|
31
31
|
export const GET = tracker.wrapNextRoute(async (req) => Response.json({ ok: true })); // Next.js
|
|
32
32
|
await tracker.span('custom', 'send-email', async (span) => { /* ... */ });
|
|
@@ -35,7 +35,7 @@ await tracker.checkIn('backup-diario', () => runBackup(), { intervalSeconds: 864
|
|
|
35
35
|
await tracker.shutdown(); // no SIGTERM
|
|
36
36
|
\`\`\`
|
|
37
37
|
No deploy: \`npx fg-tracker-release --service minha-app --previous-sha $PREV\` registra a release com commits/arquivos (commits suspeitos nas issues, comparação de latência entre versões).
|
|
38
|
-
Use \`
|
|
38
|
+
Use \`tracker_instrumentation_guide { framework, projectId }\` para o passo a passo completo por framework com a chave real.
|
|
39
39
|
|
|
40
40
|
## 3. Logs
|
|
41
41
|
\`\`\`ts
|
|
@@ -64,13 +64,13 @@ curl -X POST ${endpoint}/v1/logs -H 'content-type: application/json' -H 'x-fg-tr
|
|
|
64
64
|
Kinds de span: \`http.server\` (raiz por request), \`db.query\` (nome = SQL normalizado), \`http.client\`, \`custom\`.
|
|
65
65
|
|
|
66
66
|
## 5. Verificar e explorar
|
|
67
|
-
- \`
|
|
68
|
-
- \`
|
|
69
|
-
- \`
|
|
70
|
-
- \`
|
|
71
|
-
- \`
|
|
72
|
-
- \`
|
|
73
|
-
- \`
|
|
67
|
+
- \`tracker_verify_ingest { projectId }\` — confirma spans/logs recentes.
|
|
68
|
+
- \`tracker_get_overview { projectId, range }\` — requests, erro, p50/p95/p99, apdex, top lentos.
|
|
69
|
+
- \`tracker_list_endpoints\`, \`tracker_list_issues\`, \`tracker_list_traces\` → \`tracker_get_trace { traceId }\` (waterfall, 80 spans por página: \`spanOffset\`, filtros \`kind\`/\`service\`/\`name\`).
|
|
70
|
+
- \`tracker_search_logs { projectId, q, service, level, env, requestId, range }\` — sintaxe: texto livre, \`"frase"\`, \`key=value\`, \`~substring\`.
|
|
71
|
+
- \`tracker_get_issue { projectId, issueId }\` — ocorrências, usuários afetados, releases, regressões e commits/PRs suspeitos.
|
|
72
|
+
- \`tracker_list_releases { projectId, version? }\`, \`tracker_register_release\`, \`tracker_endpoint_history { projectId, name, days }\` — saúde por deploy e latência por rota ao longo do tempo.
|
|
73
|
+
- \`tracker_list_providers { projectId, range }\` (chamadas de saída por host) e \`tracker_list_monitors { projectId }\` (uptime e jobs) → \`tracker_get_monitor { projectId, monitorId }\` (histórico de checks com o trace de cada execução).
|
|
74
74
|
|
|
75
75
|
## 6. Detectores (rodam na ingestão)
|
|
76
76
|
| tipo | regra |
|
|
@@ -82,7 +82,7 @@ Kinds de span: \`http.server\` (raiz por request), \`db.query\` (nome = SQL norm
|
|
|
82
82
|
| ERROR | raiz com status ≥ 500 ou \`error != null\` |
|
|
83
83
|
|
|
84
84
|
## 7. Infraestrutura (AWS)
|
|
85
|
-
Prompt \`connect-infrastructure\` guia o fluxo completo: o agente detecta sozinho contas, ambientes e regiões. Nenhuma tool altera recursos na AWS; a única mudança é o stack de conexão, criado com o comando devolvido e só com confirmação do usuário. Mudanças de infra só pelo FG
|
|
85
|
+
Prompt \`connect-infrastructure\` guia o fluxo completo: o agente detecta sozinho contas, ambientes e regiões. Nenhuma tool altera recursos na AWS; a única mudança é o stack de conexão, criado com o comando devolvido e só com confirmação do usuário. Mudanças de infra só pelo FG Tracker, com aprovação humana.
|
|
86
86
|
0. \`infra_detect_environments { profiles?, includeRegions? }\` — perfis do \`~/.aws\` → conta (STS), ambiente sugerido e regiões em uso (local, somente leitura).
|
|
87
87
|
1. Por ambiente: \`infra_connect_account { projectId, level: 'read'|'operate', environment?, regions?, name?, profile? }\` → template \`./fg-tracker-connection-<ambiente>.json\` + \`aws cloudformation deploy ... --profile --region\` (confirmar com o usuário antes) → \`infra_verify_connection { projectId, connectionId, profile }\`.
|
|
88
88
|
2. \`infra_discover { projectId, regions?, profile? }\` — inventário com credenciais locais (Resource Explorer/Tagging API + CloudFormation) enviado como snapshot.
|
|
@@ -98,9 +98,9 @@ Ranges aceitos: \`15m|1h|6h|24h|7d|30d\`.
|
|
|
98
98
|
export function instrumentProjectPrompt(args, endpoint) {
|
|
99
99
|
const fw = args.framework ? `O usuário indicou o framework **${args.framework}**; confirme lendo o código antes de seguir.` : 'O framework NÃO foi informado: detecte-o.';
|
|
100
100
|
const proj = args.projectName
|
|
101
|
-
? `Use (ou crie) o projeto chamado **${args.projectName}** no FG
|
|
101
|
+
? `Use (ou crie) o projeto chamado **${args.projectName}** no FG Tracker.`
|
|
102
102
|
: 'Nenhum nome de projeto foi informado: use o nome do pacote (`package.json#name`) ou o nome do diretório.';
|
|
103
|
-
return `Você vai conectar o projeto de código atual ao FG
|
|
103
|
+
return `Você vai conectar o projeto de código atual ao FG Tracker (APM em ${endpoint}) usando as tools deste servidor MCP. Trabalhe passo a passo, mostre o que fez em cada etapa e só avance quando a anterior estiver ok.
|
|
104
104
|
|
|
105
105
|
${fw} ${proj}
|
|
106
106
|
|
|
@@ -108,20 +108,20 @@ ${fw} ${proj}
|
|
|
108
108
|
|
|
109
109
|
1. **Detectar framework e logger.** Leia \`package.json\` (dependências: express, @nestjs/core, next, fastify; pino, winston; pg, drizzle-orm, kysely, knex) e o ponto de entrada (\`src/main.ts\`, \`src/index.ts\`, \`app/\`). Anote: framework HTTP, logger, os drivers e os pontos de criação dos clientes, gerenciador de pacotes (lockfile) e como o app é iniciado.
|
|
110
110
|
|
|
111
|
-
2. **Detectar bancos.** Chame \`
|
|
111
|
+
2. **Detectar bancos.** Chame \`tracker_detect_databases { projectPath, serviceName }\`. Confirme os imports e pontos de criação retornados; siga o adapter de cada driver ativo. Não suponha pg.Pool para Postgres.js, Prisma ou outro driver.
|
|
112
112
|
|
|
113
|
-
2b. **Checar o FG
|
|
113
|
+
2b. **Checar o FG Tracker.** Chame \`tracker_status\`. Se a API estiver fora do ar ou faltar \`FG_TRACKER_TOKEN\`, pare e explique ao usuário o que configurar.
|
|
114
114
|
|
|
115
|
-
3. **Escolher ou criar o projeto.** Chame \`
|
|
115
|
+
3. **Escolher ou criar o projeto.** Chame \`tracker_list_projects\`. Se já existe um projeto com o nome esperado, use-o; senão \`tracker_create_project { name }\`. Guarde \`projectId\` e \`apiKey\`. Nunca imprima a chave em commits/logs — só em \`.env\` (confira que \`.env\` está no \`.gitignore\`; adicione a variável em \`.env.example\` sem o valor).
|
|
116
116
|
|
|
117
|
-
4. **Obter o guia.** Chame \`
|
|
117
|
+
4. **Obter o guia.** Chame \`tracker_instrumentation_guide { framework, projectId }\` para o framework detectado. Se o app tem pino ou winston, chame também com \`framework: "pino"\` ou \`"winston"\`.
|
|
118
118
|
|
|
119
119
|
5. **Instalar o SDK.** \`pnpm add fg-tracker-sdk\` (ou npm/yarn conforme o lockfile). Se o projeto já usa \`stread-track-sdk\`, troque pelo \`fg-tracker-sdk\` (mesma API).
|
|
120
120
|
|
|
121
121
|
6. **Aplicar a instrumentação** seguindo o guia, com edits mínimos e idiomáticos ao projeto:
|
|
122
122
|
- criar o tracker (um por processo) com \`apiKey\` de \`process.env.FG_TRACKER_KEY\` e \`endpoint\` de \`FG_TRACKER_URL\`;
|
|
123
123
|
- registrar \`tracker.express()\` como PRIMEIRO middleware (Express/Nest), \`wrapNextRoute\` nos route handlers (Next) ou o hook \`onRequest\` (Fastify);
|
|
124
|
-
- aplicar o adapter retornado por \`
|
|
124
|
+
- aplicar o adapter retornado por \`tracker_detect_databases\` ao cliente real antes de passá-lo ao ORM; para drivers desconhecidos envolver a conclusão da operação com \`traceDatabase\` ou \`traceDatabaseSync\`;
|
|
125
125
|
- \`tracker.wrapFetch()\` para chamadas HTTP de saída relevantes;
|
|
126
126
|
- transport de logs (pino/winston/nest) com \`createLogShipper\`;
|
|
127
127
|
- \`tracker.shutdown()\` no encerramento (SIGTERM / \`onApplicationShutdown\` / \`onClose\`).
|
|
@@ -130,11 +130,11 @@ ${fw} ${proj}
|
|
|
130
130
|
|
|
131
131
|
8. **Rodar e gerar tráfego.** Inicie o app, faça algumas requisições reais (curl nas rotas principais, inclusive uma que consulte o banco) e aguarde ~3 s.
|
|
132
132
|
|
|
133
|
-
9. **Verificar.** Chame \`
|
|
133
|
+
9. **Verificar.** Chame \`tracker_verify_ingest { projectId }\`. Se chegaram spans (e logs, quando configurados), mostre um resumo com \`tracker_get_overview { projectId, range: "15m" }\` e \`tracker_list_issues\`.
|
|
134
134
|
|
|
135
135
|
10. **Corrigir.** Se nada chegou: confira a chave (o SDK loga "4xx" uma vez quando ela é inválida), a ordem do middleware, se o tracker foi criado antes do app subir, se o processo consegue alcançar ${endpoint} (\`curl ${endpoint}/v1/health\`) e se há \`enabled: false\`. Ajuste e repita o passo 8.
|
|
136
136
|
|
|
137
|
-
Ao final, liste os arquivos alterados, as variáveis adicionadas e o resultado de \`
|
|
137
|
+
Ao final, liste os arquivos alterados, as variáveis adicionadas e o resultado de \`tracker_verify_ingest\`.`;
|
|
138
138
|
}
|
|
139
139
|
// Database driver detection must precede selection of an integration adapter.
|
|
140
140
|
//# sourceMappingURL=docs.js.map
|
package/dist/docs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"docs.js","sourceRoot":"","sources":["../src/docs.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,CAAC,MAAM,cAAc,GAAG,8BAA8B,CAAC;AAC7D,iGAAiG;AACjG,MAAM,CAAC,MAAM,qBAAqB,GAAG,gCAAgC,CAAC;AAEtE,MAAM,UAAU,kBAAkB,CAAC,QAAgB;IACjD,OAAO;;gMAEuL,QAAQ,oBAAoB,QAAQ;;;;;;;;;;;oDAWhL,QAAQ;;;;;;;;;;;;;;;;;;;;;wDAqBJ,QAAQ;;;;;;;;;eASjD,QAAQ;;;;;;;;eAQR,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoCtB,CAAC;AACF,CAAC;AAOD,MAAM,UAAU,uBAAuB,CAAC,IAA0B,EAAE,QAAgB;IAClF,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,mCAAmC,IAAI,CAAC,SAAS,8CAA8C,CAAC,CAAC,CAAC,2CAA2C,CAAC;IAC1K,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW;QAC3B,CAAC,CAAC,qCAAqC,IAAI,CAAC,WAAW,
|
|
1
|
+
{"version":3,"file":"docs.js","sourceRoot":"","sources":["../src/docs.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,CAAC,MAAM,cAAc,GAAG,8BAA8B,CAAC;AAC7D,iGAAiG;AACjG,MAAM,CAAC,MAAM,qBAAqB,GAAG,gCAAgC,CAAC;AAEtE,MAAM,UAAU,kBAAkB,CAAC,QAAgB;IACjD,OAAO;;gMAEuL,QAAQ,oBAAoB,QAAQ;;;;;;;;;;;oDAWhL,QAAQ;;;;;;;;;;;;;;;;;;;;;wDAqBJ,QAAQ;;;;;;;;;eASjD,QAAQ;;;;;;;;eAQR,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoCtB,CAAC;AACF,CAAC;AAOD,MAAM,UAAU,uBAAuB,CAAC,IAA0B,EAAE,QAAgB;IAClF,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,mCAAmC,IAAI,CAAC,SAAS,8CAA8C,CAAC,CAAC,CAAC,2CAA2C,CAAC;IAC1K,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW;QAC3B,CAAC,CAAC,qCAAqC,IAAI,CAAC,WAAW,mBAAmB;QAC1E,CAAC,CAAC,0GAA0G,CAAC;IAE/G,OAAO,qEAAqE,QAAQ;;EAEpF,EAAE,IAAI,IAAI;;;;;;;;;;;;;;;;;;;;;;;;wFAwB4E,QAAQ;;;;;;yMAMyG,QAAQ,YAAY,QAAQ;;4GAEzH,CAAC;AAC7G,CAAC;AAED,8EAA8E"}
|
package/dist/format.d.ts
CHANGED
|
@@ -1,27 +1,92 @@
|
|
|
1
|
+
/** Link para um recurso (MCP `resource_link`): o painel ou um `fg-tracker://…` legível pelo servidor. */
|
|
2
|
+
export interface ResourceLink {
|
|
3
|
+
type: 'resource_link';
|
|
4
|
+
uri: string;
|
|
5
|
+
name: string;
|
|
6
|
+
title?: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
mimeType?: string;
|
|
9
|
+
}
|
|
1
10
|
export interface ToolText {
|
|
2
11
|
[x: string]: unknown;
|
|
3
12
|
content: Array<{
|
|
4
13
|
type: 'text';
|
|
5
14
|
text: string;
|
|
6
|
-
}>;
|
|
15
|
+
} | ResourceLink>;
|
|
16
|
+
structuredContent?: Record<string, unknown>;
|
|
7
17
|
isError?: boolean;
|
|
8
18
|
}
|
|
9
19
|
export declare const MAX_SERIES_POINTS = 48;
|
|
20
|
+
/** Arrays internos (amostras, ocorrências, checks dentro de um objeto). */
|
|
10
21
|
export declare const MAX_LIST_ITEMS = 25;
|
|
11
22
|
export declare const MAX_TRACE_SPANS = 80;
|
|
23
|
+
/** Default de FG_TRACKER_MAX_ITEMS: teto da lista principal de uma ferramenta. */
|
|
24
|
+
export declare const DEFAULT_MAX_ITEMS = 200;
|
|
25
|
+
/** Maior valor aceito em FG_TRACKER_MAX_ITEMS e nos `limit` sem teto próprio da API. */
|
|
26
|
+
export declare const MAX_ITEMS_CEILING = 1000;
|
|
27
|
+
/** Linhas de prévia no texto de uma lista longa; o JSON que segue traz todos os itens. */
|
|
28
|
+
export declare const PREVIEW_LINES = 25;
|
|
12
29
|
export declare function text(value: string): ToolText;
|
|
13
30
|
export declare function json(value: unknown, preface?: string): ToolText;
|
|
31
|
+
/**
|
|
32
|
+
* Resultado com `structuredContent` (o que o `outputSchema` descreve) e o texto de sempre:
|
|
33
|
+
* o resumo e, depois de uma linha em branco, o JSON de `body` (default: o próprio `data`).
|
|
34
|
+
* `body` existe para manter o JSON antigo do texto quando o estruturado mudou de forma
|
|
35
|
+
* (ex.: lista que virou `{ items }`). `body: null` deixa só o resumo.
|
|
36
|
+
*/
|
|
37
|
+
export declare function structured(data: Record<string, unknown>, summary: string, options?: {
|
|
38
|
+
body?: unknown;
|
|
39
|
+
links?: ResourceLink[];
|
|
40
|
+
}): ToolText;
|
|
14
41
|
export declare function errorResult(err: unknown): ToolText;
|
|
15
42
|
/** Executa `fn` e converte qualquer exceção em resultado `isError`. */
|
|
16
43
|
export declare function guard(fn: () => Promise<ToolText>): Promise<ToolText>;
|
|
17
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* Reduz uma série a `max` pontos por amostragem uniforme, sempre com o primeiro e o
|
|
46
|
+
* último: numa série em ordem de tempo, o ponto mais recente nunca fica de fora.
|
|
47
|
+
*/
|
|
18
48
|
export declare function sampleSeries<T>(series: T[], max?: number): T[];
|
|
19
49
|
export declare function truncateList<T>(list: T[], max?: number): {
|
|
20
50
|
items: T[];
|
|
21
51
|
truncated: number;
|
|
22
52
|
};
|
|
23
|
-
|
|
53
|
+
export interface MaxItemsSetting {
|
|
54
|
+
value: number;
|
|
55
|
+
/** Presente quando FG_TRACKER_MAX_ITEMS foi ignorado ou reduzido. */
|
|
56
|
+
warning?: string;
|
|
57
|
+
}
|
|
58
|
+
/** Teto da lista principal: FG_TRACKER_MAX_ITEMS (inteiro de 1 a 1000, default 200). */
|
|
59
|
+
export declare function maxItemsFromEnv(env?: NodeJS.ProcessEnv): MaxItemsSetting;
|
|
60
|
+
/** Limite efetivo da lista principal: o `limit` pedido, até `maxItems`. */
|
|
61
|
+
export declare function clampLimit(requested: number, maxItems: number): {
|
|
62
|
+
limit: number;
|
|
63
|
+
capped: boolean;
|
|
64
|
+
};
|
|
65
|
+
/** Aviso para quando `clampLimit` reduziu o pedido. */
|
|
66
|
+
export declare function cappedNote(requested: number, limit: number): string;
|
|
67
|
+
export interface ListPage {
|
|
68
|
+
total: number;
|
|
69
|
+
returned: number;
|
|
70
|
+
truncated: boolean;
|
|
71
|
+
items: unknown[];
|
|
72
|
+
}
|
|
73
|
+
/** Prévia em texto de uma lista: até PREVIEW_LINES linhas e, se cortou, onde está o resto. */
|
|
74
|
+
export declare function preview(lines: string[]): string;
|
|
75
|
+
/**
|
|
76
|
+
* Lista principal de uma API sem paginação: os primeiros `limit` itens (cada um
|
|
77
|
+
* compactado) e quanto ficou de fora. Não há cursor: para ver o resto, aumente
|
|
78
|
+
* `limit` ou filtre.
|
|
79
|
+
*/
|
|
80
|
+
export declare function listPage(list: unknown[], limit: number): ListPage;
|
|
81
|
+
/**
|
|
82
|
+
* Compacta recursivamente: `series` viram amostras e arrays internos de um objeto
|
|
83
|
+
* ficam com até 25 itens (`<campo>Omitted` diz quantos saíram). Um array passado
|
|
84
|
+
* direto também é cortado em 25; a lista principal de uma ferramenta deve ser
|
|
85
|
+
* limitada antes (API ou `listPage`) e compactada item a item.
|
|
86
|
+
*/
|
|
24
87
|
export declare function compact(value: unknown, depth?: number): unknown;
|
|
25
88
|
export declare function ms(value: number | null | undefined): string;
|
|
26
89
|
export declare function pct(value: number | null | undefined): string;
|
|
90
|
+
/** Tempo até `iso` ("em 3.5 d"); no passado vira `ago`. */
|
|
91
|
+
export declare function until(iso: string, now?: number): string;
|
|
27
92
|
export declare function ago(iso: string, now?: number): string;
|
package/dist/format.js
CHANGED
|
@@ -1,19 +1,48 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Helpers de saída: compactação de payloads grandes
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Helpers de saída: compactação de payloads grandes e conversão para o formato
|
|
3
|
+
* de resultado do MCP. Regras (docs/mcp.md, "Limites de saída"):
|
|
4
|
+
* - lista principal de uma ferramenta: respeita `limit` até FG_TRACKER_MAX_ITEMS
|
|
5
|
+
* (default 200, teto 1000), cortada na API (cursor) ou por `listPage`, sempre
|
|
6
|
+
* com metadados de truncamento;
|
|
7
|
+
* - arrays internos de um objeto: ≤ 25 itens, com `<campo>Omitted`;
|
|
8
|
+
* - séries ≤ 48 pontos, amostradas por igual e sempre com o primeiro e o último
|
|
9
|
+
* (o período recente nunca some); `markers` ficam com os 25 mais recentes;
|
|
10
|
+
* - traces ≤ 80 spans por página (tracker_get_trace pagina).
|
|
11
|
+
*
|
|
12
|
+
* Toda ferramenta de leitura devolve também `structuredContent` (validado contra o
|
|
13
|
+
* `outputSchema` da ferramenta), com o texto de antes mantido para compatibilidade.
|
|
5
14
|
*/
|
|
6
|
-
import { TrackApiError } from './client.js';
|
|
15
|
+
import { TrackApiError, readEnv } from './client.js';
|
|
7
16
|
export const MAX_SERIES_POINTS = 48;
|
|
17
|
+
/** Arrays internos (amostras, ocorrências, checks dentro de um objeto). */
|
|
8
18
|
export const MAX_LIST_ITEMS = 25;
|
|
9
19
|
export const MAX_TRACE_SPANS = 80;
|
|
20
|
+
/** Default de FG_TRACKER_MAX_ITEMS: teto da lista principal de uma ferramenta. */
|
|
21
|
+
export const DEFAULT_MAX_ITEMS = 200;
|
|
22
|
+
/** Maior valor aceito em FG_TRACKER_MAX_ITEMS e nos `limit` sem teto próprio da API. */
|
|
23
|
+
export const MAX_ITEMS_CEILING = 1000;
|
|
24
|
+
/** Linhas de prévia no texto de uma lista longa; o JSON que segue traz todos os itens. */
|
|
25
|
+
export const PREVIEW_LINES = 25;
|
|
26
|
+
/** Acima disso o JSON sai sem indentação: a mesma lista ocupa bem menos da saída do cliente MCP. */
|
|
27
|
+
const PRETTY_JSON_MAX_CHARS = 20_000;
|
|
10
28
|
export function text(value) {
|
|
11
29
|
return { content: [{ type: 'text', text: value }] };
|
|
12
30
|
}
|
|
13
31
|
export function json(value, preface) {
|
|
14
|
-
const
|
|
32
|
+
const pretty = JSON.stringify(value, null, 2);
|
|
33
|
+
const body = pretty !== undefined && pretty.length > PRETTY_JSON_MAX_CHARS ? JSON.stringify(value) : pretty;
|
|
15
34
|
return text(preface ? `${preface}\n\n${body}` : body);
|
|
16
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Resultado com `structuredContent` (o que o `outputSchema` descreve) e o texto de sempre:
|
|
38
|
+
* o resumo e, depois de uma linha em branco, o JSON de `body` (default: o próprio `data`).
|
|
39
|
+
* `body` existe para manter o JSON antigo do texto quando o estruturado mudou de forma
|
|
40
|
+
* (ex.: lista que virou `{ items }`). `body: null` deixa só o resumo.
|
|
41
|
+
*/
|
|
42
|
+
export function structured(data, summary, options = {}) {
|
|
43
|
+
const base = options.body === null ? text(summary) : json(options.body === undefined ? data : options.body, summary);
|
|
44
|
+
return { ...base, content: [...base.content, ...(options.links ?? [])], structuredContent: data };
|
|
45
|
+
}
|
|
17
46
|
export function errorResult(err) {
|
|
18
47
|
const message = err instanceof TrackApiError ? err.describe() : err instanceof Error ? err.message : String(err);
|
|
19
48
|
return { content: [{ type: 'text', text: message }], isError: true };
|
|
@@ -27,14 +56,18 @@ export async function guard(fn) {
|
|
|
27
56
|
return errorResult(err);
|
|
28
57
|
}
|
|
29
58
|
}
|
|
30
|
-
/**
|
|
59
|
+
/**
|
|
60
|
+
* Reduz uma série a `max` pontos por amostragem uniforme, sempre com o primeiro e o
|
|
61
|
+
* último: numa série em ordem de tempo, o ponto mais recente nunca fica de fora.
|
|
62
|
+
*/
|
|
31
63
|
export function sampleSeries(series, max = MAX_SERIES_POINTS) {
|
|
32
64
|
if (series.length <= max)
|
|
33
65
|
return series;
|
|
34
|
-
|
|
66
|
+
if (max <= 1)
|
|
67
|
+
return series.slice(-1);
|
|
35
68
|
const out = [];
|
|
36
69
|
for (let i = 0; i < max; i++) {
|
|
37
|
-
const item = series[Math.
|
|
70
|
+
const item = series[Math.round((i * (series.length - 1)) / (max - 1))];
|
|
38
71
|
if (item !== undefined)
|
|
39
72
|
out.push(item);
|
|
40
73
|
}
|
|
@@ -45,7 +78,50 @@ export function truncateList(list, max = MAX_LIST_ITEMS) {
|
|
|
45
78
|
return { items: list, truncated: 0 };
|
|
46
79
|
return { items: list.slice(0, max), truncated: list.length - max };
|
|
47
80
|
}
|
|
48
|
-
/**
|
|
81
|
+
/** Teto da lista principal: FG_TRACKER_MAX_ITEMS (inteiro de 1 a 1000, default 200). */
|
|
82
|
+
export function maxItemsFromEnv(env = process.env) {
|
|
83
|
+
const raw = readEnv('MAX_ITEMS', env);
|
|
84
|
+
if (raw === undefined)
|
|
85
|
+
return { value: DEFAULT_MAX_ITEMS };
|
|
86
|
+
const parsed = Number(raw);
|
|
87
|
+
if (!/^\d+$/.test(raw) || !Number.isSafeInteger(parsed) || parsed < 1) {
|
|
88
|
+
return { value: DEFAULT_MAX_ITEMS, warning: `FG_TRACKER_MAX_ITEMS="${raw.slice(0, 20)}" é inválido (use um inteiro de 1 a ${MAX_ITEMS_CEILING}); usando ${DEFAULT_MAX_ITEMS}.` };
|
|
89
|
+
}
|
|
90
|
+
if (parsed > MAX_ITEMS_CEILING)
|
|
91
|
+
return { value: MAX_ITEMS_CEILING, warning: `FG_TRACKER_MAX_ITEMS=${parsed} passa do teto; usando ${MAX_ITEMS_CEILING}.` };
|
|
92
|
+
return { value: parsed };
|
|
93
|
+
}
|
|
94
|
+
/** Limite efetivo da lista principal: o `limit` pedido, até `maxItems`. */
|
|
95
|
+
export function clampLimit(requested, maxItems) {
|
|
96
|
+
return requested > maxItems ? { limit: maxItems, capped: true } : { limit: requested, capped: false };
|
|
97
|
+
}
|
|
98
|
+
/** Aviso para quando `clampLimit` reduziu o pedido. */
|
|
99
|
+
export function cappedNote(requested, limit) {
|
|
100
|
+
return requested > limit ? ` limit ${requested} reduzido para ${limit} por FG_TRACKER_MAX_ITEMS.` : '';
|
|
101
|
+
}
|
|
102
|
+
/** Prévia em texto de uma lista: até PREVIEW_LINES linhas e, se cortou, onde está o resto. */
|
|
103
|
+
export function preview(lines) {
|
|
104
|
+
if (lines.length <= PREVIEW_LINES)
|
|
105
|
+
return lines.join('\n');
|
|
106
|
+
return [...lines.slice(0, PREVIEW_LINES), `… prévia de ${PREVIEW_LINES} de ${lines.length}; todos os ${lines.length} estão no JSON abaixo.`].join('\n');
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Lista principal de uma API sem paginação: os primeiros `limit` itens (cada um
|
|
110
|
+
* compactado) e quanto ficou de fora. Não há cursor: para ver o resto, aumente
|
|
111
|
+
* `limit` ou filtre.
|
|
112
|
+
*/
|
|
113
|
+
export function listPage(list, limit) {
|
|
114
|
+
const items = list.slice(0, limit).map((item) => compact(item));
|
|
115
|
+
return { total: list.length, returned: items.length, truncated: items.length < list.length, items };
|
|
116
|
+
}
|
|
117
|
+
/** Campos que são séries temporais em ordem crescente (`points` = histórico de uma rota). */
|
|
118
|
+
const SERIES_KEYS = new Set(['series', 'seriesQueriesPerMin', 'histogram', 'points']);
|
|
119
|
+
/**
|
|
120
|
+
* Compacta recursivamente: `series` viram amostras e arrays internos de um objeto
|
|
121
|
+
* ficam com até 25 itens (`<campo>Omitted` diz quantos saíram). Um array passado
|
|
122
|
+
* direto também é cortado em 25; a lista principal de uma ferramenta deve ser
|
|
123
|
+
* limitada antes (API ou `listPage`) e compactada item a item.
|
|
124
|
+
*/
|
|
49
125
|
export function compact(value, depth = 0) {
|
|
50
126
|
if (Array.isArray(value)) {
|
|
51
127
|
const { items, truncated } = truncateList(value);
|
|
@@ -55,10 +131,16 @@ export function compact(value, depth = 0) {
|
|
|
55
131
|
if (value && typeof value === 'object') {
|
|
56
132
|
const out = {};
|
|
57
133
|
for (const [k, v] of Object.entries(value)) {
|
|
58
|
-
if ((k
|
|
134
|
+
if (SERIES_KEYS.has(k) && Array.isArray(v)) {
|
|
59
135
|
out[k] = sampleSeries(v);
|
|
60
136
|
if (v.length > MAX_SERIES_POINTS)
|
|
61
|
-
out[`${k}Points`] = `${v.length} pontos, amostrados para ${MAX_SERIES_POINTS}`;
|
|
137
|
+
out[`${k}Points`] = `${v.length} pontos, amostrados para ${MAX_SERIES_POINTS} (inclui o primeiro e o último)`;
|
|
138
|
+
}
|
|
139
|
+
else if (k === 'markers' && Array.isArray(v)) {
|
|
140
|
+
// Marcadores de deploy em ordem de tempo: os mais recentes são os que importam.
|
|
141
|
+
out[k] = v.slice(-MAX_LIST_ITEMS).map((item) => compact(item, depth + 1));
|
|
142
|
+
if (v.length > MAX_LIST_ITEMS)
|
|
143
|
+
out.markersOmitted = v.length - MAX_LIST_ITEMS;
|
|
62
144
|
}
|
|
63
145
|
else if (k === 'spans' && Array.isArray(v)) {
|
|
64
146
|
const { items, truncated } = truncateList(v, MAX_TRACE_SPANS);
|
|
@@ -69,6 +151,12 @@ export function compact(value, depth = 0) {
|
|
|
69
151
|
else if (k === 'sparkline' && Array.isArray(v)) {
|
|
70
152
|
out[k] = v; // 24 pontos, já pequeno
|
|
71
153
|
}
|
|
154
|
+
else if (Array.isArray(v)) {
|
|
155
|
+
const { items, truncated } = truncateList(v);
|
|
156
|
+
out[k] = items.map((item) => compact(item, depth + 1));
|
|
157
|
+
if (truncated > 0)
|
|
158
|
+
out[`${k}Omitted`] = truncated;
|
|
159
|
+
}
|
|
72
160
|
else {
|
|
73
161
|
out[k] = compact(v, depth + 1);
|
|
74
162
|
}
|
|
@@ -87,6 +175,20 @@ export function pct(value) {
|
|
|
87
175
|
return '—';
|
|
88
176
|
return `${(value * 100).toFixed(2)}%`;
|
|
89
177
|
}
|
|
178
|
+
/** Tempo até `iso` ("em 3.5 d"); no passado vira `ago`. */
|
|
179
|
+
export function until(iso, now = Date.now()) {
|
|
180
|
+
const t = Date.parse(iso);
|
|
181
|
+
if (Number.isNaN(t))
|
|
182
|
+
return iso;
|
|
183
|
+
if (t <= now)
|
|
184
|
+
return ago(iso, now);
|
|
185
|
+
const s = Math.round((t - now) / 1000);
|
|
186
|
+
if (s < 3600)
|
|
187
|
+
return `em ${Math.max(1, Math.round(s / 60))} min`;
|
|
188
|
+
if (s < 86400)
|
|
189
|
+
return `em ${(s / 3600).toFixed(1)} h`;
|
|
190
|
+
return `em ${(s / 86400).toFixed(1)} d`;
|
|
191
|
+
}
|
|
90
192
|
export function ago(iso, now = Date.now()) {
|
|
91
193
|
const t = Date.parse(iso);
|
|
92
194
|
if (Number.isNaN(t))
|
package/dist/format.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"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"}
|
package/dist/guide.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export type Framework = (typeof FRAMEWORKS)[number];
|
|
|
8
8
|
export interface GuideInput {
|
|
9
9
|
framework: Framework;
|
|
10
10
|
databaseDriver?: string;
|
|
11
|
-
/** URL base da API do FG
|
|
11
|
+
/** URL base da API do FG Tracker (endpoint do SDK). */
|
|
12
12
|
endpoint: string;
|
|
13
13
|
/** Chave do projeto; sem ela usa placeholder. */
|
|
14
14
|
apiKey?: string;
|
package/dist/guide.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
export const FRAMEWORKS = ['express', 'nestjs', 'nextjs', 'fastify', 'pino', 'winston'];
|
|
7
7
|
const PLACEHOLDER_KEY = 'stk_SUA_CHAVE_AQUI';
|
|
8
8
|
function envBlock(input) {
|
|
9
|
-
const key = input.apiKey
|
|
9
|
+
const key = input.apiKey || PLACEHOLDER_KEY;
|
|
10
10
|
return [
|
|
11
11
|
'```bash',
|
|
12
12
|
'# .env (não commitar a chave)',
|
|
@@ -23,18 +23,22 @@ function install(extra = []) {
|
|
|
23
23
|
`# npm install ${pkgs} | yarn add ${pkgs}`,
|
|
24
24
|
'```',
|
|
25
25
|
'',
|
|
26
|
-
'> Dentro do monorepo do FG
|
|
26
|
+
'> Dentro do monorepo do FG Tracker use `"fg-tracker-sdk": "workspace:*"`. Projetos que já usam `stread-track-sdk` só trocam o nome do pacote nos imports (mesma API; as variáveis `STREAD_TRACK_*` continuam lidas como fallback).',
|
|
27
27
|
].join('\n');
|
|
28
28
|
}
|
|
29
29
|
function header(input, title) {
|
|
30
30
|
const lines = [`# ${title}`, ''];
|
|
31
|
-
if (input.projectName && input.projectId) {
|
|
31
|
+
if (input.projectName && input.projectId && !input.apiKey) {
|
|
32
|
+
// Token de leitura (fgt_…) ou papel viewer: a API devolve apiKey ''.
|
|
33
|
+
lines.push(`Projeto: **${input.projectName}** (\`${input.projectId}\`). A API não devolveu a chave de ingestão (token de leitura \`fgt_…\` ou papel viewer): o código usa o placeholder \`${PLACEHOLDER_KEY}\`. Peça ao usuário para copiar \`FG_TRACKER_KEY\` no painel, em Conectar.`);
|
|
34
|
+
}
|
|
35
|
+
else if (input.projectName && input.projectId) {
|
|
32
36
|
lines.push(`Projeto: **${input.projectName}** (\`${input.projectId}\`). A chave abaixo é a chave real deste projeto — trate como segredo.`);
|
|
33
37
|
}
|
|
34
38
|
else {
|
|
35
|
-
lines.push(`Nenhum projeto informado: o código usa o placeholder \`${PLACEHOLDER_KEY}\`. Crie um projeto com \`
|
|
39
|
+
lines.push(`Nenhum projeto informado: o código usa o placeholder \`${PLACEHOLDER_KEY}\`. Crie um projeto com \`tracker_create_project { name }\` (ou use \`tracker_list_projects\`) e chame este guia de novo com \`projectId\` para receber a chave real.`);
|
|
36
40
|
}
|
|
37
|
-
lines.push('', `API do FG
|
|
41
|
+
lines.push('', `API do FG Tracker: \`${input.endpoint}\`.`, '');
|
|
38
42
|
return lines.join('\n');
|
|
39
43
|
}
|
|
40
44
|
function trackerFile(input, path, service) {
|
|
@@ -44,7 +48,7 @@ function trackerFile(input, path, service) {
|
|
|
44
48
|
`import { createTracker } from 'fg-tracker-sdk';`,
|
|
45
49
|
``,
|
|
46
50
|
`export const tracker = createTracker({`,
|
|
47
|
-
` apiKey: process.env.FG_TRACKER_KEY!, // ${input.apiKey
|
|
51
|
+
` apiKey: process.env.FG_TRACKER_KEY!, // ${input.apiKey || PLACEHOLDER_KEY}`,
|
|
48
52
|
` endpoint: process.env.FG_TRACKER_URL ?? '${input.endpoint}',`,
|
|
49
53
|
` serviceName: '${service}',`,
|
|
50
54
|
` flushIntervalMs: 2000,`,
|
|
@@ -76,7 +80,7 @@ const FETCH_BLOCK = [
|
|
|
76
80
|
'```',
|
|
77
81
|
].join('\n');
|
|
78
82
|
function logsSection(input, service, logger) {
|
|
79
|
-
const key = input.apiKey
|
|
83
|
+
const key = input.apiKey || PLACEHOLDER_KEY;
|
|
80
84
|
const shipper = [
|
|
81
85
|
'```ts',
|
|
82
86
|
'// src/logs.ts — um shipper por processo; correlaciona traceId/spanId automaticamente',
|
|
@@ -127,7 +131,7 @@ function logsSection(input, service, logger) {
|
|
|
127
131
|
' format: winston.format.json(),',
|
|
128
132
|
' transports: [',
|
|
129
133
|
' new winston.transports.Console(),',
|
|
130
|
-
' winstonTransport(logShipper), // envia cada log ao FG
|
|
134
|
+
' winstonTransport(logShipper), // envia cada log ao FG Tracker',
|
|
131
135
|
' ],',
|
|
132
136
|
'});',
|
|
133
137
|
'```',
|
|
@@ -153,9 +157,9 @@ function logsSection(input, service, logger) {
|
|
|
153
157
|
else if (logger === 'winston')
|
|
154
158
|
parts.push(winston);
|
|
155
159
|
else if (logger === 'nest')
|
|
156
|
-
parts.push(nest, '', 'Se o app usa pino ou winston, chame `
|
|
160
|
+
parts.push(nest, '', 'Se o app usa pino ou winston, chame `tracker_instrumentation_guide { framework: "pino" | "winston" }` para o transport correspondente.');
|
|
157
161
|
else
|
|
158
|
-
parts.push(raw, '', 'Com pino ou winston, chame `
|
|
162
|
+
parts.push(raw, '', 'Com pino ou winston, chame `tracker_instrumentation_guide { framework: "pino" | "winston" }` para o transport pronto.');
|
|
159
163
|
parts.push('', 'Contrato (§11): o shipper envia lotes para `POST /v1/logs` com o header `x-fg-tracker-key`; níveis `trace|debug|info|warn|error|fatal`; `traceId`/`spanId` são preenchidos automaticamente quando o log acontece dentro de um request instrumentado pelo tracker. Se os exports `fg-tracker-sdk/pino` e `fg-tracker-sdk/winston` ainda não existirem na versão instalada do SDK, envie os logs direto para `POST /v1/logs` (veja o resource `fg-tracker://docs/quickstart`).');
|
|
160
164
|
return parts.join('\n');
|
|
161
165
|
}
|
|
@@ -167,14 +171,14 @@ function verifySection(input) {
|
|
|
167
171
|
'2. Aguarde ~3 s (flush a cada 2 s).',
|
|
168
172
|
];
|
|
169
173
|
if (input.projectId) {
|
|
170
|
-
lines.push(`3. Chame \`
|
|
174
|
+
lines.push(`3. Chame \`tracker_verify_ingest { projectId: "${input.projectId}" }\` — deve reportar spans (e logs) recentes.`);
|
|
171
175
|
}
|
|
172
176
|
else {
|
|
173
|
-
lines.push('3. Chame `
|
|
177
|
+
lines.push('3. Chame `tracker_verify_ingest { projectId }` — deve reportar spans (e logs) recentes.');
|
|
174
178
|
}
|
|
175
179
|
lines.push('4. Se nada chegou: confira `FG_TRACKER_KEY` (4xx no envio = chave inválida, o SDK loga uma vez), se a API responde em `' +
|
|
176
180
|
input.endpoint +
|
|
177
|
-
'/v1/health`, e se `app.use(tracker.express())` é o PRIMEIRO middleware.', '', 'Depois, `
|
|
181
|
+
'/v1/health`, e se `app.use(tracker.express())` é o PRIMEIRO middleware.', '', 'Depois, `tracker_get_overview`, `tracker_list_endpoints` e `tracker_list_issues` mostram o que foi coletado.');
|
|
178
182
|
return lines.join('\n');
|
|
179
183
|
}
|
|
180
184
|
export function buildGuide(input) {
|
|
@@ -182,7 +186,7 @@ export function buildGuide(input) {
|
|
|
182
186
|
switch (input.framework) {
|
|
183
187
|
case 'express':
|
|
184
188
|
return [
|
|
185
|
-
header(input, 'Instrumentar Express com FG
|
|
189
|
+
header(input, 'Instrumentar Express com FG Tracker'),
|
|
186
190
|
'## 1. Instalar',
|
|
187
191
|
install(),
|
|
188
192
|
'',
|
|
@@ -226,7 +230,7 @@ export function buildGuide(input) {
|
|
|
226
230
|
].join('\n');
|
|
227
231
|
case 'nestjs':
|
|
228
232
|
return [
|
|
229
|
-
header(input, 'Instrumentar NestJS (platform-express) com FG
|
|
233
|
+
header(input, 'Instrumentar NestJS (platform-express) com FG Tracker'),
|
|
230
234
|
'## 1. Instalar',
|
|
231
235
|
install(),
|
|
232
236
|
'',
|
|
@@ -243,7 +247,7 @@ export function buildGuide(input) {
|
|
|
243
247
|
"import { AppModule } from './app.module';",
|
|
244
248
|
'',
|
|
245
249
|
'export const tracker = createTracker({',
|
|
246
|
-
` apiKey: process.env.FG_TRACKER_KEY!, // ${input.apiKey
|
|
250
|
+
` apiKey: process.env.FG_TRACKER_KEY!, // ${input.apiKey || PLACEHOLDER_KEY}`,
|
|
247
251
|
` endpoint: process.env.FG_TRACKER_URL ?? '${input.endpoint}',`,
|
|
248
252
|
` serviceName: '${service}',`,
|
|
249
253
|
'});',
|
|
@@ -297,7 +301,7 @@ export function buildGuide(input) {
|
|
|
297
301
|
].join('\n');
|
|
298
302
|
case 'nextjs':
|
|
299
303
|
return [
|
|
300
|
-
header(input, 'Instrumentar Next.js (App Router, runtime Node) com FG
|
|
304
|
+
header(input, 'Instrumentar Next.js (App Router, runtime Node) com FG Tracker'),
|
|
301
305
|
'## 1. Instalar',
|
|
302
306
|
install(),
|
|
303
307
|
'',
|
|
@@ -315,7 +319,7 @@ export function buildGuide(input) {
|
|
|
315
319
|
'export const tracker: Tracker =',
|
|
316
320
|
' g.__fgTracker ??',
|
|
317
321
|
' (g.__fgTracker = createTracker({',
|
|
318
|
-
` apiKey: process.env.FG_TRACKER_KEY!, // ${input.apiKey
|
|
322
|
+
` apiKey: process.env.FG_TRACKER_KEY!, // ${input.apiKey || PLACEHOLDER_KEY}`,
|
|
319
323
|
` endpoint: process.env.FG_TRACKER_URL ?? '${input.endpoint}',`,
|
|
320
324
|
` serviceName: '${service}',`,
|
|
321
325
|
' }));',
|
|
@@ -374,7 +378,7 @@ export function buildGuide(input) {
|
|
|
374
378
|
].join('\n');
|
|
375
379
|
case 'fastify':
|
|
376
380
|
return [
|
|
377
|
-
header(input, 'Instrumentar Fastify com FG
|
|
381
|
+
header(input, 'Instrumentar Fastify com FG Tracker'),
|
|
378
382
|
'O SDK não tem plugin Fastify dedicado; o middleware Express funciona num hook `onRequest` sobre `request.raw`/`reply.raw` (o hook chama `done` dentro do contexto assíncrono do span, então `instrumentPg`, `wrapFetch` e `tracker.span` enxergam o request).',
|
|
379
383
|
'',
|
|
380
384
|
'## 1. Instalar',
|
|
@@ -447,7 +451,7 @@ export function buildGuide(input) {
|
|
|
447
451
|
].join('\n');
|
|
448
452
|
case 'pino':
|
|
449
453
|
return [
|
|
450
|
-
header(input, 'Enviar logs do pino para o FG
|
|
454
|
+
header(input, 'Enviar logs do pino para o FG Tracker'),
|
|
451
455
|
'## 1. Instalar',
|
|
452
456
|
install(),
|
|
453
457
|
'',
|
|
@@ -459,7 +463,7 @@ export function buildGuide(input) {
|
|
|
459
463
|
'## 4. Correlação com traces',
|
|
460
464
|
'Se o processo também usa o tracker (`createTracker` + `tracker.express()`), cada log emitido durante um request ganha `traceId`/`spanId` e aparece com link para o trace no console. Sem tracker, os logs chegam normalmente, só sem correlação.',
|
|
461
465
|
'',
|
|
462
|
-
'## 5. Sintaxe de busca (`
|
|
466
|
+
'## 5. Sintaxe de busca (`tracker_search_logs` e página /logs)',
|
|
463
467
|
'- texto livre → busca full-text (`websearch_to_tsquery`); `"frase exata"` entre aspas;',
|
|
464
468
|
'- `key=value` → filtra `context->>key`;',
|
|
465
469
|
'- `~trecho` → ILIKE (substring, via trigram).',
|
|
@@ -468,7 +472,7 @@ export function buildGuide(input) {
|
|
|
468
472
|
].join('\n');
|
|
469
473
|
case 'winston':
|
|
470
474
|
return [
|
|
471
|
-
header(input, 'Enviar logs do winston para o FG
|
|
475
|
+
header(input, 'Enviar logs do winston para o FG Tracker'),
|
|
472
476
|
'## 1. Instalar',
|
|
473
477
|
install(),
|
|
474
478
|
'',
|
|
@@ -480,7 +484,7 @@ export function buildGuide(input) {
|
|
|
480
484
|
'## 4. Correlação com traces',
|
|
481
485
|
'Se o processo também usa o tracker (`createTracker` + `tracker.express()`), cada log emitido durante um request ganha `traceId`/`spanId`. Chame `logShipper.flush()` (ou `tracker.shutdown()` + `logShipper.shutdown()`) no encerramento para não perder o último lote.',
|
|
482
486
|
'',
|
|
483
|
-
'## 5. Sintaxe de busca (`
|
|
487
|
+
'## 5. Sintaxe de busca (`tracker_search_logs` e página /logs)',
|
|
484
488
|
'- texto livre → full-text; `"frase exata"` entre aspas; `key=value` → `context->>key`; `~trecho` → substring.',
|
|
485
489
|
'',
|
|
486
490
|
verifySection(input),
|