Skip to content

Commit e996746

Browse files
committed
add observability for windows
1 parent 1bf3323 commit e996746

File tree

7 files changed

+79
-51
lines changed

7 files changed

+79
-51
lines changed

cli/src/commands/tunnels.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ impl ServiceContainer for TunnelServiceContainer {
7474
&mut self,
7575
log: log::Logger,
7676
launcher_paths: LauncherPaths,
77-
shutdown_rx: mpsc::Receiver<ShutdownSignal>,
77+
shutdown_rx: mpsc::UnboundedReceiver<ShutdownSignal>,
7878
) -> Result<(), AnyError> {
7979
let csa = (&self.args).into();
8080
serve_with_csa(
@@ -126,7 +126,6 @@ pub async fn service(
126126
let current_exe =
127127
std::env::current_exe().map_err(|e| wrap(e, "could not get current exe"))?;
128128

129-
println!("calling register");
130129
manager
131130
.register(
132131
current_exe,
@@ -240,7 +239,7 @@ async fn serve_with_csa(
240239
log: Logger,
241240
gateway_args: TunnelServeArgs,
242241
csa: CodeServerArgs,
243-
shutdown_rx: Option<mpsc::Receiver<ShutdownSignal>>,
242+
shutdown_rx: Option<mpsc::UnboundedReceiver<ShutdownSignal>>,
244243
) -> Result<i32, AnyError> {
245244
// Intentionally read before starting the server. If the server updated and
246245
// respawn is requested, the old binary will get renamed, and then
@@ -260,7 +259,7 @@ async fn serve_with_csa(
260259
let shutdown_tx = if let Some(tx) = shutdown_rx {
261260
tx
262261
} else {
263-
let (tx, rx) = mpsc::channel::<ShutdownSignal>(2);
262+
let (tx, rx) = mpsc::unbounded_channel::<ShutdownSignal>();
264263
if let Some(process_id) = gateway_args.parent_process_id {
265264
match Pid::from_str(&process_id) {
266265
Ok(pid) => {
@@ -271,7 +270,7 @@ async fn serve_with_csa(
271270
while s.refresh_process(pid) {
272271
sleep(Duration::from_millis(2000)).await;
273272
}
274-
tx.send(ShutdownSignal::ParentProcessKilled).await.ok();
273+
tx.send(ShutdownSignal::ParentProcessKilled).ok();
275274
});
276275
}
277276
Err(_) => {
@@ -281,7 +280,7 @@ async fn serve_with_csa(
281280
}
282281
tokio::spawn(async move {
283282
tokio::signal::ctrl_c().await.ok();
284-
tx.send(ShutdownSignal::CtrlC).await.ok();
283+
tx.send(ShutdownSignal::CtrlC).ok();
285284
});
286285
rx
287286
};

cli/src/tunnels/control_server.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ pub async fn serve(
167167
launcher_paths: &LauncherPaths,
168168
code_server_args: &CodeServerArgs,
169169
platform: Platform,
170-
shutdown_rx: mpsc::Receiver<ShutdownSignal>,
170+
shutdown_rx: mpsc::UnboundedReceiver<ShutdownSignal>,
171171
) -> Result<ServerTermination, AnyError> {
172172
let mut port = tunnel.add_port_direct(CONTROL_PORT).await?;
173173
print_listening(log, &tunnel.name);

cli/src/tunnels/service.rs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
use std::path::PathBuf;
6+
use std::path::{Path, PathBuf};
77

88
use async_trait::async_trait;
99
use tokio::sync::mpsc;
1010

1111
use crate::commands::tunnels::ShutdownSignal;
1212
use crate::log;
1313
use crate::state::LauncherPaths;
14-
use crate::util::errors::AnyError;
14+
use crate::util::errors::{wrap, AnyError};
15+
use crate::util::io::{tailf, TailEvent};
1516

1617
pub const SERVICE_LOG_FILE_NAME: &str = "tunnel-service.log";
1718

@@ -21,7 +22,7 @@ pub trait ServiceContainer: Send {
2122
&mut self,
2223
log: log::Logger,
2324
launcher_paths: LauncherPaths,
24-
shutdown_rx: mpsc::Receiver<ShutdownSignal>,
25+
shutdown_rx: mpsc::UnboundedReceiver<ShutdownSignal>,
2526
) -> Result<(), AnyError>;
2627
}
2728

@@ -65,10 +66,29 @@ pub fn create_service_manager(log: log::Logger, paths: &LauncherPaths) -> Servic
6566
}
6667
#[cfg(target_os = "windows")]
6768
{
68-
super::service_windows::WindowsService::new(log)
69+
super::service_windows::WindowsService::new(log, paths)
6970
}
7071
#[cfg(target_os = "linux")]
7172
{
7273
super::service_linux::SystemdService::new(log, paths.clone())
7374
}
7475
}
76+
77+
pub(crate) async fn tail_log_file(log_file: &Path) -> Result<(), AnyError> {
78+
if !log_file.exists() {
79+
println!("The tunnel service has not started yet.");
80+
return Ok(());
81+
}
82+
83+
let file = std::fs::File::open(&log_file).map_err(|e| wrap(e, "error opening log file"))?;
84+
let mut rx = tailf(file, 20);
85+
while let Some(line) = rx.recv().await {
86+
match line {
87+
TailEvent::Line(l) => print!("{}", l),
88+
TailEvent::Reset => println!("== Tunnel service restarted =="),
89+
TailEvent::Err(e) => return Err(wrap(e, "error reading log file").into()),
90+
}
91+
}
92+
93+
Ok(())
94+
}

cli/src/tunnels/service_linux.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,10 @@ impl ServiceManager for SystemdService {
107107
launcher_paths: crate::state::LauncherPaths,
108108
mut handle: impl 'static + super::ServiceContainer,
109109
) -> Result<(), crate::util::errors::AnyError> {
110-
let (tx, rx) = mpsc::channel::<ShutdownSignal>(1);
110+
let (tx, rx) = mpsc::unbounded_channel::<ShutdownSignal>();
111111
tokio::spawn(async move {
112112
tokio::signal::ctrl_c().await.ok();
113-
tx.send(ShutdownSignal::CtrlC).await.ok();
113+
tx.send(ShutdownSignal::CtrlC).ok();
114114
});
115115

116116
handle.run_service(self.log, launcher_paths, rx).await
@@ -128,14 +128,14 @@ impl ServiceManager for SystemdService {
128128
])
129129
.status()
130130
.map(|s| s.code().unwrap_or(1))
131-
.map_err(|e| wrap(e, format!("error running journalctl")))?;
131+
.map_err(|e| wrap(e, "error running systemctl"))?;
132132

133133
// then follow log files
134134
Command::new("journalctl")
135135
.args(["--user", "-f", "-u", &SystemdService::service_name_string()])
136136
.status()
137137
.map(|s| s.code().unwrap_or(1))
138-
.map_err(|e| wrap(e, format!("error running journalctl")))?;
138+
.map_err(|e| wrap(e, "error running journalctl"))?;
139139
Ok(())
140140
}
141141

cli/src/tunnels/service_macos.rs

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,10 @@ use crate::{
2020
util::{
2121
command::capture_command_and_check_status,
2222
errors::{wrap, AnyError, MissingHomeDirectory},
23-
io::{tailf, TailEvent},
2423
},
2524
};
2625

27-
use super::ServiceManager;
26+
use super::{ServiceManager, service::tail_log_file};
2827

2928
pub struct LaunchdService {
3029
log: log::Logger,
@@ -67,34 +66,18 @@ impl ServiceManager for LaunchdService {
6766
}
6867

6968
async fn show_logs(&self) -> Result<(), AnyError> {
70-
if !self.log_file.exists() {
71-
println!("The tunnel service has not started yet.");
72-
return Ok(());
73-
}
74-
75-
let file =
76-
std::fs::File::open(&self.log_file).map_err(|e| wrap(e, "error opening log file"))?;
77-
let mut rx = tailf(file, 20);
78-
while let Some(line) = rx.recv().await {
79-
match line {
80-
TailEvent::Line(l) => print!("{}", l),
81-
TailEvent::Reset => println!("== Tunnel service restarted =="),
82-
TailEvent::Err(e) => return Err(wrap(e, "error reading log file").into()),
83-
}
84-
}
85-
86-
Ok(())
69+
tail_log_file(&self.log_file).await
8770
}
8871

8972
async fn run(
9073
self,
9174
launcher_paths: crate::state::LauncherPaths,
9275
mut handle: impl 'static + super::ServiceContainer,
9376
) -> Result<(), crate::util::errors::AnyError> {
94-
let (tx, rx) = mpsc::channel::<ShutdownSignal>(1);
77+
let (tx, rx) = mpsc::unbounded_channel::<ShutdownSignal>();
9578
tokio::spawn(async move {
9679
tokio::signal::ctrl_c().await.ok();
97-
tx.send(ShutdownSignal::CtrlC).await.ok();
80+
tx.send(ShutdownSignal::CtrlC).ok();
9881
});
9982

10083
handle.run_service(self.log, launcher_paths, rx).await

cli/src/tunnels/service_windows.rs

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
use async_trait::async_trait;
77
use dialoguer::{theme::ColorfulTheme, Input, Password};
88
use lazy_static::lazy_static;
9-
use std::{ffi::OsString, sync::Mutex, thread, time::Duration};
9+
use std::{ffi::OsString, path::PathBuf, sync::Mutex, thread, time::Duration};
1010
use tokio::sync::mpsc;
1111
use windows_service::{
1212
define_windows_service,
@@ -21,27 +21,31 @@ use windows_service::{
2121

2222
use crate::{
2323
commands::tunnels::ShutdownSignal,
24-
util::errors::{wrap, AnyError, WindowsNeedsElevation},
24+
util::errors::{wrap, wrapdbg, AnyError, WindowsNeedsElevation},
2525
};
2626
use crate::{
2727
log::{self, FileLogSink},
2828
state::LauncherPaths,
2929
};
3030

3131
use super::service::{
32-
ServiceContainer, ServiceManager as CliServiceManager, SERVICE_LOG_FILE_NAME,
32+
tail_log_file, ServiceContainer, ServiceManager as CliServiceManager, SERVICE_LOG_FILE_NAME,
3333
};
3434

3535
pub struct WindowsService {
3636
log: log::Logger,
37+
log_file: PathBuf,
3738
}
3839

3940
const SERVICE_NAME: &str = "code_tunnel";
4041
const SERVICE_TYPE: ServiceType = ServiceType::OWN_PROCESS;
4142

4243
impl WindowsService {
43-
pub fn new(log: log::Logger) -> Self {
44-
Self { log }
44+
pub fn new(log: log::Logger, paths: &LauncherPaths) -> Self {
45+
Self {
46+
log,
47+
log_file: paths.service_log_file(),
48+
}
4549
}
4650
}
4751

@@ -54,14 +58,18 @@ impl CliServiceManager for WindowsService {
5458
)
5559
.map_err(|e| WindowsNeedsElevation(format!("error getting service manager: {}", e)))?;
5660

61+
let mut args = args.iter().map(OsString::from).collect::<Vec<OsString>>();
62+
args.push(OsString::from("--log-to-file"));
63+
args.push(self.log_file.as_os_str().to_os_string());
64+
5765
let mut service_info = ServiceInfo {
5866
name: OsString::from(SERVICE_NAME),
5967
display_name: OsString::from("VS Code Tunnel"),
6068
service_type: SERVICE_TYPE,
6169
start_type: ServiceStartType::AutoStart,
6270
error_control: ServiceErrorControl::Normal,
6371
executable_path: exe,
64-
launch_arguments: args.iter().map(OsString::from).collect(),
72+
launch_arguments: args,
6573
dependencies: vec![],
6674
account_name: None,
6775
account_password: None,
@@ -74,7 +82,7 @@ impl CliServiceManager for WindowsService {
7482
let service = if let Ok(service) = existing_service {
7583
service
7684
.change_config(&service_info)
77-
.map_err(|e| wrap(e, "error updating existing service"))?;
85+
.map_err(|e| wrapdbg(e, "error updating existing service"))?;
7886
service
7987
} else {
8088
loop {
@@ -112,15 +120,15 @@ impl CliServiceManager for WindowsService {
112120
if status == ServiceState::Stopped {
113121
service
114122
.start::<&str>(&[])
115-
.map_err(|e| wrap(e, "error starting service"))?;
123+
.map_err(|e| wrapdbg(e, "error starting service"))?;
116124
}
117125

118126
info!(self.log, "Tunnel service successfully started");
119127
Ok(())
120128
}
121129

122130
async fn show_logs(&self) -> Result<(), AnyError> {
123-
todo!();
131+
tail_log_file(&self.log_file).await
124132
}
125133

126134
#[allow(unused_must_use)] // triggers incorrectly on `define_windows_service!`
@@ -136,7 +144,7 @@ impl CliServiceManager for WindowsService {
136144
Ok(sink) => self.log.tee(sink),
137145
Err(e) => {
138146
warning!(self.log, "Failed to create service log file: {}", e);
139-
self.log.clone()
147+
self.log
140148
}
141149
};
142150

@@ -176,12 +184,12 @@ impl CliServiceManager for WindowsService {
176184

177185
let service_status = service
178186
.query_status()
179-
.map_err(|e| wrap(e, "error getting service status"))?;
187+
.map_err(|e| wrapdbg(e, "error getting service status"))?;
180188

181189
if service_status.current_state != ServiceState::Stopped {
182190
service
183191
.stop()
184-
.map_err(|e| wrap(e, "error getting stopping service"))?;
192+
.map_err(|e| wrapdbg(e, "error getting stopping service"))?;
185193

186194
while let Ok(ServiceState::Stopped) = service.query_status().map(|s| s.current_state) {
187195
info!(self.log, "Polling for service to stop...");
@@ -191,7 +199,7 @@ impl CliServiceManager for WindowsService {
191199

192200
service
193201
.delete()
194-
.map_err(|e| wrap(e, "error deleting service"))?;
202+
.map_err(|e| wrapdbg(e, "error deleting service"))?;
195203

196204
Ok(())
197205
}
@@ -212,7 +220,7 @@ fn service_main(_arguments: Vec<OsString>) -> Result<(), AnyError> {
212220
let mut service = SERVICE_IMPL.lock().unwrap().take().unwrap();
213221

214222
// Create a channel to be able to poll a stop event from the service worker loop.
215-
let (shutdown_tx, shutdown_rx) = mpsc::channel::<ShutdownSignal>(5);
223+
let (shutdown_tx, shutdown_rx) = mpsc::unbounded_channel::<ShutdownSignal>();
216224
let mut shutdown_tx = Some(shutdown_tx);
217225

218226
// Define system service event handler that will be receiving service events.
@@ -222,7 +230,7 @@ fn service_main(_arguments: Vec<OsString>) -> Result<(), AnyError> {
222230
ServiceControl::Stop => {
223231
shutdown_tx
224232
.take()
225-
.and_then(|tx| tx.blocking_send(ShutdownSignal::ServiceStopped).ok());
233+
.and_then(|tx| tx.send(ShutdownSignal::ServiceStopped).ok());
226234
ServiceControlHandlerResult::NoError
227235
}
228236
_ => ServiceControlHandlerResult::NotImplemented,
@@ -245,6 +253,13 @@ fn service_main(_arguments: Vec<OsString>) -> Result<(), AnyError> {
245253
})
246254
.map_err(|e| wrap(e, "error marking service as running"))?;
247255

256+
info!(service.log, "Starting service loop...");
257+
258+
let panic_log = service.log.clone();
259+
std::panic::set_hook(Box::new(move |p| {
260+
error!(panic_log, "Service panic: {:?}", p);
261+
}));
262+
248263
let result = tokio::runtime::Builder::new_multi_thread()
249264
.enable_all()
250265
.build()

cli/src/util/errors.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,17 @@ impl From<reqwest::Error> for WrappedError {
4343
}
4444
}
4545

46+
pub fn wrapdbg<T, S>(original: T, message: S) -> WrappedError
47+
where
48+
T: std::fmt::Debug,
49+
S: Into<String>,
50+
{
51+
WrappedError {
52+
message: message.into(),
53+
original: format!("{:?}", original),
54+
}
55+
}
56+
4657
pub fn wrap<T, S>(original: T, message: S) -> WrappedError
4758
where
4859
T: Display,

0 commit comments

Comments
 (0)