Fase 2: Modo watch + argumentos CLI + timestamps
- Argumentos: --watch/-w, --interval/-i, --help/-h - Modo watch: loop infinito con intervalo configurable (default 60s) - Timestamps UTC en cada check [HH:MM:SS] - Binario ahora se genera en raíz del proyecto (no zig-out/) - Mensaje de ayuda con ejemplos de uso 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
e2e19da32f
commit
3946f83920
2 changed files with 115 additions and 10 deletions
|
|
@ -11,7 +11,10 @@ pub fn build(b: *std.Build) void {
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
|
|
||||||
b.installArtifact(exe);
|
// Instalar en carpeta raíz del proyecto (no en zig-out/)
|
||||||
|
b.getInstallStep().dependOn(&b.addInstallArtifact(exe, .{
|
||||||
|
.dest_dir = .{ .override = .{ .custom = ".." } },
|
||||||
|
}).step);
|
||||||
|
|
||||||
// Comando: zig build run
|
// Comando: zig build run
|
||||||
const run_cmd = b.addRunArtifact(exe);
|
const run_cmd = b.addRunArtifact(exe);
|
||||||
|
|
|
||||||
120
src/main.zig
120
src/main.zig
|
|
@ -5,14 +5,26 @@
|
||||||
//! dependencias externas.
|
//! dependencias externas.
|
||||||
//!
|
//!
|
||||||
//! Uso:
|
//! Uso:
|
||||||
//! zig build run - Verificar todos los servicios una vez
|
//! zig build run - Verificar todos los servicios una vez
|
||||||
//! zig build run -- --help - Mostrar ayuda
|
//! zig build run -- --watch - Modo continuo (cada 60s por defecto)
|
||||||
|
//! zig build run -- --watch -i 30 - Modo continuo cada 30 segundos
|
||||||
|
//! zig build run -- --help - Mostrar ayuda
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const http = @import("http.zig");
|
const http = @import("http.zig");
|
||||||
const tcp = @import("tcp.zig");
|
const tcp = @import("tcp.zig");
|
||||||
const config = @import("config.zig");
|
const config = @import("config.zig");
|
||||||
|
|
||||||
|
/// Opciones de línea de comandos.
|
||||||
|
const Options = struct {
|
||||||
|
/// Modo watch: ejecutar continuamente.
|
||||||
|
watch: bool = false,
|
||||||
|
/// Intervalo entre checks en segundos (solo en modo watch).
|
||||||
|
interval_seconds: u32 = 60,
|
||||||
|
/// Mostrar ayuda y salir.
|
||||||
|
help: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
defer _ = gpa.deinit();
|
defer _ = gpa.deinit();
|
||||||
|
|
@ -20,10 +32,57 @@ pub fn main() !void {
|
||||||
|
|
||||||
const stdout = std.io.getStdOut().writer();
|
const stdout = std.io.getStdOut().writer();
|
||||||
|
|
||||||
try stdout.print("\n=== Service Monitor ===\n\n", .{});
|
// Parsear argumentos
|
||||||
|
const options = parseArgs() catch |err| {
|
||||||
|
try stdout.print("Error parseando argumentos: {}\n", .{err});
|
||||||
|
try printUsage(stdout);
|
||||||
|
std.process.exit(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (options.help) {
|
||||||
|
try printUsage(stdout);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.watch) {
|
||||||
|
// Modo watch: loop infinito
|
||||||
|
try stdout.print("\n=== Service Monitor (watch mode, interval: {d}s) ===\n", .{options.interval_seconds});
|
||||||
|
try stdout.print("Presiona Ctrl+C para salir\n\n", .{});
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const had_errors = try runChecks(allocator, stdout);
|
||||||
|
_ = had_errors; // En modo watch no salimos por errores
|
||||||
|
|
||||||
|
std.time.sleep(@as(u64, options.interval_seconds) * std.time.ns_per_s);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Modo único
|
||||||
|
try stdout.print("\n=== Service Monitor ===\n\n", .{});
|
||||||
|
const had_errors = try runChecks(allocator, stdout);
|
||||||
|
|
||||||
|
if (had_errors) {
|
||||||
|
std.process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ejecuta verificación de todos los servicios.
|
||||||
|
///
|
||||||
|
/// Retorna true si hubo algún error, false si todos OK.
|
||||||
|
fn runChecks(allocator: std.mem.Allocator, stdout: anytype) !bool {
|
||||||
const services = config.getServices();
|
const services = config.getServices();
|
||||||
var all_ok = true;
|
var had_errors = false;
|
||||||
|
|
||||||
|
// Timestamp
|
||||||
|
const timestamp = std.time.timestamp();
|
||||||
|
const epoch_seconds: u64 = @intCast(timestamp);
|
||||||
|
const epoch = std.time.epoch.EpochSeconds{ .secs = epoch_seconds };
|
||||||
|
const day_seconds = epoch.getDaySeconds();
|
||||||
|
const hours = day_seconds.getHoursIntoDay();
|
||||||
|
const minutes = day_seconds.getMinutesIntoHour();
|
||||||
|
const seconds = day_seconds.getSecondsIntoMinute();
|
||||||
|
|
||||||
|
try stdout.print("[{d:0>2}:{d:0>2}:{d:0>2}]\n", .{ hours, minutes, seconds });
|
||||||
|
|
||||||
for (services) |service| {
|
for (services) |service| {
|
||||||
const result = switch (service.check_type) {
|
const result = switch (service.check_type) {
|
||||||
|
|
@ -34,14 +93,57 @@ pub fn main() !void {
|
||||||
if (result) |time_ms| {
|
if (result) |time_ms| {
|
||||||
try stdout.print("\x1b[32m✓\x1b[0m {s} - OK ({d}ms)\n", .{ service.name, time_ms });
|
try stdout.print("\x1b[32m✓\x1b[0m {s} - OK ({d}ms)\n", .{ service.name, time_ms });
|
||||||
} else |err| {
|
} else |err| {
|
||||||
all_ok = false;
|
had_errors = true;
|
||||||
try stdout.print("\x1b[31m✗\x1b[0m {s} - ERROR: {}\n", .{ service.name, err });
|
try stdout.print("\x1b[31m✗\x1b[0m {s} - ERROR: {}\n", .{ service.name, err });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try stdout.print("\n", .{});
|
try stdout.print("\n", .{});
|
||||||
|
return had_errors;
|
||||||
if (!all_ok) {
|
}
|
||||||
std.process.exit(1);
|
|
||||||
}
|
/// Parsea los argumentos de línea de comandos.
|
||||||
|
fn parseArgs() !Options {
|
||||||
|
var options = Options{};
|
||||||
|
|
||||||
|
var args = std.process.args();
|
||||||
|
_ = args.skip(); // Saltar nombre del programa
|
||||||
|
|
||||||
|
while (args.next()) |arg| {
|
||||||
|
if (std.mem.eql(u8, arg, "--watch") or std.mem.eql(u8, arg, "-w")) {
|
||||||
|
options.watch = true;
|
||||||
|
} else if (std.mem.eql(u8, arg, "--interval") or std.mem.eql(u8, arg, "-i")) {
|
||||||
|
const interval_str = args.next() orelse return error.MissingIntervalValue;
|
||||||
|
options.interval_seconds = std.fmt.parseInt(u32, interval_str, 10) catch {
|
||||||
|
return error.InvalidIntervalValue;
|
||||||
|
};
|
||||||
|
} else if (std.mem.eql(u8, arg, "--help") or std.mem.eql(u8, arg, "-h")) {
|
||||||
|
options.help = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Imprime mensaje de ayuda.
|
||||||
|
fn printUsage(stdout: anytype) !void {
|
||||||
|
try stdout.print(
|
||||||
|
\\
|
||||||
|
\\Service Monitor - Verifica servicios HTTP y TCP
|
||||||
|
\\
|
||||||
|
\\USO:
|
||||||
|
\\ service-monitor [opciones]
|
||||||
|
\\
|
||||||
|
\\OPCIONES:
|
||||||
|
\\ --watch, -w Modo continuo (ejecuta checks en loop)
|
||||||
|
\\ --interval, -i <N> Intervalo en segundos entre checks (default: 60)
|
||||||
|
\\ --help, -h Muestra esta ayuda
|
||||||
|
\\
|
||||||
|
\\EJEMPLOS:
|
||||||
|
\\ service-monitor Verificar una vez
|
||||||
|
\\ service-monitor --watch Verificar cada 60 segundos
|
||||||
|
\\ service-monitor -w -i 30 Verificar cada 30 segundos
|
||||||
|
\\
|
||||||
|
\\
|
||||||
|
, .{});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue