Skip to content

Commit 2ba6737

Browse files
authored
Merge pull request #1119 from lightpanda-io/cdp_log_entry
Emit Log.addEntry
2 parents 33d737f + fe9a10c commit 2ba6737

File tree

4 files changed

+132
-4
lines changed

4 files changed

+132
-4
lines changed

src/browser/events/mouse_event.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ pub const MouseEvent = struct {
6868
});
6969

7070
if (!std.mem.eql(u8, event_type, "click")) {
71-
log.warn(.mouse_event, "unsupported mouse event", .{ .event = event_type });
71+
log.warn(.browser, "unsupported mouse event", .{ .event = event_type });
7272
}
7373

7474
return mouse_event;

src/cdp/cdp.zig

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const Session = @import("../browser/session.zig").Session;
3030
const Page = @import("../browser/page.zig").Page;
3131
const Incrementing = @import("../id.zig").Incrementing;
3232
const Notification = @import("../notification.zig").Notification;
33+
const LogInterceptor = @import("domains/log.zig").LogInterceptor;
3334
const InterceptState = @import("domains/fetch.zig").InterceptState;
3435

3536
pub const URL_BASE = "chrome://newtab/";
@@ -338,6 +339,8 @@ pub fn BrowserContext(comptime CDP_T: type) type {
338339

339340
intercept_state: InterceptState,
340341

342+
log_interceptor: LogInterceptor(Self),
343+
341344
// When network is enabled, we'll capture the transfer.id -> body
342345
// This is awfully memory intensive, but our underlying http client and
343346
// its users (script manager and page) correctly do not hold the body
@@ -378,6 +381,7 @@ pub fn BrowserContext(comptime CDP_T: type) type {
378381
.notification_arena = cdp.notification_arena.allocator(),
379382
.intercept_state = try InterceptState.init(allocator),
380383
.captured_responses = .empty,
384+
.log_interceptor = LogInterceptor(Self).init(allocator, self),
381385
};
382386
self.node_search_list = Node.Search.List.init(allocator, &self.node_registry);
383387
errdefer self.deinit();
@@ -389,6 +393,10 @@ pub fn BrowserContext(comptime CDP_T: type) type {
389393
}
390394

391395
pub fn deinit(self: *Self) void {
396+
// safe to call even if never registered
397+
log.unregisterInterceptor();
398+
self.log_interceptor.deinit();
399+
392400
self.inspector.deinit();
393401

394402
// abort all intercepted requests before closing the sesion/page
@@ -496,6 +504,18 @@ pub fn BrowserContext(comptime CDP_T: type) type {
496504
self.cdp.browser.notification.unregister(.page_network_almost_idle, self);
497505
}
498506

507+
pub fn logEnable(self: *Self) void {
508+
log.registerInterceptor(.{
509+
.ctx = &self.log_interceptor,
510+
.done = LogInterceptor(Self).done,
511+
.writer = LogInterceptor(Self).writer,
512+
});
513+
}
514+
515+
pub fn logDisable(_: *const Self) void {
516+
log.unregisterInterceptor();
517+
}
518+
499519
pub fn onPageRemove(ctx: *anyopaque, _: Notification.PageRemove) !void {
500520
const self: *Self = @ptrCast(@alignCast(ctx));
501521
try @import("domains/page.zig").pageRemove(self);

src/cdp/domains/log.zig

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,97 @@
1717
// along with this program. If not, see <https://www.gnu.org/licenses/>.
1818

1919
const std = @import("std");
20+
const log = @import("../../log.zig");
21+
22+
const Allocator = std.mem.Allocator;
2023

2124
pub fn processMessage(cmd: anytype) !void {
2225
const action = std.meta.stringToEnum(enum {
2326
enable,
27+
disable,
2428
}, cmd.input.action) orelse return error.UnknownMethod;
2529

2630
switch (action) {
27-
.enable => return cmd.sendResult(null, .{}),
31+
.enable => return enable(cmd),
32+
.disable => return disable(cmd),
2833
}
2934
}
35+
fn enable(cmd: anytype) !void {
36+
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
37+
bc.logEnable();
38+
return cmd.sendResult(null, .{});
39+
}
40+
41+
fn disable(cmd: anytype) !void {
42+
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
43+
bc.logDisable();
44+
return cmd.sendResult(null, .{});
45+
}
46+
47+
pub fn LogInterceptor(comptime BC: type) type {
48+
return struct {
49+
bc: *BC,
50+
allocating: std.Io.Writer.Allocating,
51+
52+
const Self = @This();
53+
54+
pub fn init(allocator: Allocator, bc: *BC) Self {
55+
return .{
56+
.bc = bc,
57+
.allocating = .init(allocator),
58+
};
59+
}
60+
61+
pub fn deinit(self: *Self) void {
62+
return self.allocating.deinit();
63+
}
64+
65+
pub fn writer(ctx: *anyopaque, scope: log.Scope, level: log.Level) ?*std.Io.Writer {
66+
if (scope == .unknown_prop or scope == .telemetry) {
67+
return null;
68+
}
69+
70+
// DO NOT REMOVE this. This prevents a log message caused from a failure
71+
// to intercept to trigger another intercept, which could result in an
72+
// endless cycle.
73+
if (scope == .interceptor) {
74+
return null;
75+
}
76+
77+
if (level == .debug) {
78+
return null;
79+
}
80+
const self: *Self = @ptrCast(@alignCast(ctx));
81+
return &self.allocating.writer;
82+
}
83+
84+
pub fn done(ctx: *anyopaque, scope: log.Scope, level: log.Level) void {
85+
const self: *Self = @ptrCast(@alignCast(ctx));
86+
defer self.allocating.clearRetainingCapacity();
87+
88+
self.bc.cdp.sendEvent("Log.entryAdded", .{
89+
.entry = .{
90+
.source = switch (scope) {
91+
.js, .user_script, .console, .web_api, .script_event => "javascript",
92+
.http, .fetch, .xhr => "network",
93+
.telemetry, .unknown_prop, .interceptor => unreachable, // filtered out in writer above
94+
else => "other",
95+
},
96+
.level = switch (level) {
97+
.debug => "verbose",
98+
.info => "info",
99+
.warn => "warning",
100+
.err => "error",
101+
.fatal => "error",
102+
},
103+
.text = self.allocating.written(),
104+
.timestamp = @import("../../datetime.zig").milliTimestamp(),
105+
},
106+
}, .{
107+
.session_id = self.bc.session_id,
108+
}) catch |err| {
109+
log.err(.interceptor, "failed to send", .{.err = err});
110+
};
111+
}
112+
};
113+
}

src/log.zig

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ pub const Scope = enum {
2929
cdp,
3030
console,
3131
http,
32-
http_client,
3332
js,
3433
loop,
3534
script_event,
@@ -40,7 +39,7 @@ pub const Scope = enum {
4039
xhr,
4140
fetch,
4241
polyfill,
43-
mouse_event,
42+
interceptor,
4443
};
4544

4645
const Opts = struct {
@@ -148,6 +147,13 @@ fn logTo(comptime scope: Scope, level: Level, comptime msg: []const u8, data: an
148147
.pretty => try logPretty(scope, level, msg, data, out),
149148
}
150149
out.flush() catch return;
150+
151+
const interceptor = _interceptor orelse return;
152+
if (interceptor.writer(interceptor.ctx, scope, level)) |iwriter| {
153+
try logLogfmt(scope, level, msg, data, iwriter);
154+
try iwriter.flush();
155+
interceptor.done(interceptor.ctx, scope, level);
156+
}
151157
}
152158

153159
fn logLogfmt(comptime scope: Scope, level: Level, comptime msg: []const u8, data: anytype, writer: anytype) !void {
@@ -346,6 +352,24 @@ fn elapsed() struct { time: f64, unit: []const u8 } {
346352
return .{ .time = @as(f64, @floatFromInt(e)) / @as(f64, 1000), .unit = "s" };
347353
}
348354

355+
var _interceptor: ?Interceptor = null;
356+
pub fn registerInterceptor(interceptor: Interceptor) void {
357+
_interceptor = interceptor;
358+
}
359+
360+
pub fn unregisterInterceptor() void {
361+
_interceptor = null;
362+
}
363+
364+
const Interceptor = struct {
365+
ctx: *anyopaque,
366+
done: DoneFunc,
367+
writer: WriterFunc,
368+
369+
const DoneFunc = *const fn (ctx: *anyopaque, scope: Scope, level: Level) void;
370+
const WriterFunc = *const fn (ctx: *anyopaque, scope: Scope, level: Level) ?*std.Io.Writer;
371+
};
372+
349373
const testing = @import("testing.zig");
350374
test "log: data" {
351375
opts.format = .logfmt;

0 commit comments

Comments
 (0)