Skip to content

Commit 1d0876a

Browse files
authored
Merge pull request #691 from lightpanda-io/logger
Replace std.log with a structured logger
2 parents e9920ca + 8d3cf04 commit 1d0876a

31 files changed

+655
-325
lines changed

build.zig

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,14 @@ pub fn build(b: *std.Build) !void {
4444
b.option([]const u8, "git_commit", "Current git commit") orelse "dev",
4545
);
4646

47-
opts.addOption(
48-
std.log.Level,
49-
"log_level",
50-
b.option(std.log.Level, "log_level", "The log level") orelse std.log.Level.info,
51-
);
47+
{
48+
const log = @import("src/log.zig");
49+
opts.addOption(
50+
log.Level,
51+
"log_level",
52+
b.option(log.Level, "log_level", "The log level") orelse .info,
53+
);
54+
}
5255

5356
opts.addOption(
5457
bool,

src/app.zig

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
const std = @import("std");
22
const Allocator = std.mem.Allocator;
33

4+
const log = @import("log.zig");
45
const Loop = @import("runtime/loop.zig").Loop;
56
const HttpClient = @import("http/client.zig").Client;
67
const Telemetry = @import("telemetry/telemetry.zig").Telemetry;
78
const Notification = @import("notification.zig").Notification;
89

9-
const log = std.log.scoped(.app);
10-
1110
// Container for global state / objects that various parts of the system
1211
// might need.
1312
pub const App = struct {
@@ -84,15 +83,15 @@ fn getAndMakeAppDir(allocator: Allocator) ?[]const u8 {
8483
return allocator.dupe(u8, "/tmp") catch unreachable;
8584
}
8685
const app_dir_path = std.fs.getAppDataDir(allocator, "lightpanda") catch |err| {
87-
log.warn("failed to get lightpanda data dir: {}", .{err});
86+
log.warn(.app, "get data dir", .{ .err = err });
8887
return null;
8988
};
9089

9190
std.fs.cwd().makePath(app_dir_path) catch |err| switch (err) {
9291
error.PathAlreadyExists => return app_dir_path,
9392
else => {
9493
allocator.free(app_dir_path);
95-
log.warn("failed to create lightpanda data dir: {}", .{err});
94+
log.warn(.app, "create data dir", .{ .err = err, .path = app_dir_path });
9695
return null;
9796
},
9897
};

src/browser/console/console.zig

Lines changed: 88 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const builtin = @import("builtin");
2222
const JsObject = @import("../env.zig").Env.JsObject;
2323
const SessionState = @import("../env.zig").SessionState;
2424

25-
const log = if (builtin.is_test) &test_capture else std.log.scoped(.console);
25+
const log = if (builtin.is_test) &test_capture else @import("../../log.zig");
2626

2727
pub const Console = struct {
2828
// TODO: configurable writer
@@ -33,7 +33,7 @@ pub const Console = struct {
3333
if (values.len == 0) {
3434
return;
3535
}
36-
log.info("{s}", .{try serializeValues(values, state)});
36+
log.info(.console, "info", .{ .args = try serializeValues(values, state) });
3737
}
3838

3939
pub fn _info(console: *const Console, values: []JsObject, state: *SessionState) !void {
@@ -44,21 +44,21 @@ pub const Console = struct {
4444
if (values.len == 0) {
4545
return;
4646
}
47-
log.debug("{s}", .{try serializeValues(values, state)});
47+
log.debug(.console, "debug", .{ .args = try serializeValues(values, state) });
4848
}
4949

5050
pub fn _warn(_: *const Console, values: []JsObject, state: *SessionState) !void {
5151
if (values.len == 0) {
5252
return;
5353
}
54-
log.warn("{s}", .{try serializeValues(values, state)});
54+
log.warn(.console, "warn", .{ .args = try serializeValues(values, state) });
5555
}
5656

5757
pub fn _error(_: *const Console, values: []JsObject, state: *SessionState) !void {
5858
if (values.len == 0) {
5959
return;
6060
}
61-
log.err("{s}", .{try serializeValues(values, state)});
61+
log.info(.console, "error", .{ .args = try serializeValues(values, state) });
6262
}
6363

6464
pub fn _clear(_: *const Console) void {}
@@ -77,25 +77,24 @@ pub const Console = struct {
7777
const count = current + 1;
7878
gop.value_ptr.* = count;
7979

80-
log.info("{s}: {d}", .{ label, count });
80+
log.info(.console, "count", .{ .label = label, .count = count });
8181
}
8282

8383
pub fn _countReset(self: *Console, label_: ?[]const u8) !void {
8484
const label = label_ orelse "default";
8585
const kv = self.counts.fetchRemove(label) orelse {
86-
log.warn("Counter \"{s}\" doesn't exist.", .{label});
86+
log.info(.console, "invalid counter", .{ .label = label });
8787
return;
8888
};
89-
90-
log.info("{s}: {d}", .{ label, kv.value });
89+
log.info(.console, "count reset", .{ .label = label, .count = kv.value });
9190
}
9291

9392
pub fn _time(self: *Console, label_: ?[]const u8, state: *SessionState) !void {
9493
const label = label_ orelse "default";
9594
const gop = try self.timers.getOrPut(state.arena, label);
9695

9796
if (gop.found_existing) {
98-
log.warn("Timer \"{s}\" already exists.", .{label});
97+
log.info(.console, "duplicate timer", .{ .label = label });
9998
return;
10099
}
101100
gop.key_ptr.* = try state.arena.dupe(u8, label);
@@ -106,22 +105,21 @@ pub const Console = struct {
106105
const elapsed = timestamp();
107106
const label = label_ orelse "default";
108107
const start = self.timers.get(label) orelse {
109-
log.warn("Timer \"{s}\" doesn't exist.", .{label});
108+
log.info(.console, "invalid timer", .{ .label = label });
110109
return;
111110
};
112-
113-
log.info("\"{s}\": {d}ms", .{ label, elapsed - start });
111+
log.info(.console, "timer", .{ .label = label, .elapsed = elapsed - start });
114112
}
115113

116114
pub fn _timeStop(self: *Console, label_: ?[]const u8) void {
117115
const elapsed = timestamp();
118116
const label = label_ orelse "default";
119117
const kv = self.timers.fetchRemove(label) orelse {
120-
log.warn("Timer \"{s}\" doesn't exist.", .{label});
118+
log.info(.console, "invalid timer", .{ .label = label });
121119
return;
122120
};
123121

124-
log.info("\"{s}\": {d}ms - timer ended", .{ label, elapsed - kv.value });
122+
log.warn(.console, "timer stop", .{ .label = label, .elapsed = elapsed - kv.value });
125123
}
126124

127125
pub fn _assert(_: *Console, assertion: JsObject, values: []JsObject, state: *SessionState) !void {
@@ -132,7 +130,7 @@ pub const Console = struct {
132130
if (values.len > 0) {
133131
serialized_values = try serializeValues(values, state);
134132
}
135-
log.err("Assertion failed: {s}", .{serialized_values});
133+
log.info(.console, "assertion failed", .{ .values = serialized_values });
136134
}
137135

138136
fn serializeValues(values: []JsObject, state: *SessionState) ![]const u8 {
@@ -155,20 +153,20 @@ fn timestamp() u32 {
155153
var test_capture = TestCapture{};
156154
const testing = @import("../../testing.zig");
157155
test "Browser.Console" {
156+
defer testing.reset();
157+
158158
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
159159
defer runner.deinit();
160160

161-
defer testing.reset();
162-
163161
{
164162
try runner.testCases(&.{
165163
.{ "console.log('a')", "undefined" },
166164
.{ "console.warn('hello world', 23, true, new Object())", "undefined" },
167165
}, .{});
168166

169167
const captured = test_capture.captured.items;
170-
try testing.expectEqual("a", captured[0]);
171-
try testing.expectEqual("hello world 23 true [object Object]", captured[1]);
168+
try testing.expectEqual("[info] args=a", captured[0]);
169+
try testing.expectEqual("[warn] args=hello world 23 true [object Object]", captured[1]);
172170
}
173171

174172
{
@@ -186,15 +184,15 @@ test "Browser.Console" {
186184
}, .{});
187185

188186
const captured = test_capture.captured.items;
189-
try testing.expectEqual("Counter \"default\" doesn't exist.", captured[0]);
190-
try testing.expectEqual("default: 1", captured[1]);
191-
try testing.expectEqual("teg: 1", captured[2]);
192-
try testing.expectEqual("teg: 2", captured[3]);
193-
try testing.expectEqual("teg: 3", captured[4]);
194-
try testing.expectEqual("default: 2", captured[5]);
195-
try testing.expectEqual("teg: 3", captured[6]);
196-
try testing.expectEqual("default: 2", captured[7]);
197-
try testing.expectEqual("default: 1", captured[8]);
187+
try testing.expectEqual("[invalid counter] label=default", captured[0]);
188+
try testing.expectEqual("[count] label=default count=1", captured[1]);
189+
try testing.expectEqual("[count] label=teg count=1", captured[2]);
190+
try testing.expectEqual("[count] label=teg count=2", captured[3]);
191+
try testing.expectEqual("[count] label=teg count=3", captured[4]);
192+
try testing.expectEqual("[count] label=default count=2", captured[5]);
193+
try testing.expectEqual("[count reset] label=teg count=3", captured[6]);
194+
try testing.expectEqual("[count reset] label=default count=2", captured[7]);
195+
try testing.expectEqual("[count] label=default count=1", captured[8]);
198196
}
199197

200198
{
@@ -208,33 +206,81 @@ test "Browser.Console" {
208206
}, .{});
209207

210208
const captured = test_capture.captured.items;
211-
try testing.expectEqual("Assertion failed: ", captured[0]);
212-
try testing.expectEqual("Assertion failed: x true", captured[1]);
213-
try testing.expectEqual("Assertion failed: x", captured[2]);
209+
try testing.expectEqual("[assertion failed] values=", captured[0]);
210+
try testing.expectEqual("[assertion failed] values=x true", captured[1]);
211+
try testing.expectEqual("[assertion failed] values=x", captured[2]);
214212
}
215213
}
216-
217214
const TestCapture = struct {
218215
captured: std.ArrayListUnmanaged([]const u8) = .{},
219216

220217
fn reset(self: *TestCapture) void {
221218
self.captured = .{};
222219
}
223220

224-
fn debug(self: *TestCapture, comptime fmt: []const u8, args: anytype) void {
225-
const str = std.fmt.allocPrint(testing.arena_allocator, fmt, args) catch unreachable;
226-
self.captured.append(testing.arena_allocator, str) catch unreachable;
221+
fn debug(
222+
self: *TestCapture,
223+
comptime scope: @Type(.enum_literal),
224+
comptime msg: []const u8,
225+
args: anytype,
226+
) void {
227+
self.capture(scope, msg, args);
228+
}
229+
230+
fn info(
231+
self: *TestCapture,
232+
comptime scope: @Type(.enum_literal),
233+
comptime msg: []const u8,
234+
args: anytype,
235+
) void {
236+
self.capture(scope, msg, args);
227237
}
228238

229-
fn info(self: *TestCapture, comptime fmt: []const u8, args: anytype) void {
230-
self.debug(fmt, args);
239+
fn warn(
240+
self: *TestCapture,
241+
comptime scope: @Type(.enum_literal),
242+
comptime msg: []const u8,
243+
args: anytype,
244+
) void {
245+
self.capture(scope, msg, args);
231246
}
232247

233-
fn warn(self: *TestCapture, comptime fmt: []const u8, args: anytype) void {
234-
self.debug(fmt, args);
248+
fn err(
249+
self: *TestCapture,
250+
comptime scope: @Type(.enum_literal),
251+
comptime msg: []const u8,
252+
args: anytype,
253+
) void {
254+
self.capture(scope, msg, args);
235255
}
236256

237-
fn err(self: *TestCapture, comptime fmt: []const u8, args: anytype) void {
238-
self.debug(fmt, args);
257+
fn capture(
258+
self: *TestCapture,
259+
comptime scope: @Type(.enum_literal),
260+
comptime msg: []const u8,
261+
args: anytype,
262+
) void {
263+
self._capture(scope, msg, args) catch unreachable;
264+
}
265+
266+
fn _capture(
267+
self: *TestCapture,
268+
comptime scope: @Type(.enum_literal),
269+
comptime msg: []const u8,
270+
args: anytype,
271+
) !void {
272+
std.debug.assert(scope == .console);
273+
274+
const allocator = testing.arena_allocator;
275+
var buf: std.ArrayListUnmanaged(u8) = .empty;
276+
try buf.appendSlice(allocator, "[" ++ msg ++ "] ");
277+
278+
inline for (@typeInfo(@TypeOf(args)).@"struct".fields) |f| {
279+
try buf.appendSlice(allocator, f.name);
280+
try buf.append(allocator, '=');
281+
try @import("../../log.zig").writeValue(false, @field(args, f.name), buf.writer(allocator));
282+
try buf.append(allocator, ' ');
283+
}
284+
self.captured.append(testing.arena_allocator, std.mem.trimRight(u8, buf.items, " ")) catch unreachable;
239285
}
240286
};

src/browser/dom/element.zig

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,17 @@ const std = @import("std");
2121
const parser = @import("../netsurf.zig");
2222
const SessionState = @import("../env.zig").SessionState;
2323

24-
const collection = @import("html_collection.zig");
25-
const dump = @import("../dump.zig");
2624
const css = @import("css.zig");
25+
const log = @import("../../log.zig");
26+
const dump = @import("../dump.zig");
27+
const collection = @import("html_collection.zig");
2728

2829
const Node = @import("node.zig").Node;
2930
const Walker = @import("walker.zig").WalkerDepthFirst;
3031
const NodeList = @import("nodelist.zig").NodeList;
3132
const HTMLElem = @import("../html/elements.zig");
3233
pub const Union = @import("../html/elements.zig").Union;
3334

34-
const log = std.log.scoped(.element);
35-
3635
// WEB IDL https://dom.spec.whatwg.org/#element
3736
pub const Element = struct {
3837
pub const Self = parser.Element;
@@ -148,7 +147,7 @@ pub const Element = struct {
148147
while (true) {
149148
if (try select.match(current)) {
150149
if (!current.isElement()) {
151-
log.err("closest: is not an element: {s}", .{try current.tag()});
150+
log.err(.element, "closest invalid type", .{ .type = try current.tag() });
152151
return null;
153152
}
154153
return parser.nodeToElement(current.node);

src/browser/dom/intersection_observer.zig

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
const std = @import("std");
2020

21+
const log = @import("../../log.zig");
2122
const parser = @import("../netsurf.zig");
2223
const SessionState = @import("../env.zig").SessionState;
2324

@@ -29,8 +30,6 @@ pub const Interfaces = .{
2930
IntersectionObserverEntry,
3031
};
3132

32-
const log = std.log.scoped(.events);
33-
3433
// This is supposed to listen to change between the root and observation targets.
3534
// However, our rendered stores everything as 1 pixel sized boxes in a long row that never changes.
3635
// As such, there are no changes to intersections between the root and any target.
@@ -87,8 +86,7 @@ pub const IntersectionObserver = struct {
8786

8887
var result: Env.Function.Result = undefined;
8988
self.callback.tryCall(void, .{self.observed_entries.items}, &result) catch {
90-
log.err("intersection observer callback error: {s}", .{result.exception});
91-
log.debug("stack:\n{s}", .{result.stack orelse "???"});
89+
log.debug(.int_obs, "callback error", .{ .err = result.exception, .stack = result.stack });
9290
};
9391
}
9492

0 commit comments

Comments
 (0)