Skip to content

Commit f57aede

Browse files
committed
feat: support disabling remote log uploads
Updates tailscale/tailscale#13174 - adds a new switch to the settings page for enabling/disabling remote log uploads - calls the `Disable` function from the `logtail` package during init when the setting is turn off ref: https://pkg.go.dev/tailscale.com/logtail#Disable Signed-off-by: Michael Nahkies <[email protected]>
1 parent d2c005f commit f57aede

File tree

7 files changed

+60
-2
lines changed

7 files changed

+60
-2
lines changed

android/src/main/java/com/tailscale/ipn/App.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,9 @@ class App : UninitializedApp(), libtailscale.AppContext, ViewModelStoreOwner {
272272
override fun isChromeOS(): Boolean {
273273
return packageManager.hasSystemFeature("android.hardware.type.pc")
274274
}
275+
override fun isClientLoggingEnabled(): Boolean {
276+
return getIsClientLoggingEnabled()
277+
}
275278
override fun getInterfacesAsString(): String {
276279
val interfaces: ArrayList<NetworkInterface> =
277280
java.util.Collections.list(NetworkInterface.getNetworkInterfaces())
@@ -351,6 +354,7 @@ open class UninitializedApp : Application() {
351354
// the VPN (i.e. we're logged in and machine is authorized).
352355
private const val ABLE_TO_START_VPN_KEY = "ableToStartVPN"
353356
private const val DISALLOWED_APPS_KEY = "disallowedApps"
357+
private const val IS_CLIENT_LOGGING_ENABLED_KEY = "isClientLoggingEnabled"
354358
// File for shared preferences that are not encrypted.
355359
private const val UNENCRYPTED_PREFERENCES = "unencrypted"
356360
private lateinit var appInstance: UninitializedApp
@@ -504,6 +508,12 @@ open class UninitializedApp : Application() {
504508
}
505509
return builder.build()
506510
}
511+
fun getIsClientLoggingEnabled(): Boolean {
512+
return getUnencryptedPrefs().getBoolean(IS_CLIENT_LOGGING_ENABLED_KEY, true)
513+
}
514+
fun updateIsClientLoggingEnabled(value: Boolean) {
515+
getUnencryptedPrefs().edit().putBoolean(IS_CLIENT_LOGGING_ENABLED_KEY, value).apply()
516+
}
507517
fun updateUserDisallowedPackageNames(packageNames: List<String>) {
508518
if (packageNames.any { it.isEmpty() }) {
509519
TSLog.e(TAG, "updateUserDisallowedPackageNames called with empty packageName(s)")

android/src/main/java/com/tailscale/ipn/ui/view/SettingsView.kt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ fun SettingsView(
5858
val isVPNPrepared by appViewModel.vpnPrepared.collectAsState()
5959
val showTailnetLock by MDMSettings.manageTailnetLock.flow.collectAsState()
6060
val useTailscaleSubnets by MDMSettings.useTailscaleSubnets.flow.collectAsState()
61+
val isClientRemoteLoggingEnabled by viewModel.isClientRemoteLoggingEnabled.collectAsState()
6162

6263
Scaffold(
6364
topBar = {
@@ -106,6 +107,16 @@ fun SettingsView(
106107
Lists.ItemDivider()
107108
Setting.Text(R.string.subnet_routing, onClick = settingsNav.onNavigateToSubnetRouting)
108109
}
110+
111+
Lists.ItemDivider()
112+
Setting.Switch(
113+
R.string.client_remote_logging_enabled,
114+
subtitle = stringResource(R.string.client_remote_logging_enabled_subtitle),
115+
isOn = isClientRemoteLoggingEnabled,
116+
onToggle = {
117+
viewModel.toggleIsClientRemoteLoggingEnabled()
118+
})
119+
109120
if (!AndroidTVUtil.isAndroidTV()) {
110121
Lists.ItemDivider()
111122
Setting.Text(R.string.permissions, onClick = settingsNav.onNavigateToPermissions)
@@ -175,6 +186,7 @@ object Setting {
175186
fun Switch(
176187
titleRes: Int = 0,
177188
title: String? = null,
189+
subtitle: String? = null,
178190
isOn: Boolean,
179191
enabled: Boolean = true,
180192
onToggle: (Boolean) -> Unit = {}
@@ -187,6 +199,15 @@ object Setting {
187199
style = MaterialTheme.typography.bodyMedium,
188200
)
189201
},
202+
supportingContent =
203+
subtitle?.let {
204+
{
205+
Text(
206+
it,
207+
style = MaterialTheme.typography.bodySmall,
208+
color = MaterialTheme.colorScheme.onSurfaceVariant)
209+
}
210+
},
190211
trailingContent = {
191212
TintedSwitch(checked = isOn, onCheckedChange = onToggle, enabled = enabled)
192213
})

android/src/main/java/com/tailscale/ipn/ui/viewModel/SettingsViewModel.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package com.tailscale.ipn.ui.viewModel
55

66
import androidx.lifecycle.viewModelScope
7+
import com.tailscale.ipn.App
78
import com.tailscale.ipn.ui.localapi.Client
89
import com.tailscale.ipn.ui.notifier.Notifier
910
import com.tailscale.ipn.ui.util.LoadingIndicator
@@ -34,8 +35,11 @@ class SettingsViewModel : IpnViewModel() {
3435
val tailNetLockEnabled: StateFlow<Boolean?> = MutableStateFlow(null)
3536
// True if tailscaleDNS is enabled. nil if not yet known.
3637
val corpDNSEnabled: StateFlow<Boolean?> = MutableStateFlow(null)
38+
val isClientRemoteLoggingEnabled: StateFlow<Boolean> = MutableStateFlow(true)
3739

3840
init {
41+
isClientRemoteLoggingEnabled.set(App.get().isClientLoggingEnabled())
42+
3943
viewModelScope.launch {
4044
Notifier.netmap.collect { netmap -> isAdmin.set(netmap?.SelfNode?.isAdmin ?: false) }
4145
}
@@ -52,4 +56,9 @@ class SettingsViewModel : IpnViewModel() {
5256
}
5357
}
5458
}
59+
60+
fun toggleIsClientRemoteLoggingEnabled() {
61+
isClientRemoteLoggingEnabled.set(!isClientRemoteLoggingEnabled.value)
62+
App.get().updateIsClientLoggingEnabled(isClientRemoteLoggingEnabled.value)
63+
}
5564
}

android/src/main/res/values/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,8 @@
346346
<string name="run_as_subnet_router">Run as subnet router</string>
347347
<string name="use_tailscale_subnets_subtitle">Route traffic according to your network\'s rules. Some networks require this to access IP addresses that don\'t start with 100.x.y.z.</string>
348348
<string name="subnet_routing">Subnet routing</string>
349+
<string name="client_remote_logging_enabled">Remote client logging</string>
350+
<string name="client_remote_logging_enabled_subtitle">Equivalent to --no-logs-no-support on Linux.\nChanges require restarting the app to take effect.</string>
349351
<string name="specifies_a_device_name_to_be_used_instead_of_the_automatic_default">Specifies a device name to be used instead of the automatic default.</string>
350352
<string name="hostname">Hostname</string>
351353
<string name="failed_to_save">Failed to save</string>

libtailscale/backend.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ func (a *App) newBackend(dataDir string, appCtx AppContext, store *stateStore,
288288
log.Printf("netmon.New: %w", err)
289289
}
290290
b.netMon = netMon
291-
b.setupLogs(dataDir, logID, logf, sys.HealthTracker())
291+
b.setupLogs(dataDir, logID, logf, sys.HealthTracker(), a.isClientLoggingEnabled())
292292
dialer := new(tsdial.Dialer)
293293
vf := &VPNFacade{
294294
SetBoth: b.setCfg,

libtailscale/interfaces.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ type AppContext interface {
4848
// IsChromeOS reports whether we're on a ChromeOS device.
4949
IsChromeOS() (bool, error)
5050

51+
// IsClientLoggingEnabled reports whether the user has enabled remote client logging.
52+
IsClientLoggingEnabled() (bool, error)
53+
5154
// GetInterfacesAsString gets a string representation of all network
5255
// interfaces.
5356
GetInterfacesAsString() (string, error)

libtailscale/tailscale.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,16 @@ func (a *App) isChromeOS() bool {
9494
return isChromeOS
9595
}
9696

97+
func (a *App) isClientLoggingEnabled() bool {
98+
isClientLoggingEnabled, err := a.appCtx.IsClientLoggingEnabled()
99+
if err != nil {
100+
panic(err)
101+
}
102+
return isClientLoggingEnabled
103+
}
104+
97105
// SetupLogs sets up remote logging.
98-
func (b *backend) setupLogs(logDir string, logID logid.PrivateID, logf logger.Logf, health *health.Tracker) {
106+
func (b *backend) setupLogs(logDir string, logID logid.PrivateID, logf logger.Logf, health *health.Tracker, enableUpload bool) {
99107
if b.netMon == nil {
100108
panic("netMon must be created prior to SetupLogs")
101109
}
@@ -125,6 +133,11 @@ func (b *backend) setupLogs(logDir string, logID logid.PrivateID, logf logger.Lo
125133

126134
b.logger = logtail.NewLogger(logcfg, logf)
127135

136+
if !enableUpload {
137+
log.Printf("disabling remote log upload")
138+
logtail.Disable()
139+
}
140+
128141
log.SetFlags(0)
129142
log.SetOutput(b.logger)
130143

0 commit comments

Comments
 (0)