onveloz 0.0.0-beta.24 → 0.0.0-beta.26
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/dist/index.mjs +658 -106
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -85,17 +85,17 @@ const DISK_ENGINE_SIZES = {
|
|
|
85
85
|
},
|
|
86
86
|
nitro: {
|
|
87
87
|
label: "Nitro",
|
|
88
|
-
cpu: "
|
|
88
|
+
cpu: "1",
|
|
89
89
|
memory: "4Gi",
|
|
90
|
-
cpuLabel: "
|
|
90
|
+
cpuLabel: "1 vCPU",
|
|
91
91
|
memoryLabel: "4 GB"
|
|
92
92
|
},
|
|
93
93
|
"nitro-plus": {
|
|
94
94
|
label: "Nitro Plus",
|
|
95
|
-
cpu: "
|
|
96
|
-
memory: "
|
|
97
|
-
cpuLabel: "
|
|
98
|
-
memoryLabel: "
|
|
95
|
+
cpu: "2",
|
|
96
|
+
memory: "4Gi",
|
|
97
|
+
cpuLabel: "2 vCPU",
|
|
98
|
+
memoryLabel: "4 GB"
|
|
99
99
|
}
|
|
100
100
|
};
|
|
101
101
|
/** Redis size tiers — memory-heavy since Redis is purely in-memory, CPU stays minimal. */
|
|
@@ -138,9 +138,9 @@ const REDIS_SIZES = {
|
|
|
138
138
|
"nitro-plus": {
|
|
139
139
|
label: "Nitro Plus",
|
|
140
140
|
cpu: "1",
|
|
141
|
-
memory: "
|
|
141
|
+
memory: "3Gi",
|
|
142
142
|
cpuLabel: "1 vCPU",
|
|
143
|
-
memoryLabel: "
|
|
143
|
+
memoryLabel: "3 GB"
|
|
144
144
|
}
|
|
145
145
|
};
|
|
146
146
|
/** Per-engine size tier maps. */
|
|
@@ -179,6 +179,14 @@ function resolveDatabaseSize(cpu, memory, engine) {
|
|
|
179
179
|
for (const [key, tier] of Object.entries(sizes)) if (tier.cpu === cpu && tier.memory === memory) return key;
|
|
180
180
|
return null;
|
|
181
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* Returns the rank (0-based index) of a database size key.
|
|
184
|
+
* Higher rank = bigger tier. Returns -1 if unknown.
|
|
185
|
+
*/
|
|
186
|
+
function getDatabaseSizeRank(size, engine) {
|
|
187
|
+
const sizes = engine ? DATABASE_SIZES_BY_ENGINE[engine] : DISK_ENGINE_SIZES;
|
|
188
|
+
return Object.keys(sizes).indexOf(size);
|
|
189
|
+
}
|
|
182
190
|
const SERVICE_SIZES = {
|
|
183
191
|
basico: {
|
|
184
192
|
label: "Básico",
|
|
@@ -1106,9 +1114,29 @@ async function resolveService(serviceFlag) {
|
|
|
1106
1114
|
};
|
|
1107
1115
|
}
|
|
1108
1116
|
async function resolveServiceId(serviceFlag) {
|
|
1117
|
+
if (serviceFlag) {
|
|
1118
|
+
const config = loadConfig();
|
|
1119
|
+
if (config) {
|
|
1120
|
+
const dbFound = Object.entries(config.databases ?? {}).find(([key, db]) => key === serviceFlag || db.name !== void 0 && db.name === serviceFlag || db.id === serviceFlag);
|
|
1121
|
+
if (dbFound) {
|
|
1122
|
+
const [key, db] = dbFound;
|
|
1123
|
+
if (!db.id) throw new Error(`Banco "${db.name ?? key}" não possui ID. Execute 'veloz deploy' para vincular.`);
|
|
1124
|
+
return db.id;
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1109
1128
|
const { service } = await resolveService(serviceFlag);
|
|
1110
1129
|
return service.id;
|
|
1111
1130
|
}
|
|
1131
|
+
/** Check if a service flag refers to a database in the config */
|
|
1132
|
+
function isDatabaseService(serviceFlag) {
|
|
1133
|
+
const config = loadConfig();
|
|
1134
|
+
if (!config) return false;
|
|
1135
|
+
if (serviceFlag) return Object.entries(config.databases ?? {}).some(([key, db]) => key === serviceFlag || db.name !== void 0 && db.name === serviceFlag || db.id === serviceFlag);
|
|
1136
|
+
const entries = Object.entries(config.services);
|
|
1137
|
+
if (entries.length === 1) return entries[0][1].type === "database";
|
|
1138
|
+
return false;
|
|
1139
|
+
}
|
|
1112
1140
|
function resolveAllServices(serviceFlag) {
|
|
1113
1141
|
const config = requireConfig();
|
|
1114
1142
|
const entries = Object.entries(config.services);
|
|
@@ -2974,52 +3002,142 @@ function printSparkline(label, values, suffix, width) {
|
|
|
2974
3002
|
const latest = latestValue(values);
|
|
2975
3003
|
console.log(` ${label.padEnd(width)} ${sparkline(values)} ${chalk.bold(`${formatCompact(latest)}${suffix}`)}`);
|
|
2976
3004
|
}
|
|
3005
|
+
function formatUptime(seconds) {
|
|
3006
|
+
if (seconds < 60) return `${Math.round(seconds)}s`;
|
|
3007
|
+
if (seconds < 3600) return `${Math.round(seconds / 60)}m`;
|
|
3008
|
+
if (seconds < 86400) return `${Math.round(seconds / 3600)}h`;
|
|
3009
|
+
return `${Math.round(seconds / 86400)}d`;
|
|
3010
|
+
}
|
|
3011
|
+
function formatBytes(bytes) {
|
|
3012
|
+
if (bytes < 1024) return `${bytes.toFixed(0)} B`;
|
|
3013
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
3014
|
+
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
3015
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
|
3016
|
+
}
|
|
3017
|
+
function formatPercent(value) {
|
|
3018
|
+
return `${value.toFixed(1)}%`;
|
|
3019
|
+
}
|
|
3020
|
+
function printInsightsHint() {
|
|
3021
|
+
console.log();
|
|
3022
|
+
info(`Ative insights para métricas detalhadas do banco: ${chalk.bold("veloz db update <nome> --insights")}`);
|
|
3023
|
+
}
|
|
2977
3024
|
const metricsGroup = Cli.create("metrics", { description: "Visualizar métricas dos serviços" });
|
|
2978
3025
|
metricsGroup.command("show", {
|
|
2979
3026
|
description: "Exibir métricas atuais do serviço",
|
|
2980
3027
|
middleware: [requireAuth],
|
|
2981
|
-
options: z.object({ service: z.string().optional().describe("Filtrar por serviço") }),
|
|
3028
|
+
options: z.object({ service: z.string().optional().describe("Filtrar por serviço ou banco de dados") }),
|
|
2982
3029
|
output: z.object({
|
|
2983
3030
|
requestRate: z.number(),
|
|
2984
3031
|
errorRate: z.number(),
|
|
2985
3032
|
latencyP95: z.number(),
|
|
2986
3033
|
statusCodes: z.record(z.string(), z.number()),
|
|
2987
|
-
grafanaDashboardUrl: z.string().nullable()
|
|
3034
|
+
grafanaDashboardUrl: z.string().nullable(),
|
|
3035
|
+
resource: z.object({
|
|
3036
|
+
restarts: z.number(),
|
|
3037
|
+
uptimeSeconds: z.number(),
|
|
3038
|
+
oomEvents: z.number(),
|
|
3039
|
+
cpuThrottlePercent: z.number()
|
|
3040
|
+
}).nullable(),
|
|
3041
|
+
database: z.object({
|
|
3042
|
+
activeConnections: z.number(),
|
|
3043
|
+
queriesPerSec: z.number(),
|
|
3044
|
+
cacheHitRatio: z.number(),
|
|
3045
|
+
totalConnections: z.number(),
|
|
3046
|
+
idleConnections: z.number(),
|
|
3047
|
+
databaseSize: z.number(),
|
|
3048
|
+
insightsEnabled: z.boolean()
|
|
3049
|
+
}).nullable(),
|
|
3050
|
+
hint: z.string()
|
|
2988
3051
|
}),
|
|
2989
3052
|
async run(c) {
|
|
2990
3053
|
const serviceId = await resolveServiceId(c.options.service);
|
|
2991
3054
|
const client = await getClient();
|
|
2992
|
-
const
|
|
3055
|
+
const isDb = isDatabaseService(c.options.service);
|
|
3056
|
+
const { metrics, resource, database } = await withSpinner({
|
|
2993
3057
|
text: "Carregando métricas...",
|
|
2994
|
-
fn: () =>
|
|
3058
|
+
fn: async () => {
|
|
3059
|
+
const [metrics$1, resource$1, database$1] = await Promise.all([
|
|
3060
|
+
client.metrics.getServiceMetrics({ serviceId }),
|
|
3061
|
+
client.metrics.getResourceMetricsInstant({ serviceId }),
|
|
3062
|
+
isDb ? client.databaseInsights.getMetrics({ serviceId }) : null
|
|
3063
|
+
]);
|
|
3064
|
+
return {
|
|
3065
|
+
metrics: metrics$1,
|
|
3066
|
+
resource: resource$1,
|
|
3067
|
+
database: database$1
|
|
3068
|
+
};
|
|
3069
|
+
}
|
|
2995
3070
|
});
|
|
2996
3071
|
if (process.stdout.isTTY) {
|
|
3072
|
+
const W = 22;
|
|
2997
3073
|
console.log();
|
|
2998
|
-
|
|
3074
|
+
if (!isDb) {
|
|
3075
|
+
console.log(chalk.bold(" Métricas de Tráfego"));
|
|
3076
|
+
console.log();
|
|
3077
|
+
console.log(` ${"Requisições/min".padEnd(W)} ${chalk.bold(formatCompact(metrics.requestRate))}`);
|
|
3078
|
+
console.log(` ${"Erros/min".padEnd(W)} ${rateColor(metrics.errorRate)(formatCompact(metrics.errorRate))}`);
|
|
3079
|
+
console.log(` ${"Latência P95".padEnd(W)} ${chalk.bold(formatCompact(metrics.latencyP95, "s"))}`);
|
|
3080
|
+
const codes = Object.entries(metrics.statusCodes);
|
|
3081
|
+
if (codes.length > 0) {
|
|
3082
|
+
console.log();
|
|
3083
|
+
console.log(chalk.dim(" Status HTTP:"));
|
|
3084
|
+
for (const [code, count] of codes.sort(([a], [b]) => a.localeCompare(b))) console.log(` ${statusColor$1(code)(code)} ${formatCompact(count)}`);
|
|
3085
|
+
}
|
|
3086
|
+
console.log();
|
|
3087
|
+
}
|
|
3088
|
+
console.log(chalk.bold(" Recursos"));
|
|
2999
3089
|
console.log();
|
|
3000
|
-
console.log(`
|
|
3001
|
-
console.log(`
|
|
3002
|
-
console.log(`
|
|
3003
|
-
|
|
3004
|
-
if (
|
|
3090
|
+
console.log(` ${"Restarts".padEnd(W)} ${chalk.bold(String(resource.restarts))}`);
|
|
3091
|
+
console.log(` ${"Uptime".padEnd(W)} ${chalk.bold(formatUptime(resource.uptimeSeconds))}`);
|
|
3092
|
+
console.log(` ${"OOM Events".padEnd(W)} ${resource.oomEvents > 0 ? chalk.red(String(resource.oomEvents)) : chalk.green("0")}`);
|
|
3093
|
+
console.log(` ${"CPU Throttle".padEnd(W)} ${resource.cpuThrottlePercent > 10 ? chalk.yellow(formatPercent(resource.cpuThrottlePercent)) : chalk.green(formatPercent(resource.cpuThrottlePercent))}`);
|
|
3094
|
+
if (isDb && database) {
|
|
3095
|
+
console.log();
|
|
3096
|
+
console.log(chalk.bold(" Banco de Dados"));
|
|
3005
3097
|
console.log();
|
|
3006
|
-
|
|
3007
|
-
|
|
3098
|
+
if (database.insightsEnabled) {
|
|
3099
|
+
console.log(` ${"Conexões Ativas".padEnd(W)} ${chalk.bold(String(database.activeConnections))}`);
|
|
3100
|
+
console.log(` ${"Conexões Total".padEnd(W)} ${chalk.bold(String(database.totalConnections))}`);
|
|
3101
|
+
console.log(` ${"Conexões Idle".padEnd(W)} ${chalk.bold(String(database.idleConnections))}`);
|
|
3102
|
+
console.log(` ${"Queries/s".padEnd(W)} ${chalk.bold(formatCompact(database.queriesPerSec))}`);
|
|
3103
|
+
console.log(` ${"Cache Hit Ratio".padEnd(W)} ${database.cacheHitRatio > .9 ? chalk.green(formatPercent(database.cacheHitRatio * 100)) : chalk.yellow(formatPercent(database.cacheHitRatio * 100))}`);
|
|
3104
|
+
console.log(` ${"Tamanho".padEnd(W)} ${chalk.bold(formatBytes(database.databaseSize))}`);
|
|
3105
|
+
} else printInsightsHint();
|
|
3008
3106
|
}
|
|
3009
3107
|
if (metrics.grafanaDashboardUrl) {
|
|
3010
3108
|
console.log();
|
|
3011
3109
|
info(`Dashboard: ${chalk.underline(metrics.grafanaDashboardUrl)}`);
|
|
3012
3110
|
}
|
|
3013
3111
|
console.log();
|
|
3112
|
+
console.log(chalk.dim(" Dicas:"));
|
|
3113
|
+
console.log(chalk.dim(` veloz metrics range Sparklines ao longo do tempo`));
|
|
3114
|
+
console.log(chalk.dim(` veloz metrics list Descobrir métricas disponíveis`));
|
|
3115
|
+
console.log(chalk.dim(` veloz metrics query '<consulta>' Consulta MetricsQL personalizada`));
|
|
3116
|
+
console.log(chalk.dim(` veloz metrics query-help Referência completa de MetricsQL`));
|
|
3117
|
+
console.log();
|
|
3014
3118
|
}
|
|
3015
|
-
|
|
3119
|
+
const hints = [
|
|
3120
|
+
"Use 'veloz metrics range' para sparklines ao longo do tempo.",
|
|
3121
|
+
"Use 'veloz metrics list' para descobrir métricas disponíveis.",
|
|
3122
|
+
"Use 'veloz metrics query <metricsql>' para consultas MetricsQL personalizadas.",
|
|
3123
|
+
"Use 'veloz metrics labels' para descobrir labels e seus valores.",
|
|
3124
|
+
"Use 'veloz metrics series <seletor>' para encontrar séries por seletor.",
|
|
3125
|
+
"Use 'veloz metrics query-help' para referência completa de MetricsQL."
|
|
3126
|
+
];
|
|
3127
|
+
if (isDb && database && !database.insightsEnabled) hints.unshift("Ative insights para métricas detalhadas do banco: 'veloz db update <nome> --insights'.");
|
|
3128
|
+
return {
|
|
3129
|
+
...metrics,
|
|
3130
|
+
resource,
|
|
3131
|
+
database,
|
|
3132
|
+
hint: hints.join("\n")
|
|
3133
|
+
};
|
|
3016
3134
|
}
|
|
3017
3135
|
});
|
|
3018
3136
|
metricsGroup.command("range", {
|
|
3019
3137
|
description: "Exibir métricas em intervalo de tempo com sparklines",
|
|
3020
3138
|
middleware: [requireAuth],
|
|
3021
3139
|
options: z.object({
|
|
3022
|
-
service: z.string().optional().describe("Filtrar por serviço"),
|
|
3140
|
+
service: z.string().optional().describe("Filtrar por serviço ou banco de dados"),
|
|
3023
3141
|
range: z.enum([
|
|
3024
3142
|
"1h",
|
|
3025
3143
|
"6h",
|
|
@@ -3031,100 +3149,508 @@ metricsGroup.command("range", {
|
|
|
3031
3149
|
async run(c) {
|
|
3032
3150
|
const serviceId = await resolveServiceId(c.options.service);
|
|
3033
3151
|
const client = await getClient();
|
|
3034
|
-
const
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3152
|
+
const isDb = isDatabaseService(c.options.service);
|
|
3153
|
+
const range = c.options.range;
|
|
3154
|
+
const { data, dbData } = await withSpinner({
|
|
3155
|
+
text: `Carregando métricas (${range})...`,
|
|
3156
|
+
fn: async () => {
|
|
3157
|
+
const [data$1, dbData$1] = await Promise.all([client.metrics.getServiceMetricsRange({
|
|
3158
|
+
serviceId,
|
|
3159
|
+
range
|
|
3160
|
+
}), isDb ? client.databaseInsights.getMetricsRange({
|
|
3161
|
+
serviceId,
|
|
3162
|
+
range
|
|
3163
|
+
}) : null]);
|
|
3164
|
+
return {
|
|
3165
|
+
data: data$1,
|
|
3166
|
+
dbData: dbData$1
|
|
3167
|
+
};
|
|
3168
|
+
}
|
|
3040
3169
|
});
|
|
3041
3170
|
if (process.stdout.isTTY) {
|
|
3042
3171
|
const W = 20;
|
|
3043
|
-
const rangeLabel = chalk.dim(`(últimas ${
|
|
3172
|
+
const rangeLabel = chalk.dim(`(últimas ${range})`);
|
|
3044
3173
|
console.log();
|
|
3045
3174
|
console.log(chalk.bold(` Métricas ${rangeLabel}`));
|
|
3046
3175
|
console.log();
|
|
3047
|
-
if (
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3176
|
+
if (!isDb) {
|
|
3177
|
+
if (data.requestRate.length > 0) {
|
|
3178
|
+
const v = seriesValues(data.requestRate);
|
|
3179
|
+
console.log(` ${"Req/min".padEnd(W)} ${sparkline(v)} ${chalk.bold(formatCompact(latestValue(v)))}`);
|
|
3180
|
+
}
|
|
3181
|
+
if (data.latencyP95.length > 0) {
|
|
3182
|
+
const v = seriesValues(data.latencyP95);
|
|
3183
|
+
console.log(` ${"Latência P95".padEnd(W)} ${sparkline(v)} ${chalk.bold(formatCompact(latestValue(v), "s"))}`);
|
|
3184
|
+
}
|
|
3185
|
+
if (data.latencyP50.length > 0) {
|
|
3186
|
+
const v = seriesValues(data.latencyP50);
|
|
3187
|
+
console.log(` ${"Latência P50".padEnd(W)} ${sparkline(v)} ${chalk.bold(formatCompact(latestValue(v), "s"))}`);
|
|
3188
|
+
}
|
|
3058
3189
|
}
|
|
3059
3190
|
for (const series of data.cpuUsage) printSparkline(`CPU (${series.label})`, labeledValues(series), "m", W);
|
|
3060
3191
|
for (const series of data.memoryUsage) printSparkline(`Memória (${series.label})`, labeledValues(series), "Mi", W);
|
|
3061
3192
|
if (data.networkIn.length > 0) printSparkline("Rede ↓", seriesValues(data.networkIn), "KB/s", W);
|
|
3062
3193
|
if (data.networkOut.length > 0) printSparkline("Rede ↑", seriesValues(data.networkOut), "KB/s", W);
|
|
3194
|
+
if (isDb && dbData) if (dbData.insightsEnabled) {
|
|
3195
|
+
console.log();
|
|
3196
|
+
console.log(chalk.bold(" Banco de Dados"));
|
|
3197
|
+
console.log();
|
|
3198
|
+
if (dbData.activeConnections.length > 0) printSparkline("Conexões Ativas", seriesValues(dbData.activeConnections), "", W);
|
|
3199
|
+
if (dbData.queriesPerSec.length > 0) printSparkline("Queries/s", seriesValues(dbData.queriesPerSec), "", W);
|
|
3200
|
+
if (dbData.cacheHitRatio.length > 0) printSparkline("Cache Hit", seriesValues(dbData.cacheHitRatio).map((r) => r * 100), "%", W);
|
|
3201
|
+
} else printInsightsHint();
|
|
3202
|
+
console.log();
|
|
3203
|
+
}
|
|
3204
|
+
const hint = isDb && dbData && !dbData.insightsEnabled ? "Ative insights para métricas detalhadas do banco: 'veloz db update <nome> --insights'. Use 'veloz metrics query <metricsql>' para consultas personalizadas." : "Use 'veloz metrics query <metricsql>' para consultas MetricsQL personalizadas. Use 'veloz metrics list' para descobrir métricas disponíveis.";
|
|
3205
|
+
return {
|
|
3206
|
+
...data,
|
|
3207
|
+
database: dbData,
|
|
3208
|
+
hint
|
|
3209
|
+
};
|
|
3210
|
+
}
|
|
3211
|
+
});
|
|
3212
|
+
metricsGroup.command("query", {
|
|
3213
|
+
description: "Executar consulta MetricsQL personalizada",
|
|
3214
|
+
middleware: [requireAuth],
|
|
3215
|
+
args: z.object({ query: z.string().describe("Consulta MetricsQL (ex: up, rate(http_requests[5m]))") }),
|
|
3216
|
+
options: z.object({
|
|
3217
|
+
service: z.string().optional().describe("Serviço para contexto de autenticação"),
|
|
3218
|
+
start: z.string().optional().describe("Início do intervalo (ISO 8601, unix timestamp, ou relativo: 1h, 6h, 24h, 7d)"),
|
|
3219
|
+
end: z.string().optional().describe("Fim do intervalo (padrão: agora)"),
|
|
3220
|
+
step: z.string().optional().describe("Resolução (ex: 30s, 1m, 5m). Padrão: auto")
|
|
3221
|
+
}),
|
|
3222
|
+
output: z.object({
|
|
3223
|
+
resultType: z.string(),
|
|
3224
|
+
results: z.array(z.object({
|
|
3225
|
+
metric: z.record(z.string(), z.string()),
|
|
3226
|
+
values: z.array(z.object({
|
|
3227
|
+
timestamp: z.number(),
|
|
3228
|
+
value: z.number()
|
|
3229
|
+
}))
|
|
3230
|
+
}))
|
|
3231
|
+
}),
|
|
3232
|
+
async run(c) {
|
|
3233
|
+
const client = await getClient();
|
|
3234
|
+
let start = c.options.start;
|
|
3235
|
+
if (start && /^\d+[smhdw]$/.test(start)) start = `-${start}`;
|
|
3236
|
+
let step = c.options.step;
|
|
3237
|
+
if (start && !step) {
|
|
3238
|
+
const durationMatch = start.replace(/^-/, "").match(/^(\d+)([smhdw])$/);
|
|
3239
|
+
if (durationMatch) {
|
|
3240
|
+
const duration = parseInt(durationMatch[1], 10) * ({
|
|
3241
|
+
s: 1,
|
|
3242
|
+
m: 60,
|
|
3243
|
+
h: 3600,
|
|
3244
|
+
d: 86400,
|
|
3245
|
+
w: 604800
|
|
3246
|
+
}[durationMatch[2]] ?? 1);
|
|
3247
|
+
if (duration <= 3600) step = "30s";
|
|
3248
|
+
else if (duration <= 21600) step = "60s";
|
|
3249
|
+
else if (duration <= 86400) step = "300s";
|
|
3250
|
+
else step = "1800s";
|
|
3251
|
+
} else step = "60s";
|
|
3252
|
+
}
|
|
3253
|
+
const result = await withSpinner({
|
|
3254
|
+
text: "Executando consulta...",
|
|
3255
|
+
fn: () => client.metrics.queryMetrics({
|
|
3256
|
+
query: c.args.query,
|
|
3257
|
+
start,
|
|
3258
|
+
end: c.options.end,
|
|
3259
|
+
step
|
|
3260
|
+
})
|
|
3261
|
+
});
|
|
3262
|
+
if (process.stdout.isTTY) {
|
|
3263
|
+
console.log();
|
|
3264
|
+
if (result.results.length === 0) info("Nenhum resultado para esta consulta.");
|
|
3265
|
+
else {
|
|
3266
|
+
console.log(chalk.dim(` Tipo: ${result.resultType} | ${result.results.length} série(s)`));
|
|
3267
|
+
console.log();
|
|
3268
|
+
for (const series of result.results) {
|
|
3269
|
+
const labels = Object.entries(series.metric).map(([k, v]) => `${chalk.dim(k)}=${chalk.cyan(`"${v}"`)}`).join(" ");
|
|
3270
|
+
if (series.values.length === 1) {
|
|
3271
|
+
const val = series.values[0];
|
|
3272
|
+
console.log(` ${labels} ${chalk.bold(formatCompact(val.value))}`);
|
|
3273
|
+
} else if (series.values.length > 1) {
|
|
3274
|
+
const values = series.values.map((point) => point.value);
|
|
3275
|
+
const latest = latestValue(values);
|
|
3276
|
+
console.log(` ${labels}`);
|
|
3277
|
+
console.log(` ${sparkline(values)} ${chalk.bold(formatCompact(latest))}`);
|
|
3278
|
+
}
|
|
3279
|
+
}
|
|
3280
|
+
}
|
|
3281
|
+
console.log();
|
|
3282
|
+
info(`Execute ${chalk.bold("veloz metrics query-help")} para ver a referência completa de MetricsQL.`);
|
|
3283
|
+
console.log();
|
|
3284
|
+
}
|
|
3285
|
+
return {
|
|
3286
|
+
...result,
|
|
3287
|
+
hint: "Use 'veloz metrics list' para descobrir métricas. Use 'veloz metrics labels --label <nome>' para ver valores de labels. Use 'veloz metrics series <seletor>' para encontrar séries. Use 'veloz metrics query-help' para referência completa de MetricsQL."
|
|
3288
|
+
};
|
|
3289
|
+
}
|
|
3290
|
+
});
|
|
3291
|
+
metricsGroup.command("list", {
|
|
3292
|
+
description: "Listar nomes de métricas disponíveis",
|
|
3293
|
+
middleware: [requireAuth],
|
|
3294
|
+
options: z.object({
|
|
3295
|
+
match: z.string().optional().describe("Filtrar por seletor (ex: \"{job=\\\"node\\\"}\", \"traefik_.*\")"),
|
|
3296
|
+
limit: z.number().default(500).describe("Número máximo de resultados")
|
|
3297
|
+
}),
|
|
3298
|
+
output: z.object({ names: z.array(z.string()) }),
|
|
3299
|
+
async run(c) {
|
|
3300
|
+
const client = await getClient();
|
|
3301
|
+
const result = await withSpinner({
|
|
3302
|
+
text: "Listando métricas...",
|
|
3303
|
+
fn: () => client.metrics.listMetricNames({
|
|
3304
|
+
match: c.options.match,
|
|
3305
|
+
limit: c.options.limit
|
|
3306
|
+
})
|
|
3307
|
+
});
|
|
3308
|
+
if (process.stdout.isTTY) {
|
|
3309
|
+
console.log();
|
|
3310
|
+
if (result.names.length === 0) info("Nenhuma métrica encontrada.");
|
|
3311
|
+
else {
|
|
3312
|
+
console.log(chalk.dim(` ${result.names.length} métrica(s) encontrada(s)`));
|
|
3313
|
+
console.log();
|
|
3314
|
+
for (const name of result.names) console.log(` ${name}`);
|
|
3315
|
+
}
|
|
3316
|
+
console.log();
|
|
3317
|
+
}
|
|
3318
|
+
return {
|
|
3319
|
+
...result,
|
|
3320
|
+
hint: "Use 'veloz metrics labels --label <nome>' para ver valores de um label. Use 'veloz metrics series <nome_da_metrica>' para encontrar séries. Use 'veloz metrics query <metricsql>' para consultar dados."
|
|
3321
|
+
};
|
|
3322
|
+
}
|
|
3323
|
+
});
|
|
3324
|
+
metricsGroup.command("labels", {
|
|
3325
|
+
description: "Listar labels disponíveis ou valores de um label específico",
|
|
3326
|
+
middleware: [requireAuth],
|
|
3327
|
+
options: z.object({
|
|
3328
|
+
label: z.string().optional().describe("Nome do label para listar valores (ex: pod, namespace)"),
|
|
3329
|
+
match: z.string().optional().describe("Filtrar por seletor de métrica (ex: \"container_cpu_usage_seconds_total\")"),
|
|
3330
|
+
limit: z.number().default(500).describe("Número máximo de resultados")
|
|
3331
|
+
}),
|
|
3332
|
+
output: z.object({
|
|
3333
|
+
values: z.array(z.string()),
|
|
3334
|
+
type: z.string()
|
|
3335
|
+
}),
|
|
3336
|
+
async run(c) {
|
|
3337
|
+
const client = await getClient();
|
|
3338
|
+
const result = await withSpinner({
|
|
3339
|
+
text: c.options.label ? `Listando valores de "${c.options.label}"...` : "Listando labels...",
|
|
3340
|
+
fn: () => client.metrics.listLabels({
|
|
3341
|
+
label: c.options.label,
|
|
3342
|
+
match: c.options.match,
|
|
3343
|
+
limit: c.options.limit
|
|
3344
|
+
})
|
|
3345
|
+
});
|
|
3346
|
+
if (process.stdout.isTTY) {
|
|
3347
|
+
console.log();
|
|
3348
|
+
if (result.values.length === 0) info("Nenhum resultado encontrado.");
|
|
3349
|
+
else {
|
|
3350
|
+
const heading = result.type === "names" ? "Labels disponíveis" : `Valores de "${result.type}"`;
|
|
3351
|
+
console.log(chalk.dim(` ${heading} (${result.values.length})`));
|
|
3352
|
+
console.log();
|
|
3353
|
+
for (const val of result.values) console.log(` ${val}`);
|
|
3354
|
+
}
|
|
3355
|
+
console.log();
|
|
3356
|
+
}
|
|
3357
|
+
return {
|
|
3358
|
+
...result,
|
|
3359
|
+
hint: "Use 'veloz metrics series <nome_da_metrica>' para encontrar séries com labels. Use 'veloz metrics query <metricsql>' para consultar dados. Use 'veloz metrics query-help' para referência MetricsQL."
|
|
3360
|
+
};
|
|
3361
|
+
}
|
|
3362
|
+
});
|
|
3363
|
+
metricsGroup.command("series", {
|
|
3364
|
+
description: "Encontrar séries de métricas por seletor",
|
|
3365
|
+
middleware: [requireAuth],
|
|
3366
|
+
args: z.object({ match: z.string().describe("Seletor de séries (ex: \"traefik_service_requests_total\", '{__name__=~\"pg_.*\"}')") }),
|
|
3367
|
+
options: z.object({ limit: z.number().default(100).describe("Número máximo de séries") }),
|
|
3368
|
+
output: z.object({
|
|
3369
|
+
series: z.array(z.record(z.string(), z.string())),
|
|
3370
|
+
count: z.number()
|
|
3371
|
+
}),
|
|
3372
|
+
async run(c) {
|
|
3373
|
+
const client = await getClient();
|
|
3374
|
+
const result = await withSpinner({
|
|
3375
|
+
text: "Buscando séries...",
|
|
3376
|
+
fn: () => client.metrics.findSeries({
|
|
3377
|
+
match: c.args.match,
|
|
3378
|
+
limit: c.options.limit
|
|
3379
|
+
})
|
|
3380
|
+
});
|
|
3381
|
+
if (process.stdout.isTTY) {
|
|
3382
|
+
console.log();
|
|
3383
|
+
if (result.series.length === 0) info("Nenhuma série encontrada para este seletor.");
|
|
3384
|
+
else {
|
|
3385
|
+
console.log(chalk.dim(` ${result.count} série(s) encontrada(s)`));
|
|
3386
|
+
console.log();
|
|
3387
|
+
for (const s of result.series) {
|
|
3388
|
+
const name = s.__name__ ?? "";
|
|
3389
|
+
const labels = Object.entries(s).filter(([k]) => k !== "__name__").map(([k, v]) => `${chalk.dim(k)}=${chalk.cyan(`"${v}"`)}`).join(", ");
|
|
3390
|
+
console.log(` ${chalk.bold(name)}${labels ? ` {${labels}}` : ""}`);
|
|
3391
|
+
}
|
|
3392
|
+
}
|
|
3063
3393
|
console.log();
|
|
3064
3394
|
}
|
|
3065
|
-
return
|
|
3395
|
+
return {
|
|
3396
|
+
...result,
|
|
3397
|
+
hint: "Use 'veloz metrics query <metricsql>' para consultar os dados dessas séries. Use 'veloz metrics labels --label <nome>' para ver valores de um label específico."
|
|
3398
|
+
};
|
|
3066
3399
|
}
|
|
3067
3400
|
});
|
|
3068
|
-
const
|
|
3401
|
+
const QUERY_HELP_SECTIONS$1 = [
|
|
3069
3402
|
{
|
|
3070
|
-
title: "
|
|
3071
|
-
content: ` metrics show
|
|
3072
|
-
metrics range
|
|
3403
|
+
title: "Comandos disponíveis",
|
|
3404
|
+
content: ` metrics show Snapshot instantâneo do serviço
|
|
3405
|
+
metrics range Séries temporais com sparklines
|
|
3406
|
+
metrics query <consulta> Consulta MetricsQL personalizada
|
|
3407
|
+
metrics list Listar nomes de métricas disponíveis
|
|
3408
|
+
metrics labels Listar labels ou seus valores
|
|
3409
|
+
metrics series <seletor> Encontrar séries por seletor
|
|
3410
|
+
metrics query-help Esta referência`
|
|
3073
3411
|
},
|
|
3074
3412
|
{
|
|
3075
3413
|
title: "metrics show — campos retornados",
|
|
3076
|
-
content: `
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3414
|
+
content: ` Tráfego (serviços web/worker):
|
|
3415
|
+
requestRate Requisições por minuto (req/min)
|
|
3416
|
+
errorRate Erros 5xx por minuto
|
|
3417
|
+
latencyP95 Latência percentil 95 (segundos)
|
|
3418
|
+
statusCodes Mapa de código HTTP → contagem
|
|
3419
|
+
|
|
3420
|
+
Recursos (todos os serviços):
|
|
3421
|
+
restarts Número de restarts
|
|
3422
|
+
uptimeSeconds Tempo de atividade em segundos
|
|
3423
|
+
oomEvents Eventos de falta de memória (OOM)
|
|
3424
|
+
cpuThrottlePercent Porcentagem de throttle de CPU
|
|
3425
|
+
|
|
3426
|
+
Banco de dados (requer insights ativado):
|
|
3427
|
+
activeConnections Conexões ativas
|
|
3428
|
+
totalConnections Total de conexões
|
|
3429
|
+
idleConnections Conexões ociosas
|
|
3430
|
+
queriesPerSec Consultas por segundo
|
|
3431
|
+
cacheHitRatio Taxa de acerto do cache (0-1)
|
|
3432
|
+
databaseSize Tamanho do banco em bytes`
|
|
3081
3433
|
},
|
|
3082
3434
|
{
|
|
3083
3435
|
title: "metrics range — séries temporais retornadas",
|
|
3084
3436
|
content: ` Tráfego (Traefik):
|
|
3085
|
-
requestRate
|
|
3086
|
-
latencyP50
|
|
3087
|
-
|
|
3088
|
-
latencyP99 [{timestamp, value}] — latência P99
|
|
3089
|
-
statusCodes [{label, data}] — série por código HTTP
|
|
3437
|
+
requestRate [{timestamp, value}] — req/min
|
|
3438
|
+
latencyP50 / P95 / P99 [{timestamp, value}] — latência
|
|
3439
|
+
statusCodes [{label, data}] — por código HTTP
|
|
3090
3440
|
|
|
3091
3441
|
Recursos (por instância):
|
|
3092
|
-
cpuUsage
|
|
3093
|
-
memoryUsage
|
|
3094
|
-
networkIn
|
|
3095
|
-
|
|
3442
|
+
cpuUsage [{label, data}] — millicores
|
|
3443
|
+
memoryUsage [{label, data}] — MiB
|
|
3444
|
+
networkIn / networkOut [{timestamp, value}] — KB/s
|
|
3445
|
+
|
|
3446
|
+
Banco de dados (requer insights):
|
|
3447
|
+
activeConnections [{timestamp, value}] — conexões ativas
|
|
3448
|
+
queriesPerSec [{timestamp, value}] — queries/s
|
|
3449
|
+
cacheHitRatio [{timestamp, value}] — taxa de cache`
|
|
3450
|
+
},
|
|
3451
|
+
{
|
|
3452
|
+
title: "metrics query — consultas MetricsQL",
|
|
3453
|
+
content: ` Consulta instantânea (sem --start):
|
|
3454
|
+
veloz metrics query "up"
|
|
3455
|
+
veloz metrics query 'sum(rate(traefik_service_requests_total[5m]))'
|
|
3456
|
+
|
|
3457
|
+
Consulta com intervalo (range query):
|
|
3458
|
+
veloz metrics query "rate(traefik_service_requests_total[5m])" --start 1h
|
|
3459
|
+
veloz metrics query "container_memory_working_set_bytes" --start 6h --step 5m
|
|
3460
|
+
veloz metrics query "pg_stat_activity_count" --start 24h`
|
|
3461
|
+
},
|
|
3462
|
+
{
|
|
3463
|
+
title: "Seletores de métricas",
|
|
3464
|
+
content: ` nome_da_metrica Seleciona todas as séries
|
|
3465
|
+
nome{label="valor"} Filtro por label exato
|
|
3466
|
+
nome{label=~"regex"} Filtro por regex
|
|
3467
|
+
nome{label!="valor"} Exclusão por valor
|
|
3468
|
+
nome{label!~"regex"} Exclusão por regex
|
|
3469
|
+
nome{a="x", b="y"} Múltiplos filtros (AND)`
|
|
3470
|
+
},
|
|
3471
|
+
{
|
|
3472
|
+
title: "Vetores de intervalo (range vectors)",
|
|
3473
|
+
content: ` metrica[5m] Últimos 5 minutos de amostras
|
|
3474
|
+
metrica[1h] Última hora
|
|
3475
|
+
metrica[1d] Último dia
|
|
3476
|
+
metrica[5m] offset 1h Amostras de 1h atrás`
|
|
3477
|
+
},
|
|
3478
|
+
{
|
|
3479
|
+
title: "Funções de taxa e incremento",
|
|
3480
|
+
content: ` rate(metrica[5m]) Taxa por segundo (para counters)
|
|
3481
|
+
irate(metrica[5m]) Taxa instantânea (últimas 2 amostras)
|
|
3482
|
+
increase(metrica[5m]) Incremento total no período
|
|
3483
|
+
delta(metrica[5m]) Diferença (para gauges)
|
|
3484
|
+
deriv(metrica[5m]) Derivada (para gauges)`
|
|
3485
|
+
},
|
|
3486
|
+
{
|
|
3487
|
+
title: "Funções de agregação",
|
|
3488
|
+
content: ` sum(metrica) by (label) Soma agrupada por label
|
|
3489
|
+
avg(metrica) by (label) Média agrupada
|
|
3490
|
+
min(metrica) by (label) Mínimo
|
|
3491
|
+
max(metrica) by (label) Máximo
|
|
3492
|
+
count(metrica) by (label) Contagem
|
|
3493
|
+
topk(5, metrica) Top 5 séries por valor
|
|
3494
|
+
bottomk(3, metrica) Bottom 3 séries
|
|
3495
|
+
quantile(0.95, metrica) Percentil 95 entre séries`
|
|
3496
|
+
},
|
|
3497
|
+
{
|
|
3498
|
+
title: "Funções de rollup (over time)",
|
|
3499
|
+
content: ` avg_over_time(m[5m]) Média no período
|
|
3500
|
+
max_over_time(m[5m]) Máximo no período
|
|
3501
|
+
min_over_time(m[5m]) Mínimo no período
|
|
3502
|
+
sum_over_time(m[5m]) Soma no período
|
|
3503
|
+
count_over_time(m[5m]) Contagem no período
|
|
3504
|
+
quantile_over_time(0.95, m[5m]) Percentil no período
|
|
3505
|
+
stddev_over_time(m[5m]) Desvio padrão no período
|
|
3506
|
+
last_over_time(m[5m]) Último valor no período`
|
|
3507
|
+
},
|
|
3508
|
+
{
|
|
3509
|
+
title: "Funções de histograma",
|
|
3510
|
+
content: ` histogram_quantile(0.95, sum(rate(metrica_bucket[5m])) by (le))
|
|
3511
|
+
Calcula percentil 95 de histograma (ex: latência P95)
|
|
3512
|
+
|
|
3513
|
+
histogram_quantile(0.50, ...) Mediana (P50)
|
|
3514
|
+
histogram_quantile(0.99, ...) P99`
|
|
3515
|
+
},
|
|
3516
|
+
{
|
|
3517
|
+
title: "Operadores e funções matemáticas",
|
|
3518
|
+
content: ` Aritméticos: + - * / % ^
|
|
3519
|
+
Comparação: == != > < >= <=
|
|
3520
|
+
Lógicos: and or unless
|
|
3521
|
+
Funções: abs() ceil() floor() round()
|
|
3522
|
+
clamp(m, min, max) clamp_min(m, min) clamp_max(m, max)
|
|
3523
|
+
ln() log2() log10() exp() sqrt()`
|
|
3524
|
+
},
|
|
3525
|
+
{
|
|
3526
|
+
title: "MetricsQL — extensões sobre PromQL",
|
|
3527
|
+
content: ` MetricsQL é compatível com PromQL, com extensões extras:
|
|
3528
|
+
|
|
3529
|
+
range_median(m[5m]) Mediana no intervalo
|
|
3530
|
+
range_avg(m[5m]) Média no intervalo (alias de avg_over_time)
|
|
3531
|
+
share_gt_over_time(m[5m], 100) % de amostras acima de 100
|
|
3532
|
+
count_gt_over_time(m[5m], 100) Contagem de amostras acima de 100
|
|
3533
|
+
duration_over_time(m[5m], max) Duração máxima contínua
|
|
3534
|
+
lag(m[5m]) Tempo desde última amostra
|
|
3535
|
+
lifetime(m[5m]) Duração total da série
|
|
3536
|
+
scrape_interval(m[5m]) Intervalo médio entre amostras`
|
|
3096
3537
|
},
|
|
3097
3538
|
{
|
|
3098
|
-
title: "
|
|
3099
|
-
content: `
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3539
|
+
title: "Métricas disponíveis na Veloz",
|
|
3540
|
+
content: ` Tráfego (Traefik — serviços web):
|
|
3541
|
+
traefik_service_requests_total Requisições HTTP (counter)
|
|
3542
|
+
traefik_service_request_duration_seconds_bucket Latência (histogram)
|
|
3543
|
+
|
|
3544
|
+
Recursos (cAdvisor — todos os serviços):
|
|
3545
|
+
container_cpu_usage_seconds_total CPU usado (counter)
|
|
3546
|
+
container_memory_working_set_bytes Memória em uso (gauge)
|
|
3547
|
+
container_network_receive_bytes_total Rede entrada (counter)
|
|
3548
|
+
container_network_transmit_bytes_total Rede saída (counter)
|
|
3549
|
+
container_cpu_cfs_throttled_periods_total Períodos com throttle
|
|
3550
|
+
container_cpu_cfs_periods_total Total de períodos CFS
|
|
3551
|
+
container_threads Threads por container
|
|
3552
|
+
container_file_descriptors File descriptors abertos
|
|
3553
|
+
container_oom_events_total Eventos OOM (counter)
|
|
3554
|
+
|
|
3555
|
+
Kubernetes:
|
|
3556
|
+
kube_pod_container_status_restarts_total Restarts (counter)
|
|
3557
|
+
container_start_time_seconds Timestamp de início
|
|
3558
|
+
|
|
3559
|
+
Banco de dados (PostgreSQL — requer insights):
|
|
3560
|
+
pg_stat_activity_count Conexões por estado
|
|
3561
|
+
pg_stat_database_xact_commit Transactions commit
|
|
3562
|
+
pg_stat_database_xact_rollback Transactions rollback
|
|
3563
|
+
pg_stat_database_blks_hit Blocos do cache (buffer)
|
|
3564
|
+
pg_stat_database_blks_read Blocos lidos do disco
|
|
3565
|
+
pg_database_size_bytes Tamanho do banco
|
|
3566
|
+
|
|
3567
|
+
Plataforma (métricas internas):
|
|
3568
|
+
veloz_rpc_requests_total Chamadas RPC (counter)
|
|
3569
|
+
veloz_rpc_request_duration_seconds Latência RPC (histogram)
|
|
3570
|
+
veloz_http_requests_total Requisições HTTP internas
|
|
3571
|
+
veloz_job_total Jobs processados (counter)
|
|
3572
|
+
veloz_job_duration_seconds Duração de jobs (histogram)
|
|
3573
|
+
veloz_job_active Jobs ativos (gauge)
|
|
3574
|
+
veloz_dlq_depth Profundidade da DLQ (gauge)`
|
|
3103
3575
|
},
|
|
3104
3576
|
{
|
|
3105
|
-
title: "
|
|
3106
|
-
content: `
|
|
3107
|
-
|
|
3108
|
-
|
|
3577
|
+
title: "Descoberta de métricas (list, labels, series)",
|
|
3578
|
+
content: ` Listar todas as métricas:
|
|
3579
|
+
veloz metrics list
|
|
3580
|
+
veloz metrics list --match '{job="node"}'
|
|
3581
|
+
|
|
3582
|
+
Listar labels disponíveis:
|
|
3583
|
+
veloz metrics labels
|
|
3584
|
+
veloz metrics labels --match "container_cpu_usage_seconds_total"
|
|
3585
|
+
|
|
3586
|
+
Listar valores de um label:
|
|
3587
|
+
veloz metrics labels --label pod
|
|
3588
|
+
veloz metrics labels --label namespace
|
|
3589
|
+
veloz metrics labels --label state --match "pg_stat_activity_count"
|
|
3590
|
+
|
|
3591
|
+
Encontrar séries:
|
|
3592
|
+
veloz metrics series "traefik_service_requests_total"
|
|
3593
|
+
veloz metrics series '{__name__=~"pg_.*"}'
|
|
3594
|
+
veloz metrics series '{namespace="proj-meu-projeto"}'`
|
|
3109
3595
|
},
|
|
3110
3596
|
{
|
|
3111
|
-
title: "Exemplos
|
|
3112
|
-
content: `
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3597
|
+
title: "Exemplos práticos",
|
|
3598
|
+
content: ` Tráfego:
|
|
3599
|
+
veloz metrics query 'sum(rate(traefik_service_requests_total[5m])) * 60'
|
|
3600
|
+
veloz metrics query 'histogram_quantile(0.95, sum(rate(traefik_service_request_duration_seconds_bucket[5m])) by (le))'
|
|
3601
|
+
veloz metrics query 'sum(rate(traefik_service_requests_total[5m])) by (code) * 60'
|
|
3602
|
+
|
|
3603
|
+
Recursos:
|
|
3604
|
+
veloz metrics query 'sum by (pod) (rate(container_cpu_usage_seconds_total[5m])) * 1000'
|
|
3605
|
+
veloz metrics query 'sum by (pod) (container_memory_working_set_bytes) / 1024 / 1024'
|
|
3606
|
+
veloz metrics query 'sum(container_oom_events_total) by (pod)' --start 24h
|
|
3607
|
+
|
|
3608
|
+
Banco de dados:
|
|
3609
|
+
veloz metrics query 'pg_stat_activity_count{state="active"}' --start 1h
|
|
3610
|
+
veloz metrics query 'rate(pg_stat_database_xact_commit[5m]) + rate(pg_stat_database_xact_rollback[5m])' --start 6h
|
|
3611
|
+
veloz metrics query 'pg_database_size_bytes' --start 7d
|
|
3612
|
+
|
|
3613
|
+
Range queries com sparklines:
|
|
3614
|
+
veloz metrics query 'rate(traefik_service_requests_total[5m]) * 60' --start 1h
|
|
3615
|
+
veloz metrics query 'container_memory_working_set_bytes / 1024 / 1024' --start 24h --step 5m
|
|
3616
|
+
|
|
3617
|
+
Referência completa:
|
|
3618
|
+
https://docs.victoriametrics.com/metricsql/`
|
|
3117
3619
|
},
|
|
3118
3620
|
{
|
|
3119
|
-
title: "
|
|
3120
|
-
content: `
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
veloz metrics range --range 24h → comparar com padrão do dia`
|
|
3621
|
+
title: "Dica: insights para bancos de dados",
|
|
3622
|
+
content: ` As métricas pg_stat_* e pg_database_* requerem insights ativado.
|
|
3623
|
+
Para ativar: veloz db update <nome> --insights
|
|
3624
|
+
|
|
3625
|
+
Após ativar, o exporter coleta métricas do PostgreSQL
|
|
3626
|
+
automaticamente. As métricas ficam disponíveis em ~2 minutos.`
|
|
3126
3627
|
}
|
|
3127
3628
|
];
|
|
3629
|
+
metricsGroup.command("query-help", {
|
|
3630
|
+
description: "Referência de MetricsQL para consultas de métricas",
|
|
3631
|
+
output: z.object({ sections: z.array(z.object({
|
|
3632
|
+
title: z.string(),
|
|
3633
|
+
content: z.string()
|
|
3634
|
+
})) }),
|
|
3635
|
+
async run() {
|
|
3636
|
+
if (process.stdout.isTTY) {
|
|
3637
|
+
console.log();
|
|
3638
|
+
console.log(chalk.bold(" Referência MetricsQL"));
|
|
3639
|
+
console.log(chalk.dim(" Sintaxe de consulta para métricas VictoriaMetrics"));
|
|
3640
|
+
console.log();
|
|
3641
|
+
for (const section of QUERY_HELP_SECTIONS$1) {
|
|
3642
|
+
console.log(chalk.bold.underline(` ${section.title}`));
|
|
3643
|
+
console.log();
|
|
3644
|
+
console.log(section.content);
|
|
3645
|
+
console.log();
|
|
3646
|
+
}
|
|
3647
|
+
console.log(chalk.dim(" Uso: veloz metrics query '<consulta>' [--start 1h|6h|24h|7d] [--step 30s|1m|5m]"));
|
|
3648
|
+
console.log(chalk.dim(" Docs: https://docs.victoriametrics.com/metricsql/"));
|
|
3649
|
+
console.log();
|
|
3650
|
+
}
|
|
3651
|
+
return { sections: QUERY_HELP_SECTIONS$1 };
|
|
3652
|
+
}
|
|
3653
|
+
});
|
|
3128
3654
|
metricsGroup.command("help", {
|
|
3129
3655
|
description: "Referência de métricas disponíveis e seus campos",
|
|
3130
3656
|
output: z.object({ sections: z.array(z.object({
|
|
@@ -3135,18 +3661,18 @@ metricsGroup.command("help", {
|
|
|
3135
3661
|
if (process.stdout.isTTY) {
|
|
3136
3662
|
console.log();
|
|
3137
3663
|
console.log(chalk.bold(" Referência de Métricas"));
|
|
3138
|
-
console.log(chalk.dim("
|
|
3664
|
+
console.log(chalk.dim(" Dica: use 'veloz metrics query-help' para a referência completa de MetricsQL"));
|
|
3139
3665
|
console.log();
|
|
3140
|
-
for (const section of
|
|
3666
|
+
for (const section of QUERY_HELP_SECTIONS$1.slice(0, 4)) {
|
|
3141
3667
|
console.log(chalk.bold.underline(` ${section.title}`));
|
|
3142
3668
|
console.log();
|
|
3143
3669
|
console.log(section.content);
|
|
3144
3670
|
console.log();
|
|
3145
3671
|
}
|
|
3146
|
-
console.log(chalk.dim("
|
|
3672
|
+
console.log(chalk.dim(" Para referência completa de MetricsQL: veloz metrics query-help"));
|
|
3147
3673
|
console.log();
|
|
3148
3674
|
}
|
|
3149
|
-
return { sections:
|
|
3675
|
+
return { sections: QUERY_HELP_SECTIONS$1.slice(0, 4) };
|
|
3150
3676
|
}
|
|
3151
3677
|
});
|
|
3152
3678
|
|
|
@@ -3233,11 +3759,13 @@ logsGroup.command("show", {
|
|
|
3233
3759
|
spin.stop();
|
|
3234
3760
|
info("Streaming de logs ativo. Pressione Ctrl+C para sair.\n");
|
|
3235
3761
|
const lines$1 = await streamFollow(services, maxNameLen, tailLines);
|
|
3236
|
-
|
|
3762
|
+
const hint$1 = "\nDica: para consultas avançadas (filtros, regex, agregações), use `veloz logs search <consulta>`. Execute `veloz logs query-help` para ver a referência completa de sintaxe LogsQL.";
|
|
3763
|
+
return lines$1.length > 0 ? lines$1.join("\n") + hint$1 : "Nenhum log encontrado." + hint$1;
|
|
3237
3764
|
}
|
|
3238
3765
|
spin.stop();
|
|
3239
3766
|
const lines = await fetchRecent(services, maxNameLen, tailLines);
|
|
3240
|
-
|
|
3767
|
+
const hint = "\nDica: para consultas avançadas (filtros, regex, agregações), use `veloz logs search <consulta>`. Execute `veloz logs query-help` para ver a referência completa de sintaxe LogsQL.";
|
|
3768
|
+
return lines.length > 0 ? lines.join("\n") + hint : "Nenhum log encontrado." + hint;
|
|
3241
3769
|
}
|
|
3242
3770
|
});
|
|
3243
3771
|
logsGroup.command("search", {
|
|
@@ -3259,14 +3787,21 @@ logsGroup.command("search", {
|
|
|
3259
3787
|
})),
|
|
3260
3788
|
async run(c) {
|
|
3261
3789
|
const serviceId = await resolveServiceId(c.options.service);
|
|
3262
|
-
const
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3790
|
+
const client = await getClient();
|
|
3791
|
+
let entries;
|
|
3792
|
+
try {
|
|
3793
|
+
entries = await client.logs.search({
|
|
3794
|
+
serviceId,
|
|
3795
|
+
query: c.args.query,
|
|
3796
|
+
start: c.options.start,
|
|
3797
|
+
end: c.options.end,
|
|
3798
|
+
limit: c.options.limit,
|
|
3799
|
+
deploymentId: c.options.deployment
|
|
3800
|
+
});
|
|
3801
|
+
} catch (err) {
|
|
3802
|
+
warn(err instanceof Error ? err.message : "Erro ao pesquisar logs");
|
|
3803
|
+
return [];
|
|
3804
|
+
}
|
|
3270
3805
|
if (process.stdout.isTTY) if (entries.length === 0) info("Nenhum log encontrado para essa consulta.");
|
|
3271
3806
|
else {
|
|
3272
3807
|
console.log();
|
|
@@ -4748,7 +5283,7 @@ const LOGO_LINES = [
|
|
|
4748
5283
|
];
|
|
4749
5284
|
const BRAND_COLOR = "#FF4D00";
|
|
4750
5285
|
function getVersion() {
|
|
4751
|
-
return "0.0.0-beta.
|
|
5286
|
+
return "0.0.0-beta.26";
|
|
4752
5287
|
}
|
|
4753
5288
|
function printBanner(subtitle) {
|
|
4754
5289
|
const version = getVersion();
|
|
@@ -4975,7 +5510,14 @@ async function deployMultipleServices(services, options) {
|
|
|
4975
5510
|
urls: []
|
|
4976
5511
|
}));
|
|
4977
5512
|
output.printSummary(successful, failed);
|
|
4978
|
-
if (successful.length > 0)
|
|
5513
|
+
if (successful.length > 0) {
|
|
5514
|
+
let hasGithub = false;
|
|
5515
|
+
try {
|
|
5516
|
+
const projectId = services[0]?.projectId;
|
|
5517
|
+
if (projectId) hasGithub = !!(await client.projects.get({ projectId })).githubInstallationId;
|
|
5518
|
+
} catch {}
|
|
5519
|
+
output.printFollowUp({ hasGithub });
|
|
5520
|
+
}
|
|
4979
5521
|
const results = [];
|
|
4980
5522
|
for (const [serviceId, progress] of progressMap) results.push({
|
|
4981
5523
|
deploymentId: deploymentIdMap.get(serviceId) ?? "",
|
|
@@ -5088,7 +5630,7 @@ var TtyOutput = class {
|
|
|
5088
5630
|
}
|
|
5089
5631
|
}
|
|
5090
5632
|
}
|
|
5091
|
-
printFollowUp() {
|
|
5633
|
+
printFollowUp(_context) {
|
|
5092
5634
|
console.log(chalk.cyan("\nℹ Use 'veloz logs -f' para acompanhar os logs de execução."));
|
|
5093
5635
|
console.log(chalk.dim(" Use 'veloz builds list' para ver o histórico de builds."));
|
|
5094
5636
|
}
|
|
@@ -5144,7 +5686,7 @@ var PlainOutput = class {
|
|
|
5144
5686
|
}
|
|
5145
5687
|
}
|
|
5146
5688
|
}
|
|
5147
|
-
printFollowUp() {
|
|
5689
|
+
printFollowUp(_context) {
|
|
5148
5690
|
process.stdout.write("Use 'veloz logs -f' para acompanhar os logs de execução.\n");
|
|
5149
5691
|
process.stdout.write("Use 'veloz builds list' para ver o histórico de builds.\n");
|
|
5150
5692
|
}
|
|
@@ -5204,7 +5746,7 @@ var GhaOutput = class {
|
|
|
5204
5746
|
}
|
|
5205
5747
|
}
|
|
5206
5748
|
}
|
|
5207
|
-
printFollowUp() {
|
|
5749
|
+
printFollowUp(_context) {
|
|
5208
5750
|
process.stdout.write("Use 'veloz logs -f' para acompanhar os logs de execução.\n");
|
|
5209
5751
|
process.stdout.write("Use 'veloz builds list' para ver o histórico de builds.\n");
|
|
5210
5752
|
}
|
|
@@ -5259,9 +5801,10 @@ var McpOutput = class {
|
|
|
5259
5801
|
}
|
|
5260
5802
|
}
|
|
5261
5803
|
}
|
|
5262
|
-
printFollowUp() {
|
|
5804
|
+
printFollowUp(context) {
|
|
5263
5805
|
log("[deploy] Use 'veloz logs -f' para acompanhar os logs de execução.");
|
|
5264
5806
|
log("[deploy] Use 'veloz builds list' para ver o histórico de builds.");
|
|
5807
|
+
if (context?.hasGithub) log("[deploy] Dica: este projeto tem deploys automaticos via GitHub configurados. Pushes para o branch configurado fazem deploy automaticamente — não é necessário rodar 'veloz deploy' manualmente.");
|
|
5265
5808
|
}
|
|
5266
5809
|
};
|
|
5267
5810
|
|
|
@@ -5794,7 +6337,7 @@ async function autoUpdate() {
|
|
|
5794
6337
|
if (process.env.VELOZ_MCP === "true") return;
|
|
5795
6338
|
const pm = detectPackageManager();
|
|
5796
6339
|
if (!pm) return;
|
|
5797
|
-
const currentVersion = "0.0.0-beta.
|
|
6340
|
+
const currentVersion = "0.0.0-beta.26";
|
|
5798
6341
|
const latestVersion = await fetchLatestVersion();
|
|
5799
6342
|
if (!latestVersion || latestVersion === currentVersion) return;
|
|
5800
6343
|
const installCmd = getInstallCommand(pm, latestVersion);
|
|
@@ -5921,7 +6464,16 @@ async function provisionDatabases(config, opts) {
|
|
|
5921
6464
|
async function updateDatabaseResources(client, key, dbConfig, existing, serviceId, isLive) {
|
|
5922
6465
|
const desiredSize = dbConfig.size;
|
|
5923
6466
|
if (!desiredSize) return;
|
|
5924
|
-
|
|
6467
|
+
const currentSize = existing.size ?? resolveDatabaseSize(existing.cpuLimit, existing.memoryLimit, existing.engine);
|
|
6468
|
+
if (currentSize === desiredSize) return;
|
|
6469
|
+
if (currentSize) {
|
|
6470
|
+
const currentRank = getDatabaseSizeRank(currentSize, existing.engine);
|
|
6471
|
+
const desiredRank = getDatabaseSizeRank(desiredSize, existing.engine);
|
|
6472
|
+
if (desiredRank >= 0 && currentRank >= 0 && desiredRank < currentRank) {
|
|
6473
|
+
info(`Banco de dados "${key}" já está em ${DATABASE_SIZES[currentSize]?.label ?? currentSize} — tamanho não pode ser reduzido via deploy.`);
|
|
6474
|
+
return;
|
|
6475
|
+
}
|
|
6476
|
+
}
|
|
5925
6477
|
if (!isLive) {
|
|
5926
6478
|
warn(`Banco de dados "${key}" não está LIVE — não é possível alterar recursos agora.`);
|
|
5927
6479
|
return;
|
|
@@ -6111,13 +6663,13 @@ async function findServicesFromConfig() {
|
|
|
6111
6663
|
if (missingIds.length > 0 && config.project.id) {
|
|
6112
6664
|
const client = await getClient();
|
|
6113
6665
|
const remoteServicesByProject = /* @__PURE__ */ new Map();
|
|
6114
|
-
async
|
|
6666
|
+
const getRemoteServices = async (pid) => {
|
|
6115
6667
|
const cached = remoteServicesByProject.get(pid);
|
|
6116
6668
|
if (cached) return cached;
|
|
6117
6669
|
const services$1 = await withRetry(() => client.services.list({ projectId: pid }));
|
|
6118
6670
|
remoteServicesByProject.set(pid, services$1);
|
|
6119
6671
|
return services$1;
|
|
6120
|
-
}
|
|
6672
|
+
};
|
|
6121
6673
|
let configUpdated = false;
|
|
6122
6674
|
for (const [key, serviceConfig] of missingIds) {
|
|
6123
6675
|
const effectiveProjectId = serviceConfig.projectId ?? config.project.id;
|
|
@@ -7407,7 +7959,7 @@ async function pruneRemovedEntries(config, existingConfig, services, databases)
|
|
|
7407
7959
|
//#region src/index.ts
|
|
7408
7960
|
if (process.argv.includes("--mcp")) process.env.VELOZ_MCP = "true";
|
|
7409
7961
|
const cli = Cli.create("veloz", {
|
|
7410
|
-
version: "0.0.0-beta.
|
|
7962
|
+
version: "0.0.0-beta.26",
|
|
7411
7963
|
description: "CLI da plataforma Veloz — deploy rápido para o Brasil",
|
|
7412
7964
|
env: z.object({ VELOZ_ENV: z.string().optional().describe("Ambiente alvo (ex: preview, staging)") })
|
|
7413
7965
|
});
|