diff --git a/src/main.zig b/src/main.zig index cd5b75d..73ed1d6 100644 --- a/src/main.zig +++ b/src/main.zig @@ -11,7 +11,6 @@ //==== Imports ====// const std = @import("std"); -const logger = @import("util/trace/log.zig"); const Config = @import("config/config.zig").Config; const Mempool = @import("core/mempool.zig").Mempool; @@ -35,13 +34,6 @@ pub fn main() !void { const allocator = gpa_state.allocator(); defer _ = gpa_state.deinit(); - // Set up logger - var our_logger = logger.Logger.init(allocator, .debug); - defer our_logger.deinit(); - our_logger.spawn(); - - logger.default_logger.* = our_logger; - // Parse command-line arguments const args = try std.process.argsAlloc(allocator); defer std.process.argsFree(allocator, args); @@ -171,12 +163,12 @@ fn runNodeCommand(program: *Program) !void { defer mempool.deinit(); var storage = try Storage.init(&config); defer storage.deinit(); - var p2p = try P2P.init(program.allocator, &config, logger.default_logger.*); + var p2p = try P2P.init(program.allocator, &config); defer p2p.deinit(); - var rpc = try RPC.init(program.allocator, &config, &mempool, &storage, logger.default_logger.*); + var rpc = try RPC.init(program.allocator, &config, &mempool, &storage); defer rpc.deinit(); - var node = try Node.init(program.allocator, logger.default_logger.*, &mempool, &storage, &p2p, &rpc); + var node = try Node.init(program.allocator, &mempool, &storage, &p2p, &rpc); defer node.deinit(); // Start the node diff --git a/src/network/p2p.zig b/src/network/p2p.zig index ffae495..54d7a0c 100644 --- a/src/network/p2p.zig +++ b/src/network/p2p.zig @@ -6,7 +6,6 @@ const net = std.net; const posix = std.posix; const Config = @import("../config/config.zig").Config; const Peer = @import("peer.zig").Peer; -const Logger = @import("../util/trace/log.zig").Logger; const wire = @import("wire/lib.zig"); const protocol = @import("protocol/lib.zig"); const VersionMessage = protocol.messages.VersionMessage; @@ -22,7 +21,6 @@ pub const P2P = struct { peers: std.ArrayList(*Peer), /// Listener. listener: ?net.Server, - logger: Logger, /// Initialize the P2P network handler. /// # Arguments @@ -30,13 +28,12 @@ pub const P2P = struct { /// - `config`: Configuration. /// # Returns /// - `P2P`: P2P network handler. - pub fn init(allocator: std.mem.Allocator, config: *const Config, logger: Logger) !P2P { + pub fn init(allocator: std.mem.Allocator, config: *const Config) !P2P { return P2P{ .allocator = allocator, .config = config, .peers = std.ArrayList(*Peer).init(allocator), .listener = null, - .logger = logger, }; } @@ -51,7 +48,7 @@ pub const P2P = struct { /// Start the P2P network handler. pub fn start(self: *P2P) !void { - self.logger.infof("Starting P2P network on port {}", .{self.config.p2p_port}); + std.log.info("Starting P2P network on port {}", .{self.config.p2p_port}); for (self.config.dnsSeeds()) |seed| { const address_list = try std.net.getAddressList(self.allocator, seed.inner, 8333); @@ -62,62 +59,4 @@ pub const P2P = struct { } } } - /// Accept incoming connections. - /// The P2P network handler will accept incoming connections and handle them in a separate thread. - fn acceptConnections(self: *P2P) !void { - while (true) { - const connection = self.listener.?.accept() catch |err| { - self.logger.errf("Failed to accept connection: {}", .{err}); - continue; - }; - - // Handle the new connection in a separate thread - // TODO: Error handling - _ = try std.Thread.spawn(.{}, handleConnection, .{ self, connection }); - } - } - - /// Handle a new connection. - /// # Arguments - /// - `self`: P2P network handler. - /// - `connection`: Connection. - fn handleConnection(self: *P2P, connection: net.Server.Connection) void { - const peer = Peer.init(self.allocator, connection) catch |err| { - self.logger.errf("Failed to initialize peer: {}", .{err}); - connection.stream.close(); - return; - }; - - // Add the peer to the list of peers - self.peers.append(peer) catch |err| { - self.logger.errf("Failed to add peer: {}", .{err}); - peer.deinit(); - return; - }; - - // Start the peer in a new thread - peer.start() catch |err| { - self.logger.errf("Peer encountered an error: {}", .{err}); - _ = self.peers.swapRemove(self.peers.items.len - 1); - peer.deinit(); - }; - } - - /// Connect to seed nodes. - fn connectToSeedNodes(self: *P2P) !void { - // If no seed nodes are configured, do nothing - if (self.config.seednode.len == 0) { - return; - } - - const address = try net.Address.parseIp4(self.config.seednode, 8333); - const stream = try net.tcpConnectToAddress(address); - - const peer = try Peer.init(self.allocator, .{ .stream = stream, .address = address }); - try self.peers.append(peer); - - // Start the peer in a new thread - // TODO: Error handling - _ = try std.Thread.spawn(.{}, Peer.start, .{peer}); - } }; diff --git a/src/network/rpc.zig b/src/network/rpc.zig index 9104701..56c24de 100644 --- a/src/network/rpc.zig +++ b/src/network/rpc.zig @@ -6,7 +6,6 @@ const Config = @import("../config/config.zig").Config; const Mempool = @import("../core/mempool.zig").Mempool; const Storage = @import("../storage/storage.zig").Storage; const httpz = @import("httpz"); -const Logger = @import("../util/trace/log.zig").Logger; /// RPC Server handler. /// @@ -21,7 +20,6 @@ pub const RPC = struct { mempool: *Mempool, /// Blockchain storage. storage: *Storage, - logger: Logger, /// Initialize the RPC server. /// # Arguments /// - `allocator`: Allocator. @@ -35,14 +33,12 @@ pub const RPC = struct { config: *const Config, mempool: *Mempool, storage: *Storage, - logger: Logger, ) !RPC { const rpc = RPC{ .allocator = allocator, .config = config, .mempool = mempool, .storage = storage, - .logger = logger, }; return rpc; @@ -57,7 +53,7 @@ pub const RPC = struct { /// Start the RPC server. /// The RPC server will start a HTTP server and listen on the RPC port. pub fn start(self: *RPC) !void { - self.logger.infof("Starting RPC server on port {}", .{self.config.rpc_port}); + std.log.info("Starting RPC server on port {}", .{self.config.rpc_port}); var handler = Handler{}; var server = try httpz.Server(*Handler).init(self.allocator, .{ .port = self.config.rpc_port }, &handler); @@ -66,7 +62,7 @@ pub const RPC = struct { router.get("/", index, .{}); router.get("/error", @"error", .{}); - self.logger.infof("RPC server listening on http://localhost:{d}/\n", .{self.config.rpc_port}); + std.log.info("RPC server listening on http://localhost:{d}/\n", .{self.config.rpc_port}); // Starts the server, this is blocking. // TODO: Make it non-blocking. cc @StringNick diff --git a/src/node/ibd.zig b/src/node/ibd.zig index c622289..f030fc0 100644 --- a/src/node/ibd.zig +++ b/src/node/ibd.zig @@ -2,61 +2,15 @@ const std = @import("std"); const P2P = @import("../network/p2p.zig").P2P; const Block = @import("../types/block.zig").Block; const Transaction = @import("../types/transaction.zig").Transaction; -const Logger = @import("../util/trace/log.zig").Logger; pub const IBD = struct { p2p: *P2P, - logger: Logger, - pub fn init(p2p: *P2P, logger: Logger) IBD { + pub fn init(p2p: *P2P) IBD { return .{ .p2p = p2p, - .logger = logger, }; } - pub fn start(self: *IBD) !void { - self.logger.info("Starting Initial Block Download..."); - - try self.connectToPeers(); - try self.downloadBlocks(); - try self.validateBlocks(); - - // Simulate catching up to the tip after 10 seconds - std.time.sleep(std.time.ns_per_s * 10); - self.logger.info("Caught up to the tip of the chain!"); - } - - fn connectToPeers(self: *IBD) !void { - self.logger.info("Connecting to initial set of peers..."); - // Simulate connecting to peers - std.time.sleep(std.time.ns_per_s * 2); - self.logger.info("Connected to 8 peers."); - } - - fn downloadBlocks(self: *IBD) !void { - self.logger.info("Downloading blocks..."); - // Simulate block download - } - - fn simulateBlockDownload(self: *IBD) !Block { - _ = self; - // Simulate network delay - std.time.sleep(std.time.ns_per_ms * 10); - return Block{ .height = 0, .hash = [_]u8{0} ** 32 }; - } - - fn processBlock(self: *IBD, block: Block) !void { - _ = self; - // Simulate block processing - std.time.sleep(std.time.ns_per_ms * 5); - _ = block; - } - - fn validateBlocks(self: *IBD) !void { - self.logger.info("Validating downloaded blocks..."); - // Simulate block validation - std.time.sleep(std.time.ns_per_s * 3); - self.logger.info("All blocks validated successfully."); - } + pub fn start(_: *IBD) !void {} }; diff --git a/src/node/node.zig b/src/node/node.zig index d5b05e9..c86e986 100644 --- a/src/node/node.zig +++ b/src/node/node.zig @@ -6,7 +6,6 @@ const Mempool = @import("../core/mempool.zig").Mempool; const Storage = @import("../storage/storage.zig").Storage; const P2P = @import("../network/p2p.zig").P2P; const RPC = @import("../network/rpc.zig").RPC; -const Logger = @import("../util/trace/log.zig").Logger; const IBD = @import("ibd.zig").IBD; const Block = @import("../types/block.zig").Block; const Transaction = @import("../types/transaction.zig").Transaction; @@ -28,8 +27,6 @@ pub const Node = struct { started: std.Thread.Condition, /// Mutex to synchronize access to the node. mutex: std.Thread.Mutex, - /// Logger. - logger: Logger, /// IBD handler. ibd: IBD, @@ -40,10 +37,9 @@ pub const Node = struct { /// - `storage`: Blockchain storage. /// - `p2p`: P2P network handler. /// - `rpc`: RPC server. - pub fn init(allocator: std.mem.Allocator, logger: Logger, mempool: *Mempool, storage: *Storage, p2p: *P2P, rpc: *RPC) !Node { + pub fn init(allocator: std.mem.Allocator, mempool: *Mempool, storage: *Storage, p2p: *P2P, rpc: *RPC) !Node { return Node{ .allocator = allocator, - .logger = logger, .mempool = mempool, .storage = storage, .p2p = p2p, @@ -51,7 +47,7 @@ pub const Node = struct { .stopped = false, .started = std.Thread.Condition{}, .mutex = std.Thread.Mutex{}, - .ibd = IBD.init(p2p, logger), + .ibd = IBD.init(p2p), }; } @@ -69,7 +65,7 @@ pub const Node = struct { /// - `p2p`: P2P network handler. /// - `rpc`: RPC server. pub fn start(self: *Node) !void { - self.logger.info("Starting btczee node..."); + std.log.info("Starting btczee node...", .{}); self.mutex.lock(); defer self.mutex.unlock(); @@ -86,81 +82,8 @@ pub const Node = struct { // Main event loop while (!self.stopped) { - self.mutex.unlock(); - self.logger.debug("Processing new blocks and transactions..."); - try self.processNewBlocksAndTransactions(); std.time.sleep(5 * std.time.ns_per_s); - self.mutex.lock(); } - self.logger.info("Node stopped"); - } - - fn processNewBlocksAndTransactions(self: *Node) !void { - // Simulate processing of new blocks and transactions - const new_block = try self.simulateNewBlock(); - try self.validateBlock(new_block); - try self.addBlockToChain(new_block); - - var new_tx = try self.simulateNewTransaction(); - defer { - new_tx.deinit(); - self.allocator.destroy(new_tx); - } - try self.validateTransaction(new_tx); - try self.addTransactionToMempool(new_tx); - } - - fn simulateNewBlock(self: *Node) !Block { - _ = self; - return Block{ .height = 0, .hash = [_]u8{0} ** 32 }; - } - - fn validateBlock(self: *Node, _: Block) !void { - self.logger.debug("Validating block..."); - // Implement block validation logic here - } - - fn addBlockToChain(self: *Node, _: Block) !void { - self.logger.debug("Adding block to chain."); - // Implement logic to add block to the chain - } - - fn simulateNewTransaction(self: *Node) !*Transaction { - var tx = try self.allocator.create(Transaction); - errdefer self.allocator.destroy(tx); - - tx.* = try Transaction.init(self.allocator); - errdefer tx.deinit(); - - return tx; - } - - fn validateTransaction(self: *Node, tx: *Transaction) !void { - self.logger.debug("Validating transaction"); - // Implement transaction validation logic here - _ = tx; - } - - fn addTransactionToMempool(self: *Node, tx: *Transaction) !void { - self.logger.debug("Adding transaction to mempool"); - _ = try self.mempool.addTransaction(tx, 42, 1000); - } - - /// Stop the node. - pub fn stop(self: *Node) void { - self.mutex.lock(); - defer self.mutex.unlock(); - - self.logger.info("Stopping node..."); - self.stopped = true; - self.logger.info("Node stop signal sent"); - } - - /// Wait for the node to start. - pub fn waitForStart(self: *Node) void { - self.mutex.lock(); - defer self.mutex.unlock(); - - self.started.wait(&self.mutex); + std.log.info("Node stopped", .{}); } }; diff --git a/src/util/net/echo.zig b/src/util/net/echo.zig index 110d44b..c12c2b7 100644 --- a/src/util/net/echo.zig +++ b/src/util/net/echo.zig @@ -3,7 +3,6 @@ const builtin = @import("builtin"); const net = @import("net.zig"); const ShredVersion = @import("../core/shred.zig").ShredVersion; const SocketAddr = @import("net.zig").SocketAddr; -const logger = @import("../util/trace/log.zig").default_logger; const Atomic = std.atomic.Value; const assert = std.debug.assert; const testing = std.testing; @@ -101,16 +100,10 @@ pub fn handleEchoRequest(req: *httpz.Request, res: *httpz.Response) !void { var ip_echo_server_message = try std.json.parseFromSlice(IpEchoServerMessage, res.arena, body, .{}); defer ip_echo_server_message.deinit(); - std.debug.print("ip echo server message received: {}\n", .{ip_echo_server_message}); - // logger.debugf("ip echo server message: {any}", .{ip_echo_server_message.value}); - // convert a u32 to Ipv4 const socket_addr = SocketAddr.fromIpV4Address(res.conn.address); - std.json.stringify(IpEchoServerResponse.init(net.IpAddr{ .ipv4 = socket_addr.V4.ip }), .{}, res.writer()) catch |err| { - // logger.errf("could not json stringify IpEchoServerResponse: {any}", .{err}); - std.debug.print("could not json stringify ip echo server response message: {}\n", .{err}); - }; + try std.json.stringify(IpEchoServerResponse.init(net.IpAddr{ .ipv4 = socket_addr.V4.ip }), .{}, res.writer()); } pub fn returnBadRequest( @@ -147,74 +140,3 @@ pub fn requestIpEcho( var bufferStream = std.io.fixedBufferStream(buff[HEADER_LENGTH..len]); return try bincode.read(allocator, IpEchoServerResponse, bufferStream.reader(), .{}); } - -// TODO: FIX ME -// test "net.echo: Server works" { -// const port: u16 = 34333; - -// var exit = Atomic(bool).init(false); - -// var server = Server.init(testing.allocator, port, &exit); -// defer server.deinit(); - -// var server_thread_handle = try server.listenAndServe(); -// if (builtin.os.tag == .linux) try server_thread_handle.setName("server_thread"); - -// var client = std.http.Client{ .allocator = testing.allocator }; -// defer client.deinit(); - -// var server_header_buff = [_]u8{0} ** 1024; - -// // create request -// var req = try client.open(.POST, try std.Uri.parse("http://localhost:34333/"), .{ -// .server_header_buffer = &server_header_buff, -// .headers = .{ -// .content_type = .{ .override = "text/plain" }, -// // .accept_encoding = .{ .override = "*/*" }, -// }, -// .extra_headers = &.{ -// // .{ -// // .name = "connection", -// // .value = "close", -// // }, -// }, -// }); -// defer req.deinit(); - -// // req.transfer_encoding = .chunked; - -// var tcp_ports = [4]u16{ 1000, 2000, 3000, 4000 }; -// var udp_port = [4]u16{ 1000, 2000, 3000, 4000 }; -// const ip_echo_server_msg = IpEchoServerMessage.init(&tcp_ports, &udp_port); - -// // json stringify -// var buff = [_]u8{0} ** 128; -// var buffer = std.io.fixedBufferStream(&buff); -// try std.json.stringify(ip_echo_server_msg, .{}, buffer.writer()); - -// // write body -// try req.send(); - -// try req.writeAll(buffer.getWritten()); -// try req.finish(); -// try req.wait(); - -// if (req.response.status != .ok) { -// std.debug.print("req.response.status: {any}\n", .{req.response.status}); -// return error.ResponseStatusNot200; -// } - -// // read body -// const body = try req.reader().readAllAlloc(testing.allocator, 819200); -// defer testing.allocator.free(body); -// // logger.field("body_length", body.len).field("body", body).debugf("received body", .{}); - -// // deserialize json into type -// var resp = try std.json.parseFromSlice(IpEchoServerResponse, testing.allocator, body, .{}); -// defer resp.deinit(); - -// try testing.expectEqual([4]u8{ 127, 0, 0, 1 }, resp.value.address.asV4()); -// try testing.expectEqual(@as(u16, 0), resp.value.shred_version.?.value); - -// // server.kill(); -// } diff --git a/src/util/net/socket_utils.zig b/src/util/net/socket_utils.zig index 55cf81d..56798ad 100644 --- a/src/util/net/socket_utils.zig +++ b/src/util/net/socket_utils.zig @@ -5,7 +5,6 @@ const Packet = @import("packet.zig").Packet; const PACKET_DATA_SIZE = @import("packet.zig").PACKET_DATA_SIZE; const Channel = @import("../sync/channel.zig").Channel; const std = @import("std"); -const Logger = @import("../util/trace/log.zig").Logger; pub const SOCKET_TIMEOUT_US: usize = 1 * std.time.us_per_s; pub const PACKETS_PER_BATCH: usize = 64; @@ -15,7 +14,6 @@ pub fn readSocket( socket_: UdpSocket, incoming_channel: *Channel(std.ArrayList(Packet)), exit: *const std.atomic.Value(bool), - logger: Logger, ) !void { // NOTE: we set to non-blocking to periodically check if we should exit var socket = socket_; @@ -54,14 +52,13 @@ pub fn readSocket( try incoming_channel.send(packet_batch); } - logger.debugf("readSocket loop closed", .{}); + std.log.debug("readSocket loop closed", .{}); } pub fn sendSocket( socket: UdpSocket, outgoing_channel: *Channel(std.ArrayList(Packet)), exit: *const std.atomic.Value(bool), - logger: Logger, ) error{ SocketSendError, OutOfMemory, ChannelClosed }!void { var packets_sent: u64 = 0; @@ -82,7 +79,7 @@ pub fn sendSocket( for (packet_batches) |*packet_batch| { for (packet_batch.items) |*p| { const bytes_sent = socket.sendTo(p.addr, p.data[0..p.size]) catch |e| { - logger.debugf("send_socket error: {s}", .{@errorName(e)}); + std.log.debug("send_socket error: {s}", .{@errorName(e)}); continue; }; packets_sent +|= 1; @@ -90,7 +87,7 @@ pub fn sendSocket( } } } - logger.debugf("sendSocket loop closed", .{}); + std.log.debug("sendSocket loop closed", .{}); } /// A thread that is dedicated to either sending or receiving data over a socket. @@ -106,21 +103,21 @@ pub const SocketThread = struct { const Self = @This(); - pub fn initSender(allocator: Allocator, logger: Logger, socket: UdpSocket, exit: *Atomic(bool)) !Self { + pub fn initSender(allocator: Allocator, socket: UdpSocket, exit: *Atomic(bool)) !Self { const channel = Channel(std.ArrayList(Packet)).init(allocator, 0); return .{ .channel = channel, .exit = exit, - .handle = try std.Thread.spawn(.{}, sendSocket, .{ socket, channel, exit, logger }), + .handle = try std.Thread.spawn(.{}, sendSocket, .{ socket, channel, exit }), }; } - pub fn initReceiver(allocator: Allocator, logger: Logger, socket: UdpSocket, exit: *Atomic(bool)) !Self { + pub fn initReceiver(allocator: Allocator, socket: UdpSocket, exit: *Atomic(bool)) !Self { const channel = Channel(std.ArrayList(Packet)).init(allocator, 0); return .{ .channel = channel, .exit = exit, - .handle = try std.Thread.spawn(.{}, readSocket, .{ allocator, socket, channel, exit, logger }), + .handle = try std.Thread.spawn(.{}, readSocket, .{ allocator, socket, channel, exit }), }; } diff --git a/src/util/time/estimate.zig b/src/util/time/estimate.zig index 4501835..54cd30f 100644 --- a/src/util/time/estimate.zig +++ b/src/util/time/estimate.zig @@ -1,10 +1,8 @@ const std = @import("std"); const sig = @import("../sig.zig"); -const Logger = @import("../util/trace/log.zig").Logger; -// TODO: change to writer interface when logger has improved +// TODO: change to writer interface when std.log has mproved pub fn printTimeEstimate( - logger: Logger, // timer should be started at the beginning of the loop timer: *sig.time.Timer, total: usize, @@ -15,7 +13,7 @@ pub fn printTimeEstimate( if (i == 0 or total == 0) return; if (i > total) { if (other_info) |info| { - logger.infof("{s} [{s}]: {d}/{d} (?%) (est: ? elp: {s})", .{ + std.log.info("{s} [{s}]: {d}/{d} (?%) (est: ? elp: {s})", .{ name, info, i, @@ -23,7 +21,7 @@ pub fn printTimeEstimate( timer.read(), }); } else { - logger.infof("{s}: {d}/{d} (?%) (est: ? elp: {s})", .{ + std.log.info("{s}: {d}/{d} (?%) (est: ? elp: {s})", .{ name, i, total, @@ -41,7 +39,7 @@ pub fn printTimeEstimate( const ns_left = ns_per_vec * left; if (other_info) |info| { - logger.infof("{s} [{s}]: {d}/{d} ({d}%) (est: {s} elp: {s})", .{ + std.log.info("{s} [{s}]: {d}/{d} ({d}%) (est: {s} elp: {s})", .{ name, info, i, @@ -51,7 +49,7 @@ pub fn printTimeEstimate( timer.read(), }); } else { - logger.infof("{s}: {d}/{d} ({d}%) (est: {s} elp: {s})", .{ + std.log.info("{s}: {d}/{d} ({d}%) (est: {s} elp: {s})", .{ name, i, total, diff --git a/src/util/trace/entry.zig b/src/util/trace/entry.zig deleted file mode 100644 index 88a431c..0000000 --- a/src/util/trace/entry.zig +++ /dev/null @@ -1,217 +0,0 @@ -const std = @import("std"); -const time = @import("../time/time.zig"); -const Field = @import("field.zig").Field; -const Level = @import("level.zig").Level; -const logfmt = @import("logfmt.zig"); -const Logger = @import("./log.zig").Logger; -const Channel = @import("../sync/channel.zig").Channel; -const testing = std.testing; -const AtomicBool = std.atomic.Value(bool); - -pub const Entry = union(enum) { - standard: *StandardEntry, - noop, - - const Self = @This(); - - pub fn init(allocator: std.mem.Allocator, channel: *Channel(*StandardEntry), max_level: Level) Self { - return .{ .standard = StandardEntry.init(allocator, channel, max_level) }; - } - - pub fn deinit(self: Self) void { - switch (self) { - .standard => |entry| { - entry.deinit(); - }, - .noop => {}, - } - } - - pub fn field(self: Self, name: []const u8, value: anytype) Self { - switch (self) { - .standard => |entry| { - _ = entry.field(name, value); - return self; - }, - .noop => { - return self; - }, - } - } - - pub fn debugf(self: Self, comptime fmt: []const u8, args: anytype) void { - self.logf(.debug, fmt, args); - } - - pub fn errf(self: Self, comptime fmt: []const u8, args: anytype) void { - self.logf(.err, fmt, args); - } - - pub fn warnf(self: Self, comptime fmt: []const u8, args: anytype) void { - self.logf(.warn, fmt, args); - } - - pub fn infof(self: Self, comptime fmt: []const u8, args: anytype) void { - self.logf(.info, fmt, args); - } - - pub fn info(self: Self, msg: []const u8) void { - self.log(.info, msg); - } - - pub fn debug(self: Self, msg: []const u8) void { - self.log(.debug, msg); - } - - pub fn err(self: Self, msg: []const u8) void { - self.log(.err, msg); - } - - pub fn warn(self: Self, msg: []const u8) void { - self.log(.warn, msg); - } - - pub fn logf(self: Self, level: Level, comptime fmt: []const u8, args: anytype) void { - switch (self) { - .standard => |entry| { - entry.logf(level, fmt, args); - }, - .noop => {}, - } - } - - pub fn log(self: Self, level: Level, msg: []const u8) void { - switch (self) { - .standard => |entry| { - entry.log(level, msg); - }, - .noop => {}, - } - } - - pub fn format( - self: *const Self, - fmt: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { - switch (self) { - .standard => |entry| { - try entry.format(fmt, options, writer); - }, - .noop => {}, - } - } - - pub fn custom_format(self: *const Self, formatter: anytype, writer: anytype) !void { - switch (self) { - .standard => |entry| { - try formatter(entry, writer); - }, - .noop => {}, - } - } -}; - -pub const StandardEntry = struct { - /// Log levels more granular than this will not be logged. - max_level: Level, - /// Level to log this message as. - level: Level, - allocator: std.mem.Allocator, - fields: std.ArrayList(Field), - time: time.DateTime, - message: std.ArrayList(u8), - channel: *Channel(*StandardEntry), - - const Self = @This(); - - pub fn init(allocator: std.mem.Allocator, channel: *Channel(*StandardEntry), max_level: Level) *Self { - const self = allocator.create(Self) catch @panic("could not allocate.Create Entry"); - self.* = Self{ - .allocator = allocator, - .fields = std.ArrayList(Field).init(allocator), - .max_level = max_level, - .level = Level.debug, - .channel = channel, - .time = time.DateTime.epoch_unix, - .message = std.ArrayList(u8).init(allocator), - }; - return self; - } - - pub fn deinit(self: *Self) void { - for (self.fields.items) |*f| { - f.deinit(self.allocator); - } - self.fields.deinit(); - self.message.deinit(); - self.allocator.destroy(self); - } - - pub fn field(self: *Self, name: []const u8, value: anytype) *Self { - self.fields.append(Field.init(self.allocator, name, value)) catch @panic("could not append Field"); - return self; - } - - pub fn logf(self: *Self, level: Level, comptime fmt: []const u8, args: anytype) void { - if (@intFromEnum(self.max_level) < @intFromEnum(level)) { - self.deinit(); - return; - } - var message = std.ArrayList(u8).initCapacity(self.allocator, fmt.len * 2) catch @panic("could not initCapacity for message"); - std.fmt.format(message.writer(), fmt, args) catch @panic("could not format"); - self.message = message; - self.time = time.DateTime.now(); - self.level = level; - self.channel.send(self) catch @panic("could not send to channel"); - } - - pub fn log(self: *Self, level: Level, msg: []const u8) void { - if (@intFromEnum(self.max_level) < @intFromEnum(level)) { - self.deinit(); - return; - } - var message = std.ArrayList(u8).initCapacity(self.allocator, msg.len) catch @panic("could not initCapacity for message"); - message.appendSlice(msg[0..]) catch @panic("could not appendSlice for message"); - self.message = message; - self.time = time.DateTime.now(); - self.level = level; - self.channel.send(self) catch @panic("could not send to channel"); - } - - pub fn format( - self: *const Self, - _: []const u8, - _: std.fmt.FormatOptions, - writer: anytype, - ) !void { - // default formatting style - try logfmt.formatter(self, writer); - } - - pub fn custom_format(self: *const Self, formatter: anytype, writer: anytype) !void { - try formatter(self, writer); - } -}; - -const A = enum(u8) { - some_enum_variant, -}; - -test "trace.entry: should info log correctly" { - var logger = Logger.init(testing.allocator, Level.info); - defer logger.deinit(); - var entry = StandardEntry.init(testing.allocator, logger.standard.channel, .debug); - defer entry.deinit(); - - const anull: ?u8 = null; - - entry - .field("some_val", true) - .field("enum_field", A.some_enum_variant) - .field("name", "a-mod") - .field("elapsed", @as(i48, 135133340042)) - .field("possible_value", anull) - .logf(.info, "hello, {s}", .{"world!"}); -} diff --git a/src/util/trace/field.zig b/src/util/trace/field.zig deleted file mode 100644 index 983e2f6..0000000 --- a/src/util/trace/field.zig +++ /dev/null @@ -1,155 +0,0 @@ -const std = @import("std"); -const time = @import("../time/time.zig"); -const Allocator = std.mem.Allocator; - -pub const Field = struct { - name: []const u8, - value: Value, - - const Self = @This(); - - pub fn init(allocator: std.mem.Allocator, name: []const u8, value: anytype) Self { - return Self{ - .name = name, - .value = Value.init(allocator, value), - }; - } - - pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - self.value.deinit(allocator); - } - - pub fn custom_format(self: *const Self, writer: anytype) void { - std.fmt.format(writer, "{s}=", .{self.name}) catch @panic("could not format"); - self.value.format_as_str(writer); - std.fmt.format(writer, " ", .{}) catch @panic("could not format"); - } -}; - -pub const Value = union(enum(u8)) { - null: void, - string: []const u8, - bool: bool, - float: f64, - int: i64, - uint: u64, - enumm: [:0]const u8, - - const Self = @This(); - - pub fn init(allocator: std.mem.Allocator, val: anytype) Self { - return valToValue(allocator, val); - } - - fn format_as_str(self: *const Self, writer: anytype) void { - switch (self.*) { - .string => |str| { - std.fmt.format(writer, "\"{s}\"", .{str}) catch unreachable; - }, - .bool => |b| { - if (b) { - std.fmt.format(writer, "true", .{}) catch unreachable; - } else { - std.fmt.format(writer, "false", .{}) catch unreachable; - } - }, - .uint => |u| { - std.fmt.format(writer, "{d}", .{u}) catch unreachable; - }, - .int => |i| { - std.fmt.format(writer, "{d}", .{i}) catch unreachable; - }, - .float => |f| { - std.fmt.format(writer, "{d}", .{f}) catch unreachable; - }, - .null => { - std.fmt.format(writer, "null", .{}) catch unreachable; - }, - .enumm => |e| { - std.fmt.format(writer, "{s}", .{e}) catch unreachable; - }, - } - } - - pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - switch (self.*) { - Value.string => |str| { - allocator.free(str); - }, - else => {}, - } - } -}; - -fn valToValue(allocator: Allocator, val: anytype) Value { - switch (@typeInfo(@TypeOf(val))) { - .@"enum" => |_| { - return .{ .enumm = @tagName(val) }; - }, - .enum_literal => { - return .{ .enumm = @tagName(val) }; - }, - .bool => return .{ .bool = val }, - .optional => |info| { - _ = info; - if (val) |v| { - return valToValue(allocator, v); - } - return Value.null; - }, - .pointer => |info| { - switch (info.size) { - .One => { - const inner_child_type = switch (@typeInfo(info.child)) { - .array => |a| a.child, - else => unreachable, - }; - const inner_child_len = switch (@typeInfo(info.child)) { - .array => |a| a.len, - else => unreachable, - }; - if (inner_child_type == u8) { - const str = allocator.alloc(u8, inner_child_len) catch unreachable; - @memcpy(str, val[0..]); - return .{ .string = str }; - } else { - @compileError("┓\n|\n|--> Invalid field type: can only create value for []u8, not type '" ++ @typeName(@TypeOf(val)) ++ "'\n\n"); - } - }, - .Slice => { - if (@typeInfo(info.child).int.bits == 8) { - const str = allocator.alloc(u8, val.len) catch unreachable; - @memcpy(str, val[0..]); - return .{ .string = str }; - } else { - @compileError("┓\n|\n|--> Invalid field type: can only create value for []u8, not type '" ++ @typeName(@TypeOf(val)) ++ "'\n\n"); - } - }, - else => {}, - } - }, - .float => |info| { - _ = info; - return .{ .float = @as(f64, val) }; - }, - .int => |info| { - switch (info.signedness) { - .unsigned => { - return .{ .uint = @as(u64, val) }; - }, - .signed => { - return .{ .int = @as(i64, val) }; - }, - } - }, - .comptime_int => { - return .{ .int = @as(i64, val) }; - }, - .comptime_float => { - return .{ .float = @as(f64, val) }; - }, - else => {}, - } - - @compileError("┓\n|\n|--> Invalid field type: cannot add field of type'" ++ @typeName(@TypeOf(val)) ++ "' to log entry\n\n"); -} diff --git a/src/util/trace/level.zig b/src/util/trace/level.zig deleted file mode 100644 index 7e63619..0000000 --- a/src/util/trace/level.zig +++ /dev/null @@ -1,22 +0,0 @@ -pub const Level = enum { - /// Error: something has gone wrong. This might be recoverable or might - /// be followed by the program exiting. - err, - /// Warning: it is uncertain if something has gone wrong or not, but the - /// circumstances would be worth investigating. - warn, - /// Info: general messages about the state of the program. - info, - /// Debug: messages only useful for debugging. - debug, - - /// Returns a string literal of the given level in full text form. - pub fn asText(self: Level) []const u8 { - return switch (self) { - .err => "error", - .warn => "warning", - .info => "info", - .debug => "debug", - }; - } -}; diff --git a/src/util/trace/lib.zig b/src/util/trace/lib.zig deleted file mode 100644 index 925ee31..0000000 --- a/src/util/trace/lib.zig +++ /dev/null @@ -1,9 +0,0 @@ -pub const entry = @import("entry.zig"); -pub const field = @import("field.zig"); -pub const level = @import("level.zig"); -pub const log = @import("log.zig"); -pub const logfmt = @import("logfmt.zig"); - -pub const Logger = log.Logger; -pub const Level = level.Level; -pub const TestLogger = log.TestLogger; diff --git a/src/util/trace/log.zig b/src/util/trace/log.zig deleted file mode 100644 index 125b3e3..0000000 --- a/src/util/trace/log.zig +++ /dev/null @@ -1,377 +0,0 @@ -const std = @import("std"); -const entry = @import("entry.zig"); -const Level = @import("level.zig").Level; -const logfmt = @import("logfmt.zig"); -const Entry = entry.Entry; -const StandardEntry = entry.StandardEntry; -const testing = std.testing; -const AtomicBool = std.atomic.Value(bool); -const Channel = @import("../sync/channel.zig").Channel; -var gpa = std.heap.GeneralPurposeAllocator(.{}){}; -var gpa_allocator = gpa.allocator(); - -const INITIAL_ENTRIES_CHANNEL_SIZE: usize = 1024; - -pub const default_logger: *Logger = &global; -var global: Logger = .{ .standard = undefined }; - -pub const Logger = union(enum) { - standard: *StandardErrLogger, - test_logger: TestLogger, - noop, - - const Self = @This(); - - pub fn init(allocator: std.mem.Allocator, max_level: Level) Self { - return .{ .standard = StandardErrLogger.init(allocator, max_level) }; - } - - pub fn spawn(self: Self) void { - switch (self) { - .standard => |logger| { - logger.spawn(); - }, - .noop, .test_logger => {}, - } - } - - pub fn deinit(self: Self) void { - switch (self) { - .standard => |logger| { - logger.deinit(); - }, - .noop, .test_logger => {}, - } - } - - pub fn field(self: Self, name: []const u8, value: anytype) Entry { - switch (self) { - inline .standard, .test_logger => |logger| { - return logger.field(name, value); - }, - .noop => { - return .noop; - }, - } - } - - pub fn infof(self: Self, comptime fmt: []const u8, args: anytype) void { - switch (self) { - inline .standard, .test_logger => |logger| { - logger.infof(fmt, args); - }, - .noop => {}, - } - } - - pub fn debugf(self: Self, comptime fmt: []const u8, args: anytype) void { - switch (self) { - inline .standard, .test_logger => |logger| { - logger.debugf(fmt, args); - }, - .noop => {}, - } - } - - pub fn warnf(self: Self, comptime fmt: []const u8, args: anytype) void { - switch (self) { - inline .standard, .test_logger => |logger| { - logger.warnf(fmt, args); - }, - .noop => {}, - } - } - - pub fn errf(self: Self, comptime fmt: []const u8, args: anytype) void { - switch (self) { - inline .standard, .test_logger => |logger| { - logger.errf(fmt, args); - }, - .noop => {}, - } - } - - pub fn logf(self: Self, level: Level, comptime fmt: []const u8, args: anytype) void { - switch (self) { - inline .standard, .test_logger => |logger| { - logger.logf(level, fmt, args); - }, - .noop => {}, - } - } - - pub fn info(self: Self, msg: []const u8) void { - switch (self) { - inline .standard, .test_logger => |logger| { - logger.info(msg); - }, - .noop => {}, - } - } - - pub fn debug(self: Self, msg: []const u8) void { - switch (self) { - inline .standard, .test_logger => |logger| { - logger.debug(msg); - }, - .noop => {}, - } - } - - pub fn warn(self: Self, msg: []const u8) void { - switch (self) { - inline .standard, .test_logger => |logger| { - logger.warn(msg); - }, - .noop => {}, - } - } - - pub fn err(self: Self, msg: []const u8) void { - switch (self) { - inline .standard, .test_logger => |logger| { - logger.err(msg); - }, - .noop => {}, - } - } - - pub fn log(self: Self, level: Level, msg: []const u8) void { - switch (self) { - inline .standard, .test_logger => |logger| { - logger.log(level, msg); - }, - .noop => {}, - } - } - - /// Can be used in tests to minimize the amount of logging during tests. - pub const TEST_DEFAULT_LEVEL: Level = .warn; -}; - -pub const StandardErrLogger = struct { - allocator: std.mem.Allocator, - /// Messages more granular than this will not be logged - max_level: Level, - exit_sig: AtomicBool, - handle: ?std.Thread, - channel: *Channel(*StandardEntry), - - const Self = @This(); - - pub fn init(allocator: std.mem.Allocator, max_level: Level) *Self { - const self = allocator.create(Self) catch @panic("could not allocator.create Logger"); - self.* = .{ - .allocator = allocator, - .max_level = max_level, - .exit_sig = AtomicBool.init(false), - .handle = null, - .channel = Channel(*StandardEntry).init(allocator, INITIAL_ENTRIES_CHANNEL_SIZE), - }; - return self; - } - - pub fn spawn(self: *Self) void { - self.handle = std.Thread.spawn(.{}, StandardErrLogger.run, .{self}) catch @panic("could not spawn Logger"); - } - - pub fn deinit(self: *Self) void { - self.channel.close(); - if (self.handle) |handle| { - self.exit_sig.store(true, .seq_cst); - handle.join(); - } - self.channel.deinit(); - self.allocator.destroy(self); - } - - fn run(self: *Self) void { - const sink = StdErrSink{}; - - while (!self.exit_sig.load(.seq_cst)) { - std.time.sleep(std.time.ns_per_ms * 5); - - const entries = self.channel.drain() orelse { - // channel is closed - return; - }; - defer self.channel.allocator.free(entries); - - sink.consumeEntries(entries); - - // deinit entries - for (entries) |e| { - e.deinit(); - } - } - } - - pub fn field(self: *Self, name: []const u8, value: anytype) Entry { - var e = Entry.init(self.allocator, self.channel, self.max_level); - return e.field(name, value); - } - - pub fn info(self: *Self, msg: []const u8) void { - self.log(.info, msg); - } - - pub fn debug(self: *Self, msg: []const u8) void { - self.log(.debug, msg); - } - - pub fn warn(self: *Self, msg: []const u8) void { - self.log(.warn, msg); - } - - pub fn err(self: *Self, msg: []const u8) void { - self.log(.err, msg); - } - - pub fn infof(self: *Self, comptime fmt: []const u8, args: anytype) void { - self.logf(.info, fmt, args); - } - - pub fn debugf(self: *Self, comptime fmt: []const u8, args: anytype) void { - self.logf(.debug, fmt, args); - } - - pub fn warnf(self: *Self, comptime fmt: []const u8, args: anytype) void { - self.logf(.warn, fmt, args); - } - - pub fn errf(self: *Self, comptime fmt: []const u8, args: anytype) void { - self.logf(.err, fmt, args); - } - - pub fn log(self: *Self, level: Level, msg: []const u8) void { - if (@intFromEnum(self.max_level) >= @intFromEnum(level)) { - var e = Entry.init(self.allocator, self.channel, self.max_level); - e.log(level, msg); - } - } - - pub fn logf(self: *Self, level: Level, comptime fmt: []const u8, args: anytype) void { - if (@intFromEnum(self.max_level) >= @intFromEnum(level)) { - var e = Entry.init(self.allocator, self.channel, self.max_level); - e.logf(level, fmt, args); - } - } -}; - -/// Directly prints instead of running in a separate thread. This handles issues during tests -/// where some log messages never get logged because the logger is deinitialized before the -/// logging thread picks up the log message. -pub const TestLogger = struct { - max_level: Level = .warn, - - const Self = @This(); - - pub const default = TestLogger{}; - - pub fn logger(self: Self) Logger { - return .{ .test_logger = self }; - } - - pub fn field(_: Self, _: []const u8, _: anytype) Entry { - @panic("`Logger.field` not supported"); - } - - pub fn info(self: Self, msg: []const u8) void { - self.log(.info, msg); - } - - pub fn debug(self: Self, msg: []const u8) void { - self.log(.debug, msg); - } - - pub fn warn(self: Self, msg: []const u8) void { - self.log(.warn, msg); - } - - pub fn err(self: Self, msg: []const u8) void { - self.log(.err, msg); - } - - pub fn infof(self: Self, comptime fmt: []const u8, args: anytype) void { - self.logf(.info, fmt, args); - } - - pub fn debugf(self: Self, comptime fmt: []const u8, args: anytype) void { - self.logf(.debug, fmt, args); - } - - pub fn warnf(self: Self, comptime fmt: []const u8, args: anytype) void { - self.logf(.warn, fmt, args); - } - - pub fn errf(self: Self, comptime fmt: []const u8, args: anytype) void { - self.logf(.err, fmt, args); - } - - pub fn log(self: Self, level: Level, msg: []const u8) void { - if (@intFromEnum(self.max_level) >= @intFromEnum(level)) { - std.debug.print("{s}\n", .{msg}); - } - } - - pub fn logf(self: Self, level: Level, comptime fmt: []const u8, args: anytype) void { - if (@intFromEnum(self.max_level) >= @intFromEnum(level)) { - std.debug.print(fmt ++ "\n", args); - } - } -}; - -pub const StdErrSink = struct { - const Self = @This(); - - pub fn consumeEntries(_: Self, entries: []const *StandardEntry) void { - const std_err_writer = std.io.getStdErr().writer(); - std.debug.lockStdErr(); - defer std.debug.unlockStdErr(); - - for (entries) |e| { - logfmt.formatter(e, std_err_writer) catch unreachable; - } - } -}; - -test "trace.logger: works" { - var logger: Logger = .noop; // uncomment below to run visual test - // var logger = Logger.init(testing.allocator, .info); - logger.spawn(); - defer logger.deinit(); - - logger.field("elapsed", 4245).debugf("request with id {s} succeeded", .{"abcd1234"}); - - logger.field("kind", .some_enum_kind).infof("operation was done", .{}); - logger.field("authorized", false).warnf("api call received at {d} not authorized", .{10004}); - logger.field("error", "IOError").errf("api call received at {d} broke the system!", .{10005}); - - std.time.sleep(std.time.ns_per_ms * 100); - - logger.field("elapsed", 4245).debug("request with id succeeded"); - logger.field("kind", .some_enum_kind).info("operation was done"); - logger.field("authorized", false).warn("api call received at not authorized"); - logger.field("error", "IOError").err("api call received broke the system!"); - - const s: []const u8 = "t12312"; - logger - .field("tmp1", 123) - .field("tmp2", 456) - .field("tmp2", s) - .info("new push message"); - - std.time.sleep(std.time.ns_per_ms * 100); -} - -test "trace.logger: Logger is noop when configured as such" { - var logger: Logger = .noop; - defer logger.deinit(); - logger.spawn(); - - logger.info("should not log"); - logger.field("key", "value").info("not logging"); - logger.err("should not log also"); - - std.time.sleep(std.time.ms_per_s * 1); -} diff --git a/src/util/trace/logfmt.zig b/src/util/trace/logfmt.zig deleted file mode 100644 index 4188f5b..0000000 --- a/src/util/trace/logfmt.zig +++ /dev/null @@ -1,16 +0,0 @@ -const std = @import("std"); -const Entry = @import("./entry.zig").StandardEntry; - -pub fn formatter(e: *const Entry, writer: anytype) !void { - // format time as ISO8601 - const utc_format = "YYYY-MM-DDTHH:mm:ss"; - try std.fmt.format(writer, "time=", .{}); - try e.time.format(utc_format, .{}, writer); - try std.fmt.format(writer, "Z ", .{}); - try std.fmt.format(writer, "level={s} ", .{e.level.asText()}); - - for (e.fields.items) |f| { - f.custom_format(writer); - } - try std.fmt.format(writer, " {s}\n", .{e.message.items}); -}