mirror of
https://github.com/jetzig-framework/jetzig.git
synced 2025-05-14 22:16:08 +00:00
WIP
This commit is contained in:
parent
72a704b8cb
commit
fa5b3f240e
18
build.zig
18
build.zig
@ -128,9 +128,19 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn
|
|||||||
|
|
||||||
if (optimize != .Debug) exe.linkLibC();
|
if (optimize != .Debug) exe.linkLibC();
|
||||||
|
|
||||||
|
const Environment = enum { development, testing, production };
|
||||||
|
const environment = b.option(
|
||||||
|
Environment,
|
||||||
|
"environment",
|
||||||
|
"Jetzig server environment.",
|
||||||
|
) orelse .development;
|
||||||
|
|
||||||
const jetzig_dep = b.dependency(
|
const jetzig_dep = b.dependency(
|
||||||
"jetzig",
|
"jetzig",
|
||||||
.{ .optimize = optimize, .target = target },
|
.{
|
||||||
|
.optimize = optimize,
|
||||||
|
.target = target,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
const jetzig_module = jetzig_dep.module("jetzig");
|
const jetzig_module = jetzig_dep.module("jetzig");
|
||||||
const zmpl_module = jetzig_dep.module("zmpl");
|
const zmpl_module = jetzig_dep.module("zmpl");
|
||||||
@ -140,6 +150,12 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn
|
|||||||
const jetcommon_module = jetzig_dep.module("jetcommon");
|
const jetcommon_module = jetzig_dep.module("jetcommon");
|
||||||
const jetquery_migrate_module = jetzig_dep.module("jetquery_migrate");
|
const jetquery_migrate_module = jetzig_dep.module("jetquery_migrate");
|
||||||
|
|
||||||
|
{
|
||||||
|
const build_options = b.addOptions();
|
||||||
|
build_options.addOption(Environment, "environment", environment);
|
||||||
|
jetzig_module.addOptions("build_config", build_options);
|
||||||
|
}
|
||||||
|
|
||||||
exe.root_module.addImport("jetzig", jetzig_module);
|
exe.root_module.addImport("jetzig", jetzig_module);
|
||||||
exe.root_module.addImport("zmpl", zmpl_module);
|
exe.root_module.addImport("zmpl", zmpl_module);
|
||||||
exe.root_module.addImport("zmd", zmd_module);
|
exe.root_module.addImport("zmd", zmd_module);
|
||||||
|
@ -11,9 +11,11 @@ const database = @import("commands/database.zig");
|
|||||||
|
|
||||||
const Options = struct {
|
const Options = struct {
|
||||||
help: bool = false,
|
help: bool = false,
|
||||||
|
environment: enum { development, testing, production },
|
||||||
|
|
||||||
pub const shorthands = .{
|
pub const shorthands = .{
|
||||||
.h = "help",
|
.h = "help",
|
||||||
|
.e = "environment",
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const meta = .{
|
pub const meta = .{
|
||||||
|
@ -7,7 +7,7 @@ const Migrate = @import("jetquery_migrate");
|
|||||||
/// Command line options for the `database` command.
|
/// Command line options for the `database` command.
|
||||||
pub const Options = struct {
|
pub const Options = struct {
|
||||||
pub const meta = .{
|
pub const meta = .{
|
||||||
.usage_summary = "[migrate]",
|
.usage_summary = "[migrate|rollback|create|drop]",
|
||||||
.full_text =
|
.full_text =
|
||||||
\\Manage the application's database.
|
\\Manage the application's database.
|
||||||
\\
|
\\
|
||||||
|
@ -26,6 +26,8 @@ pub const DateTime = jetcommon.types.DateTime;
|
|||||||
pub const Time = jetcommon.types.Time;
|
pub const Time = jetcommon.types.Time;
|
||||||
pub const Date = jetcommon.types.Date;
|
pub const Date = jetcommon.types.Date;
|
||||||
|
|
||||||
|
pub const environment = @import("build_config").environment;
|
||||||
|
|
||||||
/// The primary interface for a Jetzig application. Create an `App` in your application's
|
/// The primary interface for a Jetzig application. Create an `App` in your application's
|
||||||
/// `src/main.zig` and call `start` to launch the application.
|
/// `src/main.zig` and call `start` to launch the application.
|
||||||
pub const App = @import("jetzig/App.zig");
|
pub const App = @import("jetzig/App.zig");
|
||||||
|
@ -36,10 +36,13 @@ pub fn start(self: *const App, routes_module: type, options: AppOptions) !void {
|
|||||||
try mime_map.build();
|
try mime_map.build();
|
||||||
|
|
||||||
const routes = try createRoutes(self.allocator, &routes_module.routes);
|
const routes = try createRoutes(self.allocator, &routes_module.routes);
|
||||||
defer for (routes) |var_route| {
|
defer {
|
||||||
var_route.deinitParams();
|
for (routes) |var_route| {
|
||||||
self.allocator.destroy(var_route);
|
var_route.deinitParams();
|
||||||
};
|
self.allocator.destroy(var_route);
|
||||||
|
}
|
||||||
|
self.allocator.free(routes);
|
||||||
|
}
|
||||||
|
|
||||||
defer for (self.custom_routes.items) |custom_route| {
|
defer for (self.custom_routes.items) |custom_route| {
|
||||||
self.allocator.free(custom_route.template);
|
self.allocator.free(custom_route.template);
|
||||||
@ -63,6 +66,9 @@ pub fn start(self: *const App, routes_module: type, options: AppOptions) !void {
|
|||||||
);
|
);
|
||||||
defer cache.deinit();
|
defer cache.deinit();
|
||||||
|
|
||||||
|
var repo = try jetzig.database.repo(self.allocator, self);
|
||||||
|
defer repo.deinit();
|
||||||
|
|
||||||
var log_thread = try std.Thread.spawn(
|
var log_thread = try std.Thread.spawn(
|
||||||
.{ .allocator = self.allocator },
|
.{ .allocator = self.allocator },
|
||||||
jetzig.loggers.LogQueue.Reader.publish,
|
jetzig.loggers.LogQueue.Reader.publish,
|
||||||
@ -70,9 +76,6 @@ pub fn start(self: *const App, routes_module: type, options: AppOptions) !void {
|
|||||||
);
|
);
|
||||||
defer log_thread.join();
|
defer log_thread.join();
|
||||||
|
|
||||||
var repo = try jetzig.database.repo(self.allocator, self);
|
|
||||||
defer repo.deinit();
|
|
||||||
|
|
||||||
if (self.env.detach) {
|
if (self.env.detach) {
|
||||||
const argv = try std.process.argsAlloc(self.allocator);
|
const argv = try std.process.argsAlloc(self.allocator);
|
||||||
defer std.process.argsFree(self.allocator, argv);
|
defer std.process.argsFree(self.allocator, argv);
|
||||||
|
@ -7,6 +7,8 @@ const jetzig = @import("../jetzig.zig");
|
|||||||
const Environment = @This();
|
const Environment = @This();
|
||||||
|
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
|
parent_allocator: std.mem.Allocator,
|
||||||
|
arena: *std.heap.ArenaAllocator,
|
||||||
logger: jetzig.loggers.Logger,
|
logger: jetzig.loggers.Logger,
|
||||||
bind: []const u8,
|
bind: []const u8,
|
||||||
port: u16,
|
port: u16,
|
||||||
@ -60,10 +62,10 @@ const Options = struct {
|
|||||||
help: bool = false,
|
help: bool = false,
|
||||||
bind: []const u8 = "127.0.0.1",
|
bind: []const u8 = "127.0.0.1",
|
||||||
port: u16 = 8080,
|
port: u16 = 8080,
|
||||||
environment: EnvironmentName = .development,
|
|
||||||
log: []const u8 = "-",
|
log: []const u8 = "-",
|
||||||
@"log-error": []const u8 = "-",
|
@"log-error": []const u8 = "-",
|
||||||
@"log-level": ?jetzig.loggers.LogLevel = null,
|
@"log-level": ?jetzig.loggers.LogLevel = null,
|
||||||
|
// TODO: Create a production logger and select default logger based on environment.
|
||||||
@"log-format": jetzig.loggers.LogFormat = .development,
|
@"log-format": jetzig.loggers.LogFormat = .development,
|
||||||
detach: bool = false,
|
detach: bool = false,
|
||||||
|
|
||||||
@ -71,7 +73,6 @@ const Options = struct {
|
|||||||
.h = "help",
|
.h = "help",
|
||||||
.b = "bind",
|
.b = "bind",
|
||||||
.p = "port",
|
.p = "port",
|
||||||
.e = "environment",
|
|
||||||
.d = "detach",
|
.d = "detach",
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -81,7 +82,6 @@ const Options = struct {
|
|||||||
.option_docs = .{
|
.option_docs = .{
|
||||||
.bind = "IP address/hostname to bind to (default: 127.0.0.1)",
|
.bind = "IP address/hostname to bind to (default: 127.0.0.1)",
|
||||||
.port = "Port to listen on (default: 8080)",
|
.port = "Port to listen on (default: 8080)",
|
||||||
.environment = "Set the server environment. Must be one of: { development, production } (default: development)",
|
|
||||||
.log = "Path to log file. Use '-' for stdout (default: '-')",
|
.log = "Path to log file. Use '-' for stdout (default: '-')",
|
||||||
.@"log-error" =
|
.@"log-error" =
|
||||||
\\Optional path to separate error log file. Use '-' for stderr. If omitted, errors are logged to the location specified by the `log` option (or stderr if `log` is '-').
|
\\Optional path to separate error log file. Use '-' for stderr. If omitted, errors are logged to the location specified by the `log` option (or stderr if `log` is '-').
|
||||||
@ -100,7 +100,30 @@ const Options = struct {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator) !Environment {
|
const LaunchLogger = struct {
|
||||||
|
stdout: std.fs.File,
|
||||||
|
stderr: std.fs.File,
|
||||||
|
|
||||||
|
pub fn log(
|
||||||
|
self: LaunchLogger,
|
||||||
|
comptime level: jetzig.loggers.LogLevel,
|
||||||
|
comptime message: []const u8,
|
||||||
|
log_args: anytype,
|
||||||
|
) !void {
|
||||||
|
const target = @field(self, @tagName(jetzig.loggers.logTarget(level)));
|
||||||
|
const writer = target.writer();
|
||||||
|
try writer.print(
|
||||||
|
std.fmt.comptimePrint("[startup:{s}] {s}\n", .{ @tagName(level), message }),
|
||||||
|
log_args,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn init(parent_allocator: std.mem.Allocator) !Environment {
|
||||||
|
const arena = try parent_allocator.create(std.heap.ArenaAllocator);
|
||||||
|
arena.* = std.heap.ArenaAllocator.init(parent_allocator);
|
||||||
|
const allocator = arena.allocator();
|
||||||
|
|
||||||
const options = try args.parseForCurrentProcess(Options, allocator, .print);
|
const options = try args.parseForCurrentProcess(Options, allocator, .print);
|
||||||
defer options.deinit();
|
defer options.deinit();
|
||||||
|
|
||||||
@ -117,10 +140,15 @@ pub fn init(allocator: std.mem.Allocator) !Environment {
|
|||||||
std.process.exit(0);
|
std.process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
const environment = options.options.environment;
|
const environment = std.enums.nameCast(EnvironmentName, jetzig.environment);
|
||||||
const vars = Vars{ .env_map = try std.process.getEnvMap(allocator) };
|
const vars = Vars{ .env_map = try std.process.getEnvMap(allocator) };
|
||||||
|
|
||||||
var logger = switch (options.options.@"log-format") {
|
var launch_logger = LaunchLogger{
|
||||||
|
.stdout = try getLogFile(.stdout, options.options),
|
||||||
|
.stderr = try getLogFile(.stdout, options.options),
|
||||||
|
};
|
||||||
|
|
||||||
|
const logger = switch (options.options.@"log-format") {
|
||||||
.development => jetzig.loggers.Logger{
|
.development => jetzig.loggers.Logger{
|
||||||
.development_logger = jetzig.loggers.DevelopmentLogger.init(
|
.development_logger = jetzig.loggers.DevelopmentLogger.init(
|
||||||
allocator,
|
allocator,
|
||||||
@ -138,30 +166,51 @@ pub fn init(allocator: std.mem.Allocator) !Environment {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (options.options.detach and std.mem.eql(u8, options.options.log, "-")) {
|
if (options.options.detach and std.mem.eql(u8, options.options.log, "-")) {
|
||||||
try logger.ERROR("Must pass `--log` when using `--detach`.", .{});
|
try launch_logger.log(.ERROR, "Must pass `--log` when using `--detach`.", .{});
|
||||||
std.process.exit(1);
|
std.process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const secret_len = jetzig.http.Session.Cipher.key_length;
|
const secret_len = jetzig.http.Session.Cipher.key_length;
|
||||||
const secret = (try getSecret(allocator, &logger, secret_len, environment))[0..secret_len];
|
const secret_value = try getSecret(allocator, launch_logger, secret_len, environment);
|
||||||
|
const secret = if (secret_value.len > secret_len) secret_value[0..secret_len] else secret_value;
|
||||||
|
|
||||||
if (secret.len != secret_len) {
|
if (secret.len != secret_len) {
|
||||||
try logger.ERROR("Expected secret length: {}, found: {}.", .{ secret_len, secret.len });
|
try launch_logger.log(
|
||||||
try logger.ERROR("Use `jetzig generate secret` to create a secure secret value.", .{});
|
.ERROR,
|
||||||
|
"Expected secret length: {}, found: {}.",
|
||||||
|
.{ secret_len, secret.len },
|
||||||
|
);
|
||||||
|
try launch_logger.log(
|
||||||
|
.ERROR,
|
||||||
|
"Use `jetzig generate secret` to create a secure secret value.",
|
||||||
|
.{},
|
||||||
|
);
|
||||||
std.process.exit(1);
|
std.process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (jetzig.database.adapter == .null) {
|
if (jetzig.database.adapter == .null) {
|
||||||
try logger.WARN("No database configured in `config/database.zig`. Database operations are not available.", .{});
|
try launch_logger.log(
|
||||||
|
.WARN,
|
||||||
|
"No database configured in `config/database.zig`. Database operations are not available.",
|
||||||
|
.{},
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
try logger.INFO(
|
try launch_logger.log(
|
||||||
|
.INFO,
|
||||||
"Using `{s}` database adapter with database: `{s}`.",
|
"Using `{s}` database adapter with database: `{s}`.",
|
||||||
.{ @tagName(jetzig.database.adapter), jetzig.jetquery.config.database.database },
|
.{
|
||||||
|
@tagName(jetzig.database.adapter),
|
||||||
|
switch (environment) {
|
||||||
|
inline else => |tag| @field(jetzig.jetquery.config.database, @tagName(tag)).database,
|
||||||
|
},
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
|
.parent_allocator = parent_allocator,
|
||||||
|
.arena = arena,
|
||||||
.logger = logger,
|
.logger = logger,
|
||||||
.secret = secret,
|
.secret = secret,
|
||||||
.bind = try allocator.dupe(u8, options.options.bind),
|
.bind = try allocator.dupe(u8, options.options.bind),
|
||||||
@ -174,9 +223,8 @@ pub fn init(allocator: std.mem.Allocator) !Environment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: Environment) void {
|
pub fn deinit(self: Environment) void {
|
||||||
self.vars.deinit();
|
self.arena.deinit();
|
||||||
self.allocator.free(self.bind);
|
self.parent_allocator.destroy(self.arena);
|
||||||
self.allocator.free(self.secret);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getLogFile(stream: enum { stdout, stderr }, options: Options) !std.fs.File {
|
fn getLogFile(stream: enum { stdout, stderr }, options: Options) !std.fs.File {
|
||||||
@ -200,7 +248,7 @@ fn getLogFile(stream: enum { stdout, stderr }, options: Options) !std.fs.File {
|
|||||||
|
|
||||||
fn getSecret(
|
fn getSecret(
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
logger: *jetzig.loggers.Logger,
|
logger: LaunchLogger,
|
||||||
comptime len: u10,
|
comptime len: u10,
|
||||||
environment: EnvironmentName,
|
environment: EnvironmentName,
|
||||||
) ![]const u8 {
|
) ![]const u8 {
|
||||||
@ -209,18 +257,28 @@ fn getSecret(
|
|||||||
return std.process.getEnvVarOwned(allocator, env_var) catch |err| {
|
return std.process.getEnvVarOwned(allocator, env_var) catch |err| {
|
||||||
switch (err) {
|
switch (err) {
|
||||||
error.EnvironmentVariableNotFound => {
|
error.EnvironmentVariableNotFound => {
|
||||||
if (environment != .development) {
|
if (environment == .production) {
|
||||||
try logger.ERROR("Environment variable `{s}` must be defined in production mode.", .{env_var});
|
try logger.log(
|
||||||
try logger.ERROR("Run `jetzig generate secret` to generate an appropriate value.", .{});
|
.ERROR,
|
||||||
|
"Environment variable `{s}` must be defined in production mode.",
|
||||||
|
.{env_var},
|
||||||
|
);
|
||||||
|
try logger.log(
|
||||||
|
.ERROR,
|
||||||
|
"Run `jetzig generate secret` to generate an appropriate value.",
|
||||||
|
.{},
|
||||||
|
);
|
||||||
std.process.exit(1);
|
std.process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const secret = try jetzig.util.generateSecret(allocator, len);
|
const secret = try jetzig.util.generateSecret(allocator, len);
|
||||||
try logger.WARN(
|
try logger.log(
|
||||||
"Running in development mode, using auto-generated cookie encryption key: {s}",
|
.WARN,
|
||||||
.{secret},
|
"Running in {s} mode, using auto-generated cookie encryption key: {s}",
|
||||||
|
.{ @tagName(environment), secret },
|
||||||
);
|
);
|
||||||
try logger.WARN(
|
try logger.log(
|
||||||
|
.WARN,
|
||||||
"Run `jetzig generate secret` and set `JETZIG_SECRET` to remove this warning.",
|
"Run `jetzig generate secret` and set `JETZIG_SECRET` to remove this warning.",
|
||||||
.{},
|
.{},
|
||||||
);
|
);
|
||||||
@ -236,6 +294,6 @@ fn resolveLogLevel(level: ?jetzig.loggers.LogLevel, environment: EnvironmentName
|
|||||||
return level orelse switch (environment) {
|
return level orelse switch (environment) {
|
||||||
.testing => .DEBUG,
|
.testing => .DEBUG,
|
||||||
.development => .DEBUG,
|
.development => .DEBUG,
|
||||||
.production => .INFO,
|
.production => .DEBUG,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,9 @@ const std = @import("std");
|
|||||||
|
|
||||||
const jetzig = @import("../jetzig.zig");
|
const jetzig = @import("../jetzig.zig");
|
||||||
|
|
||||||
|
pub const adapter = @field(jetzig.jetquery.config.database, @tagName(jetzig.environment)).adapter;
|
||||||
pub const Schema = jetzig.config.get(type, "Schema");
|
pub const Schema = jetzig.config.get(type, "Schema");
|
||||||
pub const adapter = jetzig.jetquery.config.database.adapter;
|
pub const Repo = jetzig.jetquery.Repo(adapter, Schema);
|
||||||
pub const Repo = jetzig.jetquery.Repo(jetzig.jetquery.config.database.adapter, Schema);
|
|
||||||
|
|
||||||
pub fn Query(comptime model: anytype) type {
|
pub fn Query(comptime model: anytype) type {
|
||||||
return jetzig.jetquery.Query(adapter, Schema, model);
|
return jetzig.jetquery.Query(adapter, Schema, model);
|
||||||
@ -22,12 +22,16 @@ pub fn repo(allocator: std.mem.Allocator, app: *const jetzig.App) !Repo {
|
|||||||
|
|
||||||
return try Repo.loadConfig(
|
return try Repo.loadConfig(
|
||||||
allocator,
|
allocator,
|
||||||
.{ .eventCallback = Callback.callbackFn, .lazy_connect = true },
|
std.enums.nameCast(jetzig.jetquery.Environment, jetzig.environment),
|
||||||
|
.{
|
||||||
|
.eventCallback = Callback.callbackFn,
|
||||||
|
.lazy_connect = jetzig.environment == .development,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eventCallback(event: jetzig.jetquery.events.Event, app: *const jetzig.App) !void {
|
fn eventCallback(event: jetzig.jetquery.events.Event, app: *const jetzig.App) !void {
|
||||||
try app.server.logger.INFO("[database] {?s}", .{event.sql});
|
try app.server.logger.logSql(event);
|
||||||
if (event.err) |err| {
|
if (event.err) |err| {
|
||||||
try app.server.logger.ERROR("[database] {?s}", .{err.message});
|
try app.server.logger.ERROR("[database] {?s}", .{err.message});
|
||||||
}
|
}
|
||||||
|
@ -72,6 +72,12 @@ pub const Logger = union(enum) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn logSql(self: *const Logger, request: jetzig.jetquery.events.Event) !void {
|
||||||
|
switch (self.*) {
|
||||||
|
inline else => |*logger| try logger.logSql(request),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn logError(self: *const Logger, err: anyerror) !void {
|
pub fn logError(self: *const Logger, err: anyerror) !void {
|
||||||
switch (self.*) {
|
switch (self.*) {
|
||||||
inline else => |*logger| try logger.logError(err),
|
inline else => |*logger| try logger.logError(err),
|
||||||
|
@ -104,6 +104,127 @@ pub fn logRequest(self: DevelopmentLogger, request: *const jetzig.http.Request)
|
|||||||
}, .stdout);
|
}, .stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn logSql(self: *const DevelopmentLogger, event: jetzig.jetquery.events.Event) !void {
|
||||||
|
// XXX: This function does not make any effort to prevent log messages clobbering each other
|
||||||
|
// from multiple threads. JSON logger etc. write in one call and the logger's mutex prevents
|
||||||
|
// clobbering, but this is not the case here.
|
||||||
|
const formatted_level = if (self.stdout_colorized) colorizedLogLevel(.INFO) else @tagName(.INFO);
|
||||||
|
try self.log_queue.print(
|
||||||
|
"{s} [database] ",
|
||||||
|
.{formatted_level},
|
||||||
|
.stdout,
|
||||||
|
);
|
||||||
|
try self.printSql(event.sql orelse "");
|
||||||
|
|
||||||
|
var duration_buf: [256]u8 = undefined;
|
||||||
|
const formatted_duration = if (event.duration) |duration| try jetzig.colors.duration(
|
||||||
|
&duration_buf,
|
||||||
|
duration,
|
||||||
|
self.stdout_colorized,
|
||||||
|
) else "";
|
||||||
|
|
||||||
|
try self.log_queue.print(
|
||||||
|
std.fmt.comptimePrint(" [{s}]\n", .{jetzig.colors.cyan("{s}")}),
|
||||||
|
.{formatted_duration},
|
||||||
|
.stdout,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sql_tokens = .{
|
||||||
|
"SELECT",
|
||||||
|
"INSERT",
|
||||||
|
"UPDATE",
|
||||||
|
"DELETE",
|
||||||
|
"WHERE",
|
||||||
|
"ANY",
|
||||||
|
"FROM",
|
||||||
|
"INTO",
|
||||||
|
"IN",
|
||||||
|
"IS",
|
||||||
|
"NOT",
|
||||||
|
"NULL",
|
||||||
|
"LIMIT",
|
||||||
|
"ORDER BY",
|
||||||
|
"GROUP BY",
|
||||||
|
"HAVING",
|
||||||
|
"LEFT OUTER JOIN",
|
||||||
|
"INNER JOIN",
|
||||||
|
"ASC",
|
||||||
|
"DESC",
|
||||||
|
"MAX",
|
||||||
|
"MIN",
|
||||||
|
"COUNT",
|
||||||
|
"SUM",
|
||||||
|
};
|
||||||
|
|
||||||
|
fn printSql(self: *const DevelopmentLogger, sql: []const u8) !void {
|
||||||
|
const string_color = jetzig.colors.codes.escape ++ jetzig.colors.codes.green;
|
||||||
|
const identifier_color = jetzig.colors.codes.escape ++ jetzig.colors.codes.yellow;
|
||||||
|
const reset_color = jetzig.colors.codes.escape ++ jetzig.colors.codes.reset;
|
||||||
|
|
||||||
|
var index: usize = 0;
|
||||||
|
var single_quote: bool = false;
|
||||||
|
var double_quote: bool = false;
|
||||||
|
while (index < sql.len) {
|
||||||
|
// TODO: Escapes
|
||||||
|
switch (sql[index]) {
|
||||||
|
'"' => {
|
||||||
|
if (!single_quote) {
|
||||||
|
double_quote = !double_quote;
|
||||||
|
if (double_quote) {
|
||||||
|
try self.log_queue.print(identifier_color ++ "\"", .{}, .stdout);
|
||||||
|
} else {
|
||||||
|
try self.log_queue.print("\"" ++ reset_color, .{}, .stdout);
|
||||||
|
}
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'\'' => {
|
||||||
|
if (!double_quote) {
|
||||||
|
single_quote = !single_quote;
|
||||||
|
if (single_quote) {
|
||||||
|
try self.log_queue.print(string_color ++ "'", .{}, .stdout);
|
||||||
|
} else {
|
||||||
|
try self.log_queue.print("'" ++ reset_color, .{}, .stdout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index += 1;
|
||||||
|
},
|
||||||
|
'$' => {
|
||||||
|
if (double_quote or single_quote) {
|
||||||
|
try self.log_queue.print("{c}", .{sql[index]}, .stdout);
|
||||||
|
index += 1;
|
||||||
|
} else {
|
||||||
|
const param = sql[index..][0 .. std.mem.indexOfAny(
|
||||||
|
u8,
|
||||||
|
sql[index..],
|
||||||
|
&std.ascii.whitespace,
|
||||||
|
) orelse sql.len - index];
|
||||||
|
try self.log_queue.print(jetzig.colors.magenta("{s}"), .{param}, .stdout);
|
||||||
|
index += param.len;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => {
|
||||||
|
if (double_quote or single_quote) {
|
||||||
|
try self.log_queue.print("{c}", .{sql[index]}, .stdout);
|
||||||
|
index += 1;
|
||||||
|
} else {
|
||||||
|
inline for (sql_tokens) |token| {
|
||||||
|
if (std.mem.startsWith(u8, sql[index..], token)) {
|
||||||
|
try self.log_queue.print(jetzig.colors.cyan(token), .{}, .stdout);
|
||||||
|
index += token.len;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try self.log_queue.print("{c}", .{sql[index]}, .stdout);
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn logError(self: *const DevelopmentLogger, err: anyerror) !void {
|
pub fn logError(self: *const DevelopmentLogger, err: anyerror) !void {
|
||||||
if (@errorReturnTrace()) |stack| {
|
if (@errorReturnTrace()) |stack| {
|
||||||
try self.log(.ERROR, "\nStack Trace:\n{}", .{stack});
|
try self.log(.ERROR, "\nStack Trace:\n{}", .{stack});
|
||||||
|
@ -101,6 +101,17 @@ pub fn logRequest(self: *const JsonLogger, request: *const jetzig.http.Request)
|
|||||||
try self.log_queue.print("{s}\n", .{stream.getWritten()}, .stdout);
|
try self.log_queue.print("{s}\n", .{stream.getWritten()}, .stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn logSql(self: *const JsonLogger, event: jetzig.jetquery.events.Event) !void {
|
||||||
|
var buf: [4096]u8 = undefined;
|
||||||
|
var stream = std.io.fixedBufferStream(&buf);
|
||||||
|
try std.json.stringify(
|
||||||
|
.{ .sql = event.sql, .duration = event.duration },
|
||||||
|
.{ .whitespace = .minified },
|
||||||
|
stream.writer(),
|
||||||
|
);
|
||||||
|
try self.log_queue.print("{s}\n", .{stream.getWritten()}, .stdout);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn logError(self: *const JsonLogger, err: anyerror) !void {
|
pub fn logError(self: *const JsonLogger, err: anyerror) !void {
|
||||||
try self.log(.ERROR, "Encountered error: {s}", .{@errorName(err)});
|
try self.log(.ERROR, "Encountered error: {s}", .{@errorName(err)});
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,10 @@ pub fn logRequest(self: TestLogger, request: *const jetzig.http.Request) !void {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn logSql(self: TestLogger, event: jetzig.jetquery.events.Event) !void {
|
||||||
|
try self.log(.INFO, "[database] {?s}", .{event.sql});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn logError(self: TestLogger, err: anyerror) !void {
|
pub fn logError(self: TestLogger, err: anyerror) !void {
|
||||||
try self.log(.ERROR, "Encountered error: {s}", .{@errorName(err)});
|
try self.log(.ERROR, "Encountered error: {s}", .{@errorName(err)});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user