logging: document GHOSTTY_LOG and make it more flexible
parent
6d2beed1b0
commit
f8c03bb6f6
30
HACKING.md
30
HACKING.md
|
|
@ -93,6 +93,36 @@ produced.
|
||||||
> may ask you to fix it and close the issue. It isn't a maintainers job to
|
> may ask you to fix it and close the issue. It isn't a maintainers job to
|
||||||
> review a PR so broken that it requires significant rework to be acceptable.
|
> review a PR so broken that it requires significant rework to be acceptable.
|
||||||
|
|
||||||
|
## Logging
|
||||||
|
|
||||||
|
Ghostty can write logs to a number of destinations. On all platforms, logging to
|
||||||
|
`stderr` is available. Depending on the platform and how Ghostty was launched,
|
||||||
|
logs sent to `stderr` may be stored by the system and made available for later
|
||||||
|
retrieval.
|
||||||
|
|
||||||
|
On Linux if Ghostty is launched by the default `systemd` user service, you can use
|
||||||
|
`journald` to see Ghostty's logs: `journalctl --user --unit app-com.mitchellh.ghostty.service`.
|
||||||
|
|
||||||
|
On macOS logging to the macOS unified log is available and enabled by default.
|
||||||
|
Use the system `log` CLI to view Ghostty's logs: `sudo log stream --level debug --predicate 'subsystem=="com.mitchellh.ghostty"'`.
|
||||||
|
|
||||||
|
Ghostty's logging can be configured in two ways. The first is by what
|
||||||
|
optimization level Ghostty is compiled with. If Ghostty is compiled with `Debug`
|
||||||
|
optimizations debug logs will be output to `stderr`. If Ghostty is compiled with
|
||||||
|
any other optimization the debug logs will not be output to `stderr`.
|
||||||
|
|
||||||
|
Ghostty also checks the `GHOSTTY_LOG` environment variable. It can be used
|
||||||
|
to control which destinations receive logs. Ghostty currently defines two
|
||||||
|
destinations:
|
||||||
|
|
||||||
|
- `stderr` - logging to `stderr`.
|
||||||
|
- `macos` - logging to macOS's unified log (has no effect on non-macOS platforms).
|
||||||
|
|
||||||
|
Combine values with a comma to enable multiple destinations. Prefix a
|
||||||
|
destination with `no-` to disable it. Enabling and disabling destinations
|
||||||
|
can be done at the same time. Setting `GHOSTTY_LOG` to `true` will enable all
|
||||||
|
destinations. Setting `GHOSTTY_LOG` to `false` will disable all destinations.
|
||||||
|
|
||||||
## Linting
|
## Linting
|
||||||
|
|
||||||
### Prettier
|
### Prettier
|
||||||
|
|
|
||||||
|
|
@ -607,6 +607,9 @@ pub fn init(
|
||||||
};
|
};
|
||||||
errdefer env.deinit();
|
errdefer env.deinit();
|
||||||
|
|
||||||
|
// don't leak GHOSTTY_LOG to any subprocesses
|
||||||
|
env.remove("GHOSTTY_LOG");
|
||||||
|
|
||||||
// Initialize our IO backend
|
// Initialize our IO backend
|
||||||
var io_exec = try termio.Exec.init(alloc, .{
|
var io_exec = try termio.Exec.init(alloc, .{
|
||||||
.command = command,
|
.command = command,
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@ const glib = @import("glib");
|
||||||
const gobject = @import("gobject");
|
const gobject = @import("gobject");
|
||||||
const gtk = @import("gtk");
|
const gtk = @import("gtk");
|
||||||
|
|
||||||
|
const build_config = @import("../../../build_config.zig");
|
||||||
|
const state = &@import("../../../global.zig").state;
|
||||||
const i18n = @import("../../../os/main.zig").i18n;
|
const i18n = @import("../../../os/main.zig").i18n;
|
||||||
const apprt = @import("../../../apprt.zig");
|
const apprt = @import("../../../apprt.zig");
|
||||||
const cgroup = @import("../cgroup.zig");
|
const cgroup = @import("../cgroup.zig");
|
||||||
|
|
@ -2677,7 +2679,9 @@ fn setGtkEnv(config: *const CoreConfig) error{NoSpaceLeft}!void {
|
||||||
/// disable it.
|
/// disable it.
|
||||||
@"vulkan-disable": bool = false,
|
@"vulkan-disable": bool = false,
|
||||||
} = .{
|
} = .{
|
||||||
.opengl = config.@"gtk-opengl-debug",
|
// `gtk-opengl-debug` dumps logs directly to stderr so both must be true
|
||||||
|
// to enable OpenGL debugging.
|
||||||
|
.opengl = state.logging.stderr and config.@"gtk-opengl-debug",
|
||||||
};
|
};
|
||||||
|
|
||||||
var gdk_disable: struct {
|
var gdk_disable: struct {
|
||||||
|
|
|
||||||
|
|
@ -151,7 +151,7 @@ pub fn init(
|
||||||
|
|
||||||
// This overrides our default behavior and forces logs to show
|
// This overrides our default behavior and forces logs to show
|
||||||
// up on stderr (in addition to the centralized macOS log).
|
// up on stderr (in addition to the centralized macOS log).
|
||||||
open.setEnvironmentVariable("GHOSTTY_LOG", "1");
|
open.setEnvironmentVariable("GHOSTTY_LOG", "stderr,macos");
|
||||||
|
|
||||||
// Configure how we're launching
|
// Configure how we're launching
|
||||||
open.setEnvironmentVariable("GHOSTTY_MAC_LAUNCH_SOURCE", "zig_run");
|
open.setEnvironmentVariable("GHOSTTY_MAC_LAUNCH_SOURCE", "zig_run");
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,19 @@ precedence over the XDG environment locations.
|
||||||
|
|
||||||
: **WINDOWS ONLY:** alternate location to search for configuration files.
|
: **WINDOWS ONLY:** alternate location to search for configuration files.
|
||||||
|
|
||||||
|
**GHOSTTY_LOG**
|
||||||
|
|
||||||
|
: The `GHOSTTY_LOG` environment variable can be used to control which
|
||||||
|
destinations receive logs. Ghostty currently defines two destinations:
|
||||||
|
|
||||||
|
: - `stderr` - logging to `stderr`.
|
||||||
|
: - `macos` - logging to macOS's unified log (has no effect on non-macOS platforms).
|
||||||
|
|
||||||
|
: Combine values with a comma to enable multiple destinations. Prefix a
|
||||||
|
destination with `no-` to disable it. Enabling and disabling destinations
|
||||||
|
can be done at the same time. Setting `GHOSTTY_LOG` to `true` will enable all
|
||||||
|
destinations. Setting `GHOSTTY_LOG` to `false` will disable all destinations.
|
||||||
|
|
||||||
# BUGS
|
# BUGS
|
||||||
|
|
||||||
See GitHub issues: <https://github.com/ghostty-org/ghostty/issues>
|
See GitHub issues: <https://github.com/ghostty-org/ghostty/issues>
|
||||||
|
|
|
||||||
|
|
@ -73,16 +73,12 @@ the public config files of many Ghostty users for examples and inspiration.
|
||||||
## Configuration Errors
|
## Configuration Errors
|
||||||
|
|
||||||
If your configuration file has any errors, Ghostty does its best to ignore
|
If your configuration file has any errors, Ghostty does its best to ignore
|
||||||
them and move on. Configuration errors currently show up in the log. The log
|
them and move on. Configuration errors will be logged.
|
||||||
is written directly to stderr, so it is up to you to figure out how to access
|
|
||||||
that for your system (for now). On macOS, you can also use the system `log` CLI
|
|
||||||
utility with `log stream --level debug --predicate 'subsystem=="com.mitchellh.ghostty"'`.
|
|
||||||
|
|
||||||
## Debugging Configuration
|
## Debugging Configuration
|
||||||
|
|
||||||
You can verify that configuration is being properly loaded by looking at the
|
You can verify that configuration is being properly loaded by looking at the
|
||||||
debug output of Ghostty. Documentation for how to view the debug output is in
|
debug output of Ghostty.
|
||||||
the "building Ghostty" section at the end of the README.
|
|
||||||
|
|
||||||
In the debug output, you should see in the first 20 lines or so messages about
|
In the debug output, you should see in the first 20 lines or so messages about
|
||||||
loading (or not loading) a configuration file, as well as any errors it may have
|
loading (or not loading) a configuration file, as well as any errors it may have
|
||||||
|
|
@ -93,3 +89,34 @@ will fall back to default values for erroneous keys.
|
||||||
You can also view the full configuration Ghostty is loading using `ghostty
|
You can also view the full configuration Ghostty is loading using `ghostty
|
||||||
+show-config` from the command-line. Use the `--help` flag to additional options
|
+show-config` from the command-line. Use the `--help` flag to additional options
|
||||||
for that command.
|
for that command.
|
||||||
|
|
||||||
|
## Logging
|
||||||
|
|
||||||
|
Ghostty can write logs to a number of destinations. On all platforms, logging to
|
||||||
|
`stderr` is available. Depending on the platform and how Ghostty was launched,
|
||||||
|
logs sent to `stderr` may be stored by the system and made available for later
|
||||||
|
retrieval.
|
||||||
|
|
||||||
|
On Linux if Ghostty is launched by the default `systemd` user service, you can use
|
||||||
|
`journald` to see Ghostty's logs: `journalctl --user --unit app-com.mitchellh.ghostty.service`.
|
||||||
|
|
||||||
|
On macOS logging to the macOS unified log is available and enabled by default.
|
||||||
|
--Use the system `log` CLI to view Ghostty's logs: `sudo log stream level debug
|
||||||
|
--predicate 'subsystem=="com.mitchellh.ghostty"'`.
|
||||||
|
|
||||||
|
Ghostty's logging can be configured in two ways. The first is by what
|
||||||
|
optimization level Ghostty is compiled with. If Ghostty is compiled with `Debug`
|
||||||
|
optimizations debug logs will be output to `stderr`. If Ghostty is compiled with
|
||||||
|
any other optimization the debug logs will not be output to `stderr`.
|
||||||
|
|
||||||
|
Ghostty also checks the `GHOSTTY_LOG` environment variable. It can be used
|
||||||
|
to control which destinations receive logs. Ghostty currently defines two
|
||||||
|
destinations:
|
||||||
|
|
||||||
|
- `stderr` - logging to `stderr`.
|
||||||
|
- `macos` - logging to macOS's unified log (has no effect on non-macOS platforms).
|
||||||
|
|
||||||
|
Combine values with a comma to enable multiple destinations. Prefix a
|
||||||
|
destination with `no-` to disable it. Enabling and disabling destinations
|
||||||
|
can be done at the same time. Setting `GHOSTTY_LOG` to `true` will enable all
|
||||||
|
destinations. Setting `GHOSTTY_LOG` to `false` will disable all destinations.
|
||||||
|
|
|
||||||
|
|
@ -604,7 +604,7 @@ pub fn parseAutoStruct(
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parsePackedStruct(comptime T: type, v: []const u8) !T {
|
pub fn parsePackedStruct(comptime T: type, v: []const u8) !T {
|
||||||
const info = @typeInfo(T).@"struct";
|
const info = @typeInfo(T).@"struct";
|
||||||
comptime assert(info.layout == .@"packed");
|
comptime assert(info.layout == .@"packed");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,9 +39,13 @@ pub const GlobalState = struct {
|
||||||
resources_dir: internal_os.ResourcesDir,
|
resources_dir: internal_os.ResourcesDir,
|
||||||
|
|
||||||
/// Where logging should go
|
/// Where logging should go
|
||||||
pub const Logging = union(enum) {
|
pub const Logging = packed struct {
|
||||||
disabled: void,
|
/// Whether to log to stderr. For lib mode we always disable stderr
|
||||||
stderr: void,
|
/// logging by default. Otherwise it's enabled by default.
|
||||||
|
stderr: bool = build_config.app_runtime != .none,
|
||||||
|
/// Whether to log to macOS's unified logging. Enabled by default
|
||||||
|
/// on macOS.
|
||||||
|
macos: bool = builtin.os.tag.isDarwin(),
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Initialize the global state.
|
/// Initialize the global state.
|
||||||
|
|
@ -61,7 +65,7 @@ pub const GlobalState = struct {
|
||||||
.gpa = null,
|
.gpa = null,
|
||||||
.alloc = undefined,
|
.alloc = undefined,
|
||||||
.action = null,
|
.action = null,
|
||||||
.logging = .{ .stderr = {} },
|
.logging = .{},
|
||||||
.rlimits = .{},
|
.rlimits = .{},
|
||||||
.resources_dir = .{},
|
.resources_dir = .{},
|
||||||
};
|
};
|
||||||
|
|
@ -100,12 +104,7 @@ pub const GlobalState = struct {
|
||||||
// If we have an action executing, we disable logging by default
|
// If we have an action executing, we disable logging by default
|
||||||
// since we write to stderr we don't want logs messing up our
|
// since we write to stderr we don't want logs messing up our
|
||||||
// output.
|
// output.
|
||||||
if (self.action != null) self.logging = .{ .disabled = {} };
|
if (self.action != null) self.logging.stderr = false;
|
||||||
|
|
||||||
// For lib mode we always disable stderr logging by default.
|
|
||||||
if (comptime build_config.app_runtime == .none) {
|
|
||||||
self.logging = .{ .disabled = {} };
|
|
||||||
}
|
|
||||||
|
|
||||||
// I don't love the env var name but I don't have it in my heart
|
// I don't love the env var name but I don't have it in my heart
|
||||||
// to parse CLI args 3 times (once for actions, once for config,
|
// to parse CLI args 3 times (once for actions, once for config,
|
||||||
|
|
@ -114,9 +113,7 @@ pub const GlobalState = struct {
|
||||||
// easy to set.
|
// easy to set.
|
||||||
if ((try internal_os.getenv(self.alloc, "GHOSTTY_LOG"))) |v| {
|
if ((try internal_os.getenv(self.alloc, "GHOSTTY_LOG"))) |v| {
|
||||||
defer v.deinit(self.alloc);
|
defer v.deinit(self.alloc);
|
||||||
if (v.value.len > 0) {
|
self.logging = cli.args.parsePackedStruct(Logging, v.value) catch .{};
|
||||||
self.logging = .{ .stderr = {} };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup our signal handlers before logging
|
// Setup our signal handlers before logging
|
||||||
|
|
|
||||||
|
|
@ -118,19 +118,17 @@ fn logFn(
|
||||||
comptime format: []const u8,
|
comptime format: []const u8,
|
||||||
args: anytype,
|
args: anytype,
|
||||||
) void {
|
) void {
|
||||||
// Stuff we can do before the lock
|
|
||||||
const level_txt = comptime level.asText();
|
|
||||||
const prefix = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";
|
|
||||||
|
|
||||||
// Lock so we are thread-safe
|
|
||||||
std.debug.lockStdErr();
|
|
||||||
defer std.debug.unlockStdErr();
|
|
||||||
|
|
||||||
// On Mac, we use unified logging. To view this:
|
// On Mac, we use unified logging. To view this:
|
||||||
//
|
//
|
||||||
// sudo log stream --level debug --predicate 'subsystem=="com.mitchellh.ghostty"'
|
// sudo log stream --level debug --predicate 'subsystem=="com.mitchellh.ghostty"'
|
||||||
//
|
//
|
||||||
if (builtin.target.os.tag.isDarwin()) {
|
// macOS logging is thread safe so no need for locks/mutexes
|
||||||
|
macos: {
|
||||||
|
if (comptime !builtin.target.os.tag.isDarwin()) break :macos;
|
||||||
|
if (!state.logging.macos) break :macos;
|
||||||
|
|
||||||
|
const prefix = if (scope == .default) "" else @tagName(scope) ++ ": ";
|
||||||
|
|
||||||
// Convert our levels to Mac levels
|
// Convert our levels to Mac levels
|
||||||
const mac_level: macos.os.LogType = switch (level) {
|
const mac_level: macos.os.LogType = switch (level) {
|
||||||
.debug => .debug,
|
.debug => .debug,
|
||||||
|
|
@ -143,26 +141,35 @@ fn logFn(
|
||||||
// but we shouldn't be logging too much.
|
// but we shouldn't be logging too much.
|
||||||
const logger = macos.os.Log.create(build_config.bundle_id, @tagName(scope));
|
const logger = macos.os.Log.create(build_config.bundle_id, @tagName(scope));
|
||||||
defer logger.release();
|
defer logger.release();
|
||||||
logger.log(std.heap.c_allocator, mac_level, format, args);
|
logger.log(std.heap.c_allocator, mac_level, prefix ++ format, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (state.logging) {
|
stderr: {
|
||||||
.disabled => {},
|
// don't log debug messages to stderr unless we are a debug build
|
||||||
|
if (comptime builtin.mode != .Debug and level == .debug) break :stderr;
|
||||||
|
|
||||||
.stderr => {
|
// skip if we are not logging to stderr
|
||||||
// Always try default to send to stderr
|
if (!state.logging.stderr) break :stderr;
|
||||||
var buffer: [1024]u8 = undefined;
|
|
||||||
var stderr = std.fs.File.stderr().writer(&buffer);
|
// Lock so we are thread-safe
|
||||||
const writer = &stderr.interface;
|
var buf: [64]u8 = undefined;
|
||||||
nosuspend writer.print(level_txt ++ prefix ++ format ++ "\n", args) catch return;
|
const stderr = std.debug.lockStderrWriter(&buf);
|
||||||
// TODO: Do we want to use flushless stderr in the future?
|
defer std.debug.unlockStderrWriter();
|
||||||
writer.flush() catch {};
|
|
||||||
},
|
const level_txt = comptime level.asText();
|
||||||
|
const prefix = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";
|
||||||
|
nosuspend stderr.print(level_txt ++ prefix ++ format ++ "\n", args) catch break :stderr;
|
||||||
|
nosuspend stderr.flush() catch break :stderr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const std_options: std.Options = .{
|
pub const std_options: std.Options = .{
|
||||||
// Our log level is always at least info in every build mode.
|
// Our log level is always at least info in every build mode.
|
||||||
|
//
|
||||||
|
// Note, we don't lower this to debug even with conditional logging
|
||||||
|
// via GHOSTTY_LOG because our debug logs are very expensive to
|
||||||
|
// calculate and we want to make sure they're optimized out in
|
||||||
|
// builds.
|
||||||
.log_level = switch (builtin.mode) {
|
.log_level = switch (builtin.mode) {
|
||||||
.Debug => .debug,
|
.Debug => .debug,
|
||||||
else => .info,
|
else => .info,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue