chore: diagnose sync issues with sync.log file (#6950)

* chore: filter sync log

* chore: filter sync log

* chore: enable/disable sync log

* chore: enable/disable sync log

* chore: observer document and folder

* fix: integration test

---------

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
This commit is contained in:
Nathan.fooo 2024-12-10 10:02:48 +08:00 committed by GitHub
parent 2c88653a69
commit d2b2f17b1c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 252 additions and 49 deletions

View file

@ -1,16 +1,17 @@
import 'data_migration/data_migration_test_runner.dart'
as data_migration_test_runner;
import 'document/document_test_runner.dart' as document_test_runner;
import 'set_env.dart' as preset_af_cloud_env_test;
import 'sidebar/sidebar_move_page_test.dart' as sidebar_move_page_test;
import 'uncategorized/uncategorized_test_runner.dart'
as uncategorized_test_runner;
import 'workspace/workspace_test_runner.dart' as workspace_test_runner;
import 'data_migration/data_migration_test_runner.dart'
as data_migration_test_runner;
import 'set_env.dart' as preset_af_cloud_env_test;
Future<void> main() async {
preset_af_cloud_env_test.main();
data_migration_test_runner.main();
// uncategorized
uncategorized_test_runner.main();

View file

@ -58,6 +58,7 @@ class KVKeys {
static const String kCloudType = 'kCloudType';
static const String kAppflowyCloudBaseURL = 'kAppFlowyCloudBaseURL';
static const String kAppFlowyEnableSyncTrace = 'kAppFlowyEnableSyncTrace';
/// The key for saving the text scale factor.
///

View file

@ -39,6 +39,7 @@ class AppFlowyCloudConfiguration {
required this.base_url,
required this.ws_base_url,
required this.gotrue_url,
required this.enable_sync_trace,
});
factory AppFlowyCloudConfiguration.fromJson(Map<String, dynamic> json) =>
@ -47,6 +48,7 @@ class AppFlowyCloudConfiguration {
final String base_url;
final String ws_base_url;
final String gotrue_url;
final bool enable_sync_trace;
Map<String, dynamic> toJson() => _$AppFlowyCloudConfigurationToJson(this);
@ -55,6 +57,7 @@ class AppFlowyCloudConfiguration {
base_url: '',
ws_base_url: '',
gotrue_url: '',
enable_sync_trace: false,
);
}

View file

@ -212,6 +212,7 @@ class AppFlowyCloudSharedEnv {
base_url: Env.afCloudUrl,
ws_base_url: await _getAppFlowyCloudWSUrl(Env.afCloudUrl),
gotrue_url: await _getAppFlowyCloudGotrueUrl(Env.afCloudUrl),
enable_sync_trace: false,
);
return AppFlowyCloudSharedEnv(
@ -241,12 +242,14 @@ Future<AppFlowyCloudConfiguration> configurationFromUri(
base_url: "$baseUrl:8000",
ws_base_url: "ws://${baseUri.host}:8000/ws/v1",
gotrue_url: "$baseUrl:9999",
enable_sync_trace: true,
);
} else {
return AppFlowyCloudConfiguration(
base_url: baseUrl,
ws_base_url: await _getAppFlowyCloudWSUrl(baseUrl),
gotrue_url: await _getAppFlowyCloudGotrueUrl(baseUrl),
enable_sync_trace: await getSyncLogEnabled(),
);
}
}
@ -271,6 +274,24 @@ Future<String> getAppFlowyCloudUrl() async {
return result ?? kAppflowyCloudUrl;
}
Future<bool> getSyncLogEnabled() async {
final result =
await getIt<KeyValueStorage>().get(KVKeys.kAppFlowyEnableSyncTrace);
if (result == null) {
return false;
}
return result.toLowerCase() == "true";
}
Future<void> setSyncLogEnabled(bool enable) async {
await getIt<KeyValueStorage>().set(
KVKeys.kAppFlowyEnableSyncTrace,
enable.toString().toLowerCase(),
);
}
Future<String> _getAppFlowyCloudWSUrl(String baseURL) async {
try {
final uri = Uri.parse(baseURL);

View file

@ -1,3 +1,4 @@
import 'package:appflowy/env/cloud_env.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/workspace/application/settings/cloud_setting_listener.dart';
import 'package:appflowy_backend/dispatch/dispatch.dart';
@ -14,7 +15,7 @@ class AppFlowyCloudSettingBloc
extends Bloc<AppFlowyCloudSettingEvent, AppFlowyCloudSettingState> {
AppFlowyCloudSettingBloc(CloudSettingPB setting)
: _listener = UserCloudConfigListener(),
super(AppFlowyCloudSettingState.initial(setting)) {
super(AppFlowyCloudSettingState.initial(setting, false)) {
_dispatch();
}
@ -31,6 +32,10 @@ class AppFlowyCloudSettingBloc
(event, emit) async {
await event.when(
initial: () async {
await getSyncLogEnabled().then((value) {
emit(state.copyWith(isSyncLogEnabled: value));
});
_listener.start(
onSettingChanged: (result) {
if (isClosed) {
@ -48,6 +53,10 @@ class AppFlowyCloudSettingBloc
final config = UpdateCloudConfigPB.create()..enableSync = isEnable;
await UserEventSetCloudConfig(config).send();
},
enableSyncLog: (isEnable) async {
await setSyncLogEnabled(isEnable);
emit(state.copyWith(isSyncLogEnabled: isEnable));
},
didReceiveSetting: (CloudSettingPB setting) {
emit(
state.copyWith(
@ -67,6 +76,8 @@ class AppFlowyCloudSettingEvent with _$AppFlowyCloudSettingEvent {
const factory AppFlowyCloudSettingEvent.initial() = _Initial;
const factory AppFlowyCloudSettingEvent.enableSync(bool isEnable) =
_EnableSync;
const factory AppFlowyCloudSettingEvent.enableSyncLog(bool isEnable) =
_EnableSyncLog;
const factory AppFlowyCloudSettingEvent.didReceiveSetting(
CloudSettingPB setting,
) = _DidUpdateSetting;
@ -77,12 +88,17 @@ class AppFlowyCloudSettingState with _$AppFlowyCloudSettingState {
const factory AppFlowyCloudSettingState({
required CloudSettingPB setting,
required bool showRestartHint,
required bool isSyncLogEnabled,
}) = _AppFlowyCloudSettingState;
factory AppFlowyCloudSettingState.initial(CloudSettingPB setting) =>
factory AppFlowyCloudSettingState.initial(
CloudSettingPB setting,
bool isSyncLogEnabled,
) =>
AppFlowyCloudSettingState(
setting: setting,
showRestartHint: setting.serverUrl.isNotEmpty,
isSyncLogEnabled: isSyncLogEnabled,
);
}

View file

@ -102,7 +102,9 @@ class SpaceBloc extends Bloc<SpaceEvent, SpaceState> {
if (openFirstPage) {
if (currentSpace != null) {
add(SpaceEvent.open(currentSpace));
if (!isClosed) {
add(SpaceEvent.open(currentSpace));
}
}
}
},

View file

@ -68,6 +68,7 @@ class AppFlowyCloudViewSetting extends StatelessWidget {
return Column(
children: [
const AppFlowyCloudEnableSync(),
const AppFlowyCloudSyncLogEnabled(),
const VSpace(12),
RestartButton(
onClick: () {
@ -123,6 +124,7 @@ class CustomAppFlowyCloudView extends StatelessWidget {
final List<Widget> children = [];
children.addAll([
const AppFlowyCloudEnableSync(),
const AppFlowyCloudSyncLogEnabled(),
const VSpace(40),
]);
@ -331,6 +333,47 @@ class AppFlowyCloudEnableSync extends StatelessWidget {
}
}
class AppFlowyCloudSyncLogEnabled extends StatelessWidget {
const AppFlowyCloudSyncLogEnabled({super.key});
@override
Widget build(BuildContext context) {
return BlocBuilder<AppFlowyCloudSettingBloc, AppFlowyCloudSettingState>(
builder: (context, state) {
return Row(
children: [
FlowyText.medium(LocaleKeys.settings_menu_enableSyncLog.tr()),
const Spacer(),
Toggle(
value: state.isSyncLogEnabled,
onChanged: (value) {
if (value) {
showCancelAndConfirmDialog(
context: context,
title: LocaleKeys.settings_menu_enableSyncLog.tr(),
description:
LocaleKeys.settings_menu_enableSyncLogWarning.tr(),
confirmLabel: LocaleKeys.button_confirm.tr(),
onConfirm: () {
context
.read<AppFlowyCloudSettingBloc>()
.add(AppFlowyCloudSettingEvent.enableSyncLog(value));
},
);
} else {
context
.read<AppFlowyCloudSettingBloc>()
.add(AppFlowyCloudSettingEvent.enableSyncLog(value));
}
},
),
],
);
},
);
}
}
class BillingGateGuard extends StatelessWidget {
const BillingGateGuard({required this.builder, super.key});

View file

@ -1036,6 +1036,8 @@
"syncSetting": "Sync Setting",
"cloudSettings": "Cloud Settings",
"enableSync": "Enable sync",
"enableSyncLog": "Enable sync logging",
"enableSyncLogWarning": "Thank you for helping diagnose sync issues. This will log your document edits to a local file. Please quit and reopen the app after enabling",
"enableEncrypt": "Encrypt data",
"cloudURL": "Base URL",
"invalidCloudURLScheme": "Invalid Scheme",
@ -2904,4 +2906,4 @@
"permissionDenied": "No permission to open this file",
"unknownError": "File open failed"
}
}
}

View file

@ -1388,7 +1388,7 @@ dependencies = [
"cssparser-macros",
"dtoa-short",
"itoa",
"phf 0.8.0",
"phf 0.11.2",
"smallvec",
]
@ -4369,7 +4369,7 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
dependencies = [
"phf_macros",
"phf_macros 0.8.0",
"phf_shared 0.8.0",
"proc-macro-hack",
]
@ -4389,6 +4389,7 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
dependencies = [
"phf_macros 0.11.2",
"phf_shared 0.11.2",
]
@ -4456,6 +4457,19 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "phf_macros"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
dependencies = [
"phf_generator 0.11.2",
"phf_shared 0.11.2",
"proc-macro2",
"quote",
"syn 2.0.47",
]
[[package]]
name = "phf_shared"
version = "0.8.0"
@ -6589,9 +6603,9 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
[[package]]
name = "tracing"
version = "0.1.40"
version = "0.1.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
dependencies = [
"log",
"pin-project-lite",
@ -6613,9 +6627,9 @@ dependencies = [
[[package]]
name = "tracing-attributes"
version = "0.1.27"
version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
dependencies = [
"proc-macro2",
"quote",
@ -6624,9 +6638,9 @@ dependencies = [
[[package]]
name = "tracing-bunyan-formatter"
version = "0.3.9"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5c266b9ac83dedf0e0385ad78514949e6d89491269e7065bee51d2bb8ec7373"
checksum = "2d637245a0d8774bd48df6482e086c59a8b5348a910c3b0579354045a9d82411"
dependencies = [
"ahash 0.8.6",
"gethostname",
@ -6642,9 +6656,9 @@ dependencies = [
[[package]]
name = "tracing-core"
version = "0.1.32"
version = "0.1.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
dependencies = [
"once_cell",
"valuable",
@ -6674,9 +6688,9 @@ dependencies = [
[[package]]
name = "tracing-serde"
version = "0.1.3"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1"
checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1"
dependencies = [
"serde",
"tracing-core",
@ -6684,9 +6698,9 @@ dependencies = [
[[package]]
name = "tracing-subscriber"
version = "0.3.18"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
dependencies = [
"matchers",
"nu-ansi-term",

View file

@ -444,6 +444,7 @@ pub async fn use_localhost_af_cloud() {
base_url,
ws_base_url,
gotrue_url,
enable_sync_trace: true,
maximum_upload_file_size_in_bytes: None,
}
.write_env();

View file

@ -40,6 +40,7 @@ impl fmt::Debug for AppFlowyCoreConfig {
debug.field("base_url", &config.base_url);
debug.field("ws_url", &config.ws_base_url);
debug.field("gotrue_url", &config.gotrue_url);
debug.field("enable_sync_trace", &config.enable_sync_trace);
}
debug.finish()
}
@ -83,11 +84,22 @@ impl AppFlowyCoreConfig {
name: String,
) -> Self {
let cloud_config = AFCloudConfiguration::from_env().ok();
let mut log_crates = vec![];
let storage_path = match &cloud_config {
None => custom_application_path,
Some(config) => make_user_data_folder(&custom_application_path, &config.base_url),
Some(config) => {
if config.enable_sync_trace {
log_crates.push("sync_trace_log".to_string());
}
make_user_data_folder(&custom_application_path, &config.base_url)
},
};
let log_filter = create_log_filter("info".to_owned(), vec![], OperatingSystem::from(&platform));
let log_filter = create_log_filter(
"info".to_owned(),
log_crates,
OperatingSystem::from(&platform),
);
AppFlowyCoreConfig {
app_version,

View file

@ -5,12 +5,17 @@ use crate::notification::{document_notification_builder, DocumentNotification};
use collab::preclude::Collab;
use collab_document::document::Document;
use futures::StreamExt;
use lib_infra::sync_trace;
pub fn subscribe_document_changed(doc_id: &str, document: &mut Document) {
let doc_id_clone_for_block_changed = doc_id.to_owned();
document.subscribe_block_changed("key", move |events, is_remote| {
#[cfg(feature = "verbose_log")]
tracing::trace!("subscribe_document_changed: {:?}", events);
sync_trace!(
"[Document] block changed in doc_id: {}, is_remote: {}, events: {:?}",
doc_id_clone_for_block_changed,
is_remote,
events
);
// send notification to the client.
document_notification_builder(
@ -23,8 +28,12 @@ pub fn subscribe_document_changed(doc_id: &str, document: &mut Document) {
let doc_id_clone_for_awareness_state = doc_id.to_owned();
document.subscribe_awareness_state("key", move |events| {
#[cfg(feature = "verbose_log")]
tracing::trace!("subscribe_awareness_state: {:?}", events);
sync_trace!(
"[Document] awareness state in doc_id: {}, events: {:?}",
doc_id_clone_for_awareness_state,
events
);
document_notification_builder(
&doc_id_clone_for_awareness_state,
DocumentNotification::DidUpdateDocumentAwarenessState,

View file

@ -1188,7 +1188,7 @@ impl FolderManager {
if let Some(view) = &view {
let view_layout: ViewLayout = view.layout.clone().into();
if let Some(handle) = self.operation_handlers.get(&view_layout) {
info!("Open view: {}", view.id);
info!("Open view: {}-{}", view.name, view.id);
if let Err(err) = handle.open_view(&view_id).await {
error!("Open view error: {:?}", err);
}

View file

@ -10,6 +10,7 @@ use collab_folder::{
Folder, SectionChange, SectionChangeReceiver, TrashSectionChange, View, ViewChange,
ViewChangeReceiver,
};
use lib_infra::sync_trace;
use std::collections::HashSet;
use std::sync::Weak;
@ -45,10 +46,14 @@ pub(crate) fn subscribe_folder_view_changed(
ChildViewChangeReason::Create,
);
let folder = lock.read().await;
notify_parent_view_did_change(&workspace_id, &folder, vec![view.parent_view_id]);
let parent_view_id = view.parent_view_id.clone();
notify_parent_view_did_change(&workspace_id, &folder, vec![parent_view_id]);
sync_trace!("[Folder] create view: {:?}", view);
},
ViewChange::DidDeleteView { views } => {
for view in views {
sync_trace!("[Folder] delete view: {:?}", view);
notify_child_views_changed(
view_pb_without_child_views(view.as_ref().clone()),
ChildViewChangeReason::Delete,
@ -56,6 +61,8 @@ pub(crate) fn subscribe_folder_view_changed(
}
},
ViewChange::DidUpdate { view } => {
sync_trace!("[Folder] update view: {:?}", view);
notify_view_did_change(view.clone());
notify_child_views_changed(
view_pb_without_child_views(view.clone()),

View file

@ -7,6 +7,7 @@ use flowy_error::{ErrorCode, FlowyError};
pub const APPFLOWY_CLOUD_BASE_URL: &str = "APPFLOWY_CLOUD_ENV_APPFLOWY_CLOUD_BASE_URL";
pub const APPFLOWY_CLOUD_WS_BASE_URL: &str = "APPFLOWY_CLOUD_ENV_APPFLOWY_CLOUD_WS_BASE_URL";
pub const APPFLOWY_CLOUD_GOTRUE_URL: &str = "APPFLOWY_CLOUD_ENV_APPFLOWY_CLOUD_GOTRUE_URL";
pub const APPFLOWY_ENABLE_SYNC_TRACE: &str = "APPFLOWY_ENABLE_SYNC_TRACE";
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct AFCloudConfiguration {
@ -14,6 +15,8 @@ pub struct AFCloudConfiguration {
pub ws_base_url: String,
pub gotrue_url: String,
#[serde(default)]
pub enable_sync_trace: bool,
#[serde(default)]
pub maximum_upload_file_size_in_bytes: Option<u64>,
}
@ -55,10 +58,15 @@ impl AFCloudConfiguration {
);
}
let enable_sync_trace = std::env::var(APPFLOWY_ENABLE_SYNC_TRACE)
.map(|v| v == "true" || v == "1")
.unwrap_or(false);
Ok(Self {
base_url,
ws_base_url,
gotrue_url,
enable_sync_trace,
maximum_upload_file_size_in_bytes: None,
})
}
@ -68,5 +76,13 @@ impl AFCloudConfiguration {
std::env::set_var(APPFLOWY_CLOUD_BASE_URL, &self.base_url);
std::env::set_var(APPFLOWY_CLOUD_WS_BASE_URL, &self.ws_base_url);
std::env::set_var(APPFLOWY_CLOUD_GOTRUE_URL, &self.gotrue_url);
std::env::set_var(
APPFLOWY_ENABLE_SYNC_TRACE,
if self.enable_sync_trace {
"true"
} else {
"false"
},
);
}
}

View file

@ -1,3 +1,6 @@
#[allow(unused_imports)]
use tracing::info;
#[macro_export]
macro_rules! if_native {
($($item:item)*) => {$(
@ -136,3 +139,10 @@ pub fn get_operating_system() -> OperatingSystem {
}
}
}
#[macro_export]
macro_rules! sync_trace {
($($arg:tt)*) => {
tracing::info!(target: "sync_trace_log", $($arg)*);
};
}

View file

@ -6,8 +6,8 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
tracing-subscriber = { version = "0.3.17", features = ["registry", "env-filter", "ansi", "json"] }
tracing-bunyan-formatter = "0.3.9"
tracing-subscriber = { version = "0.3.19", features = ["registry", "env-filter", "ansi", "json"] }
tracing-bunyan-formatter = "0.3.10"
tracing-appender = "0.2.3"
tracing-core = "0.1"
tracing.workspace = true

View file

@ -7,6 +7,7 @@ use tracing::{Event, Id, Subscriber};
use tracing_bunyan_formatter::JsonStorage;
use tracing_core::metadata::Level;
use tracing_core::span::Attributes;
use tracing_core::Metadata;
use tracing_subscriber::{fmt::MakeWriter, layer::Context, registry::SpanRef, Layer};
const LEVEL: &str = "level";
@ -22,6 +23,8 @@ const IGNORE_FIELDS: [&str; 2] = [LOG_MODULE_PATH, LOG_TARGET_PATH];
pub struct FlowyFormattingLayer<'a, W: MakeWriter<'static> + 'static> {
make_writer: W,
with_target: bool,
#[allow(clippy::type_complexity)]
target_filter: Option<Box<dyn Fn(&str) -> bool + Send + Sync>>,
phantom: std::marker::PhantomData<&'a ()>,
}
@ -34,10 +37,26 @@ where
Self {
make_writer,
with_target: true,
target_filter: None,
phantom: std::marker::PhantomData,
}
}
pub fn with_target_filter<F>(mut self, filter: F) -> Self
where
F: Fn(&str) -> bool + Send + Sync + 'static,
{
self.target_filter = Some(Box::new(filter));
self
}
fn should_log(&self, metadata: &Metadata<'_>) -> bool {
self
.target_filter
.as_ref()
.map_or(true, |f| f(metadata.target()))
}
fn serialize_fields(
&self,
map_serializer: &mut impl SerializeMap<Error = serde_json::Error>,
@ -45,7 +64,6 @@ where
_level: &Level,
) -> Result<(), std::io::Error> {
map_serializer.serialize_entry(MESSAGE, &message)?;
// map_serializer.serialize_entry(LEVEL, &format!("{}", level))?;
map_serializer.serialize_entry(TIME, &Local::now().format("%m-%d %H:%M:%S").to_string())?;
Ok(())
}
@ -160,6 +178,10 @@ where
W: for<'writer> MakeWriter<'writer> + 'static,
{
fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
if !self.should_log(event.metadata()) {
return;
}
// Events do not necessarily happen in the context of a span, hence
// lookup_current returns an `Option<SpanRef<_>>` instead of a
// `SpanRef<_>`.
@ -177,14 +199,9 @@ where
let message = format_event_message(&current_span, event, &event_visitor, &ctx);
self.serialize_fields(&mut map_serializer, &message, event.metadata().level())?;
// Additional metadata useful for debugging
// They should be nested under `src` (see https://github.com/trentm/node-bunyan#src )
// but `tracing` does not support nested values yet
if self.with_target {
map_serializer.serialize_entry("target", event.metadata().target())?;
}
// map_serializer.serialize_entry("line", &event.metadata().line())?;
// map_serializer.serialize_entry("file", &event.metadata().file())?;
@ -224,6 +241,9 @@ where
fn on_new_span(&self, _attrs: &Attributes, id: &Id, ctx: Context<'_, S>) {
let span = ctx.span(id).expect("Span not found, this is a bug");
if !self.should_log(span.metadata()) {
return;
}
if let Ok(serialized) = self.serialize_span(&span, Type::EnterSpan, &ctx) {
let _ = self.emit(serialized);
}
@ -231,6 +251,9 @@ where
fn on_close(&self, id: Id, ctx: Context<'_, S>) {
let span = ctx.span(&id).expect("Span not found, this is a bug");
if !self.should_log(span.metadata()) {
return;
}
if let Ok(serialized) = self.serialize_span(&span, Type::ExitSpan, &ctx) {
let _ = self.emit(serialized);
}

View file

@ -2,6 +2,8 @@ use std::io;
use std::io::Write;
use std::sync::{Arc, RwLock};
use crate::layer::FlowyFormattingLayer;
use crate::stream_log::{StreamLog, StreamLogSender};
use chrono::Local;
use lazy_static::lazy_static;
use lib_infra::util::OperatingSystem;
@ -13,26 +15,26 @@ use tracing_subscriber::fmt::format::Writer;
use tracing_subscriber::fmt::MakeWriter;
use tracing_subscriber::{layer::SubscriberExt, EnvFilter};
use crate::layer::FlowyFormattingLayer;
use crate::stream_log::{StreamLog, StreamLogSender};
mod layer;
pub mod stream_log;
lazy_static! {
static ref LOG_GUARD: RwLock<Option<WorkerGuard>> = RwLock::new(None);
static ref APP_LOG_GUARD: RwLock<Option<WorkerGuard>> = RwLock::new(None);
static ref COLLAB_SYNC_LOG_GUARD: RwLock<Option<WorkerGuard>> = RwLock::new(None);
}
pub struct Builder {
#[allow(dead_code)]
name: String,
env_filter: String,
file_appender: RollingFileAppender,
app_log_appender: RollingFileAppender,
sync_log_appender: RollingFileAppender,
#[allow(dead_code)]
platform: OperatingSystem,
stream_log_sender: Option<Arc<dyn StreamLogSender>>,
}
const SYNC_TARGET: &str = "sync_trace_log";
impl Builder {
pub fn new(
name: &str,
@ -40,17 +42,26 @@ impl Builder {
platform: &OperatingSystem,
stream_log_sender: Option<Arc<dyn StreamLogSender>>,
) -> Self {
let file_appender = RollingFileAppender::builder()
let app_log_appender = RollingFileAppender::builder()
.rotation(Rotation::DAILY)
.filename_prefix(name)
.max_log_files(6)
.build(directory)
.unwrap_or(tracing_appender::rolling::daily(directory, name));
let sync_log_name = "log.sync";
let sync_log_appender = RollingFileAppender::builder()
.rotation(Rotation::HOURLY)
.filename_prefix(sync_log_name)
.max_log_files(24)
.build(directory)
.unwrap_or(tracing_appender::rolling::hourly(directory, sync_log_name));
Builder {
name: name.to_owned(),
env_filter: "info".to_owned(),
file_appender,
app_log_appender,
sync_log_appender,
platform: platform.clone(),
stream_log_sender,
}
@ -63,8 +74,18 @@ impl Builder {
pub fn build(self) -> Result<(), String> {
let env_filter = EnvFilter::new(self.env_filter);
let (non_blocking, guard) = tracing_appender::non_blocking(self.file_appender);
let file_layer = FlowyFormattingLayer::new(non_blocking);
let (appflowy_log_non_blocking, app_log_guard) =
tracing_appender::non_blocking(self.app_log_appender);
*APP_LOG_GUARD.write().unwrap() = Some(app_log_guard);
let app_file_layer = FlowyFormattingLayer::new(appflowy_log_non_blocking)
.with_target_filter(|target| target != SYNC_TARGET);
let (sync_log_non_blocking, sync_log_guard) =
tracing_appender::non_blocking(self.sync_log_appender);
*COLLAB_SYNC_LOG_GUARD.write().unwrap() = Some(sync_log_guard);
let collab_sync_file_layer = FlowyFormattingLayer::new(sync_log_non_blocking)
.with_target_filter(|target| target == SYNC_TARGET);
if let Some(stream_log_sender) = &self.stream_log_sender {
let subscriber = tracing_subscriber::fmt()
@ -79,7 +100,8 @@ impl Builder {
.with_env_filter(env_filter)
.finish()
.with(JsonStorageLayer)
.with(file_layer);
.with(app_file_layer)
.with(collab_sync_file_layer);
set_global_default(subscriber).map_err(|e| format!("{:?}", e))?;
} else {
let subscriber = tracing_subscriber::fmt()
@ -92,11 +114,11 @@ impl Builder {
.finish()
.with(FlowyFormattingLayer::new(DebugStdoutWriter))
.with(JsonStorageLayer)
.with(file_layer);
.with(app_file_layer)
.with(collab_sync_file_layer);
set_global_default(subscriber).map_err(|e| format!("{:?}", e))?;
};
*LOG_GUARD.write().unwrap() = Some(guard);
Ok(())
}
}