1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00

Log screen in RN app

This commit is contained in:
Laurent Cozic 2017-07-07 18:19:24 +01:00
parent 216a6780cb
commit e0664167eb
6 changed files with 142 additions and 33 deletions

View File

@ -41,6 +41,13 @@ class ScreenHeaderComponent extends Component {
}
}
log_press() {
this.props.dispatch({
type: 'Navigation/NAVIGATE',
routeName: 'Log',
});
}
render() {
let key = 0;
let menuOptionComponents = [];
@ -56,10 +63,10 @@ class ScreenHeaderComponent extends Component {
menuOptionComponents.push(<View key={'menuOption_' + key++} style={styles.divider}/>);
}
// menuOptionComponents.push(
// <MenuOption value={1} key={'menuOption_' + key++}>
// <Text>{_('Configuration')}</Text>
// </MenuOption>);
menuOptionComponents.push(
<MenuOption value={() => this.log_press()} key={'menuOption_' + key++}>
<Text>{_('Log')}</Text>
</MenuOption>);
let title = 'title' in this.props && this.props.title !== null ? this.props.title : _(this.props.navState.routeName);

View File

@ -0,0 +1,62 @@
import React, { Component } from 'react';
import { ListView, View, Text } from 'react-native';
import { connect } from 'react-redux'
import { Log } from 'lib/log.js'
import { reg } from 'lib/registry.js'
import { ScreenHeader } from 'lib/components/screen-header.js';
import { time } from 'lib/time-utils'
class LogScreenComponent extends React.Component {
static navigationOptions(options) {
return { header: null };
}
constructor() {
super();
const ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => { return r1 !== r2; }
});
this.state = {
dataSource: ds,
};
}
componentWillMount() {
reg.logger().lastEntries(1000).then((entries) => {
const newDataSource = this.state.dataSource.cloneWithRows(entries);
this.setState({ dataSource: newDataSource });
});
}
render() {
let renderRow = (item) => {
return (
<View style={{flexDirection: 'row', paddingLeft: 1, paddingRight: 1, paddingTop:0, paddingBottom:0 }}>
<Text style={{fontFamily: 'monospace', fontSize: 10}}>{time.unixMsToIsoSec(item.timestamp) + ': ' + item.message}</Text>
</View>
);
}
// `enableEmptySections` is to fix this warning: https://github.com/FaridSafi/react-native-gifted-listview/issues/39
return (
<View style={{flex: 1}}>
<ScreenHeader navState={this.props.navigation.state} />
<ListView
dataSource={this.state.dataSource}
renderRow={renderRow}
enableEmptySections={true}
/>
</View>
);
}
}
const LogScreen = connect(
(state) => {
return {};
}
)(LogScreenComponent)
export { LogScreen };

View File

@ -55,9 +55,17 @@ class Logger {
return output;
}
objectsToString(...object) {
let output = [];
for (let i = 0; i < object.length; i++) {
output.push('"' + this.objectToString(object[i]) + '"');
}
return output.join(', ');
}
static databaseCreateTableSql() {
let output = `
CREATE TABLE logs (
CREATE TABLE IF NOT EXISTS logs (
id INTEGER PRIMARY KEY,
source TEXT,
level INT NOT NULL,
@ -68,14 +76,25 @@ class Logger {
return output.split("\n").join(' ');
}
log(level, object) {
// Only for database at the moment
async lastEntries(limit = 100) {
for (let i = 0; i < this.targets_.length; i++) {
const target = this.targets_[i];
if (target.type == 'database') {
return await target.database.selectAll('SELECT * FROM logs ORDER BY timestamp DESC LIMIT ' + limit);
}
}
return [];
}
log(level, ...object) {
if (this.level() < level || !this.targets_.length) return;
let levelString = '';
if (this.level() == Logger.LEVEL_INFO) levelString = '[info] ';
if (this.level() == Logger.LEVEL_WARN) levelString = '[warn] ';
if (this.level() == Logger.LEVEL_ERROR) levelString = '[error] ';
let line = moment().format('YYYY-MM-DD HH:mm:ss') + ': ' + levelString;
let line = moment().format('YYYY-MM-DD HH:mm:ss') + ': ';
if (level == Logger.LEVEL_WARN) levelString += '[warn] ';
if (level == Logger.LEVEL_ERROR) levelString += '[error] ';
for (let i = 0; i < this.targets_.length; i++) {
let target = this.targets_[i];
@ -84,27 +103,23 @@ class Logger {
if (level = Logger.LEVEL_ERROR) fn = 'error';
if (level = Logger.LEVEL_WARN) fn = 'warn';
if (level = Logger.LEVEL_INFO) fn = 'info';
if (typeof object === 'object') {
console[fn](line, object);
} else {
console[fn](line + object);
}
console[fn](line + this.objectsToString(...object));
} else if (target.type == 'file') {
let serializedObject = this.objectToString(object);
let serializedObject = this.objectsToString(...object);
Logger.fsDriver().appendFileSync(target.path, line + serializedObject + "\n");
} else if (target.type == 'vorpal') {
target.vorpal.log(object);
target.vorpal.log(...object);
} else if (target.type == 'database') {
let msg = this.objectToString(object);
let msg = this.objectsToString(...object);
target.database.exec('INSERT INTO logs (`source`, `level`, `message`, `timestamp`) VALUES (?, ?, ?, ?)', [target.source, level, msg, time.unixMs()]);
}
}
}
error(object) { return this.log(Logger.LEVEL_ERROR, object); }
warn(object) { return this.log(Logger.LEVEL_WARN, object); }
info(object) { return this.log(Logger.LEVEL_INFO, object); }
debug(object) { return this.log(Logger.LEVEL_DEBUG, object); }
error(...object) { return this.log(Logger.LEVEL_ERROR, ...object); }
warn(...object) { return this.log(Logger.LEVEL_WARN, ...object); }
info(...object) { return this.log(Logger.LEVEL_INFO, ...object); }
debug(...object) { return this.log(Logger.LEVEL_DEBUG, ...object); }
static levelStringToId(s) {
if (s == 'none') return Logger.LEVEL_NONE;

9
ReactNativeClient/lib/react-logger.js vendored Normal file
View File

@ -0,0 +1,9 @@
import { Logger } from 'lib/logger.js';
class ReactLogger extends Logger {
}
export { ReactLogger }

View File

@ -18,6 +18,10 @@ let time = {
return moment.unix(ms / 1000).utc().format('YYYY-MM-DDTHH:mm:ss.SSS') + 'Z';
},
unixMsToIsoSec(ms) {
return moment.unix(ms / 1000).utc().format('YYYY-MM-DDTHH:mm:ss') + 'Z';
},
msleep(ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {

View File

@ -8,6 +8,7 @@ import { StackNavigator } from 'react-navigation';
import { addNavigationHelpers } from 'react-navigation';
import { shim } from 'lib/shim.js';
import { Log } from 'lib/log.js'
import { Logger } from 'lib/logger.js'
import { Note } from 'lib/models/note.js'
import { Folder } from 'lib/models/folder.js'
import { Resource } from 'lib/models/resource.js'
@ -16,6 +17,7 @@ import { NoteTag } from 'lib/models/note-tag.js'
import { BaseItem } from 'lib/models/base-item.js'
import { BaseModel } from 'lib/base-model.js'
import { JoplinDatabase } from 'lib/joplin-database.js'
import { Database } from 'lib/database.js'
import { ItemList } from 'lib/components/item-list.js'
import { NotesScreen } from 'lib/components/screens/notes.js'
import { NotesScreenUtils } from 'lib/components/screens/notes-utils.js'
@ -23,6 +25,7 @@ import { NoteScreen } from 'lib/components/screens/note.js'
import { FolderScreen } from 'lib/components/screens/folder.js'
import { FoldersScreen } from 'lib/components/screens/folders.js'
import { LoginScreen } from 'lib/components/screens/login.js'
import { LogScreen } from 'lib/components/screens/log.js'
import { LoadingScreen } from 'lib/components/screens/loading.js'
import { OneDriveLoginScreen } from 'lib/components/screens/onedrive-login.js'
import { Setting } from 'lib/models/setting.js'
@ -45,7 +48,7 @@ let defaultState = {
};
const reducer = (state = defaultState, action) => {
Log.info('Reducer action', action.type);
reg.logger().info('Reducer action', action.type);
let newState = state;
@ -58,8 +61,8 @@ const reducer = (state = defaultState, action) => {
const currentRoute = r.length ? r[r.length - 1] : null;
const currentRouteName = currentRoute ? currentRoute.routeName : '';
Log.info('Current route name', currentRouteName);
Log.info('New route name', action.routeName);
reg.logger().info('Current route name', currentRouteName);
reg.logger().info('New route name', action.routeName);
newState = Object.assign({}, state);
@ -178,7 +181,7 @@ const reducer = (state = defaultState, action) => {
}
// Log.info('newState.selectedFolderId', newState.selectedFolderId);
// reg.logger().info('newState.selectedFolderId', newState.selectedFolderId);
return newState;
}
@ -193,6 +196,7 @@ const AppNavigator = StackNavigator({
Login: { screen: LoginScreen },
Loading: { screen: LoadingScreen },
OneDriveLogin: { screen: OneDriveLoginScreen },
Log: { screen: LogScreen },
});
class AppComponent extends React.Component {
@ -233,6 +237,16 @@ class AppComponent extends React.Component {
}
}
Setting.setConstant('appId', 'net.cozic.joplin-android');
Setting.setConstant('appType', 'mobile');
const logDatabase = new Database(new DatabaseDriverReactNative());
await logDatabase.open({ name: 'log.sqlite' });
await logDatabase.exec(Logger.databaseCreateTableSql());
reg.logger().addTarget('database', { database: logDatabase, source: 'm' });
reg.logger().info('Starting application' + Setting.value('appId'));
let db = new JoplinDatabase(new DatabaseDriverReactNative());
reg.setDb(db);
@ -247,8 +261,8 @@ class AppComponent extends React.Component {
BaseItem.loadClass('NoteTag', NoteTag);
try {
await db.open({ name: '/storage/emulated/0/Download/joplin-48.sqlite' })
Log.info('Database is ready.');
await db.open({ name: 'joplin-50.sqlite' })
reg.logger().info('Database is ready.');
//await db.exec('DELETE FROM notes');
//await db.exec('DELETE FROM folders');
@ -257,14 +271,12 @@ class AppComponent extends React.Component {
//await db.exec('DELETE FROM resources');
//await db.exec('DELETE FROM deleted_items');
Log.info('Loading settings...');
reg.logger().info('Loading settings...');
await Setting.load();
Setting.setConstant('appId', 'net.cozic.joplin-android');
Setting.setConstant('appType', 'mobile');
Setting.setConstant('resourceDir', RNFetchBlob.fs.dirs.DocumentDir);
Log.info('Loading folders...');
reg.logger().info('Loading folders...');
let folders = await Folder.all();